`@extern` with `library_name` causes compiler to segfault/error when building for windows targets
Zig Version
0.15.0-dev.621+a63f7875f
Steps to Reproduce and Observed Behavior
pub fn main() void {
@extern(*const fn () callconv(.c) void, .{
.library_name = "foo",
.name = "bar",
})();
}
zig build-exe foo.lib repro.zig -target x86_64-windows-gnu
# -or-
zig build-exe repro.zig -target x86_64-windows-gnu
Results in random compiler segfaults and errors. Here's a few:
error: unable to generate DLL import .lib file for +7╥: InvalidWtf8
error(link): DLL import library for -lbI not found
error: failed to link with LLD: DllImportLibraryNotFound
error: unable to generate DLL import .lib file for ☺D#: Unexpected
error: unable to generate DLL import .lib file for ☻
You get the same behavior regardless of whether or not you pass an implib on the command line.
This is probably caused by some kind of UAF. Sema.handleExternLibName() seems like a good place to start investigating.
Expected Behavior
No segfault.
Can we just delete this field and extern "libname"?
No? They are used for the Windows PE format (and probably other non-ELF formats as well) and important for resolving conflicts when two libraries export functions with the same name.
The Import Tables (interpreted .rdata section contents)
vma: Hint Time Forward DLL First
Table Stamp Chain Name Thunk
000de7f8 000de848 00000000 00000000 000dee52 000de9a8
DLL Name: foo.dll
vma: Ordinal Hint Member-Name Bound-To
000de9a8 <none> 0000 hello
000de80c 000de858 00000000 00000000 000dee5f 000de9b8
DLL Name: bar.dll
vma: Ordinal Hint Member-Name Bound-To
000de9b8 <none> 0000 hello
000de820 00000000 00000000 00000000 00000000 00000000
Okay, that seems valid.
However, I still think the Sema code that actually looks up and links the library based on library_name should go; this responsibility just clearly doesn't belong there, and (effectively) getting implicit link inputs based on comptime evaluation of Zig code is fairly bizarre.
I also think the value of the extern "libname" syntax becomes questionable at that point, vs just using @extern with library_name.
i think he's also asking whats currently preventing you from doing extern "foo" fn bar() void; ?
Removing it and requiring @extern seems fair, it also currently has the .is_dll_import field so there's a precedent for shoving away advanced options there.
As for my actual use case, I'm building an exe and a dll which interact with each other and to ensure all the symbols they import/export stay in sync I have a source file that declares all library names and function names/signatures so that there's a single source of truth and I can statically assert that both compilation units export and import the exact same things. You can only do this with the builtins, not the keywords.
#23971