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>
7 THIS SOFTWARE IS NOT COPYRIGHTED
9 This source code is offered for use in the public domain. You may
10 use, modify or distribute it freely.
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.
18 //#include <windows.h>
25 #if defined(__CYGWIN__)
28 #include <sys/cygwin.h>
29 /* copied from winsup.h */
30 # define NO_COPY __attribute__((nocommon)) __attribute__((section(".data_cygwin_nocopy")))
31 /* custom status code: */
32 #define STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION ((NTSTATUS) 0xe0000269)
33 #define SHORT_MSG_BUF_SZ 128
39 #define ATTRIBUTE_NORETURN __attribute__ ((noreturn))
41 #define ATTRIBUTE_NORETURN
44 #ifndef __MINGW_LSYMBOL
45 #define __MINGW_LSYMBOL(sym) sym
48 extern char __RUNTIME_PSEUDO_RELOC_LIST__
;
49 extern char __RUNTIME_PSEUDO_RELOC_LIST_END__
;
50 extern char __MINGW_LSYMBOL(_image_base__
);
52 void _pei386_runtime_relocator (void);
54 /* v1 relocation is basically:
55 * *(base + .target) += .addend
56 * where (base + .target) is always assumed to point
57 * to a DWORD (4 bytes).
62 } runtime_pseudo_reloc_item_v1
;
64 /* v2 relocation is more complex. In effect, it is
65 * *(base + .target) += *(base + .sym) - (base + .sym)
66 * with care taken in both reading, sign extension, and writing
67 * because .flags may indicate that (base + .target) may point
68 * to a BYTE, WORD, DWORD, or QWORD (w64).
74 } runtime_pseudo_reloc_item_v2
;
80 } runtime_pseudo_reloc_v2
;
82 static void ATTRIBUTE_NORETURN
83 __report_error (const char *msg
, ...)
86 /* This function is used to print short error messages
87 * to stderr, which may occur during DLL initialization
88 * while fixing up 'pseudo' relocations. This early, we
89 * may not be able to use cygwin stdio functions, so we
90 * use the win32 WriteFile api. This should work with both
91 * normal win32 console IO handles, redirected ones, and
94 char buf
[SHORT_MSG_BUF_SZ
];
95 wchar_t module
[MAX_PATH
];
96 char * posix_module
= NULL
;
97 static const char UNKNOWN_MODULE
[] = "<unknown module>: ";
98 static const size_t UNKNOWN_MODULE_LEN
= sizeof (UNKNOWN_MODULE
) - 1;
99 static const char CYGWIN_FAILURE_MSG
[] = "Cygwin runtime failure: ";
100 static const size_t CYGWIN_FAILURE_MSG_LEN
= sizeof (CYGWIN_FAILURE_MSG
) - 1;
104 HANDLE errh
= GetStdHandle (STD_ERROR_HANDLE
);
105 ssize_t modulelen
= GetModuleFileNameW (NULL
, module
, sizeof (module
));
107 if (errh
== INVALID_HANDLE_VALUE
)
108 cygwin_internal (CW_EXIT_PROCESS
,
109 STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION
,
113 posix_module
= cygwin_create_path (CCP_WIN_W_TO_POSIX
, module
);
115 va_start (args
, msg
);
116 len
= (DWORD
) vsnprintf (buf
, SHORT_MSG_BUF_SZ
, msg
, args
);
118 buf
[SHORT_MSG_BUF_SZ
-1] = '\0'; /* paranoia */
122 WriteFile (errh
, (PCVOID
)CYGWIN_FAILURE_MSG
,
123 CYGWIN_FAILURE_MSG_LEN
, &done
, NULL
);
124 WriteFile (errh
, (PCVOID
)posix_module
,
125 strlen(posix_module
), &done
, NULL
);
126 WriteFile (errh
, (PCVOID
)": ", 2, &done
, NULL
);
127 WriteFile (errh
, (PCVOID
)buf
, len
, &done
, NULL
);
132 WriteFile (errh
, (PCVOID
)CYGWIN_FAILURE_MSG
,
133 CYGWIN_FAILURE_MSG_LEN
, &done
, NULL
);
134 WriteFile (errh
, (PCVOID
)UNKNOWN_MODULE
,
135 UNKNOWN_MODULE_LEN
, &done
, NULL
);
136 WriteFile (errh
, (PCVOID
)buf
, len
, &done
, NULL
);
138 WriteFile (errh
, (PCVOID
)"\n", 1, &done
, NULL
);
140 cygwin_internal (CW_EXIT_PROCESS
,
141 STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION
,
143 /* not reached, but silences noreturn warning */
147 va_start (argp
, msg
);
148 # ifdef __MINGW64_VERSION_MAJOR
149 __mingw_fprintf (stderr
, "Mingw-w64 runtime failure:\n");
150 __mingw_vfprintf (stderr
, msg
, argp
);
152 fprintf (stderr
, "Mingw runtime failure:\n");
153 vfprintf (stderr
, msg
, argp
);
160 /* For mingw-w64 we have additional helpers to get image information
161 on runtime. This allows us to cache for pseudo-relocation pass
162 the temporary access of code/read-only sections.
163 This step speeds up pseudo-relocation pass. */
164 #ifdef __MINGW64_VERSION_MAJOR
165 extern int __mingw_GetSectionCount (void);
166 extern PIMAGE_SECTION_HEADER
__mingw_GetSectionForAddress (LPVOID p
);
167 extern PBYTE
_GetPEImageBase (void);
169 typedef struct sSecInfo
{
170 /* Keeps altered section flags, or zero if nothing was changed. */
173 PIMAGE_SECTION_HEADER hash
;
176 static sSecInfo
*the_secs
= NULL
;
177 static int maxSections
= 0;
180 mark_section_writable (LPVOID addr
)
182 MEMORY_BASIC_INFORMATION b
;
183 PIMAGE_SECTION_HEADER h
;
186 for (i
= 0; i
< maxSections
; i
++)
188 if (the_secs
[i
].sec_start
<= ((LPBYTE
) addr
)
189 && ((LPBYTE
) addr
) < (the_secs
[i
].sec_start
+ the_secs
[i
].hash
->Misc
.VirtualSize
))
192 h
= __mingw_GetSectionForAddress (addr
);
195 __report_error ("Address %p has no image-section", addr
);
198 the_secs
[i
].hash
= h
;
199 the_secs
[i
].old_protect
= 0;
200 the_secs
[i
].sec_start
= _GetPEImageBase () + h
->VirtualAddress
;
202 if (!VirtualQuery (the_secs
[i
].sec_start
, &b
, sizeof(b
)))
204 __report_error (" VirtualQuery failed for %d bytes at address %p",
205 (int) h
->Misc
.VirtualSize
, the_secs
[i
].sec_start
);
209 if (b
.Protect
!= PAGE_EXECUTE_READWRITE
&& b
.Protect
!= PAGE_READWRITE
)
211 if (!VirtualProtect (b
.BaseAddress
, b
.RegionSize
,
212 PAGE_EXECUTE_READWRITE
,
213 &the_secs
[i
].old_protect
))
214 __report_error (" VirtualProtect failed with code 0x%x",
215 (int) GetLastError ());
222 restore_modified_sections (void)
225 MEMORY_BASIC_INFORMATION b
;
228 for (i
= 0; i
< maxSections
; i
++)
230 if (the_secs
[i
].old_protect
== 0)
232 if (!VirtualQuery (the_secs
[i
].sec_start
, &b
, sizeof(b
)))
234 __report_error (" VirtualQuery failed for %d bytes at address %p",
235 (int) the_secs
[i
].hash
->Misc
.VirtualSize
,
236 the_secs
[i
].sec_start
);
239 VirtualProtect (b
.BaseAddress
, b
.RegionSize
, the_secs
[i
].old_protect
,
244 #endif /* __MINGW64_VERSION_MAJOR */
246 /* This function temporarily marks the page containing addr
247 * writable, before copying len bytes from *src to *addr, and
248 * then restores the original protection settings to the page.
250 * Using this function eliminates the requirement with older
251 * pseudo-reloc implementations, that sections containing
252 * pseudo-relocs (such as .text and .rdata) be permanently
253 * marked writable. This older behavior sabotaged any memory
254 * savings achieved by shared libraries on win32 -- and was
255 * slower, too. However, on cygwin as of binutils 2.20 the
256 * .text section is still marked writable, and the .rdata section
257 * is folded into the (writable) .data when --enable-auto-import.
260 __write_memory (void *addr
, const void *src
, size_t len
)
262 MEMORY_BASIC_INFORMATION b
;
264 int call_unprotect
= 0;
269 #ifdef __MINGW64_VERSION_MAJOR
270 mark_section_writable ((LPVOID
) addr
);
273 if (!VirtualQuery (addr
, &b
, sizeof(b
)))
275 __report_error (" VirtualQuery failed for %d bytes at address %p",
276 (int) sizeof(b
), addr
);
279 /* Temporarily allow write access to read-only protected memory. */
280 if (b
.Protect
!= PAGE_EXECUTE_READWRITE
&& b
.Protect
!= PAGE_READWRITE
)
283 VirtualProtect (b
.BaseAddress
, b
.RegionSize
, PAGE_EXECUTE_READWRITE
,
287 /* write the data. */
288 memcpy (addr
, src
, len
);
289 /* Restore original protection. */
290 if (call_unprotect
&& b
.Protect
!= PAGE_EXECUTE_READWRITE
&& b
.Protect
!= PAGE_READWRITE
)
291 VirtualProtect (b
.BaseAddress
, b
.RegionSize
, oldprot
, &oldprot
);
294 #define RP_VERSION_V1 0
295 #define RP_VERSION_V2 1
298 do_pseudo_reloc (void * start
, void * end
, void * base
)
300 ptrdiff_t addr_imp
, reldata
;
301 ptrdiff_t reloc_target
= (ptrdiff_t) ((char *)end
- (char*)start
);
302 runtime_pseudo_reloc_v2
*v2_hdr
= (runtime_pseudo_reloc_v2
*) start
;
303 runtime_pseudo_reloc_item_v2
*r
;
305 /* A valid relocation list will contain at least one entry, and
306 * one v1 data structure (the smallest one) requires two DWORDs.
307 * So, if the relocation list is smaller than 8 bytes, bail.
309 if (reloc_target
< 8)
312 /* Check if this is the old pseudo relocation version. */
313 /* There are two kinds of v1 relocation lists:
314 * 1) With a (v2-style) version header. In this case, the
315 * first entry in the list is a 3-DWORD structure, with
317 * { 0, 0, RP_VERSION_V1 }
318 * In this case, we skip to the next entry in the list,
319 * knowing that all elements after the head item can
320 * be cast to runtime_pseudo_reloc_item_v1.
321 * 2) Without a (v2-style) version header. In this case, the
322 * first element in the list IS an actual v1 relocation
323 * record, which is two DWORDs. Because there will never
324 * be a case where a v1 relocation record has both
325 * addend == 0 and target == 0, this case will not be
326 * confused with the prior one.
327 * All current binutils, when generating a v1 relocation list,
328 * use the second (e.g. original) form -- that is, without the
329 * v2-style version header.
331 if (reloc_target
>= 12
332 && v2_hdr
->magic1
== 0 && v2_hdr
->magic2
== 0
333 && v2_hdr
->version
== RP_VERSION_V1
)
335 /* We have a list header item indicating that the rest
336 * of the list contains v1 entries. Move the pointer to
337 * the first true v1 relocation record. By definition,
338 * that v1 element will not have both addend == 0 and
339 * target == 0 (and thus, when interpreted as a
340 * runtime_pseudo_reloc_v2, it will not have both
341 * magic1 == 0 and magic2 == 0).
346 if (v2_hdr
->magic1
!= 0 || v2_hdr
->magic2
!= 0)
348 /*************************
349 * Handle v1 relocations *
350 *************************/
351 runtime_pseudo_reloc_item_v1
* o
;
352 for (o
= (runtime_pseudo_reloc_item_v1
*) v2_hdr
;
353 o
< (runtime_pseudo_reloc_item_v1
*)end
;
357 reloc_target
= (ptrdiff_t) base
+ o
->target
;
358 newval
= (*((DWORD
*) reloc_target
)) + o
->addend
;
359 __write_memory ((void *) reloc_target
, &newval
, sizeof(DWORD
));
364 /* If we got this far, then we have relocations of version 2 or newer */
366 /* Check if this is a known version. */
367 if (v2_hdr
->version
!= RP_VERSION_V2
)
369 __report_error (" Unknown pseudo relocation protocol version %d.\n",
370 (int) v2_hdr
->version
);
374 /*************************
375 * Handle v2 relocations *
376 *************************/
378 /* Walk over header. */
379 r
= (runtime_pseudo_reloc_item_v2
*) &v2_hdr
[1];
381 for (; r
< (runtime_pseudo_reloc_item_v2
*) end
; r
++)
383 /* location where new address will be written */
384 reloc_target
= (ptrdiff_t) base
+ r
->target
;
386 /* get sym pointer. It points either to the iat entry
387 * of the referenced element, or to the stub function.
389 addr_imp
= (ptrdiff_t) base
+ r
->sym
;
390 addr_imp
= *((ptrdiff_t *) addr_imp
);
392 /* read existing relocation value from image, casting to the
393 * bitsize indicated by the 8 LSBs of flags. If the value is
394 * negative, manually sign-extend to ptrdiff_t width. Raise an
395 * error if the bitsize indicated by the 8 LSBs of flags is not
398 switch ((r
->flags
& 0xff))
401 reldata
= (ptrdiff_t) (*((unsigned char *)reloc_target
));
402 if ((reldata
& 0x80) != 0)
403 reldata
|= ~((ptrdiff_t) 0xff);
406 reldata
= (ptrdiff_t) (*((unsigned short *)reloc_target
));
407 if ((reldata
& 0x8000) != 0)
408 reldata
|= ~((ptrdiff_t) 0xffff);
411 reldata
= (ptrdiff_t) (*((unsigned int *)reloc_target
));
413 if ((reldata
& 0x80000000) != 0)
414 reldata
|= ~((ptrdiff_t) 0xffffffff);
419 reldata
= (ptrdiff_t) (*((unsigned long long *)reloc_target
));
424 __report_error (" Unknown pseudo relocation bit size %d.\n",
425 (int) (r
->flags
& 0xff));
429 /* Adjust the relocation value */
430 reldata
-= ((ptrdiff_t) base
+ r
->sym
);
433 /* Write the new relocation value back to *reloc_target */
434 switch ((r
->flags
& 0xff))
437 __write_memory ((void *) reloc_target
, &reldata
, 1);
440 __write_memory ((void *) reloc_target
, &reldata
, 2);
443 __write_memory ((void *) reloc_target
, &reldata
, 4);
447 __write_memory ((void *) reloc_target
, &reldata
, 8);
455 _pei386_runtime_relocator (void)
457 static NO_COPY
int was_init
= 0;
458 #ifdef __MINGW64_VERSION_MAJOR
460 #endif /* __MINGW64_VERSION_MAJOR */
465 #ifdef __MINGW64_VERSION_MAJOR
466 mSecs
= __mingw_GetSectionCount ();
467 the_secs
= (sSecInfo
*) alloca (sizeof (sSecInfo
) * (size_t) mSecs
);
469 #endif /* __MINGW64_VERSION_MAJOR */
471 do_pseudo_reloc (&__RUNTIME_PSEUDO_RELOC_LIST__
,
472 &__RUNTIME_PSEUDO_RELOC_LIST_END__
,
474 &__MINGW_LSYMBOL(_image_base__
)
479 #ifdef __MINGW64_VERSION_MAJOR
480 restore_modified_sections ();
481 #endif /* __MINGW64_VERSION_MAJOR */