A friend of mine gets a bunch of error when creating a DLL. Visual Studio complains about unresolved external symbols. I am mainly an Unix user so I might be mistaken there. On Unix, when you create a static library (an archive), it does not do much more than concatenating the different object files into an archive file. I would expect dynamic objects to be created the same way, but apparently, an extra link stage takes place.
First question: Why is there a link stage for the dll?
In this case, the DLL indeeds contains undefined symbols because we expect the DLL to find those symbols within the EXE file. This is quite opposite the typical DLL behaviour, where an EXE uses the symbols defined in the DLL. To make it clear, I expect those symbols to be found right when the DLL is loaded in memory.
Second question: How can I make a DLL use symbols defined in the EXE file?
EDIT: I reformulated the question, as I think I did not state the problem clearly enough.
So when we try to assign it a value in the main function, the linker doesn't find the symbol and may result in an “unresolved external symbol” or “undefined reference”. The way to fix this error is to explicitly scope the variable using '::' outside the main before using it.
The compiler can identify when a symbol isn't declared, but it can't tell when the symbol isn't defined. That's because the definition may be in a different source file or library. If a symbol is referred to but never defined, the linker generates an unresolved external symbol error.
To fix this issue, add the /NOENTRY option to the link command. This error can occur if you use incorrect /SUBSYSTEM or /ENTRY settings in your project. For example, if you write a console application and specify /SUBSYSTEM:WINDOWS, an unresolved external error is generated for WinMain .
Answer. Unresolved external references occur when the symbol for a function or global variable is referenced in a program, but none of the object files or libraries specified in the link step contain a definition for that symbol.
You described the origin of your problem as: "I want the DLL to import some symbols from the exe file" in the comment to the answer of Luchian Grigore. You wrote additionally in the text of your question that you want "the DLL to find those symbols within the EXE file. we expect the DLL to find those symbols within the EXE file."
Mostly it's design question whether to export functions or data from the exe or not. Typically one creates export only from DLL. If EXE need to provide some information to DLL it provide the information by parameters. For example you an call in EXE some function MyFunc
implemented and exported in the DLL. As additional parameter of MyFunc
you get context
pointer which can get DLL directly or indirectly all information for EXE which needed.
In some seldom situations you do can export data or functions from EXE. For example you an use DumpBin.exe
utility (just start "Visual Studio Command Prompt (2010)" to use it) to verify that Outlook.exe exports
DumpBin.exe /exports "C:\Program Files\Microsoft Office\Office14\OUTLOOK.EXE"
File Type: EXECUTABLE IMAGE
Section contains the following exports for outlook.exe
00000000 characteristics
4E79B6C8 time date stamp Wed Sep 21 12:04:56 2011
0.00 version
1 ordinal base
66 number of functions
66 number of names
ordinal hint RVA name
1 0 00B58A88 CleanupAddressComponents
2 1 00B58A88 CleanupNameComponents
3 2 00228DC4 DllCanUnloadNow
4 3 004848F8 DllGetClassObject
...
65 40 0038EF30 UpdateContactTracker
66 41 00902788 dwIsLoggingEnabled
I can explain how you can implement the scenario without long discussion when and whether you really should do this.
First of all the LIB file contains OBJ files which has another format as Program Executable (PE). During compilation different common section will be placed in OBJ file. It's very important, that program executable (EXE or DLL) contains not only from the code, but it has many additional information in the header part of the PE. The most important are
You can use DumpBin.exe
utility (just start "Visual Studio Command Prompt (2010)" to easy use it). To see information about the headers you can use DumpBin.exe /headers my.exe
. To see contain of the Export Directory you can use DumpBin.exe /exports my.exe
and so on.
If you compile DLL which exports some functions or data the LIB file will be additionally created. It's so named import library. If you use the LIB in your EXE project which uses some functions or data from the DLL the linker will resolve the external references and places in import directory of EXE information about the functions which should be resolved at the load time.
So the import library contains only the templates for filling Import Directory and Import Address Table Directory in EXE.
In general one can in the same way export some data of functions from EXE, create LIB, uses the LIB in the DLL project and in the way implement importing some information in DLL from EXE.
I made the demo project which demonstrate the way. Please, read carefully the compilation instructions at the end of my answer if you want delete all LIBs from the Project and create all yourself. The code of ExportFromExe.c
(the EXE):
//#define CREATE_IMPORT_LIBRARY_ONLY
#include <Windows.h>
EXTERN_C __declspec(dllexport) int someData = 0;
EXTERN_C __declspec(dllexport) int __stdcall myFunc (int x);
EXTERN_C __declspec(dllexport) int __stdcall MyFunc();
int __stdcall myFunc (int x)
{
return x + 10;
}
#ifndef _DEBUG
int mainCRTStartup()
#else
int main()
#endif
{
someData = 5;
#ifndef CREATE_IMPORT_LIBRARY_ONLY
return MyFunc();
#endif
}
The code of MyDll.c
(the DLL):
#include <Windows.h>
EXTERN_C __declspec(dllexport) int myData = 3;
EXTERN_C __declspec(dllimport) int someData;
EXTERN_C __declspec(dllimport) int __stdcall myFunc (int x);
#ifndef _DEBUG
EXTERN_C BOOL WINAPI _DllMainCRTStartup (HINSTANCE hinstDLL, DWORD fdwReason,
LPVOID lpvReserved)
#else
BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
#endif
{
if (fdwReason == DLL_PROCESS_ATTACH)
DisableThreadLibraryCalls(hinstDLL);
return TRUE;
UNREFERENCED_PARAMETER (lpvReserved);
}
EXTERN_C __declspec(dllexport) int WINAPI MyFunc()
{
return someData + myFunc(myData);
}
To be able to create the project successfully at the first time we have to solve the problem: "who was first: the chicken or the egg?" because the EXE project depend from MyDll.lib
and the DLL project depends from ExportFromExe.lib
. For the first compilation of EXE we can temoprary remove $(OutDir)MyDll.lib
from the linker setting of EXE project and define CREATE_IMPORT_LIBRARY_ONLY
. As the result we'll create ExportFromExe.exe
and ExportFromExe.lib
. In more large projects one can use Undefined Symbol Only (/FORCE:UNRESOLVED)
option of linker instead. Then we can build MyDll
project which creates MyDll.dll
and MyDll.lib
. Now you can remove CREATE_IMPORT_LIBRARY_ONLY
from the EXE and include $(OutDir)MyDll.lib
as the linker setting ("Additional Depandencies" in "Input" part of settings). The next build of EXE project produce the final solution.
I used some small tricks to remove C-Runtime and reduce the size of EXE and DLL to 2,5 or 3 KB. So you can use /all
switch of DumpBin.exe
to examine full information from EXE and DLL inclusive RAW binary data.
Because the EXE return results as ERRORLEVEL you can test the application in the commend prompt:
echo %ERRORLEVEL%
0
ExportFromExe.exe
echo %ERRORLEVEL%
18
First question: Why is there a link stage for the dll ?
Because that's the way it is. There's a linking stage everytime you want to create a binary. The symbols need to be resolved somehow, right?
Second question: How can I do that ?
You add the lib
file that is generated alongside the dll
to the Additional Dependencies
from your project - Properties -> Configuration Properties -> Linker -> Input
In case you don't already do this, in order to be exported to the lib
, symbols must be declared with _declspec(dllexport)
. When you include the headers, you tell the compiler that those symbols are to be imported with _declspec(dllimport)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With