/* 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) { #ifndef __MINGW64_VERSION_MAJOR MEMORY_BASIC_INFORMATION b; DWORD oldprot; #endif /* ! __MINGW64_VERSION_MAJOR */ if (!len) return; #ifndef __MINGW64_VERSION_MAJOR if (!VirtualQuery (addr, &b, sizeof(b))) { __report_error (" VirtualQuery failed for %d bytes at address %p", (int) sizeof(b), addr); } /* 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); #else /* ! __MINGW64_VERSION_MAJOR */ mark_section_writable ((LPVOID) addr); #endif /* __MINGW64_VERSION_MAJOR */ /* write the data. */ memcpy (addr, src, len); /* Restore original protection. */ #ifndef __MINGW64_VERSION_MAJOR if (b.Protect != PAGE_EXECUTE_READWRITE && b.Protect != PAGE_READWRITE) VirtualProtect (b.BaseAddress, b.RegionSize, oldprot, &oldprot); #endif /* !__MINGW64_VERSION_MAJOR */ }
/* 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; if (!len) return; if (!VirtualQuery (addr, &b, sizeof(b))) { __report_error (" VirtualQuery failed for %d bytes at address %p", (int) sizeof(b), addr); } /* 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); }
static void mark_section_writable (LPVOID addr) { MEMORY_BASIC_INFORMATION b; PIMAGE_SECTION_HEADER h; int i; for (i = 0; i < maxSections; i++) { if (the_secs[i].sec_start <= ((LPBYTE) addr) && ((LPBYTE) addr) < (the_secs[i].sec_start + the_secs[i].hash->Misc.VirtualSize)) return; } h = __mingw_GetSectionForAddress (addr); if (!h) { __report_error ("Address %p has no image-section", addr); return; } the_secs[i].hash = h; the_secs[i].old_protect = 0; the_secs[i].sec_start = _GetPEImageBase () + h->VirtualAddress; if (!VirtualQuery (the_secs[i].sec_start, &b, sizeof(b))) { __report_error (" VirtualQuery failed for %d bytes at address %p", (int) h->Misc.VirtualSize, the_secs[i].sec_start); return; } if (b.Protect != PAGE_EXECUTE_READWRITE && b.Protect != PAGE_READWRITE) { if (!VirtualProtect (b.BaseAddress, b.RegionSize, PAGE_EXECUTE_READWRITE, &the_secs[i].old_protect)) __report_error (" VirtualProtect failed with code 0x%x", (int) GetLastError ()); } ++maxSections; return; }
static void restore_modified_sections (void) { int i; MEMORY_BASIC_INFORMATION b; DWORD oldprot; for (i = 0; i < maxSections; i++) { if (the_secs[i].old_protect == 0) continue; if (!VirtualQuery (the_secs[i].sec_start, &b, sizeof(b))) { __report_error (" VirtualQuery failed for %d bytes at address %p", (int) the_secs[i].hash->Misc.VirtualSize, the_secs[i].sec_start); return; } VirtualProtect (b.BaseAddress, b.RegionSize, the_secs[i].old_protect, &oldprot); } }
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; /* 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 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) { /* 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; 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; } /* 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) { __report_error (" Unknown pseudo relocation protocol version %d.\n", (int) v2_hdr->version); return; } /************************* * 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: 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; __report_error (" Unknown pseudo relocation bit size %d.\n", (int) (r->flags & 0xff)); 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: __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 } } }