Skip to content

Link error when calling Windows API functions from function in C static library #2073

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
ghost opened this issue Mar 16, 2019 · 7 comments
Labels
bug Observed behavior contradicts documented or intended behavior enhancement Solving this issue will likely involve adding new logic or components to the codebase. os-windows
Milestone

Comments

@ghost
Copy link

ghost commented Mar 16, 2019

C code (compiled as static library in visual studio 2017):

#include <Windows.h>
void load_library() {
	LoadLibrary("opengl32.dll");
}

Zig code (clibtesting.zig):

extern fn load_library() void;
pub fn main() void {
    load_library();
}

Build command:
zig build-exe clibtesting.zig --library clib.lib

Error:

lld: error: undefined symbol: __imp_LoadLibraryA
referenced by c:\users\dabbo\programming\zig\clibtesting\clib\clib\clib.c:3
clib.lib(clib.obj):(load_library)

When running zig build-exe a kernel32.def file is created which lists some Windows API functions.

LIBRARY kernel32
EXPORTS
ExitProcess
GetModuleFileNameW
GetLastError
GetStdHandle
SetFilePointerEx
GetCurrentDirectoryW
GetFileSizeEx
GetModuleHandleW
GetConsoleScreenBufferInfo
SetConsoleTextAttribute
ReadFile
HeapReAlloc
CreateFileW
GetEnvironmentVariableW
WriteFile
HeapCreate
HeapDestroy
HeapAlloc
CloseHandle
GetFileInformationByHandleEx
GetConsoleMode

If the C code is changed to call any of those functions, the program links fine.

Changing the C code to call OutputDebugStringA results in lld: error: undefined symbol: __imp_OutputDebugStringA
Changing the C code to call FreeLibrary results in lld: error: undefined symbol: __imp_FreeLibrary

@andrewrk andrewrk added this to the 0.5.0 milestone Mar 16, 2019
@andrewrk
Copy link
Member

Quick note: it should actually be --object clib.lib for static libraries. Sorry about the poor naming choice; I'll get that sorted out soon.

Here's what's going on:

When you call an extern Windows function from Zig code, Zig keeps track of it and creates that .def file that you pointed out. This will enumerate all the functions that are called, and the corresponding .lib file that is created will be sufficient.

However there is no way for Zig to find out about what external function calls from C code should be added to .def files. So there are two workarounds:

Link libc. --library c. This will use kernel32.lib provided by msvc which has all the functions including LoadLibraryA.

Or, you can enumerate all the external DLL calls that the C code uses, in your Zig source, like this:

extern "kernel32" stdcallcc fn LoadLibraryA() void;

comptime {
    _ = LoadLibraryA;
}

The function prototype doesn't even have to be correct, just the "kernel32" and the name. The comptime block is to "poke" the extern declarations; otherwise Zig wouldn't include them in the .def because they were unused.

A better long term solution will probably come from #514 when we solve the libc on Windows. Probably Zig will simply ship with a complete .def file for kernel32.dll, ntdll.dll, etc.

@ghost
Copy link
Author

ghost commented Mar 17, 2019

Linking the C library caused even more errors which is odd as I've linked the C library before in a zig program without issue so something must have changed.
These linker errors don't seem to be connected to linking with the C static library.

a.zig:

const warn = @import("std").debug.warn;
pub fn main() void {
    warn("123\n");
}

Compiled with:

zig build-exe a.zig --library c

Results in these linker errors:

lld: error: undefined symbol: __imp_RtlCaptureContext
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\gs\gs_report.c:68
>>>               msvcrtd.lib(gs_report.obj):(capture_current_context)
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\gs\gs_report.c:96
>>>               msvcrtd.lib(gs_report.obj):(capture_previous_context)


lld: error: undefined symbol: __imp_RtlLookupFunctionEntry
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\gs\gs_report.c:71
>>>               msvcrtd.lib(gs_report.obj):(capture_current_context)
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\gs\gs_report.c:103
>>>               msvcrtd.lib(gs_report.obj):(capture_previous_context)


lld: error: undefined symbol: __imp_RtlVirtualUnwind
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\gs\gs_report.c:75
>>>               msvcrtd.lib(gs_report.obj):(capture_current_context)
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\gs\gs_report.c:107
>>>               msvcrtd.lib(gs_report.obj):(capture_previous_context)


lld: error: undefined symbol: __imp_UnhandledExceptionFilter
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\gs\gs_report.c:147
>>>               msvcrtd.lib(gs_report.obj):($LN3)


lld: error: undefined symbol: __imp_SetUnhandledExceptionFilter
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\gs\gs_report.c:146
>>>               msvcrtd.lib(gs_report.obj):($LN3)


lld: error: undefined symbol: __imp_GetCurrentProcess
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\gs\gs_report.c:160
>>>               msvcrtd.lib(gs_report.obj):($LN3)


lld: error: undefined symbol: __imp_TerminateProcess
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\gs\gs_report.c:160
>>>               msvcrtd.lib(gs_report.obj):($LN3)


lld: error: undefined symbol: IsProcessorFeaturePresent
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\gs\gs_report.c:463
>>>               msvcrtd.lib(gs_report.obj):($LN4)
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\gs\gs_report.c:339
>>>               msvcrtd.lib(gs_report.obj):($LN9)
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\gs\gs_report.c:218
>>>               msvcrtd.lib(gs_report.obj):($LN4)


lld: error: undefined symbol: __imp_QueryPerformanceCounter
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\gs\gs_support.c:113
>>>               msvcrtd.lib(gs_support.obj):(__get_entropy)


lld: error: undefined symbol: __imp_GetCurrentProcessId
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\gs\gs_support.c:104
>>>               msvcrtd.lib(gs_support.obj):(__get_entropy)


lld: error: undefined symbol: __imp_GetCurrentThreadId
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\gs\gs_support.c:103
>>>               msvcrtd.lib(gs_support.obj):(__get_entropy)


lld: error: undefined symbol: __imp_GetSystemTimeAsFileTime
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\gs\gs_support.c:95
>>>               msvcrtd.lib(gs_support.obj):(__get_entropy)


lld: error: undefined symbol: __imp_InitializeSListHead
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\eh\tncleanup.cpp:17
>>>               msvcrtd.lib(tncleanup.obj):($LN3)


lld: error: undefined symbol: __imp_RtlCaptureContext
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\utility\utility_desktop.cpp:189
>>>               msvcrtd.lib(utility_desktop.obj):($LN8)


lld: error: undefined symbol: __imp_RtlLookupFunctionEntry
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\utility\utility_desktop.cpp:194
>>>               msvcrtd.lib(utility_desktop.obj):($LN8)


lld: error: undefined symbol: __imp_RtlVirtualUnwind
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\utility\utility_desktop.cpp:200
>>>               msvcrtd.lib(utility_desktop.obj):($LN8)


lld: error: undefined symbol: __imp_IsDebuggerPresent
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\utility\utility_desktop.cpp:221
>>>               msvcrtd.lib(utility_desktop.obj):($LN8)


lld: error: undefined symbol: __imp_UnhandledExceptionFilter
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\utility\utility_desktop.cpp:227
>>>               msvcrtd.lib(utility_desktop.obj):($LN8)


lld: error: undefined symbol: __imp_SetUnhandledExceptionFilter
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\utility\utility_desktop.cpp:99
>>>               msvcrtd.lib(utility_desktop.obj):($LN3)
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\utility\utility_desktop.cpp:226
>>>               msvcrtd.lib(utility_desktop.obj):($LN8)


lld: error: undefined symbol: __imp_GetStartupInfoW
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\utility\utility_desktop.cpp:24
>>>               msvcrtd.lib(utility_desktop.obj):($LN5)


lld: error: undefined symbol: IsProcessorFeaturePresent
>>> referenced by d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\utility\utility_desktop.cpp:150
>>>               msvcrtd.lib(utility_desktop.obj):($LN8)

andrewrk added a commit that referenced this issue Mar 17, 2019
@andrewrk
Copy link
Member

I was able to reproduce the problem. In the above linked commit I added ntdll.lib to the linker line when building against msvcrt and it fixed it for me.

@ghost
Copy link
Author

ghost commented Mar 17, 2019

The problem is still there for me.
I have discovered that I can get it to link with this build command:
zig build-exe a.zig --library c --library "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.17763.0\um\x64\kernel32.lib"
Linking to kernel32.lib like that got glad and glfw to work perfectly.

@andrewrk
Copy link
Member

I suspect the problem is that *.lib files are ending up in the cwd and the linker line uses -L "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.17763.0\um\x64\" kernel32.lib arguments, and this ends up finding .\kernel32.lib rather than C:\Program Files (x86)\Windows Kits\10\Lib\10.0.17763.0\um\x64\kernel32.lib. Let me try changing the linker line to explicitly put the full path.

@Sahnvour
Copy link
Contributor

I also ran into this. Leftovers from a zig build-exe were incorrectly picked up by the linker, instead of the official windows ones, causing undefined symbol errors when using libc. Changing the generated linker command line to use absolute paths fixed it, but is there a reason these files are emitted in the cwd instead of zig-cache in the first place?

@andrewrk
Copy link
Member

andrewrk commented Jan 7, 2020

is there a reason these files are emitted in the cwd instead of zig-cache in the first place?

They're emitted into the "artifact directory" which when doing zig build-exe is the cwd by default. However, as this issue points out, this is problematic. I agree that even for zig build-exe they should not be dumped to the cwd. That's probably also true for .o files and such. Maybe we can have it use a temp directory in zig-cache instead. Or maybe we can have zig build-exe by default do a cached build, and "install" to the cwd. This would create bin/<executable> and nothing else. I think I favor that proposal, maybe even enough to write up separately.

In the meantime, I think the impact of this issue was lessened by @LemonBoy removing automatic .lib file creation in favor of relying more on our bundled MinGW-w64 .lib files.

@andrewrk andrewrk added enhancement Solving this issue will likely involve adding new logic or components to the codebase. bug Observed behavior contradicts documented or intended behavior labels Jan 7, 2020
@andrewrk andrewrk modified the milestones: 0.6.0, 0.7.0 Jan 7, 2020
@andrewrk andrewrk modified the milestones: 0.7.0, 0.8.0 Oct 17, 2020
@andrewrk andrewrk modified the milestones: 0.8.0, 0.9.0 Nov 6, 2020
@andrewrk andrewrk modified the milestones: 0.9.0, 0.10.0 May 19, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Observed behavior contradicts documented or intended behavior enhancement Solving this issue will likely involve adding new logic or components to the codebase. os-windows
Projects
None yet
Development

No branches or pull requests

2 participants