[Mingw-w64-public] [PATCH] ld: pep: Stop emitting reference to the pseudo-relocator function
LIU Hao
lh_mouse@126.com
Mon Jul 13 12:59:50 GMT 2026
在 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
-------------- next part --------------
A non-text attachment was scrubbed...
Name: OpenPGP_signature.asc
Type: application/pgp-signature
Size: 840 bytes
Desc: OpenPGP digital signature
URL: <https://sourceware.org/pipermail/binutils/attachments/20260713/f2eda335/attachment-0001.sig>
More information about the Binutils
mailing list