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.
24 #if defined(__CYGWIN__)
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
38 #define ATTRIBUTE_NORETURN __attribute__ ((noreturn))
40 #define ATTRIBUTE_NORETURN
43 #ifndef __MINGW_LSYMBOL
44 #define __MINGW_LSYMBOL(sym) sym
47 extern char __RUNTIME_PSEUDO_RELOC_LIST__
;
48 extern char __RUNTIME_PSEUDO_RELOC_LIST_END__
;
49 extern char __MINGW_LSYMBOL(_image_base__
);
51 void _pei386_runtime_relocator (void);
53 /* v1 relocation is basically:
54 * *(base + .target) += .addend
55 * where (base + .target) is always assumed to point
56 * to a DWORD (4 bytes).
61 } runtime_pseudo_reloc_item_v1
;
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).
73 } runtime_pseudo_reloc_item_v2
;
79 } runtime_pseudo_reloc_v2
;
81 static void ATTRIBUTE_NORETURN
82 __report_error (const char *msg
, ...)
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
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;
103 HANDLE errh
= GetStdHandle (STD_ERROR_HANDLE
);
104 ssize_t modulelen
= GetModuleFileNameW (NULL
, module
, sizeof (module
));
106 if (errh
== INVALID_HANDLE_VALUE
)
107 cygwin_internal (CW_EXIT_PROCESS
,
108 STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION
,
112 posix_module
= cygwin_create_path (CCP_WIN_W_TO_POSIX
, module
);
114 va_start (args
, msg
);
115 len
= (DWORD
) vsnprintf (buf
, SHORT_MSG_BUF_SZ
, msg
, args
);
117 buf
[SHORT_MSG_BUF_SZ
-1] = '\0'; /* paranoia */
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
);
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
);
137 WriteFile (errh
, (PCVOID
)"\n", 1, &done
, NULL
);
139 cygwin_internal (CW_EXIT_PROCESS
,
140 STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION
,
142 /* not reached, but silences noreturn warning */
146 va_start (argp
, msg
);
147 # ifdef __MINGW64_VERSION_MAJOR
148 fprintf (stderr
, "Mingw-w64 runtime failure:\n");
150 fprintf (stderr
, "Mingw runtime failure:\n");
152 vfprintf (stderr
, msg
, argp
);
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.
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.
172 __write_memory (void *addr
, const void *src
, size_t len
)
174 MEMORY_BASIC_INFORMATION b
;
180 if (!VirtualQuery (addr
, &b
, sizeof(b
)))
182 __report_error (" VirtualQuery failed for %d bytes at address %p",
183 (int) sizeof(b
), addr
);
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
,
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
);
197 #define RP_VERSION_V1 0
198 #define RP_VERSION_V2 1
201 do_pseudo_reloc (void * start
, void * end
, void * base
)
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
;
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.
212 if (reloc_target
< 8)
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
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.
234 if (reloc_target
>= 12
235 && v2_hdr
->magic1
== 0 && v2_hdr
->magic2
== 0
236 && v2_hdr
->version
== RP_VERSION_V1
)
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).
249 if (v2_hdr
->magic1
!= 0 || v2_hdr
->magic2
!= 0)
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
;
260 reloc_target
= (ptrdiff_t) base
+ o
->target
;
261 newval
= (*((DWORD
*) reloc_target
)) + o
->addend
;
262 __write_memory ((void *) reloc_target
, &newval
, sizeof(DWORD
));
267 /* If we got this far, then we have relocations of version 2 or newer */
269 /* Check if this is a known version. */
270 if (v2_hdr
->version
!= RP_VERSION_V2
)
272 __report_error (" Unknown pseudo relocation protocol version %d.\n",
273 (int) v2_hdr
->version
);
277 /*************************
278 * Handle v2 relocations *
279 *************************/
281 /* Walk over header. */
282 r
= (runtime_pseudo_reloc_item_v2
*) &v2_hdr
[1];
284 for (; r
< (runtime_pseudo_reloc_item_v2
*) end
; r
++)
286 /* location where new address will be written */
287 reloc_target
= (ptrdiff_t) base
+ r
->target
;
289 /* get sym pointer. It points either to the iat entry
290 * of the referenced element, or to the stub function.
292 addr_imp
= (ptrdiff_t) base
+ r
->sym
;
293 addr_imp
= *((ptrdiff_t *) addr_imp
);
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
301 switch ((r
->flags
& 0xff))
304 reldata
= (ptrdiff_t) (*((unsigned char *)reloc_target
));
305 if ((reldata
& 0x80) != 0)
306 reldata
|= ~((ptrdiff_t) 0xff);
309 reldata
= (ptrdiff_t) (*((unsigned short *)reloc_target
));
310 if ((reldata
& 0x8000) != 0)
311 reldata
|= ~((ptrdiff_t) 0xffff);
314 reldata
= (ptrdiff_t) (*((unsigned int *)reloc_target
));
316 if ((reldata
& 0x80000000) != 0)
317 reldata
|= ~((ptrdiff_t) 0xffffffff);
322 reldata
= (ptrdiff_t) (*((unsigned long long *)reloc_target
));
327 __report_error (" Unknown pseudo relocation bit size %d.\n",
328 (int) (r
->flags
& 0xff));
332 /* Adjust the relocation value */
333 reldata
-= ((ptrdiff_t) base
+ r
->sym
);
336 /* Write the new relocation value back to *reloc_target */
337 switch ((r
->flags
& 0xff))
340 __write_memory ((void *) reloc_target
, &reldata
, 1);
343 __write_memory ((void *) reloc_target
, &reldata
, 2);
346 __write_memory ((void *) reloc_target
, &reldata
, 4);
350 __write_memory ((void *) reloc_target
, &reldata
, 8);
358 _pei386_runtime_relocator (void)
360 static NO_COPY
int was_init
= 0;
364 do_pseudo_reloc (&__RUNTIME_PSEUDO_RELOC_LIST__
,
365 &__RUNTIME_PSEUDO_RELOC_LIST_END__
,
366 &__MINGW_LSYMBOL(_image_base__
));