Andreas Andreas - 9 months ago 43
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
symbol be defined in the 32-bit
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 Source

Edited to correct error spotted by IInspectable and for clarity


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"

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


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"

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