[PATCH v3 1/4] bfd,ld,dlltool: Emit delay-load import data into its own section
Jan Beulich
jbeulich@suse.com
Fri Jun 6 08:40:50 GMT 2025
On 06.05.2025 08:50, Jeremy Drake wrote:
> From: LIU Hao <lh_mouse@126.com>
>
> A delay-import symbol (of a function) is resolved when a call to it is made.
> The delay loader may overwrite the `__imp_` pointer to the actual function
> after it has been resolved, which requires the pointer itself be in a
> writeable section.
>
> Previously it was placed in the ordinary Import Address Table (IAT), which
> is emitted into the `.idata` section, which had been changed to read-only
> in db00f6c3aceabbf03acdb69e74b59b2d2b043cd7, which caused segmentation
> faults when functions from delay-import library were called. This is
> PR 32675.
>
> This commit makes DLLTOOL emit delay-import IAT into `.didat`, as specified
> by Microsoft. Most of the code is copied from `.idata`, except that this
> section is writeable. As a side-effect of this, PR 14339 is also fixed.
>
> Using this DEF:
>
> ```
> ; ws2_32.def
> LIBRARY "WS2_32.DLL"
> EXPORTS
> WSAGetLastError
> ```
>
> and this C program:
>
> ```
> // delay.c
> #define WIN32_LEAN_AND_MEAN 1
> #include <windows.h>
> #include <stdio.h>
>
> /////////////////////////////////////////////////////////
> // User code
> /////////////////////////////////////////////////////////
>
> DWORD WINAPI WSAGetLastError(void);
> extern PVOID __imp_WSAGetLastError;
>
> int
> main(void)
> {
> fprintf(stderr, "before delay load, __imp_WSAGetLastError = %p\n", __imp_WSAGetLastError);
> SetLastError(123);
> fprintf(stderr, "WSAGetLastError() = %d\n", WSAGetLastError());
> fprintf(stderr, "after delay load, __imp_WSAGetLastError = %p\n", __imp_WSAGetLastError);
> __imp_WSAGetLastError = (PVOID) 1234567;
> fprintf(stderr, "after plain write, __imp_WSAGetLastError = %p\n", __imp_WSAGetLastError);
> }
>
> /////////////////////////////////////////////////////////
> // Overridden `__delayLoadHelper2` facility
> /////////////////////////////////////////////////////////
>
> extern char __ImageBase[];
> PVOID WINAPI ResolveDelayLoadedAPI(PVOID ParentModuleBase, LPCVOID DelayloadDescriptor,
> PVOID FailureDllHook, PVOID FailureSystemHook,
> FARPROC* ThunkAddress, ULONG Flags);
> FARPROC WINAPI DelayLoadFailureHook(LPCSTR name, LPCSTR function);
>
> FARPROC WINAPI __delayLoadHelper2(LPCVOID pidd, FARPROC* ppfnIATEntry)
> {
> return ResolveDelayLoadedAPI(&__ImageBase, pidd, NULL, (PVOID) DelayLoadFailureHook,
> ppfnIATEntry, 0);
> }
> ```
>
> This program used to crash:
>
> ```
> $ dlltool -nn -d ws2_32.def -y delay_ws2_32.a
> $ gcc -g delay.c delay_ws2_32.a -o delay.exe
> $ ./delay.exe
> before delay load, __imp_WSAGetLastError = 00007FF6937215C6
> Segmentation fault
> ```
>
> After this commit, it loads and calls `WSAGetLastError()` properly, and
> `__imp_WSAGetLastError` is writeable:
>
> ```
> $ dlltool -nn -d ws2_32.def -y delay_ws2_32.a
> $ gcc -g delay.c delay_ws2_32.a -o delay.exe
> $ ./delay.exe
> before delay load, __imp_WSAGetLastError = 00007FF76E2215C6
> WSAGetLastError() = 123
> after delay load, __imp_WSAGetLastError = 00007FFF191FA720
> after plain write, __imp_WSAGetLastError = 000000000012D687
> ```
>
> Reference: https://learn.microsoft.com/en-us/windows/win32/secbp/pe-metadata#import-handling
> Co-authored-by: Jeremy Drake <sourceware-bugzilla@jdrake.com>
> Signed-off-by: LIU Hao <lh_mouse@126.com>
> Signed-off-by: Jeremy Drake <sourceware-bugzilla@jdrake.com>
I was meaning to commit this today, but obviously(?) ran it through the testsuite
first. This introduces regressions for 3 testcases for both Cygwin and MinGW. Did
you not observe these, or did you simply not run the testsuite for either of
these (pretty common when it comes to PE/COFF) targets?
The failures also appear to point out a problem with the code changes themselves,
i.e. it's not just the testcase expectations that need adjustment. For
ld-pe/secrel_64.d and ld-pe/secidx_64.d I don't see why suddenly a .didat section
would be present in the output. Neither test looks to be using delayload
functionality. For ld-pe/tlssec64.d the issue is likely the same, even if it
expresses itself differently in the expected vs actual output delta.
Overall this also reminds me of another shortcoming of the series: There's no
new testcase being added, making sure the new functionality won#t regress.
Jan
More information about the Binutils
mailing list