From 060a658fe19b5506bcac41c4207ac92310b2dc8c Mon Sep 17 00:00:00 2001 From: Charles Wilson Date: Fri, 23 Oct 2009 22:19:42 +0000 Subject: [PATCH] Sync pseudo-reloc.c with cygwin/lib/ --- winsup/mingw/ChangeLog | 17 +++ winsup/mingw/pseudo-reloc.c | 224 +++++++++++++++++++++++++++++++++--- 2 files changed, 225 insertions(+), 16 deletions(-) diff --git a/winsup/mingw/ChangeLog b/winsup/mingw/ChangeLog index 70c644fe4..42c8a5fc2 100644 --- a/winsup/mingw/ChangeLog +++ b/winsup/mingw/ChangeLog @@ -1,3 +1,20 @@ +2009-10-23 Charles Wilson + + Sync pseudo-reloc.c with cygwin/lib/ + * pseudo-reloc.c [CYGWIN]: Added comments throughout and various + whitespace fixes. Exploit cygwin_internal(CW_EXIT_PROCESS,...) + for fatal error handling that is consistent with cygwin process + life-cycle. Ensure state variable (in _pei386_runtime_relocator) + is unique to each address space, across fork(). + [CYGWIN] (__print_reloc_error): New function for reporting + errors in a manner supported by cygwin at this early stage of + the process life-cycle. + [CYGWIN] (_pei386_runtime_relocator): Ensure relocations + performed only once for each address space, but are repeated + after fork() in the new address space. + [MINGW] (__write_memory): Ensure that b is always initialized + by call to VirtualQuery, even if -DNDEBUG. + 2009-09-29 Keith Marshall Make MinGW printf() "%p" format compatible with MSVCRT scanf(). diff --git a/winsup/mingw/pseudo-reloc.c b/winsup/mingw/pseudo-reloc.c index ea9f37631..8e5b0bb7e 100644 --- a/winsup/mingw/pseudo-reloc.c +++ b/winsup/mingw/pseudo-reloc.c @@ -20,16 +20,40 @@ #include #include #include - - extern char __RUNTIME_PSEUDO_RELOC_LIST__; - extern char __RUNTIME_PSEUDO_RELOC_LIST_END__; - extern char _image_base__; - + +#if defined(__CYGWIN__) +#include +#include +#include +#include +/* copied from winsup.h */ +# define NO_COPY __attribute__((nocommon)) __attribute__((section(".data_cygwin_nocopy"))) +/* custom status code: */ +#define STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION ((NTSTATUS) 0xe0000269) +#else +# define NO_COPY +#endif + +extern char __RUNTIME_PSEUDO_RELOC_LIST__; +extern char __RUNTIME_PSEUDO_RELOC_LIST_END__; +extern char _image_base__; + +/* v1 relocation is basically: + * *(base + .target) += .addend + * where (base + .target) is always assumed to point + * to a DWORD (4 bytes). + */ typedef struct { DWORD addend; DWORD target; } runtime_pseudo_reloc_item_v1; +/* v2 relocation is more complex. In effect, it is + * *(base + .target) += *(base + .sym) - (base + .sym) + * with care taken in both reading, sign extension, and writing + * because .flags may indicate that (base + .target) may point + * to a BYTE, WORD, DWORD, or QWORD (w64). + */ typedef struct { DWORD sym; DWORD target; @@ -42,26 +66,114 @@ typedef struct { DWORD version; } runtime_pseudo_reloc_v2; +#if defined(__CYGWIN__) +#define SHORT_MSG_BUF_SZ 128 +/* This function is used to print short error messages + * to stderr, which may occur during DLL initialization + * while fixing up 'pseudo' relocations. This early, we + * may not be able to use cygwin stdio functions, so we + * use the win32 WriteFile api. This should work with both + * normal win32 console IO handles, redirected ones, and + * cygwin ptys. + */ +static BOOL +__print_reloc_error (const char *fmt, ...) +{ + char buf[SHORT_MSG_BUF_SZ]; + wchar_t module[MAX_PATH]; + char * posix_module = NULL; + BOOL rVal = FALSE; + static const char * UNKNOWN_MODULE = ": "; + DWORD len; + DWORD done; + va_list args; + HANDLE errh = GetStdHandle (STD_ERROR_HANDLE); + ssize_t modulelen = GetModuleFileNameW (NULL, module, sizeof (module)); + + if (errh == INVALID_HANDLE_VALUE) + return FALSE; + + if (modulelen > 0) + posix_module = cygwin_create_path (CCP_WIN_W_TO_POSIX, module); + + va_start (args, fmt); + len = (DWORD) vsnprintf (buf, SHORT_MSG_BUF_SZ, fmt, args); + va_end (args); + buf[SHORT_MSG_BUF_SZ-1] = '\0'; /* paranoia */ + + if (posix_module) + { + rVal = WriteFile (errh, (PCVOID)posix_module, + strlen(posix_module), &done, NULL) && + WriteFile (errh, (PCVOID)": ", 2, &done, NULL) && + WriteFile (errh, (PCVOID)buf, len, &done, NULL); + free (posix_module); + } + else + { + rVal = WriteFile (errh, (PCVOID)UNKNOWN_MODULE, + sizeof(UNKNOWN_MODULE), &done, NULL) && + WriteFile (errh, (PCVOID)buf, len, &done, NULL); + } + return rVal; +} +#endif /* __CYGWIN__ */ + +/* This function temporarily marks the page containing addr + * writable, before copying len bytes from *src to *addr, and + * then restores the original protection settings to the page. + * + * Using this function eliminates the requirement with older + * pseudo-reloc implementations, that sections containing + * pseudo-relocs (such as .text and .rdata) be permanently + * marked writable. This older behavior sabotaged any memory + * savings achieved by shared libraries on win32 -- and was + * slower, too. However, on cygwin as of binutils 2.20 the + * .text section is still marked writable, and the .rdata section + * is folded into the (writable) .data when --enable-auto-import. + */ static void __write_memory (void *addr,const void *src,size_t len) { MEMORY_BASIC_INFORMATION b; DWORD oldprot; + SIZE_T memsz; + if (!len) return; - assert (VirtualQuery (addr, &b, sizeof(b))); + + memsz = VirtualQuery (addr, &b, sizeof(b)); + +#if defined(__CYGWIN__) + /* CYGWIN: If error, print error message and die. */ + if (memsz == 0) + { + __print_reloc_error ( + "error while loading shared libraries: bad address specified 0x%08x.\n", + addr); + cygwin_internal (CW_EXIT_PROCESS, + STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION, + 1); + } +#else + /* MINGW: If error, die. assert() may print error message when !NDEBUG */ + assert (memsz); +#endif + /* Temporarily allow write access to read-only protected memory. */ if (b.Protect != PAGE_EXECUTE_READWRITE && b.Protect != PAGE_READWRITE) VirtualProtect (b.BaseAddress, b.RegionSize, PAGE_EXECUTE_READWRITE, &oldprot); + /* write the data. */ memcpy (addr, src, len); + /* Restore original protection. */ if (b.Protect != PAGE_EXECUTE_READWRITE && b.Protect != PAGE_READWRITE) VirtualProtect (b.BaseAddress, b.RegionSize, oldprot, &oldprot); } #define RP_VERSION_V1 0 #define RP_VERSION_V2 1 - + static void do_pseudo_reloc (void * start, void * end, void * base) { @@ -70,15 +182,52 @@ do_pseudo_reloc (void * start, void * end, void * base) runtime_pseudo_reloc_v2 *v2_hdr = (runtime_pseudo_reloc_v2 *) start; runtime_pseudo_reloc_item_v2 *r; + /* A valid relocation list will contain at least one entry, and + * one v1 data structure (the smallest one) requires two DWORDs. + * So, if the relocation list is smaller than 8 bytes, bail. + */ if (reloc_target < 8) return; - /* Check if this is old version pseudo relocation version. */ + + /* Check if this is the old pseudo relocation version. */ + /* There are two kinds of v1 relocation lists: + * 1) With a (v2-style) version header. In this case, the + * first entry in the list is a 3-DWORD structure, with + * value: + * { 0, 0, RP_VERSION_V1 } + * In this case, we skip to the next entry in the list, + * knowing that all elements after the head item can + * be cast to runtime_pseudo_reloc_item_v1. + * 2) Without a (v2-style) version header. In this case, the + * first element in the list IS an actual v1 relocation + * record, which is two DWORDs. Because there will never + * be a case where a v1 relocation record has both + * addend == 0 and target == 0, this case will not be + * confused with the prior one. + * All current binutils, when generating a v1 relocation list, + * use the second (e.g. original) form -- that is, without the + * v2-style version header. + */ if (reloc_target >= 12 && v2_hdr->magic1 == 0 && v2_hdr->magic2 == 0 && v2_hdr->version == RP_VERSION_V1) - v2_hdr++; + { + /* We have a list header item indicating that the rest + * of the list contains v1 entries. Move the pointer to + * the first true v1 relocation record. By definition, + * that v1 element will not have both addend == 0 and + * target == 0 (and thus, when interpreted as a + * runtime_pseudo_reloc_v2, it will not have both + * magic1 == 0 and magic2 == 0). + */ + v2_hdr++; + } + if (v2_hdr->magic1 != 0 || v2_hdr->magic2 != 0) { + /************************* + * Handle v1 relocations * + *************************/ runtime_pseudo_reloc_item_v1 * o; for (o = (runtime_pseudo_reloc_item_v1 *) v2_hdr; o < (runtime_pseudo_reloc_item_v1 *)end; @@ -92,26 +241,54 @@ do_pseudo_reloc (void * start, void * end, void * base) return; } + /* If we got this far, then we have relocations of version 2 or newer */ + /* Check if this is a known version. */ if (v2_hdr->version != RP_VERSION_V2) { -#ifdef DEBUG +#if defined(__CYGWIN__) + /* CYGWIN: Print error message and die, even when !DEBUGGING */ + __print_reloc_error ( + "error while loading shared libraries: invalid pseudo_reloc version %d.\n", + (int) v2_hdr->version); + cygwin_internal (CW_EXIT_PROCESS, + STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION, + 1); +#else +# if defined(DEBUG) + /* MINGW: Don't die; just return to caller. If DEBUG, print error message. */ fprintf (stderr, "internal mingw runtime error:" "psuedo_reloc version %d is unknown to this runtime.\n", (int) v2_hdr->version); +# endif #endif return; } - /* Walk over header. */ + /************************* + * Handle v2 relocations * + *************************/ + + /* Walk over header. */ r = (runtime_pseudo_reloc_item_v2 *) &v2_hdr[1]; for (; r < (runtime_pseudo_reloc_item_v2 *) end; r++) { + /* location where new address will be written */ reloc_target = (ptrdiff_t) base + r->target; + + /* get sym pointer. It points either to the iat entry + * of the referenced element, or to the stub function. + */ addr_imp = (ptrdiff_t) base + r->sym; addr_imp = *((ptrdiff_t *) addr_imp); + /* read existing relocation value from image, casting to the + * bitsize indicated by the 8 LSBs of flags. If the value is + * negative, manually sign-extend to ptrdiff_t width. Raise an + * error if the bitsize indicated by the 8 LSBs of flags is not + * supported. + */ switch ((r->flags & 0xff)) { case 8: @@ -138,15 +315,30 @@ do_pseudo_reloc (void * start, void * end, void * base) #endif default: reldata=0; -#ifdef DEBUG - fprintf(stderr, "internal mingw runtime error: " +#if defined(__CYGWIN__) + /* Print error message and die, even when !DEBUGGING */ + __print_reloc_error ( + "error while loading shared libraries: unknown pseudo_reloc bit size %d.\n", + (int) (r->flags & 0xff)); + cygwin_internal (CW_EXIT_PROCESS, + STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION, + 1); +#else +# ifdef DEBUG + /* MINGW: If error, don't die; just print message if DEBUG */ + fprintf(stderr, "internal mingw runtime error: " "unknown pseudo_reloc bit size %d\n", (int) (r->flags & 0xff)); +# endif #endif break; } + + /* Adjust the relocation value */ reldata -= ((ptrdiff_t) base + r->sym); reldata += addr_imp; + + /* Write the new relocation value back to *reloc_target */ switch ((r->flags & 0xff)) { case 8: @@ -166,11 +358,11 @@ do_pseudo_reloc (void * start, void * end, void * base) } } } - + void - _pei386_runtime_relocator () +_pei386_runtime_relocator () { - static int was_init = 0; + static NO_COPY int was_init = 0; if (was_init) return; ++was_init;