Skip to content

Zig DLL imports generate thunks on Windows #11822

@drew-gpf

Description

@drew-gpf

When reversing Zig binaries it's not uncommon to come across import thunks:
image
These are generated by a linker when the compiler emits a 32-bit relative call or jump to an import instead of an IDT dereference. This happens because the compiler doesn't know whether or not the function it's calling is from a DLL or if it's internal to the module itself; the linker can only correct this by making an extra function (a "thunk") that properly jumps to it.

Microsoft fixed this by allowing programmers to specify __declspec(dllimport) to an extern, forcing the compiler to generate the correct instruction for the linker. LLVM followed suit by adding DLL import/export storage classes. In fact, zig even weakly supports this with maybe_import_dll: https://github.com/ziglang/zig/blob/61844b6bd405b4cca3ab673284609aa6a651d506/src/stage1/codegen.cpp

I say weakly of course because it was commented out; probably because they were applying it to every extern function, which is definitely a bad idea. However, the alternative is that applications on Windows have to be slightly slower and larger than something generated by Clang because the Zig compiler has no concept of dll-specific imports.

There is, however, a workaround; because LLVM will (other than generating a load) just prepend __imp_ to the import name, one can manually dereference the IDT entry:
image
image
(Notice how we're explicitly doing NtQuerySystemInformation.* because otherwise LLVM doesn't know it needs the call instruction to perform that load for us; this can otherwise lead to nasty unexpected segfaults where the instruction pointer ends up in .rdata)

This generates the following:
image
Which is still not as efficient as a call cs:[ptr], but much better than calling a thunk.

I'm not sure how to classify this but I'd personally call it a bug that there's no clean way of preventing thunking when all other Windows dev environments support it. I assume the most straightforward way would be to change extern fn declarations to let people specify this; known Windows system dlls could (should) also be automatically caught.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugObserved behavior contradicts documented or intended behaviorenhancementSolving this issue will likely involve adding new logic or components to the codebase.os-windowsMicrosoft Windows

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions