6

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.

qdii
  • 12,505
  • 10
  • 59
  • 116

2 Answers2

12

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
Oleg
  • 220,925
  • 34
  • 403
  • 798
  • +1. Thanks for taking the time to understand the problem I am having, and for answering it with so many details and examples. You’ll definitely get the response point. – qdii Apr 01 '12 at 09:17
  • @qdii: You are welcome! There are so many options which one have with exporting. There are too many aspects which are important for understanding of the problem, but I didn't wanted to write a book here :-). I just tried to stay short and to explains some background information and show only one possibility on the working example. I'am glad to hear that I could help you to understand and to solve your problem. – Oleg Apr 01 '12 at 09:28
  • An excellent answer, and a shame to see it languishing with only 4 upvotes. So now it has a fifth. :) – Jules Apr 14 '13 at 13:53
  • @Jules: I'm glad that you find my answer interesting. – Oleg Apr 14 '13 at 14:16
3

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

Community
  • 1
  • 1
Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • I think he's asking how to call functions in the exe from the DLL, not how to call the DLL functions from the exe. – tinman Mar 30 '12 at 21:31
  • @LuchianGrigore But there is no linking stage when you create a static library file. The object files are just concatenated. Yet it’s a binary file. – qdii Mar 30 '12 at 21:31
  • @tinman: yes, exactly. I want the DLL to import some symbols from the exe file. – qdii Mar 30 '12 at 21:32
  • @qdii if you statically link your dll to another library, that doesn't make it a static library. – Luchian Grigore Mar 30 '12 at 21:33
  • 1
    @qdii it's the same thing. There really isn't that much difference between an exe and a dll, other than the entry point and that the exe can be ran. The same principle applies. – Luchian Grigore Mar 30 '12 at 21:34
  • @LuchianGrigore: I didn’t mean to link a dynamic library to a static library. I don’t even know what this means. I just wanted to point out that what you said: "There's a linking stage everytime you want to create a binary" is wrong. When creating an archive file (library.a) there is no linking stage, and it is a binary yet. I would have expected DLL files to work the same, and I can’t figure why it would not. – qdii Mar 30 '12 at 21:44
  • @LuchianGrigore: I can see one difference between an exe and a dll: the first is runnable, so its symbols must be linked. The second is imported, so all it needs is an export table. I expect the linking to take place when loading the DLL, i.e. on dlopen() or the windows equivalent. Which is why I can’t seem to understand why the linking is done so early. – qdii Mar 30 '12 at 21:46
  • @qdii you can either load libraries programatically (in which case you don't need to link) and you won't get errors (via LoadLibrary() or similar). But since you're getting errors, you're including headers and using symbols from a library but not importing it. – Luchian Grigore Mar 30 '12 at 21:47
  • @qdii: If you just need to allow to import some "symbols" from the EXE you can export there not only from DLL. For example if you start `dumpbin /exports WINWORD.EXE` you will see that WinWord exports some function. If you start `dumpbin /exports "C:\Program Files\Microsoft Office\Office14\outlook.EXE"` you will see many "symbols" which are exported. – Oleg Mar 30 '12 at 21:47
  • @qdii: Could you explain what you want to export from the EXE? – Oleg Mar 30 '12 at 22:05
  • @Oleg I think my question was not stated clearly enough. Guys on IRC shed some light. I want the DLL to use symbols from the EXE. – qdii Mar 30 '12 at 22:24
  • @LuchianGrigore: I reformulated my question, as I think it was really badly stated in the first place. – qdii Mar 30 '12 at 23:02