Bug 14339 - MinGW ld omits stubs when linking delay import library and using __declspec(dllimport)
Summary: MinGW ld omits stubs when linking delay import library and using __declspec(d...
Status: NEW
Alias: None
Product: binutils
Classification: Unclassified
Component: ld (show other bugs)
Version: 2.22
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-07-08 12:19 UTC by Thomas Faber
Modified: 2022-01-02 23:39 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments
Test case source (7.84 KB, application/zip)
2012-07-08 12:19 UTC, Thomas Faber
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Thomas Faber 2012-07-08 12:19:14 UTC
Created attachment 6520 [details]
Test case source

Delay import libraries contain stubs in the form of:
_WSAStartup@8:
  00000000: FF 25 00 00 00 00  jmp         dword ptr [.idata$5]
  00000006: B8 00 00 00 00     mov         eax,offset .idata$5
  0000000B: E9 00 00 00 00     jmp         ___tailMerge_libws2_32_delayed_a


These are then referenced in the corresponding .idata section à la:
RAW DATA #5
  00000000: 06 00 00 00                                      ....

RELOCATIONS #5
                                                Symbol    Symbol
 Offset    Type              Applied To         Index     Name
 --------  ----------------  -----------------  --------  ------
 00000000  DIR32                      00000006         0  .text


When using __declspec(dllimport), only the symbol in .idata$5 (__imp__WSAStartup@8) is referenced, not _WSAStartup@8 in the .text section.
This apparently causes the linker to omit the stub, and turn the corresponding relocation into a no-op (i.e. the application will call the address 6).

--whole-archive has no effect on this, the stubs are still omitted.
Linking the individual object files as extracted from the library DOES produce correct output, though.
Adding '-u _WSAStartup@8' to the command line also seems to work.

I've made a small test case including an analysis of the generated code.

The source for this test case is attached. It can also (including compiled binaries and disassembly) be found at:
http://thfabba.ath.cx/~thfabba/pub/delaylib/

I've tested this with ld version 2.20.51.20091222 (ReactOS Build Environment), as well as MinGW's 2.22 and MinGW-w64's 2.22.51.20111217.

Possibly, but not necessarily related to bug 12614.
Comment 1 Hannes Domani 2017-02-07 16:24:53 UTC
At https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=ld/emultempl/pe.em;h=8f41f27709a11c5b3bc4f028c05f7915ce540a86;hb=HEAD#l1768 is this:

/* The following chunk of code tries to identify jump stubs in
   import libraries which are dead code and eliminates them
   from the final link. For each exported symbol <sym>, there
   is a object file in the import library with a .text section
   and several .idata\$* sections. The .text section contains the
   symbol definition for <sym> which is a jump stub of the form
   jmp *__imp_<sym>. The .idata\$5 contains the symbol definition
   for __imp_<sym> which is the address of the slot for <sym> in
   the import address table. When a symbol is imported explicitly
   using __declspec(dllimport) declaration, the compiler generates
   a reference to __imp_<sym> which directly resolves to the
   symbol in .idata\$5, in which case the jump stub code is not
   needed. The following code tries to identify jump stub sections
   in import libraries which are not referred to by anyone and
   marks them for exclusion from the final link.  */

This code removes the delay-loading stubs as well.

My simple fix/workaround was to check if the last symbol name starts with "___tailMerge_", and in this case not exclude it from the final link.
Comment 2 Mark Jansen 2017-07-07 08:20:41 UTC
Domani Hannes,

This seems like the correct place for the fix, however not an 100% correct fix.
I do believe that in this case, we have to check if someone references any of the parts of the delayload chain, the entire chain should be included.
Comment 3 Adam J Ryan 2022-01-02 21:32:13 UTC
I've also hit this issue and removing the __declspec(dllimport) resolved it.
In the documentation provided by Microsoft it does mention a compiler optimisation that is performed when this is added as noted in a previous comment. Documentation is here https://docs.microsoft.com/en-us/cpp/build/importing-into-an-application-using-declspec-dllimport?view=msvc-170.
Maybe something could be put into the generated assembly of the static wrapper library to disable compile optimisation for that block.
Comment 4 Adam J Ryan 2022-01-02 23:39:28 UTC
I've also found that adding the WINAPI macro (__stdcall) is also problematic. It allows the library to load and it fails after the dliNotePreGetProcAddress query stage and doesn't hit dliNoteEndProcessing.
BTW this ticket (https://www.sourceware.org/bugzilla/show_bug.cgi?id=22676) will be related as it has the __declspec(dllimport).