Sync pseudo-reloc.c with cygwin/lib/
This commit is contained in:
parent
5d74ab42d5
commit
060a658fe1
|
@ -1,3 +1,20 @@
|
|||
2009-10-23 Charles Wilson <mingw@cwilson.fastmail.fm>
|
||||
|
||||
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 <keithmarshall@users.sourceforge.net>
|
||||
|
||||
Make MinGW printf() "%p" format compatible with MSVCRT scanf().
|
||||
|
|
|
@ -21,15 +21,39 @@
|
|||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
extern char __RUNTIME_PSEUDO_RELOC_LIST__;
|
||||
extern char __RUNTIME_PSEUDO_RELOC_LIST_END__;
|
||||
extern char _image_base__;
|
||||
#if defined(__CYGWIN__)
|
||||
#include <wchar.h>
|
||||
#include <ntdef.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/cygwin.h>
|
||||
/* 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,19 +66,107 @@ 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 = "<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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/*************************
|
||||
* 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
|
||||
#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:
|
||||
|
@ -168,9 +360,9 @@ 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;
|
||||
|
|
Loading…
Reference in New Issue