在 2026-7-10 20:40, Jan Beulich via Mingw-w64-public 写道:
The other aspect I'd like to understand a little better before (possibly) ack-ing is why pseudo-relocations need producing in the first place. If ld's generating of real relocations still isn't good enough, maybe that wants fixing? Then the question of whether a reference to that function is needed simply vanishes.
It's needed by code that uses dllimport'd data without marking them `dllimport`. In C++, vtables of polymorphic classes are also data, and there's a lot of code which doesn't apply `dllimport` to classes (boost for example).
This makes some sense - if a header uses explicit `dllimport` then it will usually be unusable with static libraries. Not having explicit `dllimport` enables headers to be used with either a static library or a DLL.
When a call to a function without `dllimport` is made, the compiler generates:
call target_function
which is provided by an import library (lib*.dll.a) and effects an indirect
tail call:
target_function:
jmp [rip + __imp_target_function]
x86-32 is similar, except for name decoration and address size, and the address is absolute instead of
being RIP-relative.
And when a call to a function with `dllimport`, the compiler doesn't go through the thunk, and it just generates:
call [rip + __imp_target_function]
As for dllimport'd data, given C code:
extern int plain_data[];
int get_plain_data_1(void) { return plain_data[1]; }
__declspec(dllimport) extern int dllimp_data[];
int get_dllimp_data_1(void) { return dllimp_data[1]; }
Compiling this with `gcc test.c -O2 -mcmodel=small -S` gives:
get_plain_data_1:
mov eax, [rip + plain_data + 0x4] # eax = plain_data [1]
ret
get_dllimp_data_1:
mov rax, [rip + __imp_dllimp_data] # rax = __imp_dllimp_data
mov eax, [rax + 0x4] # eax = *(int*) (rax+4)
# = (*__imp_dllimp_data) [1]
ret
`get_plain_data_1` references the relocation `plain_data + 0x4` which is not dllimport'able; and the
linker is not able to rewrite it to the latter, which would take more space.
This is solved in three steps. First, by default (mingw-w64 GCC without `-mcmodel=small`, or Clang without `-fno-auto-import`) the compiler must always reference a datum outside the current translation unit through a level of indirection. Compiling the above example with `gcc test.c -O2 -S` gives:
get_plain_data_1:
mov rax, [rip + .refptr.plain_data] # rax = .refptr.plain_data
mov eax, [rax + 4] # eax = *(int*) (rax+4)
# = (*.refptr.plain_data) [1]
ret
As on x86-64 the 32-bit displacement is incapable of covering the full 64-bit address space, this level
of indirection allows access of an arbitrary 64-bit address.
Second, with `--enable-auto-import`, the linker sets `.refptr.plain_data` to `&__imp_plain_data` (the address of `__imp_plain_data`), and generates pseudo-relocation information for it.
And finally, `_pei386_runtime_relocator()` (despite its name) in mingw-w64 CRT reads the pseudo-relocation information, and sets `.refptr.plain_data` to the value in `__imp_plain_data`, which is the absolute address of the imported datum in the DLL. User code can now access the datum through `.refptr.plain_data`.
(I have omitted some details in here; what `_pei386_runtime_relocator()` actually does is `.refptr.plain_data += __imp_plain_data - &__imp_plain_data`. Initially `.refptr.plain_data` has a value of `&__imp_plain_data` so this can be simplified to `.refptr.plain_data = __imp_plain_data`.)
-- Best regards, LIU Hao
OpenPGP_signature.asc
Description: OpenPGP digital signature
_______________________________________________ Mingw-w64-public mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/mingw-w64-public
