OSDN Git Service

gas/opcodes: blackfin: move dsp mac func defines to common header
[pf3gnuchains/sourceware.git] / winsup / cygwin / pseudo-reloc.cc
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 #ifndef __CYGWIN__
19 # include "windows.h"
20 # define NO_COPY
21 #else
22 # include "winsup.h"
23 # include <wchar.h>
24 # include <ntdef.h>
25 # include <sys/cygwin.h>
26 /* custom status code: */
27 # define STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION ((NTSTATUS) 0xe0000269)
28 #endif
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <memory.h>
34
35 #ifdef __GNUC__
36 #define ATTRIBUTE_NORETURN __attribute__ ((noreturn))
37 #else
38 #define ATTRIBUTE_NORETURN
39 #endif
40
41 #ifndef __MINGW_LSYMBOL
42 #define __MINGW_LSYMBOL(sym) sym
43 #endif
44
45 extern char __RUNTIME_PSEUDO_RELOC_LIST__;
46 extern char __RUNTIME_PSEUDO_RELOC_LIST_END__;
47 extern char __MINGW_LSYMBOL(_image_base__);
48
49 /* v1 relocation is basically:
50  *   *(base + .target) += .addend
51  * where (base + .target) is always assumed to point
52  * to a DWORD (4 bytes).
53  */
54 typedef struct {
55   DWORD addend;
56   DWORD target;
57 } runtime_pseudo_reloc_item_v1;
58
59 /* v2 relocation is more complex. In effect, it is
60  *    *(base + .target) += *(base + .sym) - (base + .sym)
61  * with care taken in both reading, sign extension, and writing
62  * because .flags may indicate that (base + .target) may point
63  * to a BYTE, WORD, DWORD, or QWORD (w64).
64  */
65 typedef struct {
66   DWORD sym;
67   DWORD target;
68   DWORD flags;
69 } runtime_pseudo_reloc_item_v2;
70
71 typedef struct {
72   DWORD magic1;
73   DWORD magic2;
74   DWORD version;
75 } runtime_pseudo_reloc_v2;
76
77 static void ATTRIBUTE_NORETURN
78 __report_error (const char *msg, ...)
79 {
80 #ifdef __CYGWIN__
81   /* This function is used to print short error messages
82    * to stderr, which may occur during DLL initialization
83    * while fixing up 'pseudo' relocations. This early, we
84    * may not be able to use cygwin stdio functions, so we
85    * use the win32 WriteFile api. This should work with both
86    * normal win32 console IO handles, redirected ones, and
87    * cygwin ptys.
88    */
89   char buf[128];
90   wchar_t module[MAX_PATH];
91   char * posix_module = NULL;
92   static const char UNKNOWN_MODULE[] = "<unknown module>: ";
93   static const char CYGWIN_FAILURE_MSG[] = "Cygwin runtime failure: ";
94   HANDLE errh = GetStdHandle (STD_ERROR_HANDLE);
95   ssize_t modulelen = GetModuleFileNameW (NULL, module, sizeof (module));
96   va_list args;
97
98   /* FIXME: cleanup further to avoid old use of cygwin_internal */
99   if (errh == INVALID_HANDLE_VALUE)
100     cygwin_internal (CW_EXIT_PROCESS, STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION, 1);
101
102   if (modulelen > 0)
103     posix_module = (char *) cygwin_create_path (CCP_WIN_W_TO_POSIX, module);
104
105   va_start (args, msg);
106   vsnprintf (buf, sizeof (buf), msg, args);
107   va_end (args);
108   buf[sizeof (buf) - 1] = '\0'; /* paranoia */
109
110   small_printf ("%s%s: %s\n", CYGWIN_FAILURE_MSG, posix_module ?: UNKNOWN_MODULE, buf);
111   if (posix_module)
112     free (posix_module);
113
114   cygwin_internal (CW_EXIT_PROCESS, STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION, 1);
115   /* not reached, but silences noreturn warning */
116   abort ();
117 #else
118   va_list argp;
119   va_start (argp, msg);
120 # ifdef __MINGW64_VERSION_MAJOR
121   fprintf (stderr, "Mingw-w64 runtime failure:\n");
122 # else
123   fprintf (stderr, "Mingw runtime failure:\n");
124 # endif
125   vfprintf (stderr, msg, argp);
126   va_end (argp);
127   abort ();
128 #endif
129 }
130
131 /* This function temporarily marks the page containing addr
132  * writable, before copying len bytes from *src to *addr, and
133  * then restores the original protection settings to the page.
134  *
135  * Using this function eliminates the requirement with older
136  * pseudo-reloc implementations, that sections containing
137  * pseudo-relocs (such as .text and .rdata) be permanently
138  * marked writable. This older behavior sabotaged any memory
139  * savings achieved by shared libraries on win32 -- and was
140  * slower, too.  However, on cygwin as of binutils 2.20 the
141  * .text section is still marked writable, and the .rdata section
142  * is folded into the (writable) .data when --enable-auto-import.
143  */
144 static void
145 __write_memory (void *addr, const void *src, size_t len)
146 {
147   MEMORY_BASIC_INFORMATION b;
148   DWORD oldprot;
149
150   if (!len)
151     return;
152
153   if (!VirtualQuery (addr, &b, sizeof (b)))
154     {
155       __report_error ("  VirtualQuery failed for %d bytes at address %p",
156                       (int) sizeof (b), addr);
157     }
158
159   /* Temporarily allow write access to read-only protected memory.  */
160   if (b.Protect != PAGE_EXECUTE_READWRITE && b.Protect != PAGE_READWRITE)
161     VirtualProtect (b.BaseAddress, b.RegionSize, PAGE_EXECUTE_READWRITE,
162                   &oldprot);
163   /* write the data. */
164   memcpy (addr, src, len);
165   /* Restore original protection. */
166   if (b.Protect != PAGE_EXECUTE_READWRITE && b.Protect != PAGE_READWRITE)
167     VirtualProtect (b.BaseAddress, b.RegionSize, oldprot, &oldprot);
168 }
169
170 #define RP_VERSION_V1 0
171 #define RP_VERSION_V2 1
172
173 static void
174 do_pseudo_reloc (void * start, void * end, void * base)
175 {
176   ptrdiff_t addr_imp, reldata;
177   ptrdiff_t reloc_target = (ptrdiff_t) ((char *)end - (char*)start);
178   runtime_pseudo_reloc_v2 *v2_hdr = (runtime_pseudo_reloc_v2 *) start;
179   runtime_pseudo_reloc_item_v2 *r;
180
181   /* A valid relocation list will contain at least one entry, and
182    * one v1 data structure (the smallest one) requires two DWORDs.
183    * So, if the relocation list is smaller than 8 bytes, bail.
184    */
185   if (reloc_target < 8)
186     return;
187
188   /* Check if this is the old pseudo relocation version.  */
189   /* There are two kinds of v1 relocation lists:
190    *   1) With a (v2-style) version header. In this case, the
191    *      first entry in the list is a 3-DWORD structure, with
192    *      value:
193    *         { 0, 0, RP_VERSION_V1 }
194    *      In this case, we skip to the next entry in the list,
195    *      knowing that all elements after the head item can
196    *      be cast to runtime_pseudo_reloc_item_v1.
197    *   2) Without a (v2-style) version header. In this case, the
198    *      first element in the list IS an actual v1 relocation
199    *      record, which is two DWORDs.  Because there will never
200    *      be a case where a v1 relocation record has both
201    *      addend == 0 and target == 0, this case will not be
202    *      confused with the prior one.
203    * All current binutils, when generating a v1 relocation list,
204    * use the second (e.g. original) form -- that is, without the
205    * v2-style version header.
206    */
207   if (reloc_target >= 12
208       && v2_hdr->magic1 == 0 && v2_hdr->magic2 == 0
209       && v2_hdr->version == RP_VERSION_V1)
210     {
211       /* We have a list header item indicating that the rest
212        * of the list contains v1 entries.  Move the pointer to
213        * the first true v1 relocation record.  By definition,
214        * that v1 element will not have both addend == 0 and
215        * target == 0 (and thus, when interpreted as a
216        * runtime_pseudo_reloc_v2, it will not have both
217        * magic1 == 0 and magic2 == 0).
218        */
219       v2_hdr++;
220     }
221
222   if (v2_hdr->magic1 != 0 || v2_hdr->magic2 != 0)
223     {
224       /*************************
225        * Handle v1 relocations *
226        *************************/
227       runtime_pseudo_reloc_item_v1 * o;
228       for (o = (runtime_pseudo_reloc_item_v1 *) v2_hdr;
229            o < (runtime_pseudo_reloc_item_v1 *)end;
230            o++)
231         {
232           DWORD newval;
233           reloc_target = (ptrdiff_t) base + o->target;
234           newval = (*((DWORD*) reloc_target)) + o->addend;
235           __write_memory ((void *) reloc_target, &newval, sizeof (DWORD));
236         }
237       return;
238     }
239
240   /* If we got this far, then we have relocations of version 2 or newer */
241
242   /* Check if this is a known version.  */
243   if (v2_hdr->version != RP_VERSION_V2)
244     {
245       __report_error ("  Unknown pseudo relocation protocol version %d.\n",
246                       (int) v2_hdr->version);
247       return;
248     }
249
250   /*************************
251    * Handle v2 relocations *
252    *************************/
253
254   /* Walk over header. */
255   r = (runtime_pseudo_reloc_item_v2 *) &v2_hdr[1];
256
257   for (; r < (runtime_pseudo_reloc_item_v2 *) end; r++)
258     {
259       /* location where new address will be written */
260       reloc_target = (ptrdiff_t) base + r->target;
261
262       /* get sym pointer. It points either to the iat entry
263        * of the referenced element, or to the stub function.
264        */
265       addr_imp = (ptrdiff_t) base + r->sym;
266       addr_imp = *((ptrdiff_t *) addr_imp);
267
268       /* read existing relocation value from image, casting to the
269        * bitsize indicated by the 8 LSBs of flags. If the value is
270        * negative, manually sign-extend to ptrdiff_t width. Raise an
271        * error if the bitsize indicated by the 8 LSBs of flags is not
272        * supported.
273        */
274       switch ((r->flags & 0xff))
275         {
276         case 8:
277           reldata = (ptrdiff_t) (*((unsigned char *)reloc_target));
278           if ((reldata & 0x80) != 0)
279             reldata |= ~((ptrdiff_t) 0xff);
280           break;
281         case 16:
282           reldata = (ptrdiff_t) (*((unsigned short *)reloc_target));
283           if ((reldata & 0x8000) != 0)
284             reldata |= ~((ptrdiff_t) 0xffff);
285           break;
286         case 32:
287           reldata = (ptrdiff_t) (*((unsigned int *)reloc_target));
288 #ifdef _WIN64
289           if ((reldata & 0x80000000) != 0)
290             reldata |= ~((ptrdiff_t) 0xffffffff);
291 #endif
292           break;
293 #ifdef _WIN64
294         case 64:
295           reldata = (ptrdiff_t) (*((unsigned long long *)reloc_target));
296           break;
297 #endif
298         default:
299           reldata=0;
300           __report_error ("  Unknown pseudo relocation bit size %d.\n",
301                   (int) (r->flags & 0xff));
302           break;
303         }
304
305       /* Adjust the relocation value */
306       reldata -= ((ptrdiff_t) base + r->sym);
307       reldata += addr_imp;
308
309       /* Write the new relocation value back to *reloc_target */
310       switch ((r->flags & 0xff))
311         {
312         case 8:
313           __write_memory ((void *) reloc_target, &reldata, 1);
314           break;
315         case 16:
316           __write_memory ((void *) reloc_target, &reldata, 2);
317           break;
318         case 32:
319           __write_memory ((void *) reloc_target, &reldata, 4);
320           break;
321 #ifdef _WIN64
322         case 64:
323           __write_memory ((void *) reloc_target, &reldata, 8);
324           break;
325 #endif
326         }
327      }
328 }
329
330 #ifdef __CYGWIN__
331 void
332 _pei386_runtime_relocator (per_process *u)
333 {
334   if (CYGWIN_VERSION_USE_PSEUDO_RELOC_IN_DLL (u))
335     do_pseudo_reloc (u->pseudo_reloc_start, u->pseudo_reloc_end, u->image_base);
336 }
337 #else
338 extern "C" void
339 _pei386_runtime_relocator (void)
340 {
341   static NO_COPY int was_init = 0;
342   if (was_init)
343     return;
344   ++was_init;
345   do_pseudo_reloc (&__RUNTIME_PSEUDO_RELOC_LIST__,
346                    &__RUNTIME_PSEUDO_RELOC_LIST_END__,
347                    &__MINGW_LSYMBOL(_image_base__));
348 }
349 #endif