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>
8 * THIS SOFTWARE IS NOT COPYRIGHTED
10 * This source code is offered for use in the public domain. You may
11 * use, modify or distribute it freely.
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.
25 /* We need some of the MS-Windows data types and structures;
26 * define them, but with minimal namespace pollution.
28 #define WIN32_LEAN_AND_MEAN
31 #if defined(__CYGWIN__)
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
45 #define ATTRIBUTE_NORETURN __attribute__ ((noreturn))
47 #define ATTRIBUTE_NORETURN
50 #ifndef __MINGW_LSYMBOL
51 #define __MINGW_LSYMBOL(sym) sym
54 extern char __RUNTIME_PSEUDO_RELOC_LIST__;
55 extern char __RUNTIME_PSEUDO_RELOC_LIST_END__;
56 extern char __MINGW_LSYMBOL(_image_base__);
58 void _pei386_runtime_relocator (void);
60 /* v1 relocation is basically:
61 * *(base + .target) += .addend
62 * where (base + .target) is always assumed to point
63 * to a DWORD (4 bytes).
68 } runtime_pseudo_reloc_item_v1;
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).
80 } runtime_pseudo_reloc_item_v2;
86 } runtime_pseudo_reloc_v2;
88 static void ATTRIBUTE_NORETURN
89 __report_error (const char *msg, ...)
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
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;
110 HANDLE errh = GetStdHandle (STD_ERROR_HANDLE);
111 ssize_t modulelen = GetModuleFileNameW (NULL, module, sizeof (module));
113 if (errh == INVALID_HANDLE_VALUE)
114 cygwin_internal (CW_EXIT_PROCESS,
115 STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION,
119 posix_module = cygwin_create_path (CCP_WIN_W_TO_POSIX, module);
121 va_start (args, msg);
122 len = (DWORD) vsnprintf (buf, SHORT_MSG_BUF_SZ, msg, args);
124 buf[SHORT_MSG_BUF_SZ-1] = '\0'; /* paranoia */
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);
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);
144 WriteFile (errh, (PCVOID)"\n", 1, &done, NULL);
146 cygwin_internal (CW_EXIT_PROCESS,
147 STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION,
149 /* not reached, but silences noreturn warning */
153 va_start (argp, msg);
154 # ifdef __MINGW64_VERSION_MAJOR
155 fprintf (stderr, "Mingw-w64 runtime failure:\n");
157 fprintf (stderr, "Mingw runtime failure:\n");
159 vfprintf (stderr, msg, argp);
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.
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.
179 __write_memory (void *addr, const void *src, size_t len)
181 MEMORY_BASIC_INFORMATION b;
187 if (!VirtualQuery (addr, &b, sizeof(b)))
189 __report_error (" VirtualQuery failed for %d bytes at address %p",
190 (int) sizeof(b), addr);
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,
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);
204 #define RP_VERSION_V1 0
205 #define RP_VERSION_V2 1
208 do_pseudo_reloc (void * start, void * end, void * base)
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;
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.
219 if (reloc_target < 8)
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
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.
241 if (reloc_target >= 12
242 && v2_hdr->magic1 == 0 && v2_hdr->magic2 == 0
243 && v2_hdr->version == RP_VERSION_V1)
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).
256 if (v2_hdr->magic1 != 0 || v2_hdr->magic2 != 0)
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;
267 reloc_target = (ptrdiff_t) base + o->target;
268 newval = (*((DWORD*) reloc_target)) + o->addend;
269 __write_memory ((void *) reloc_target, &newval, sizeof(DWORD));
274 /* If we got this far, then we have relocations of version 2 or newer */
276 /* Check if this is a known version. */
277 if (v2_hdr->version != RP_VERSION_V2)
279 __report_error (" Unknown pseudo relocation protocol version %d.\n",
280 (int) v2_hdr->version);
284 /*************************
285 * Handle v2 relocations *
286 *************************/
288 /* Walk over header. */
289 r = (runtime_pseudo_reloc_item_v2 *) &v2_hdr[1];
291 for (; r < (runtime_pseudo_reloc_item_v2 *) end; r++)
293 /* location where new address will be written */
294 reloc_target = (ptrdiff_t) base + r->target;
296 /* get sym pointer. It points either to the iat entry
297 * of the referenced element, or to the stub function.
299 addr_imp = (ptrdiff_t) base + r->sym;
300 addr_imp = *((ptrdiff_t *) addr_imp);
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
308 switch ((r->flags & 0xff))
311 reldata = (ptrdiff_t) (*((unsigned char *)reloc_target));
312 if ((reldata & 0x80) != 0)
313 reldata |= ~((ptrdiff_t) 0xff);
316 reldata = (ptrdiff_t) (*((unsigned short *)reloc_target));
317 if ((reldata & 0x8000) != 0)
318 reldata |= ~((ptrdiff_t) 0xffff);
321 reldata = (ptrdiff_t) (*((unsigned int *)reloc_target));
323 if ((reldata & 0x80000000) != 0)
324 reldata |= ~((ptrdiff_t) 0xffffffff);
329 reldata = (ptrdiff_t) (*((unsigned long long *)reloc_target));
334 __report_error (" Unknown pseudo relocation bit size %d.\n",
335 (int) (r->flags & 0xff));
339 /* Adjust the relocation value */
340 reldata -= ((ptrdiff_t) base + r->sym);
343 /* Write the new relocation value back to *reloc_target */
344 switch ((r->flags & 0xff))
347 __write_memory ((void *) reloc_target, &reldata, 1);
350 __write_memory ((void *) reloc_target, &reldata, 2);
353 __write_memory ((void *) reloc_target, &reldata, 4);
357 __write_memory ((void *) reloc_target, &reldata, 8);
365 _pei386_runtime_relocator (void)
367 static NO_COPY int was_init = 0;
371 do_pseudo_reloc (&__RUNTIME_PSEUDO_RELOC_LIST__,
372 &__RUNTIME_PSEUDO_RELOC_LIST_END__,
373 &__MINGW_LSYMBOL(_image_base__));