OSDN Git Service

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