在 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

Attachment: OpenPGP_signature.asc
Description: OpenPGP digital signature

_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to