/* pseudo-reloc.c 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 use, modify or distribute it freely. This code is distributed in the hope that it will be useful but WITHOUT ANY WARRANTY. ALL WARRENTIES, EXPRESS OR IMPLIED ARE HEREBY DISCLAMED. This includes but is not limited to warrenties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #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; typedef struct { DWORD sym; DWORD target; DWORD flags; } runtime_pseudo_reloc_item_v2; typedef struct { DWORD magic1; DWORD magic2; DWORD version; } runtime_pseudo_reloc_v2; static void __write_memory (void *addr,const void *src,size_t len) { 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); } #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__); }