77f041114d738b82d27ff176b485cf10648421c1
[reactos.git] / lib / 3rdparty / 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 <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 }