This is the mail archive of the cygwin-patches mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Merge pseudo-reloc-v2 support from mingw/pseudo-reloc.c


As discussed on cygwin-developers...
http://cygwin.com/ml/cygwin-developers/2009-10/msg00052.html
and thread. Please refer to that thread for the justification of this
patch, and some of the details of its evolution.

The changelog entry below is somewhat odd (multiple sections) because of
the licensing differences in the various files. Kai's only contribution
was to the pseudo-reloc.c code, which is entirely public domain (and he
placed his contributions to that file into the public domain, as well).
Similarly, my contributions to pseudo-reloc.c are also public domain.

Bowever, the changes to the other cygwin files are mine and are covered
by assignment to Red Hat.

Hence, three separate "entries". One question: when it comes time to
commit this to CVS, should it be done all in one lump, or 1-2-3 very
quick separate commits (even though the tree would be broken between,
say, #1 and #2)?

2009-10-06  Charles Wilson  <...>

	Additional pseudo-reloc-v2 support
	* ntdll.h: Add custom NTSTATUS value for pseudo-reloc
	errors STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION.
	* pinfo.cc (status_exit): Map custom pseudo-reloc
	error value STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION to 127.
	* sigproc.cc (child_info::proc_retry): Return exit code when
        STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION.

2009-10-06  Charles Wilson  <...>

	Cygwin modifications to pseudo-reloc.c
	* lib/pseudo-reloc.c: 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().
	(__print_reloc_error): New function for reporting errors in a
	manner supported by cygwin at this early stage of the process
	life-cycle.
[*]	(_pei386_runtime_relocator): Ensure relocations performed
	only once for each address space, but are repeated after fork()
	in the new address space.
	(__write_memory) [MINGW]: Ensure that b is always initialized
	by call to VirtualQuery, even if -DNDEBUG.

2009-10-06  Kai Tietz  <...>

	Rewrite (copied from winsup/mingw/pseudo-reloc.c)
	* lib/pseudo-reloc.c: Rewritten to support v2 pseudo-relocs.
	(struct runtime_pseudo_reloc): Renamed to...
	(struct runtime_pseudo_reloc_item_v1): this.
	(struct runtime_pseudo_reloc_item_v2): New typedef.
	(struct runtime_pseudo_reloc_v2): New typedef.
	(__write_memory): New function (no longer need writable .text
	section, nor need to fold .rdata into .data).
	(do_pseudo_reloc): Rewritten entirely, to support both
	v1 and v2 relocations.
	(_pei386_runtime_relocator): Ensure relocations performed
	only once for each address space.


There are three attachments:

1) The full patch (04-pseudo-reloc-v2-support.patch)
2) The diff between winsup/mingw/pseudo-reloc.c and
winsup/cygwin/lib/pseudo-reloc.c -- that is, the diff between
pseudo-commit #1 and pseudo-commit #2.
(04-pseudo-reloc-cygwin-changes.patch)
3) A tarball containing a number of (standalone) test cases
(pseudo-reloc-tests-v3.tar.bz2).

After building and installing the new DLL (and libcygwin.a), the test
cases should be fairly explanatory. There are individual READMEs and
expected output is documented. To build, simply do 'make' in the
appropriate subdirectory.  You will need binutils-2.19.51-1 (or CVS
binutils newer than 2008-11-24) to test the v2 relocation, but the v1
tests will work with any cygwin binutils newer than 2003.

BTW: applications compiled to use v2 relocs will still work if you
execute those apps under an older cygwin DLL -- *unless* an error
condition occurs and the cygwin_internal(CW_EXIT_PROCESS, ...) is
triggered. Older cygwins don't support that particular cygwin_internal flag.

[*] by using NO_COPY on the was_init state variable in
_pei386_runtime_relocator, we ensure that the relocation is repeated in
the forkee. However, if at some point ld is changed so that .text (and
.rdata) are marked non-writable (unlike now, where .text is explicitly
writable and .rdata is folded in to the writable .data section), it's
not clear to me what effect that might have on the cygwin's (and
win32's) behavior when setting up the forkee's memory contents.

But...we can cross that bridge when we come to it. It may be that
nothing would need to change.

--
Chuck
? newlib/libc/libc.info
? newlib/libm/libm.info
Index: winsup/cygwin/ntdll.h
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/ntdll.h,v
retrieving revision 1.94
diff -u -p -u -r1.94 ntdll.h
--- winsup/cygwin/ntdll.h	14 Jul 2009 17:37:42 -0000	1.94
+++ winsup/cygwin/ntdll.h	6 Oct 2009 22:26:37 -0000
@@ -46,6 +46,8 @@
 #define STATUS_ENTRYPOINT_NOT_FOUND   ((NTSTATUS) 0xc0000139)
 #define STATUS_BAD_DLL_ENTRYPOINT     ((NTSTATUS) 0xc0000251)
 #define STATUS_ILLEGAL_DLL_RELOCATION ((NTSTATUS) 0xc0000269)
+/* custom status code: */
+#define STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION ((NTSTATUS) 0xe0000269)
 
 #define PDI_MODULES 0x01
 #define PDI_HEAPS 0x04
Index: winsup/cygwin/pinfo.cc
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/pinfo.cc,v
retrieving revision 1.255
diff -u -p -u -r1.255 pinfo.cc
--- winsup/cygwin/pinfo.cc	6 Oct 2009 21:51:17 -0000	1.255
+++ winsup/cygwin/pinfo.cc	6 Oct 2009 22:26:37 -0000
@@ -128,6 +128,10 @@ status_exit (DWORD x)
 	x = 127;
       }
       break;
+    case STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION: /* custom error value */
+      /* We've already printed the error message in pseudo-reloc.c */
+      x = 127;
+      break;
     default:
       x = 127;
     }
Index: winsup/cygwin/sigproc.cc
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/sigproc.cc,v
retrieving revision 1.317
diff -u -p -u -r1.317 sigproc.cc
--- winsup/cygwin/sigproc.cc	2 Aug 2009 21:38:40 -0000	1.317
+++ winsup/cygwin/sigproc.cc	6 Oct 2009 22:26:37 -0000
@@ -923,6 +923,8 @@ child_info::proc_retry (HANDLE h)
       break;
     case STATUS_DLL_NOT_FOUND:
       return exit_code;
+    case STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION: /* pseudo-reloc.c specific */
+      return exit_code;
     case STATUS_CONTROL_C_EXIT:
       if (saw_ctrl_c ())
 	return EXITCODE_OK;
Index: winsup/cygwin/lib/pseudo-reloc.c
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/lib/pseudo-reloc.c,v
retrieving revision 1.1
diff -u -p -u -r1.1 pseudo-reloc.c
--- winsup/cygwin/lib/pseudo-reloc.c	9 Dec 2002 22:49:12 -0000	1.1
+++ winsup/cygwin/lib/pseudo-reloc.c	6 Oct 2009 22:26:38 -0000
@@ -1,6 +1,9 @@
 /* pseudo-reloc.c
 
-   Written by Egor Duda <deo@logos-m.ru>
+   Contributed by Egor Duda  <deo@logos-m.ru>
+   Modified by addition of runtime_pseudo_reloc version 2
+   by Kai Tietz  <kai.tietz@onevision.com>
+	
    THIS SOFTWARE IS NOT COPYRIGHTED
 
    This source code is offered for use in the public domain. You may
@@ -13,33 +16,356 @@
 */
 
 #include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#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__;
 
-typedef struct
-  {
-    DWORD addend;
-    DWORD target;
-  }
-runtime_pseudo_reloc;
+/* 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;
 
-void
-do_pseudo_reloc (void* start, void* end, void* base)
+/* 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;
+  DWORD flags;
+} runtime_pseudo_reloc_item_v2;
+
+typedef struct {
+  DWORD magic1;
+  DWORD magic2;
+  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, ...)
 {
-  DWORD reloc_target;
-  runtime_pseudo_reloc* r;
-  for (r = (runtime_pseudo_reloc*) start; r < (runtime_pseudo_reloc*) end; r++)
+  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)
     {
-      reloc_target = (DWORD) base + r->target;
-      *((DWORD*) reloc_target) += r->addend;
+      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;
+
+  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)
+{
+  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)
+    {
+#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:
+	    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;
+#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:
+           __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 NO_COPY int was_init = 0;
+  if (was_init)
+    return;
+  ++was_init;
   do_pseudo_reloc (&__RUNTIME_PSEUDO_RELOC_LIST__,
 		   &__RUNTIME_PSEUDO_RELOC_LIST_END__,
 		   &_image_base__);
--- winsup/mingw/pseudo-reloc.c	2009-03-17 09:18:08.463300000 -0400
+++ winsup/cygwin/lib/pseudo-reloc.c	2009-10-06 18:26:07.166100000 -0400
@@ -20,16 +20,40 @@
 #include <stdlib.h>
 #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,26 +66,114 @@
   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);
 }
 
 #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 @@
   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 @@
       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 @@
 #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 @@
         }
      }
  }
- 
+
 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;

Attachment: pseudo-reloc-tests-v3.tar.bz2
Description: Binary data


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]