zig icon indicating copy to clipboard operation
zig copied to clipboard

`@extern` with `library_name` causes compiler to segfault/error when building for windows targets

Open castholm opened this issue 7 months ago • 6 comments

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.

castholm avatar May 22 '25 22:05 castholm

Can we just delete this field and extern "libname"?

alexrp avatar May 22 '25 22:05 alexrp

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

castholm avatar May 22 '25 23:05 castholm

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.

alexrp avatar May 22 '25 23:05 alexrp

i think he's also asking whats currently preventing you from doing extern "foo" fn bar() void; ?

nektro avatar May 22 '25 23:05 nektro

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.

castholm avatar May 22 '25 23:05 castholm

#23971

alexrp avatar May 23 '25 09:05 alexrp