This is the mail archive of the cygwin-developers 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]

Re: cygwin-1.7, pseudo-reloc v2


Christopher Faylor wrote:
> I think we have to leave it to the MinGW team to replace the two files
> in the mingw directory.

OK.

> If I am reading the DISCLAIMER file in the mingw-w64 directory correctly
> it seems like this is public-domain code so we should be able to use it.

Yes. This is restated here:
http://sourceware.org/ml/binutils/2008-11/msg00171.html
and here:
http://sourceforge.net/tracker/index.php?func=detail&aid=2537769&group_id=2435&atid=302435

> This file is linked into the executable and doesn't seem to reside in
> cygwin1.dll so it shouldn't have any effect on existing binaries.

True.

> Can anyone confirm that it works as advertised?  It will require linking
> an app which relies on this with the above object.  I don't think there
> is any reason to rebuild cygwin.  You just need that pseudo-reloc.o.

For basic tests, yes -- you could just link pseudo-reloc.o in advance of
libcygwin.a. It gets a little tricky when your test cases includes a DLL
that itself needs to import (and relocate) symbols from a second DLL.
Then you have to worry about exporting those symbols (by default the
functions from all .o's on the command line will be exported; however
the "normal" case excludes symbols that come from libcygwin.a); so a
little care is needed.

I found it easier to rebuild cygwin and install the updated libcygwin.a,
as described below.

> If it works, and I have no reason to think that it won't, would you mind
> checking this in, Chuck?

Well, the very very latest version of Kai's pseudo-reloc doesn't seem to
work in the app<--DLL<--DLL case. More below.

>> BTW, one part of Kai's change (now in binutils) changes this (for PE
>> 32bit):
>> -  link_info.pei386_runtime_pseudo_reloc = -1;
>> +  link_info.pei386_runtime_pseudo_reloc = 1; /* Use by default version
>> 1.  */
>> That is, we will always act as tho --enable-runtime-pseudo-reloc were
>> specified on the command line. This means the existing behavior ("-1" --
>> where we DO pseudo-relocs, but warn about it unless the user explicitly
>> specified --enable-runtime-pseudo-reloc) is changed to "just DO it and
>> be quiet" unless the user specifically disables it.
>>
>> I guess it's about time we made that change, but there was no discussion
>> of this particular bit.
> 
> I always reset this to -1 when I release binutils.  Maybe it's time to
> stop doing that and just let the default through.

As Kai pointed out, the warning I mentioned is actually triggered by the
presence/absence of --enable-auto-import, NOT --enable-pseudo. So
really, link_info.pei386_runtime_pseudo_reloc = -1 is no different than
link_info.pei386_runtime_pseudo_reloc = 1, for cygwin.

####################################################

FWIW, the latest version of Kai's patch doesn't seem to work very well
on cygwin. With a slight additional modification (reversion) in his
code, -v1 can be made to work as currently while still requiring .text
and .rdata sections to be writable (as currently) -- that is, no
regressions.  But -v2 just seems completely bolluxed, and the
__write_data function doesn't seem to work properly.

As it happens, you *do* need to rebuild cygwin -- because (1) the
pseudo-reloc code needs to go in libcygwin.a (2) if you include
pseudo-reloc.o directly -- expecially in a DLL -- then you must take
great care not to export its symbols (symbols in libcygwin.a are
excluded automatically).  (Or you could play games with 'ar x' and
rebuilding your existing libcygwin.a manually; but that's a lot of
hassle AND makes it difficult to debug the relocation code...)

Plus, I'm not sure that
 (a) this code should call abort()
 (b) or that errors should be reported via fprintf.

Test cases taken from Egor Duda's original ones when the pseudo-reloc
stuff was first introduced. There are two test cases: 'crtest.exe' is an
app that imports a DLL with complex data structures and accesses members
of those structs.  crtest1.exe is an app that imports a DLL -- and THAT
DLL imports a second DLL with complex data structures; the first DLL
then accesses members of those structs in the second DLL.

Also attached:  Kai's most recent pseudo-reloc.c patch, with the "slight
reversion" mentioned in [3] below and the public domain disclosure restored.

I used ld.exe built from today's CVS (includes Kai's most recent
changes). I can make that available, but it'll take me a while to
produce the proper src package so as to abide by GPL.

Tests performed under cygwin-1.7, Vista, as a non-Administrators-group user.

--
Chuck

[1] Original (existing cygwin) test-app, test-dll with -v1 relocs. These
are the expected results.
$ ./crtest
child: data=1 1
parent: data=1 23 e
$ ./crtest1
child: data=1 23 e
parent: data=1 23 e

[2] With Kai's change, and most recent ld.exe (20080126) with -v1 relocs
$ ./crtest
child: data=1 1
parent: data=1 23 e
$ ./crtest1
      3 [main] crtest1 6040 child_copy: linked dll data write copy
failed, 0x242000..0x242040, done 0, windows pid 5556, Win32 error 487
fork: Resource temporarily unavailable

[3] Modify -v1 handling in pseudo-reloc.c to directly rewrite the reloc,
rather than using __write_memory)
$ ./crtest
child: data=1 1
parent: data=1 23 e
$ ./crtest1
child: data=1 23 e
parent: data=1 23 e

[4] Using pseudo-reloc code as in [2] OR [3], but with -v2 relocs
$ ./crtest
child: data=268443648 1628191118 ±
parent: data=1 23 e
$ ./crtest1
      3 [main] crtest1 3080 child_copy: linked dll data write copy
failed, 0x842000..0x842040, done 0, windows pid 2308, Win32 error 87
fork: Resource temporarily unavailable


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 -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	26 Jan 2009 22:53:24 -0000
@@ -1,6 +1,7 @@
 /* pseudo-reloc.c
 
    Written by Egor Duda <deo@logos-m.ru>
+   Substantial modifications by Kai Teitz <ktietz70@googlemail.com>
    THIS SOFTWARE IS NOT COPYRIGHTED
 
    This source code is offered for use in the public domain. You may
@@ -13,34 +14,153 @@
 */
 
 #include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
 
 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;
+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;
+  if (!VirtualQuery (addr, &b, sizeof(b)))
+    abort ();
+  // Protect
+  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
 
 void
-do_pseudo_reloc (void* start, void* end, void* base)
+do_pseudo_reloc (void* start,void *end,void *base)
 {
-  DWORD reloc_target;
-  runtime_pseudo_reloc* r;
-  for (r = (runtime_pseudo_reloc*) start; r < (runtime_pseudo_reloc*) end; r++)
+  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)); */
+          *((DWORD*) reloc_target) = newval;
+        }
+      return;
+    }
+  /* Check if this is a known version.  */
+  if (v2_hdr->version != RP_VERSION_V2)
+    {
+      fprintf (stderr, "pseudo_relocation protocol version %d is unknown to this runtime.\n",
+	       (int) v2_hdr->version);
+      return;
+    }
+  /* Walk over header.  */
+  r = (runtime_pseudo_reloc_item_v2 *) &v2_hdr[1];
+
+  for (; r < (runtime_pseudo_reloc_item_v2 *) end; r++)
     {
-      reloc_target = (DWORD) base + r->target;
-      *((DWORD*) reloc_target) += r->addend;
+      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;
+	    fprintf(stderr, "Unknown pseudo relocation bit size %d\n",(int) (r->flags & 0xff));
+	    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 ()
 {
-  do_pseudo_reloc (&__RUNTIME_PSEUDO_RELOC_LIST__,
-		   &__RUNTIME_PSEUDO_RELOC_LIST_END__,
-		   &_image_base__);
+  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__);
 }

Attachment: pseudo-reloc-tests.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]