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>
#include "libcoff.h"
#include "elf-bfd.h"
#include "hashtab.h"
+#include "safe-ctype.h"
/* Extract a long section name at STRINDEX and copy it to the bfd objstack.
Return NULL in case of error. */
typedef bool (*gc_sweep_hook_fn)
(bfd *, struct bfd_link_info *, asection *, const struct internal_reloc *);
+static inline bool
+is_subsection (const char *str, const char *prefix)
+{
+ size_t n = strlen (prefix);
+ if (strncmp (str, prefix, n) != 0)
+ return false;
+ if (str[n] == 0)
+ return true;
+ else if (str[n] != '$')
+ return false;
+ return ISDIGIT (str[n + 1]) && str[n + 2] == 0;
+}
+
static bool
coff_gc_sweep (bfd *abfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
{
else if (startswith (o->name, ".idata")
|| startswith (o->name, ".pdata")
|| startswith (o->name, ".xdata")
+ || is_subsection (o->name, ".didat")
|| startswith (o->name, ".rsrc"))
o->gc_mark = 1;
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".idata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
+{ COFF_SECTION_NAME_PARTIAL_MATCH (".didat"), \
+ COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_EXACT_MATCH (".pdata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".debug"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".idata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
+{ COFF_SECTION_NAME_PARTIAL_MATCH (".didat"), \
+ COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_EXACT_MATCH (".pdata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".debug"), \
#define COFF_SECTION_ALIGNMENT_ENTRIES \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".idata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
+{ COFF_SECTION_NAME_PARTIAL_MATCH (".didat"), \
+ COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_EXACT_MATCH (".pdata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".debug"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 4 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".idata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
+{ COFF_SECTION_NAME_PARTIAL_MATCH (".didat"), \
+ COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_EXACT_MATCH (".pdata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".debug"), \
{ ".arch", IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_ALIGN_8BYTES },
{ ".bss", IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_WRITE },
{ ".data", IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_WRITE },
+ { ".didat", IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_WRITE },
{ ".edata", IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA },
{ ".idata", IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA },
{ ".pdata", IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA },
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".idata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
+{ COFF_SECTION_NAME_PARTIAL_MATCH (".didat"), \
+ COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_EXACT_MATCH (".pdata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".debug"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".idata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
+{ COFF_SECTION_NAME_PARTIAL_MATCH (".didat"), \
+ COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_EXACT_MATCH (".pdata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".debug"), \
#define COFF_SECTION_ALIGNMENT_ENTRIES \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".idata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
+{ COFF_SECTION_NAME_PARTIAL_MATCH (".didat"), \
+ COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_EXACT_MATCH (".pdata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".debug"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".idata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
+{ COFF_SECTION_NAME_PARTIAL_MATCH (".didat"), \
+ COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_EXACT_MATCH (".pdata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".debug"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".idata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
+{ COFF_SECTION_NAME_PARTIAL_MATCH (".didat"), \
+ COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_EXACT_MATCH (".pdata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".debug"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 4 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".idata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
+{ COFF_SECTION_NAME_PARTIAL_MATCH (".didat"), \
+ COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_EXACT_MATCH (".pdata"), \
COFF_ALIGNMENT_FIELD_EMPTY, COFF_ALIGNMENT_FIELD_EMPTY, 2 }, \
{ COFF_SECTION_NAME_PARTIAL_MATCH (".debug"), \
adding entries. Since it is so short, a linear search is used. */
static const struct section_to_type stt[] =
{
+ {".didat", 'i'}, /* MSVC's .didat (delay import) section */
{".drectve", 'i'}, /* MSVC's .drective section */
{".edata", 'e'}, /* MSVC's .edata (export) section */
{".idata", 'i'}, /* MSVC's .idata (import) section */
#define DATA_SEC_FLAGS (SEC_ALLOC | SEC_LOAD | SEC_DATA)
#define BSS_SEC_FLAGS SEC_ALLOC
-static sinfo secdata[NSECS] =
+static sinfo secdata_plain[NSECS] =
{
INIT_SEC_DATA (TEXT, ".text", TEXT_SEC_FLAGS, 2),
INIT_SEC_DATA (DATA, ".data", DATA_SEC_FLAGS, 2),
INIT_SEC_DATA (IDATA6, ".idata$6", SEC_HAS_CONTENTS, 1)
};
+static sinfo secdata_delay[NSECS] =
+{
+ INIT_SEC_DATA (TEXT, ".text", TEXT_SEC_FLAGS, 2),
+ INIT_SEC_DATA (DATA, ".data", DATA_SEC_FLAGS, 2),
+ INIT_SEC_DATA (BSS, ".bss", BSS_SEC_FLAGS, 2),
+ INIT_SEC_DATA (IDATA7, ".didat$7", SEC_HAS_CONTENTS, 2),
+ INIT_SEC_DATA (IDATA5, ".didat$5", SEC_HAS_CONTENTS, 2),
+ INIT_SEC_DATA (IDATA4, ".didat$4", SEC_HAS_CONTENTS, 2),
+ INIT_SEC_DATA (IDATA6, ".didat$6", SEC_HAS_CONTENTS, 1)
+};
+
/* This is what we're trying to make. We generate the imp symbols with
both single and double underscores, for compatibility.
static bfd *
make_one_lib_file (export_type *exp, int i, int delay)
{
+ sinfo *const secdata = delay ? secdata_delay : secdata_plain;
char *outname = TMP_STUB;
size_t name_len = strlen (outname);
sprintf (outname + name_len - 7, "%05d.o", i);
if (!no_idata5)
{
- fprintf (f, "\t.section\t.idata$5\n");
+ fprintf (f, "\t.section\t.didat$5\n");
/* NULL terminating list. */
if (create_for_pep)
fprintf (f, "\t%s\t0\n\t%s\t0\n", ASM_LONG, ASM_LONG);
if (!no_idata4)
{
- fprintf (f, "\t.section\t.idata$4\n");
+ fprintf (f, "\t.section\t.didat$4\n");
fprintf (f, "\t%s\t0\n", ASM_LONG);
if (create_for_pep)
fprintf (f, "\t%s\t0\n", ASM_LONG);
- fprintf (f, "\t.section\t.idata$4\n");
+ fprintf (f, "\t.section\t.didat$4\n");
fprintf (f, "__INT_%s:\n", imp_name_lab);
}
- fprintf (f, "\t.section\t.idata$2\n");
+ fprintf (f, "\t.section\t.didat$2\n");
fclose (f);
return abfd;
}
+static bfd *
+make_delay_tail (void)
+{
+ FILE *f = fopen (TMP_TAIL_S, FOPEN_WT);
+ bfd *abfd;
+
+ if (f == NULL)
+ {
+ fatal (_("failed to open temporary tail file: %s"), TMP_TAIL_S);
+ return NULL;
+ }
+
+ temp_file_to_remove[TEMP_TAIL_FILE] = TMP_TAIL_S;
+
+ if (!no_idata4)
+ {
+ fprintf (f, "\t.section\t.didat$4\n");
+ if (create_for_pep)
+ fprintf (f, "\t%s\t0\n\t%s\t0\n", ASM_LONG, ASM_LONG);
+ else
+ fprintf (f, "\t%s\t0\n", ASM_LONG); /* NULL terminating list. */
+ }
+
+ if (!no_idata5)
+ {
+ fprintf (f, "\t.section\t.didat$5\n");
+ if (create_for_pep)
+ fprintf (f, "\t%s\t0\n\t%s\t0\n", ASM_LONG, ASM_LONG);
+ else
+ fprintf (f, "\t%s\t0\n", ASM_LONG); /* NULL terminating list. */
+ }
+
+ fprintf (f, "\t.section\t.didat$7\n");
+ fprintf (f, "\t%s\t__%s_iname\n", ASM_GLOBAL, imp_name_lab);
+ fprintf (f, "__%s_iname:\t%s\t\"%s\"\n",
+ imp_name_lab, ASM_TEXT, dll_name);
+
+ fclose (f);
+
+ assemble_file (TMP_TAIL_S, TMP_TAIL_O);
+
+ abfd = bfd_openr (TMP_TAIL_O, HOW_BFD_READ_TARGET);
+ if (abfd == NULL)
+ /* xgettext:c-format */
+ fatal (_("failed to open temporary tail file: %s: %s"),
+ TMP_TAIL_O, bfd_get_errmsg ());
+
+ temp_file_to_remove[TEMP_TAIL_O_FILE] = TMP_TAIL_O;
+ return abfd;
+}
+
static void
gen_lib_file (int delay)
{
if (delay)
{
ar_head = make_delay_head ();
+ ar_tail = make_delay_tail();
}
else
{
ar_head = make_head ();
+ ar_tail = make_tail();
}
- ar_tail = make_tail();
if (ar_head == NULL || ar_tail == NULL)
return;
# substitution, so we do this instead.
# Sorting of the .foo$* sections is required by the definition of
# grouped sections in PE.
-# Sorting of the file names in R_IDATA is required by the
+# Sorting of the file names in R_IDATA and R_DIDAT is required by the
# current implementation of dlltool (this could probably be changed to
# use grouped sections instead).
if test "${RELOCATING}"; then
R_IDATA67='
KEEP (SORT(*)(.idata$6))
KEEP (SORT(*)(.idata$7))'
+ R_DIDAT234='
+ __DELAY_IMPORT_DIRECTORY_start__ = .;
+ KEEP (SORT(*)(.didat$2))
+ KEEP (SORT(*)(.didat$3))
+ __DELAY_IMPORT_DIRECTORY_end__ = .;
+ /* These zeroes mark the end of the import list. */
+ . += (__DELAY_IMPORT_DIRECTORY_end__ - __DELAY_IMPORT_DIRECTORY_start__) ? 8*4 : 0;
+ KEEP (SORT(*)(.didat$4))'
+ R_DIDAT5='KEEP (SORT(*)(.didat$5))'
+ R_DIDAT67='
+ KEEP (SORT(*)(.didat$6))
+ KEEP (SORT(*)(.didat$7))'
R_CRT_XC='KEEP (*(SORT(.CRT$XC*))) /* C initialization */'
R_CRT_XI='KEEP (*(SORT(.CRT$XI*))) /* C++ initialization */'
R_CRT_XL='KEEP (*(SORT(.CRT$XL*))) /* TLS callbacks */'
R_IDATA234=
R_IDATA5=
R_IDATA67=
+ R_DIDAT234=
+ R_DIDAT5=
+ R_DIDAT67=
R_CRT_XC=
R_CRT_XI=
R_CRT_XL=
${R_IDATA67}
}
+ .didat ${RELOCATING+BLOCK(__section_alignment__)} :
+ {
+ /* This cannot currently be handled with grouped sections.
+ See pe.em:sort_sections. */
+ ${R_DIDAT234}
+ ${R_DIDAT5}
+ ${R_DIDAT67}
+ }
+
/* Windows TLS expects .tls\$AAA to be at the start and .tls\$ZZZ to be
at the end of section. This is important because _tls_start MUST
be at the beginning of the section to enable SECREL32 relocations with TLS
# substitution, so we do this instead.
# Sorting of the .foo$* sections is required by the definition of
# grouped sections in PE.
-# Sorting of the file names in R_IDATA is required by the
+# Sorting of the file names in R_IDATA and R_DIDAT is required by the
# current implementation of dlltool (this could probably be changed to
# use grouped sections instead).
if test "${RELOCATING}"; then
R_IDATA67='
KEEP (SORT(*)(.idata$6))
KEEP (SORT(*)(.idata$7))'
+ R_DIDAT234='
+ __DELAY_IMPORT_DIRECTORY_start__ = .;
+ KEEP (SORT(*)(.didat$2))
+ KEEP (SORT(*)(.didat$3))
+ __DELAY_IMPORT_DIRECTORY_end__ = .;
+ /* These zeroes mark the end of the import list. */
+ . += (__DELAY_IMPORT_DIRECTORY_end__ - __DELAY_IMPORT_DIRECTORY_start__) ? 8*4 : 0;
+ . = ALIGN(8);
+ KEEP (SORT(*)(.didat$4))'
+ R_DIDAT5='SORT(*)(.didat$5)'
+ R_DIDAT67='
+ KEEP (SORT(*)(.didat$6))
+ KEEP (SORT(*)(.didat$7))'
R_CRT_XC='KEEP (*(SORT(.CRT$XC*))) /* C initialization */'
R_CRT_XI='KEEP (*(SORT(.CRT$XI*))) /* C++ initialization */'
R_CRT_XL='KEEP (*(SORT(.CRT$XL*))) /* TLS callbacks */'
R_IDATA234=
R_IDATA5=
R_IDATA67=
+ R_DIDAT234=
+ R_DIDAT5=
+ R_DIDAT67=
R_CRT_XC=
R_CRT_XI=
R_CRT_XL=
${R_IDATA67}
}
+ .didat ${RELOCATING+BLOCK(__section_alignment__)} :
+ {
+ /* This cannot currently be handled with grouped sections.
+ See pep.em:sort_sections. */
+ ${R_DIDAT234}
+ ${R_DIDAT5}
+ ${R_DIDAT67}
+ }
+
/* Windows TLS expects .tls\$AAA to be at the start and .tls\$ZZZ to be
at the end of the .tls section. This is important because _tls_start MUST
be at the beginning of the section to enable SECREL32 relocations with TLS