Andreas Andreas - 1 month ago 10
C Question

Visual C linker behaves differently on 32-bit and 64-bit

Take a look at the following example code:

__declspec(dllexport) FreeLibrary(void)
{
}


I build it as a DLL with the following little script:

cl /EHsc /MT /c test.c /Fotest.o
link /dll /out:test.dll test.o


This works fine when compiling for the 32-bit architecture. When compiling for the 64-bit architecture, however, the DLL isn't built and I get the following error:

Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.

kernel32.lib(KERNEL32.dll) : error LNK2005: FreeLibrary already defined in test.o
Creating library test.lib and object test.exp
test.dll : fatal error LNK1169: one or more multiply defined symbols found


Why is that? Why does it work on 32-bit but not on 64-bit? Shouldn't the
FreeLibrary
symbol be defined in the 32-bit
kernel32.dll
as well? But why doesn't the 32-bit linker complain then and builds the DLL just fine?

This is really confusing to me...

Answer

Edited to correct error spotted by IInspectable and for clarity

32-bit

The default calling convention is __cdecl, which causes the symbol name to be prepended with an underscore: _FreeLibrary.

>dumpbin /symbols test.o | find "FreeLibrary"
008 00000000 SECT3  notype ()    External     | _FreeLibrary

The FreeLibrary in kernel32.dll is declared with WINAPI. In 32-bit, WINAPI expands to __stdcall, so kernel32's function is named _FreeLibrary@4.

>dumpbin /exports kernel32.lib | find "FreeLibrary"
    _FreeLibrary@4
    _FreeLibraryAndExitThread@8
    _FreeLibraryWhenCallbackReturns@8

Since _FreeLibrary doesn't match _FreeLibrary@4, there's no conflict.

64-bit

The default calling convention is a four-register fastcall scheme that does not decorate the name of a plain C function. Thus test.o defines a symbol named FreeLibrary:

>dumpbin /symbols test.o | find "FreeLibrary"
008 00000000 SECT3  notype ()    External     | FreeLibrary

Also, the WINAPI macro expands to nothing, so kernel32.dll uses the same default calling convention as your plugin code. Thus it gets the same, unadorned symbol:

>dumpbin /exports kernel32.lib | find "FreeLibrary"
    FreeLibrary
    FreeLibraryAndExitThread
    FreeLibraryWhenCallbackReturns

This gives you two FreeLibrary symbols and results in the linker error.

Comments