diff --git a/winsup/mingw/ChangeLog b/winsup/mingw/ChangeLog index 1a8d264c7..51d473fe7 100644 --- a/winsup/mingw/ChangeLog +++ b/winsup/mingw/ChangeLog @@ -1,3 +1,7 @@ +2009-03-05 Kai Tietz + + * pseudo-reloc.c: Rewrite to enable pseudo_reloc version 2. + 2009-02-08 Keith Marshall MinGW-Feature-Request [2222263]: Make getopt() GNU / BSD compatibile. diff --git a/winsup/mingw/pseudo-reloc.c b/winsup/mingw/pseudo-reloc.c index 9fe607d2c..ea9f37631 100644 --- a/winsup/mingw/pseudo-reloc.c +++ b/winsup/mingw/pseudo-reloc.c @@ -1,6 +1,9 @@ /* pseudo-reloc.c - Written by Egor Duda + Contributed by Egor Duda + Modified by addition of runtime_pseudo_reloc version 2 + by Kai Tietz + THIS SOFTWARE IS NOT COPYRIGHTED This source code is offered for use in the public domain. You may @@ -13,33 +16,164 @@ */ #include +#include +#include +#include +#include + + extern char __RUNTIME_PSEUDO_RELOC_LIST__; + extern char __RUNTIME_PSEUDO_RELOC_LIST_END__; + extern char _image_base__; + +typedef struct { + DWORD addend; + DWORD target; +} runtime_pseudo_reloc_item_v1; -extern char __RUNTIME_PSEUDO_RELOC_LIST__; -extern char __RUNTIME_PSEUDO_RELOC_LIST_END__; -extern char _image_base__; +typedef struct { + DWORD sym; + DWORD target; + DWORD flags; +} runtime_pseudo_reloc_item_v2; -typedef struct - { - DWORD addend; - DWORD target; - } -runtime_pseudo_reloc; +typedef struct { + DWORD magic1; + DWORD magic2; + DWORD version; +} runtime_pseudo_reloc_v2; static void -do_pseudo_reloc (void* start, void* end, void* base) +__write_memory (void *addr,const void *src,size_t len) { - DWORD reloc_target; - runtime_pseudo_reloc* r; - for (r = (runtime_pseudo_reloc*) start; r < (runtime_pseudo_reloc*) end; r++) - { - reloc_target = (DWORD) base + r->target; - *((DWORD*) reloc_target) += r->addend; - } + MEMORY_BASIC_INFORMATION b; + DWORD oldprot; + if (!len) + return; + assert (VirtualQuery (addr, &b, sizeof(b))); + /* 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); + memcpy (addr, src, len); + if (b.Protect != PAGE_EXECUTE_READWRITE && b.Protect != PAGE_READWRITE) + VirtualProtect (b.BaseAddress, b.RegionSize, oldprot, &oldprot); } -void -_pei386_runtime_relocator () +#define RP_VERSION_V1 0 +#define RP_VERSION_V2 1 + +static void +do_pseudo_reloc (void * start, void * end, void * base) { + ptrdiff_t addr_imp, reldata; + ptrdiff_t reloc_target = (ptrdiff_t) ((char *)end - (char*)start); + runtime_pseudo_reloc_v2 *v2_hdr = (runtime_pseudo_reloc_v2 *) start; + runtime_pseudo_reloc_item_v2 *r; + + if (reloc_target < 8) + return; + /* Check if this is old version pseudo relocation version. */ + if (reloc_target >= 12 + && v2_hdr->magic1 == 0 && v2_hdr->magic2 == 0 + && v2_hdr->version == RP_VERSION_V1) + v2_hdr++; + if (v2_hdr->magic1 != 0 || v2_hdr->magic2 != 0) + { + runtime_pseudo_reloc_item_v1 * o; + for (o = (runtime_pseudo_reloc_item_v1 *) v2_hdr; + o < (runtime_pseudo_reloc_item_v1 *)end; + o++) + { + DWORD newval; + reloc_target = (ptrdiff_t) base + o->target; + newval = (*((DWORD*) reloc_target)) + o->addend; + __write_memory ((void *) reloc_target, &newval, sizeof(DWORD)); + } + return; + } + + /* Check if this is a known version. */ + if (v2_hdr->version != RP_VERSION_V2) + { +#ifdef DEBUG + fprintf (stderr, "internal mingw runtime error:" + "psuedo_reloc version %d is unknown to this runtime.\n", + (int) v2_hdr->version); +#endif + return; + } + + /* Walk over header. */ + r = (runtime_pseudo_reloc_item_v2 *) &v2_hdr[1]; + + for (; r < (runtime_pseudo_reloc_item_v2 *) end; r++) + { + reloc_target = (ptrdiff_t) base + r->target; + addr_imp = (ptrdiff_t) base + r->sym; + addr_imp = *((ptrdiff_t *) addr_imp); + + switch ((r->flags & 0xff)) + { + case 8: + reldata = (ptrdiff_t) (*((unsigned char *)reloc_target)); + if ((reldata & 0x80) != 0) + reldata |= ~((ptrdiff_t) 0xff); + break; + case 16: + reldata = (ptrdiff_t) (*((unsigned short *)reloc_target)); + if ((reldata & 0x8000) != 0) + reldata |= ~((ptrdiff_t) 0xffff); + break; + case 32: + reldata = (ptrdiff_t) (*((unsigned int *)reloc_target)); +#ifdef _WIN64 + if ((reldata & 0x80000000) != 0) + reldata |= ~((ptrdiff_t) 0xffffffff); +#endif + break; +#ifdef _WIN64 + case 64: + reldata = (ptrdiff_t) (*((unsigned long long *)reloc_target)); + break; +#endif + default: + reldata=0; +#ifdef DEBUG + fprintf(stderr, "internal mingw runtime error: " + "unknown pseudo_reloc bit size %d\n", + (int) (r->flags & 0xff)); +#endif + break; + } + reldata -= ((ptrdiff_t) base + r->sym); + reldata += addr_imp; + switch ((r->flags & 0xff)) + { + case 8: + __write_memory ((void *) reloc_target, &reldata, 1); + break; + case 16: + __write_memory ((void *) reloc_target, &reldata, 2); + break; + case 32: + __write_memory ((void *) reloc_target, &reldata, 4); + break; +#ifdef _WIN64 + case 64: + __write_memory ((void *) reloc_target, &reldata, 8); + break; +#endif + } + } + } + +void + _pei386_runtime_relocator () +{ + static int was_init = 0; + if (was_init) + return; + ++was_init; do_pseudo_reloc (&__RUNTIME_PSEUDO_RELOC_LIST__, &__RUNTIME_PSEUDO_RELOC_LIST_END__, &_image_base__);