ReactOS  0.4.13-dev-92-gf251225
pseudo-reloc.c
Go to the documentation of this file.
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 #include <internal.h>
24 
25 #if defined(__CYGWIN__)
26 #include <wchar.h>
27 #include <ntdef.h>
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
34 #else
35 # define NO_COPY
36 #endif
37 
38 #ifdef __GNUC__
39 #define ATTRIBUTE_NORETURN __attribute__ ((noreturn))
40 #else
41 #define ATTRIBUTE_NORETURN
42 #endif
43 
44 #ifndef __MINGW_LSYMBOL
45 #define __MINGW_LSYMBOL(sym) sym
46 #endif
47 
50 extern char __MINGW_LSYMBOL(_image_base__);
51 
52 void _pei386_runtime_relocator (void);
53 
54 /* v1 relocation is basically:
55  * *(base + .target) += .addend
56  * where (base + .target) is always assumed to point
57  * to a DWORD (4 bytes).
58  */
59 typedef struct {
63 
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).
69  */
70 typedef struct {
75 
76 typedef struct {
81 
82 static void ATTRIBUTE_NORETURN
83 __report_error (const char *msg, ...)
84 {
85 #ifdef __CYGWIN__
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
92  * cygwin ptys.
93  */
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;
101  DWORD len;
102  DWORD done;
103  va_list args;
105  ssize_t modulelen = GetModuleFileNameW (NULL, module, sizeof (module));
106 
107  if (errh == INVALID_HANDLE_VALUE)
108  cygwin_internal (CW_EXIT_PROCESS,
109  STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION,
110  1);
111 
112  if (modulelen > 0)
113  posix_module = cygwin_create_path (CCP_WIN_W_TO_POSIX, module);
114 
115  va_start (args, msg);
116  len = (DWORD) vsnprintf (buf, SHORT_MSG_BUF_SZ, msg, args);
117  va_end (args);
118  buf[SHORT_MSG_BUF_SZ-1] = '\0'; /* paranoia */
119 
120  if (posix_module)
121  {
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);
128  free (posix_module);
129  }
130  else
131  {
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);
137  }
138  WriteFile (errh, (PCVOID)"\n", 1, &done, NULL);
139 
140  cygwin_internal (CW_EXIT_PROCESS,
141  STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION,
142  1);
143  /* not reached, but silences noreturn warning */
144  abort ();
145 #else
146  va_list argp;
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);
151 # else
152  fprintf (stderr, "Mingw runtime failure:\n");
153  vfprintf (stderr, msg, argp);
154 #endif
155  va_end (argp);
156  abort ();
157 #endif
158 }
159 
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);
167 extern PBYTE _GetPEImageBase (void);
168 
169 typedef struct sSecInfo {
170  /* Keeps altered section flags, or zero if nothing was changed. */
171  DWORD old_protect;
172  PBYTE sec_start;
174 } sSecInfo;
175 
176 static sSecInfo *the_secs = NULL;
177 static int maxSections = 0;
178 
179 static void
180 mark_section_writable (LPVOID addr)
181 {
184  int i;
185 
186  for (i = 0; i < maxSections; i++)
187  {
188  if (the_secs[i].sec_start <= ((LPBYTE) addr)
189  && ((LPBYTE) addr) < (the_secs[i].sec_start + the_secs[i].hash->Misc.VirtualSize))
190  return;
191  }
193  if (!h)
194  {
195  __report_error ("Address %p has no image-section", addr);
196  return;
197  }
198  the_secs[i].hash = h;
199  the_secs[i].old_protect = 0;
200  the_secs[i].sec_start = _GetPEImageBase () + h->VirtualAddress;
201 
202  if (!VirtualQuery (the_secs[i].sec_start, &b, sizeof(b)))
203  {
204  __report_error (" VirtualQuery failed for %d bytes at address %p",
205  (int) h->Misc.VirtualSize, the_secs[i].sec_start);
206  return;
207  }
208 
209  if (b.Protect != PAGE_EXECUTE_READWRITE && b.Protect != PAGE_READWRITE)
210  {
211  if (!VirtualProtect (b.BaseAddress, b.RegionSize,
213  &the_secs[i].old_protect))
214  __report_error (" VirtualProtect failed with code 0x%x",
215  (int) GetLastError ());
216  }
217  ++maxSections;
218  return;
219 }
220 
221 static void
222 restore_modified_sections (void)
223 {
224  int i;
226  DWORD oldprot;
227 
228  for (i = 0; i < maxSections; i++)
229  {
230  if (the_secs[i].old_protect == 0)
231  continue;
232  if (!VirtualQuery (the_secs[i].sec_start, &b, sizeof(b)))
233  {
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);
237  return;
238  }
239  VirtualProtect (b.BaseAddress, b.RegionSize, the_secs[i].old_protect,
240  &oldprot);
241  }
242 }
243 
244 #endif /* __MINGW64_VERSION_MAJOR */
245 
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.
249  *
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.
258  */
259 static void
260 __write_memory (void *addr, const void *src, size_t len)
261 {
263  DWORD oldprot;
264  int call_unprotect = 0;
265 
266  if (!len)
267  return;
268 
269 #ifdef __MINGW64_VERSION_MAJOR
270  mark_section_writable ((LPVOID) addr);
271 #endif
272 
273  if (!VirtualQuery (addr, &b, sizeof(b)))
274  {
275  __report_error (" VirtualQuery failed for %d bytes at address %p",
276  (int) sizeof(b), addr);
277  }
278 
279  /* Temporarily allow write access to read-only protected memory. */
280  if (b.Protect != PAGE_EXECUTE_READWRITE && b.Protect != PAGE_READWRITE)
281  {
282  call_unprotect = 1;
283  VirtualProtect (b.BaseAddress, b.RegionSize, PAGE_EXECUTE_READWRITE,
284  &oldprot);
285  }
286 
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);
292 }
293 
294 #define RP_VERSION_V1 0
295 #define RP_VERSION_V2 1
296 
297 static void
298 do_pseudo_reloc (void * start, void * end, void * base)
299 {
300  ptrdiff_t addr_imp, reldata;
301  ptrdiff_t reloc_target = (ptrdiff_t) ((char *)end - (char*)start);
304 
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.
308  */
309  if (reloc_target < 8)
310  return;
311 
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
316  * value:
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.
330  */
331  if (reloc_target >= 12
332  && v2_hdr->magic1 == 0 && v2_hdr->magic2 == 0
333  && v2_hdr->version == RP_VERSION_V1)
334  {
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).
342  */
343  v2_hdr++;
344  }
345 
346  if (v2_hdr->magic1 != 0 || v2_hdr->magic2 != 0)
347  {
348  /*************************
349  * Handle v1 relocations *
350  *************************/
352  for (o = (runtime_pseudo_reloc_item_v1 *) v2_hdr;
354  o++)
355  {
356  DWORD newval;
357  reloc_target = (ptrdiff_t) base + o->target;
358  newval = (*((DWORD*) reloc_target)) + o->addend;
359  __write_memory ((void *) reloc_target, &newval, sizeof(DWORD));
360  }
361  return;
362  }
363 
364  /* If we got this far, then we have relocations of version 2 or newer */
365 
366  /* Check if this is a known version. */
367  if (v2_hdr->version != RP_VERSION_V2)
368  {
369  __report_error (" Unknown pseudo relocation protocol version %d.\n",
370  (int) v2_hdr->version);
371  return;
372  }
373 
374  /*************************
375  * Handle v2 relocations *
376  *************************/
377 
378  /* Walk over header. */
379  r = (runtime_pseudo_reloc_item_v2 *) &v2_hdr[1];
380 
381  for (; r < (runtime_pseudo_reloc_item_v2 *) end; r++)
382  {
383  /* location where new address will be written */
384  reloc_target = (ptrdiff_t) base + r->target;
385 
386  /* get sym pointer. It points either to the iat entry
387  * of the referenced element, or to the stub function.
388  */
389  addr_imp = (ptrdiff_t) base + r->sym;
390  addr_imp = *((ptrdiff_t *) addr_imp);
391 
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
396  * supported.
397  */
398  switch ((r->flags & 0xff))
399  {
400  case 8:
401  reldata = (ptrdiff_t) (*((unsigned char *)reloc_target));
402  if ((reldata & 0x80) != 0)
403  reldata |= ~((ptrdiff_t) 0xff);
404  break;
405  case 16:
406  reldata = (ptrdiff_t) (*((unsigned short *)reloc_target));
407  if ((reldata & 0x8000) != 0)
408  reldata |= ~((ptrdiff_t) 0xffff);
409  break;
410  case 32:
411  reldata = (ptrdiff_t) (*((unsigned int *)reloc_target));
412 #ifdef _WIN64
413  if ((reldata & 0x80000000) != 0)
414  reldata |= ~((ptrdiff_t) 0xffffffff);
415 #endif
416  break;
417 #ifdef _WIN64
418  case 64:
419  reldata = (ptrdiff_t) (*((unsigned long long *)reloc_target));
420  break;
421 #endif
422  default:
423  reldata=0;
424  __report_error (" Unknown pseudo relocation bit size %d.\n",
425  (int) (r->flags & 0xff));
426  break;
427  }
428 
429  /* Adjust the relocation value */
430  reldata -= ((ptrdiff_t) base + r->sym);
431  reldata += addr_imp;
432 
433  /* Write the new relocation value back to *reloc_target */
434  switch ((r->flags & 0xff))
435  {
436  case 8:
437  __write_memory ((void *) reloc_target, &reldata, 1);
438  break;
439  case 16:
440  __write_memory ((void *) reloc_target, &reldata, 2);
441  break;
442  case 32:
443  __write_memory ((void *) reloc_target, &reldata, 4);
444  break;
445 #ifdef _WIN64
446  case 64:
447  __write_memory ((void *) reloc_target, &reldata, 8);
448  break;
449 #endif
450  }
451  }
452 }
453 
454 void
456 {
457  static NO_COPY int was_init = 0;
458 #ifdef __MINGW64_VERSION_MAJOR
459  int mSecs;
460 #endif /* __MINGW64_VERSION_MAJOR */
461 
462  if (was_init)
463  return;
464  ++was_init;
465 #ifdef __MINGW64_VERSION_MAJOR
466  mSecs = __mingw_GetSectionCount ();
467  the_secs = (sSecInfo *) alloca (sizeof (sSecInfo) * (size_t) mSecs);
468  maxSections = 0;
469 #endif /* __MINGW64_VERSION_MAJOR */
470 
473 #ifdef __GNUC__
474  &__MINGW_LSYMBOL(_image_base__)
475 #else
476  &__ImageBase
477 #endif
478  );
479 #ifdef __MINGW64_VERSION_MAJOR
480  restore_modified_sections ();
481 #endif /* __MINGW64_VERSION_MAJOR */
482 }
DWORD WINAPI GetModuleFileNameW(HINSTANCE hModule, LPWSTR lpFilename, DWORD nSize)
Definition: loader.c:607
#define vsnprintf
Definition: tif_win32.c:406
BOOL WINAPI WriteFile(IN HANDLE hFile, IN LPCVOID lpBuffer, IN DWORD nNumberOfBytesToWrite OPTIONAL, OUT LPDWORD lpNumberOfBytesWritten, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:24
int __mingw_GetSectionCount(void)
Definition: pesect.c:115
ACPI_SIZE strlen(const char *String)
Definition: utclib.c:269
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: glext.h:7751
#define __GNUC__
Definition: _icc.h:38
GLdouble GLdouble GLdouble r
Definition: gl.h:2055
PIMAGE_SECTION_HEADER __mingw_GetSectionForAddress(LPVOID p)
Definition: pesect.c:101
#define free
Definition: debug_ros.c:5
static void do_pseudo_reloc(void *start, void *end, void *base)
Definition: pseudo-reloc.c:298
#define INVALID_HANDLE_VALUE
Definition: compat.h:391
DWORD WINAPI GetLastError(VOID)
Definition: except.c:1059
HANDLE WINAPI GetStdHandle(IN DWORD nStdHandle)
Definition: console.c:152
GLuint GLuint end
Definition: gl.h:1545
Definition: match.c:390
#define DWORD
Definition: nt_native.h:44
#define alloca
Definition: malloc.h:361
GLfloat GLfloat GLfloat GLfloat h
Definition: glext.h:7723
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
#define RP_VERSION_V2
Definition: pseudo-reloc.c:295
unsigned char * LPBYTE
Definition: typedefs.h:52
#define va_end(ap)
Definition: acmsvcex.h:90
void _pei386_runtime_relocator(void)
Definition: pseudo-reloc.c:455
int hash
Definition: main.c:58
_Check_return_opt_ _CRTIMP int __cdecl fprintf(_Inout_ FILE *_File, _In_z_ _Printf_format_string_ const char *_Format,...)
#define RP_VERSION_V1
Definition: pseudo-reloc.c:294
smooth NULL
Definition: ftsmooth.c:416
char * va_list
Definition: acmsvcex.h:78
#define b
Definition: ke_i.h:79
__kernel_size_t size_t
Definition: linux.h:237
GLboolean GLboolean GLboolean b
Definition: glext.h:6204
#define STD_ERROR_HANDLE
Definition: winbase.h:266
#define MAX_PATH
Definition: compat.h:26
unsigned long DWORD
Definition: ntddk_ex.h:95
SIZE_T NTAPI VirtualQuery(IN LPCVOID lpAddress, OUT PMEMORY_BASIC_INFORMATION lpBuffer, IN SIZE_T dwLength)
Definition: virtmem.c:220
BOOL NTAPI VirtualProtect(IN LPVOID lpAddress, IN SIZE_T dwSize, IN DWORD flNewProtect, OUT PDWORD lpflOldProtect)
Definition: virtmem.c:144
#define NO_COPY
Definition: pseudo-reloc.c:35
GLenum const GLvoid * addr
Definition: glext.h:9621
char __RUNTIME_PSEUDO_RELOC_LIST__
#define memcpy(s1, s2, n)
Definition: mkisofs.h:878
GLenum GLsizei len
Definition: glext.h:6722
int ssize_t
Definition: rosdhcp.h:48
GLenum src
Definition: glext.h:6340
CONST VOID * PCVOID
Definition: cfgmgr32.h:44
static void __write_memory(void *addr, const void *src, size_t len)
Definition: pseudo-reloc.c:260
GLuint start
Definition: gl.h:1545
#define abort()
Definition: i386-dis.c:35
static void ATTRIBUTE_NORETURN __report_error(const char *msg,...)
Definition: pseudo-reloc.c:83
#define va_start(ap, A)
Definition: acmsvcex.h:91
__kernel_ptrdiff_t ptrdiff_t
Definition: linux.h:247
#define msg(x)
Definition: auth_time.c:54
FILE * stderr
#define __MINGW_LSYMBOL(sym)
Definition: pseudo-reloc.c:45
#define PAGE_EXECUTE_READWRITE
Definition: nt_native.h:1308
#define __ImageBase
Definition: crt_handler.c:22
GLfloat GLfloat p
Definition: glext.h:8902
_Check_return_opt_ _CRTIMP int __cdecl vfprintf(_Inout_ FILE *_File, _In_z_ _Printf_format_string_ const char *_Format, va_list _ArgList)
Definition: _hash_fun.h:40
PBYTE _GetPEImageBase(void)
Definition: pesect.c:163
#define args
Definition: format.c:66
char __RUNTIME_PSEUDO_RELOC_LIST_END__
BYTE * PBYTE
Definition: pedump.c:66
#define ATTRIBUTE_NORETURN
Definition: pseudo-reloc.c:41
#define PAGE_READWRITE
Definition: nt_native.h:1304