OSDN Git Service

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