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

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

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.

Related

Undefined reference to `WinMain` [duplicate]

When I try to build a program using Eclipse CDT, I get the following:
/mingw/lib/libmingw32.a(main.o):main.c:(.text+0x106):
undefined reference to `WinMain#16
Why is that? And, how can I solve this issue?
This error occurs when the linker can't find WinMain function, so it is probably missing. In your case, you are probably missing main too.
Consider the following Windows API-level program:
#define NOMINMAX
#include <windows.h>
int main()
{
MessageBox( 0, "Blah blah...", "My Windows app!", MB_SETFOREGROUND );
}
Now let's build it using GNU toolchain (i.e. g++), no special options. Here gnuc is just a batch file that I use for that. It only supplies options to make g++ more standard:
C:\test> gnuc x.cpp
C:\test> objdump -x a.exe | findstr /i "^subsystem"
Subsystem 00000003 (Windows CUI)
C:\test> _
This means that the linker by default produced a console subsystem executable. The subsystem value in the file header tells Windows what services the program requires. In this case, with console system, that the program requires a console window.
This also causes the command interpreter to wait for the program to complete.
Now let's build it with GUI subsystem, which just means that the program does not require a console window:
C:\test> gnuc x.cpp -mwindows
C:\test> objdump -x a.exe | findstr /i "^subsystem"
Subsystem 00000002 (Windows GUI)
C:\test> _
Hopefully that's OK so far, although the -mwindows flag is just semi-documented.
Building without that semi-documented flag one would have to more specifically tell the linker which subsystem value one desires, and some Windows API import libraries will then in general have to be specified explicitly:
C:\test> gnuc x.cpp -Wl,-subsystem,windows
C:\test> objdump -x a.exe | findstr /i "^subsystem"
Subsystem 00000002 (Windows GUI)
C:\test> _
That worked fine, with the GNU toolchain.
But what about the Microsoft toolchain, i.e. Visual C++?
Well, building as a console subsystem executable works fine:
C:\test> msvc x.cpp user32.lib
x.cpp
C:\test> dumpbin /headers x.exe | find /i "subsystem" | find /i "Windows"
3 subsystem (Windows CUI)
C:\test> _
However, with Microsoft's toolchain building as GUI subsystem does not work by default:
C:\test> msvc x.cpp user32.lib /link /subsystem:windows
x.cpp
LIBCMT.lib(wincrt0.obj) : error LNK2019: unresolved external symbol _WinMain#16 referenced in function ___tmainCRTStartu
p
x.exe : fatal error LNK1120: 1 unresolved externals
C:\test> _
Technically this is because Microsoft’s linker is non-standard by default for GUI subsystem. By default, when the subsystem is GUI, then Microsoft's linker uses a runtime library entry point, the function where the machine code execution starts, called winMainCRTStartup, that calls Microsoft's non-standard WinMain instead of standard main.
No big deal to fix that, though.
All you have to do is to tell Microsoft's linker which entry point to use, namely mainCRTStartup, which calls standard main:
C:\test> msvc x.cpp user32.lib /link /subsystem:windows /entry:mainCRTStartup
x.cpp
C:\test> dumpbin /headers x.exe | find /i "subsystem" | find /i "Windows"
2 subsystem (Windows GUI)
C:\test> _
No problem, but very tedious. And so arcane and hidden that most Windows programmers, who mostly only use Microsoft’s non-standard-by-default tools, do not even know about it, and mistakenly think that a Windows GUI subsystem program “must” have non-standard WinMain instead of standard main. In passing, with C++0x Microsoft will have a problem with this, since the compiler must then advertize whether it's free-standing or hosted (when hosted it must support standard main).
Anyway, that's the reason why g++ can complain about WinMain missing: it's a silly non-standard startup function that Microsoft's tools require by default for GUI subsystem programs.
But as you can see above, g++ has no problem with standard main even for a GUI subsystem program.
So what could be the problem?
Well, you are probably missing a main. And you probably have no (proper) WinMain either! And then g++, after having searched for main (no such), and for Microsoft's non-standard WinMain (no such), reports that the latter is missing.
Testing with an empty source:
C:\test> type nul >y.cpp
C:\test> gnuc y.cpp -mwindows
c:/program files/mingw/bin/../lib/gcc/mingw32/4.4.1/../../../libmingw32.a(main.o):main.c:(.text+0xd2): undefined referen
ce to `WinMain#16'
collect2: ld returned 1 exit status
C:\test> _
To summarize the above post by Cheers and hth. - Alf, Make sure you have main() or WinMain() defined and g++ should do the right thing.
My problem was that main() was defined inside of a namespace by accident.
I was encountering this error while compiling my application with SDL. This was caused by SDL defining it's own main function in SDL_main.h. To prevent SDL define the main function an SDL_MAIN_HANDLED macro has to be defined before the SDL.h header is included.
Try saving your .c file before building. I believe your computer is referencing a path to a file with no information inside of it.
My situation was that I did not have a main function.
Had same problem. To fix it I clicked save to save my .c file before building. I believe my computer was referencing a path to a file with no information inside of it.
Check that All Files are Included in Your Project:
I had this same error pop up after I updated cLion. After hours of tinkering, I noticed one of my files was not included in the project target. After I added it back to the active project, I stopped getting the undefined reference to winmain16, and the code compiled.
Edit: It's also worthwhile to check the build settings within your IDE.
(Not sure if this error is related to having recently updated the IDE - could be causal or simply correlative. Feel free to comment with any insight on that factor!)

How to link Windows universal CRT to obj file compiled with nasm

Environment: Windows 10. I happen to be using a MinGW version of ld for linking, but I'm happy to use the visual studio link.exe if it makes things simpler.
I have the following basic program in nasm:
global _main
extern _printf
section .text
_main:
push message
call _printf
add esp, 4
ret
message:
db 'Hello, World', 10, 0
And it builds fine using
nasm -f win32 test.nasm
When trying to link it to the windows CRT (ucrt.lib), I get the following error:
$ ld -o test.exe test.obj
test.obj:test.nasm:(.text+0x6): undefined reference to `printf'
Ok, so I need to point the linker at the ucrt library:
$ ld -o test.exe /c/Program\ Files\ \(x86\)/Windows\
Kits/10/Lib/10.0.14393.0/ucrt/x86/ucrt.lib test.obj
test.obj:test.nasm:(.text+0x6): undefined reference to `printf'
Trying the equivalent with the visual studio linker:
d:\Code\nasm>link -out:test.exe -entry:main -subsystem:console test.obj
Microsoft (R) Incremental Linker Version 14.10.25017.0
Copyright (C) Microsoft Corporation. All rights reserved.
test.obj : error LNK2001: unresolved external symbol _printf
test.exe : fatal error LNK1120: 1 unresolved externals
This raises a couple of questions:
Why does one of these try to find printf and the other, _printf? I get that there's an underscore convention, but it doesn't seem to be understood by both linkers.
I used objdump -t to look at the symbols in the ucrt.lib file. I won't paste the entire list, but it contains entries such as:
[ 4](sec 1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 __imp____conio_common_vcprintf
[ 5](sec 3)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 ___conio_common_vcprintf
[ 4](sec 1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 __imp____conio_common_vcprintf_p
Neither printf nor _printf appears in the list. Does this mean it's not exported by that library? If not, which library should I be linking?
According to this MS article, ucrt.lib is the de-facto library for the c runtime and C standard library.
Thanks to a comment from Michael Petch, it looks like I need to manually link 1 or more extra libs which are in a completely separate location from the ucrt.lib library. The relevant one for printf is legacy_stdio_definitions.lib, which I found deep in a sub-dir of my VS2017 install directory, as opposed to the ucrt.lib which is in the Windows SDK install directory.
The definition of printf is provided inline in stdio.h unless the macro _NO_CRT_STDIO_INLINE is defined, which I guess it usually is when building inside the VS toolchain.

Clang, Microsoft linker and standard library

I have successfully built Clang with Microsoft C++ and I'm trying to get it to compile a hello world test case; it gets as far as generating an object file, linking with the standard library being the remaining stumbling block:
hello-202520.o : error LNK2019: unresolved external symbol _printf referenced in function _main
LINK : error LNK2001: unresolved external symbol _mainCRTStartup
There are comments from previous years saying Clang doesn't yet do Windows linking at all, but I get the impression those are outdated, and indeed it does now seem to be able to generate Windows format object files:
clang -c hello.c
ren hello.o hello.obj
link hello.obj
... doesn't barf, so the file format seems to be correct, but still gets the unresolved external symbol errors. Likely guess is the Microsoft compiler tags its output object files with an indication of the standard library they need to be linked with whereas Clang doesn't, and eyeballing a hex dump of the respective object files seems to confirm this and gives a hint of the linker command line to use:
link /defaultlib:libcmt /defaultlib:oldnames hello.obj
I had high hopes by this stage but alas it still gives the same unresolved external symbol errors.
What am I still missing?
Turns out Clang was by default generating 32-bit code but I was using 64-bit MSC, and the leading _ on C symbols has been dropped with the move to x64, so specifying -m64 on the clang command line did the job.

GetUserName under Win64

I am trying to compile a C program using the Intel icl compiler under MINGW/64. The program uses the following code:
#include <Userenv.h>
HANDLE process;
HANLDE token;
GetUserProfileDirectory(process, TOKEN_QUERY, &ptoken)
I am using the following compile command:
$ icl -g -DMINGW32 -DTESTMAIN user.c -o user -UserEnv.Lib
and I am linking against the UserEnv.Lib from the Microsoft SDK.
Intel(R) C++ Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version Microsoft (R) Incremental Linker Version 9.00.21022.08
-out:user.exe
user.obj
user.obj : error LNK2019: unresolved external symbol __imp_GetUserProfileDirectoryA referenced in function main
Any idea how to solve this problem?
SOLUTION:
The solution is to use
/link /c/Program\ Files/Microsoft\ SDKs/Windows/v6.0A/Lib/x64/UserEnv.Lib /c/Program\ Files/Microsoft\ SDKs/Windows/v6.0A/Lib/x64/A
I copied the file UserEnv.lib from the Microsoft SDK (x64, 6.0) into the current working directory and compiled the program with
$ icl test.c -DMINGW32 ./UserEnv.Lib
LNK2019: unresolved external symbol __imp_GetUserNameA referenced in function main test.obj : error LNK2019: unresolved external symbol __imp_OpenProcessToken referenced in function main test.obj : error LNK2019: unresolved external symbol –
and I am still getting the unresolved symbols.
Olaf
You need to add userenv.lib to your input libraries for the linker to see GetUserProfileDirectory().
EDIT: It's been a while since I've touched an Intel compiler, but IIRC you're supposed to use /link to introduce linker options:
$ icl test.c -DMINGW32 /link ./UserEnv.Lib

Solaris linker equivalent to the GNU LD --export-dynamic flag

Like the question says: We are building on Linux using the GNU linker, and on Solaris using the solaris ld. GNU ld supports the --export-dynamic flag, which:
When creating a dynamically linked executable, add all symbols to the dynamic
symbol table. The dynamic symbol table is the set of symbols which are visible
from dynamic objects at run time.
What is the equivalent to this flag using the solaris linker? Is there an equivalent?
The Sun Studio linker (ld), by default, exports all symbols.
You can find the complete reference for the Sun linker on docs.sun.com.
Search for the "Linker and Libraries Guide".
By "all symbols" you mean all global symbols, right? C file-static symbols
are not promoted to global right? I don't think that would work.

Resources