Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unresolved external symbol when creating a DLL

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.

like image 372
qdii Avatar asked Mar 30 '12 21:03

qdii


People also ask

How do I fix unresolved external symbol?

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.

What causes unresolved external symbol?

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.

How do I fix unresolved external symbol lnk2001?

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 .

What is unresolved external error?

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.


2 Answers

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

  • Export Directory
  • Import Directory
  • Import Address Table Directory
  • Base Relocation Directory
  • Resource Directory

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
like image 107
Oleg Avatar answered Oct 03 '22 13:10

Oleg


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

Note:

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).

like image 28
Luchian Grigore Avatar answered Oct 03 '22 13:10

Luchian Grigore