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.
25 # include <sys/cygwin.h>
26 /* custom status code: */
27 # define STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION ((NTSTATUS) 0xe0000269)
36 #define ATTRIBUTE_NORETURN __attribute__ ((noreturn))
38 #define ATTRIBUTE_NORETURN
41 #ifndef __MINGW_LSYMBOL
42 #define __MINGW_LSYMBOL(sym) sym
45 extern char __RUNTIME_PSEUDO_RELOC_LIST__;
46 extern char __RUNTIME_PSEUDO_RELOC_LIST_END__;
47 extern char __MINGW_LSYMBOL(_image_base__);
49 /* v1 relocation is basically:
50 * *(base + .target) += .addend
51 * where (base + .target) is always assumed to point
52 * to a DWORD (4 bytes).
57 } runtime_pseudo_reloc_item_v1;
59 /* v2 relocation is more complex. In effect, it is
60 * *(base + .target) += *(base + .sym) - (base + .sym)
61 * with care taken in both reading, sign extension, and writing
62 * because .flags may indicate that (base + .target) may point
63 * to a BYTE, WORD, DWORD, or QWORD (w64).
69 } runtime_pseudo_reloc_item_v2;
75 } runtime_pseudo_reloc_v2;
77 static void ATTRIBUTE_NORETURN
78 __report_error (const char *msg, ...)
81 /* This function is used to print short error messages
82 * to stderr, which may occur during DLL initialization
83 * while fixing up 'pseudo' relocations. This early, we
84 * may not be able to use cygwin stdio functions, so we
85 * use the win32 WriteFile api. This should work with both
86 * normal win32 console IO handles, redirected ones, and
90 wchar_t module[MAX_PATH];
91 char * posix_module = NULL;
92 static const char UNKNOWN_MODULE[] = "<unknown module>: ";
93 static const char CYGWIN_FAILURE_MSG[] = "Cygwin runtime failure: ";
94 HANDLE errh = GetStdHandle (STD_ERROR_HANDLE);
95 ssize_t modulelen = GetModuleFileNameW (NULL, module, sizeof (module));
98 /* FIXME: cleanup further to avoid old use of cygwin_internal */
99 if (errh == INVALID_HANDLE_VALUE)
100 cygwin_internal (CW_EXIT_PROCESS, STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION, 1);
103 posix_module = (char *) cygwin_create_path (CCP_WIN_W_TO_POSIX, module);
105 va_start (args, msg);
106 vsnprintf (buf, sizeof (buf), msg, args);
108 buf[sizeof (buf) - 1] = '\0'; /* paranoia */
110 small_printf ("%s%s: %s\n", CYGWIN_FAILURE_MSG, posix_module ?: UNKNOWN_MODULE, buf);
114 cygwin_internal (CW_EXIT_PROCESS, STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION, 1);
115 /* not reached, but silences noreturn warning */
119 va_start (argp, msg);
120 # ifdef __MINGW64_VERSION_MAJOR
121 fprintf (stderr, "Mingw-w64 runtime failure:\n");
123 fprintf (stderr, "Mingw runtime failure:\n");
125 vfprintf (stderr, msg, argp);
131 /* This function temporarily marks the page containing addr
132 * writable, before copying len bytes from *src to *addr, and
133 * then restores the original protection settings to the page.
135 * Using this function eliminates the requirement with older
136 * pseudo-reloc implementations, that sections containing
137 * pseudo-relocs (such as .text and .rdata) be permanently
138 * marked writable. This older behavior sabotaged any memory
139 * savings achieved by shared libraries on win32 -- and was
140 * slower, too. However, on cygwin as of binutils 2.20 the
141 * .text section is still marked writable, and the .rdata section
142 * is folded into the (writable) .data when --enable-auto-import.
145 __write_memory (void *addr, const void *src, size_t len)
147 MEMORY_BASIC_INFORMATION b;
153 if (!VirtualQuery (addr, &b, sizeof (b)))
155 __report_error (" VirtualQuery failed for %d bytes at address %p",
156 (int) sizeof (b), addr);
159 /* Temporarily allow write access to read-only protected memory. */
160 if (b.Protect != PAGE_EXECUTE_READWRITE && b.Protect != PAGE_READWRITE)
161 VirtualProtect (b.BaseAddress, b.RegionSize, PAGE_EXECUTE_READWRITE,
163 /* write the data. */
164 memcpy (addr, src, len);
165 /* Restore original protection. */
166 if (b.Protect != PAGE_EXECUTE_READWRITE && b.Protect != PAGE_READWRITE)
167 VirtualProtect (b.BaseAddress, b.RegionSize, oldprot, &oldprot);
170 #define RP_VERSION_V1 0
171 #define RP_VERSION_V2 1
174 do_pseudo_reloc (void * start, void * end, void * base)
176 ptrdiff_t addr_imp, reldata;
177 ptrdiff_t reloc_target = (ptrdiff_t) ((char *)end - (char*)start);
178 runtime_pseudo_reloc_v2 *v2_hdr = (runtime_pseudo_reloc_v2 *) start;
179 runtime_pseudo_reloc_item_v2 *r;
181 /* A valid relocation list will contain at least one entry, and
182 * one v1 data structure (the smallest one) requires two DWORDs.
183 * So, if the relocation list is smaller than 8 bytes, bail.
185 if (reloc_target < 8)
188 /* Check if this is the old pseudo relocation version. */
189 /* There are two kinds of v1 relocation lists:
190 * 1) With a (v2-style) version header. In this case, the
191 * first entry in the list is a 3-DWORD structure, with
193 * { 0, 0, RP_VERSION_V1 }
194 * In this case, we skip to the next entry in the list,
195 * knowing that all elements after the head item can
196 * be cast to runtime_pseudo_reloc_item_v1.
197 * 2) Without a (v2-style) version header. In this case, the
198 * first element in the list IS an actual v1 relocation
199 * record, which is two DWORDs. Because there will never
200 * be a case where a v1 relocation record has both
201 * addend == 0 and target == 0, this case will not be
202 * confused with the prior one.
203 * All current binutils, when generating a v1 relocation list,
204 * use the second (e.g. original) form -- that is, without the
205 * v2-style version header.
207 if (reloc_target >= 12
208 && v2_hdr->magic1 == 0 && v2_hdr->magic2 == 0
209 && v2_hdr->version == RP_VERSION_V1)
211 /* We have a list header item indicating that the rest
212 * of the list contains v1 entries. Move the pointer to
213 * the first true v1 relocation record. By definition,
214 * that v1 element will not have both addend == 0 and
215 * target == 0 (and thus, when interpreted as a
216 * runtime_pseudo_reloc_v2, it will not have both
217 * magic1 == 0 and magic2 == 0).
222 if (v2_hdr->magic1 != 0 || v2_hdr->magic2 != 0)
224 /*************************
225 * Handle v1 relocations *
226 *************************/
227 runtime_pseudo_reloc_item_v1 * o;
228 for (o = (runtime_pseudo_reloc_item_v1 *) v2_hdr;
229 o < (runtime_pseudo_reloc_item_v1 *)end;
233 reloc_target = (ptrdiff_t) base + o->target;
234 newval = (*((DWORD*) reloc_target)) + o->addend;
235 __write_memory ((void *) reloc_target, &newval, sizeof (DWORD));
240 /* If we got this far, then we have relocations of version 2 or newer */
242 /* Check if this is a known version. */
243 if (v2_hdr->version != RP_VERSION_V2)
245 __report_error (" Unknown pseudo relocation protocol version %d.\n",
246 (int) v2_hdr->version);
250 /*************************
251 * Handle v2 relocations *
252 *************************/
254 /* Walk over header. */
255 r = (runtime_pseudo_reloc_item_v2 *) &v2_hdr[1];
257 for (; r < (runtime_pseudo_reloc_item_v2 *) end; r++)
259 /* location where new address will be written */
260 reloc_target = (ptrdiff_t) base + r->target;
262 /* get sym pointer. It points either to the iat entry
263 * of the referenced element, or to the stub function.
265 addr_imp = (ptrdiff_t) base + r->sym;
266 addr_imp = *((ptrdiff_t *) addr_imp);
268 /* read existing relocation value from image, casting to the
269 * bitsize indicated by the 8 LSBs of flags. If the value is
270 * negative, manually sign-extend to ptrdiff_t width. Raise an
271 * error if the bitsize indicated by the 8 LSBs of flags is not
274 switch ((r->flags & 0xff))
277 reldata = (ptrdiff_t) (*((unsigned char *)reloc_target));
278 if ((reldata & 0x80) != 0)
279 reldata |= ~((ptrdiff_t) 0xff);
282 reldata = (ptrdiff_t) (*((unsigned short *)reloc_target));
283 if ((reldata & 0x8000) != 0)
284 reldata |= ~((ptrdiff_t) 0xffff);
287 reldata = (ptrdiff_t) (*((unsigned int *)reloc_target));
289 if ((reldata & 0x80000000) != 0)
290 reldata |= ~((ptrdiff_t) 0xffffffff);
295 reldata = (ptrdiff_t) (*((unsigned long long *)reloc_target));
300 __report_error (" Unknown pseudo relocation bit size %d.\n",
301 (int) (r->flags & 0xff));
305 /* Adjust the relocation value */
306 reldata -= ((ptrdiff_t) base + r->sym);
309 /* Write the new relocation value back to *reloc_target */
310 switch ((r->flags & 0xff))
313 __write_memory ((void *) reloc_target, &reldata, 1);
316 __write_memory ((void *) reloc_target, &reldata, 2);
319 __write_memory ((void *) reloc_target, &reldata, 4);
323 __write_memory ((void *) reloc_target, &reldata, 8);
332 _pei386_runtime_relocator (per_process *u)
334 if (CYGWIN_VERSION_USE_PSEUDO_RELOC_IN_DLL (u))
335 do_pseudo_reloc (u->pseudo_reloc_start, u->pseudo_reloc_end, u->image_base);
339 _pei386_runtime_relocator (void)
341 static NO_COPY int was_init = 0;
345 do_pseudo_reloc (&__RUNTIME_PSEUDO_RELOC_LIST__,
346 &__RUNTIME_PSEUDO_RELOC_LIST_END__,
347 &__MINGW_LSYMBOL(_image_base__));