OSDN Git Service

2009-07-18 Gregory McGarry <gregorymcgarry@users.sourceforge.net>
[pf3gnuchains/pf3gnuchains4x.git] / winsup / mingw / pseudo-reloc.c
1 /* pseudo-reloc.c
2
3    Contributed by Egor Duda  <deo@logos-m.ru>
4    Modified by addition of runtime_pseudo_reloc version 2
5    by Kai Tietz  <kai.tietz@onevision.com>
6         
7    THIS SOFTWARE IS NOT COPYRIGHTED
8
9    This source code is offered for use in the public domain. You may
10    use, modify or distribute it freely.
11
12    This code is distributed in the hope that it will be useful but
13    WITHOUT ANY WARRANTY. ALL WARRENTIES, EXPRESS OR IMPLIED ARE HEREBY
14    DISCLAMED. This includes but is not limited to warrenties of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 */
17
18 #include <windows.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <assert.h>
23  
24  extern char __RUNTIME_PSEUDO_RELOC_LIST__;
25  extern char __RUNTIME_PSEUDO_RELOC_LIST_END__;
26  extern char _image_base__;
27  
28 typedef struct {
29   DWORD addend;
30   DWORD target;
31 } runtime_pseudo_reloc_item_v1;
32
33 typedef struct {
34   DWORD sym;
35   DWORD target;
36   DWORD flags;
37 } runtime_pseudo_reloc_item_v2;
38
39 typedef struct {
40   DWORD magic1;
41   DWORD magic2;
42   DWORD version;
43 } runtime_pseudo_reloc_v2;
44
45 static void
46 __write_memory (void *addr,const void *src,size_t len)
47 {
48   MEMORY_BASIC_INFORMATION b;
49   DWORD oldprot;
50   if (!len)
51     return;
52   assert (VirtualQuery (addr, &b, sizeof(b)));
53   /* Temporarily allow write access to read-only protected memory.  */
54   if (b.Protect != PAGE_EXECUTE_READWRITE && b.Protect != PAGE_READWRITE)
55     VirtualProtect (b.BaseAddress, b.RegionSize, PAGE_EXECUTE_READWRITE,
56                   &oldprot);
57   memcpy (addr, src, len);
58   if (b.Protect != PAGE_EXECUTE_READWRITE && b.Protect != PAGE_READWRITE)
59     VirtualProtect (b.BaseAddress, b.RegionSize, oldprot, &oldprot);
60 }
61
62 #define RP_VERSION_V1 0
63 #define RP_VERSION_V2 1
64  
65 static void
66 do_pseudo_reloc (void * start, void * end, void * base)
67 {
68   ptrdiff_t addr_imp, reldata;
69   ptrdiff_t reloc_target = (ptrdiff_t) ((char *)end - (char*)start);
70   runtime_pseudo_reloc_v2 *v2_hdr = (runtime_pseudo_reloc_v2 *) start;
71   runtime_pseudo_reloc_item_v2 *r;
72
73   if (reloc_target < 8)
74     return;
75   /* Check if this is old version pseudo relocation version.  */
76   if (reloc_target >= 12
77       && v2_hdr->magic1 == 0 && v2_hdr->magic2 == 0
78       && v2_hdr->version == RP_VERSION_V1)
79     v2_hdr++;
80   if (v2_hdr->magic1 != 0 || v2_hdr->magic2 != 0)
81     {
82       runtime_pseudo_reloc_item_v1 * o;
83       for (o = (runtime_pseudo_reloc_item_v1 *) v2_hdr;
84            o < (runtime_pseudo_reloc_item_v1 *)end;
85            o++)
86         {
87           DWORD newval;
88           reloc_target = (ptrdiff_t) base + o->target;
89           newval = (*((DWORD*) reloc_target)) + o->addend;
90           __write_memory ((void *) reloc_target, &newval, sizeof(DWORD));
91         }
92       return;
93     }
94
95   /* Check if this is a known version.  */
96   if (v2_hdr->version != RP_VERSION_V2)
97     {
98 #ifdef DEBUG
99       fprintf (stderr, "internal mingw runtime error:"
100                "psuedo_reloc version %d is unknown to this runtime.\n",
101                (int) v2_hdr->version);
102 #endif
103       return;
104     }
105
106   /* Walk over header.  */
107   r = (runtime_pseudo_reloc_item_v2 *) &v2_hdr[1];
108
109   for (; r < (runtime_pseudo_reloc_item_v2 *) end; r++)
110     {
111       reloc_target = (ptrdiff_t) base + r->target;
112       addr_imp = (ptrdiff_t) base + r->sym;
113       addr_imp = *((ptrdiff_t *) addr_imp);
114
115       switch ((r->flags & 0xff))
116         {
117           case 8:
118             reldata = (ptrdiff_t) (*((unsigned char *)reloc_target));
119             if ((reldata & 0x80) != 0)
120               reldata |= ~((ptrdiff_t) 0xff);
121             break;
122           case 16:
123             reldata = (ptrdiff_t) (*((unsigned short *)reloc_target));
124             if ((reldata & 0x8000) != 0)
125               reldata |= ~((ptrdiff_t) 0xffff);
126             break;
127           case 32:
128             reldata = (ptrdiff_t) (*((unsigned int *)reloc_target));
129 #ifdef _WIN64
130             if ((reldata & 0x80000000) != 0)
131               reldata |= ~((ptrdiff_t) 0xffffffff);
132 #endif
133             break;
134 #ifdef _WIN64
135           case 64:
136             reldata = (ptrdiff_t) (*((unsigned long long *)reloc_target));
137             break;
138 #endif
139           default:
140             reldata=0;
141 #ifdef DEBUG
142             fprintf(stderr, "internal mingw runtime error: "
143                     "unknown pseudo_reloc bit size %d\n",
144                     (int) (r->flags & 0xff));
145 #endif
146             break;
147         }
148       reldata -= ((ptrdiff_t) base + r->sym);
149       reldata += addr_imp;
150       switch ((r->flags & 0xff))
151         {
152          case 8:
153            __write_memory ((void *) reloc_target, &reldata, 1);
154            break;
155          case 16:
156            __write_memory ((void *) reloc_target, &reldata, 2);
157            break;
158          case 32:
159            __write_memory ((void *) reloc_target, &reldata, 4);
160            break;
161 #ifdef _WIN64
162          case 64:
163            __write_memory ((void *) reloc_target, &reldata, 8);
164            break;
165 #endif
166         }
167      }
168  }
169  
170 void
171  _pei386_runtime_relocator ()
172 {
173   static int was_init = 0;
174   if (was_init)
175     return;
176   ++was_init;
177   do_pseudo_reloc (&__RUNTIME_PSEUDO_RELOC_LIST__,
178                    &__RUNTIME_PSEUDO_RELOC_LIST_END__,
179                    &_image_base__);
180 }