Rev 4349 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 1 | /* pseudo-reloc.c |
2 | |||
3 | Contributed by Egor Duda |
||
4 | Modified by addition of runtime_pseudo_reloc version 2 |
||
5 | by Kai Tietz |
||
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 |
||
19 | #include |
||
20 | #include |
||
21 | #include |
||
22 | |||
23 | extern char __RUNTIME_PSEUDO_RELOC_LIST__; |
||
24 | extern char __RUNTIME_PSEUDO_RELOC_LIST_END__; |
||
25 | extern char _image_base__; |
||
26 | |||
27 | void _pei386_runtime_relocator (void); |
||
28 | |||
29 | /* v1 relocation is basically: |
||
30 | * *(base + .target) += .addend |
||
31 | * where (base + .target) is always assumed to point |
||
32 | * to a DWORD (4 bytes). |
||
33 | */ |
||
34 | typedef struct { |
||
35 | uint32_t addend; |
||
36 | uint32_t target; |
||
37 | } runtime_pseudo_reloc_item_v1; |
||
38 | |||
39 | /* v2 relocation is more complex. In effect, it is |
||
40 | * *(base + .target) += *(base + .sym) - (base + .sym) |
||
41 | * with care taken in both reading, sign extension, and writing |
||
42 | * because .flags may indicate that (base + .target) may point |
||
43 | * to a BYTE, WORD, DWORD, or QWORD (w64). |
||
44 | */ |
||
45 | typedef struct { |
||
46 | uint32_t sym; |
||
47 | uint32_t target; |
||
48 | uint32_t flags; |
||
49 | } runtime_pseudo_reloc_item_v2; |
||
50 | |||
51 | typedef struct { |
||
52 | uint32_t magic1; |
||
53 | uint32_t magic2; |
||
54 | uint32_t version; |
||
55 | } runtime_pseudo_reloc_v2; |
||
56 | |||
57 | #define RP_VERSION_V1 0 |
||
58 | #define RP_VERSION_V2 1 |
||
59 | |||
60 | static void |
||
61 | do_pseudo_reloc (void * start, void * end, void * base) |
||
62 | { |
||
63 | ptrdiff_t addr_imp, reldata; |
||
64 | ptrdiff_t reloc_target = (ptrdiff_t) ((char *)end - (char*)start); |
||
65 | runtime_pseudo_reloc_v2 *v2_hdr = (runtime_pseudo_reloc_v2 *) start; |
||
66 | runtime_pseudo_reloc_item_v2 *r; |
||
67 | |||
68 | /* A valid relocation list will contain at least one entry, and |
||
69 | * one v1 data structure (the smallest one) requires two DWORDs. |
||
70 | * So, if the relocation list is smaller than 8 bytes, bail. |
||
71 | */ |
||
72 | if (reloc_target < 8) |
||
73 | return; |
||
74 | |||
75 | /* Check if this is the old pseudo relocation version. */ |
||
76 | /* There are two kinds of v1 relocation lists: |
||
77 | * 1) With a (v2-style) version header. In this case, the |
||
78 | * first entry in the list is a 3-DWORD structure, with |
||
79 | * value: |
||
80 | * { 0, 0, RP_VERSION_V1 } |
||
81 | * In this case, we skip to the next entry in the list, |
||
82 | * knowing that all elements after the head item can |
||
83 | * be cast to runtime_pseudo_reloc_item_v1. |
||
84 | * 2) Without a (v2-style) version header. In this case, the |
||
85 | * first element in the list IS an actual v1 relocation |
||
86 | * record, which is two DWORDs. Because there will never |
||
87 | * be a case where a v1 relocation record has both |
||
88 | * addend == 0 and target == 0, this case will not be |
||
89 | * confused with the prior one. |
||
90 | * All current binutils, when generating a v1 relocation list, |
||
91 | * use the second (e.g. original) form -- that is, without the |
||
92 | * v2-style version header. |
||
93 | */ |
||
94 | if (reloc_target >= 12 |
||
95 | && v2_hdr->magic1 == 0 && v2_hdr->magic2 == 0 |
||
96 | && v2_hdr->version == RP_VERSION_V1) |
||
97 | { |
||
98 | /* We have a list header item indicating that the rest |
||
99 | * of the list contains v1 entries. Move the pointer to |
||
100 | * the first true v1 relocation record. By definition, |
||
101 | * that v1 element will not have both addend == 0 and |
||
102 | * target == 0 (and thus, when interpreted as a |
||
103 | * runtime_pseudo_reloc_v2, it will not have both |
||
104 | * magic1 == 0 and magic2 == 0). |
||
105 | */ |
||
106 | v2_hdr++; |
||
107 | } |
||
108 | |||
109 | if (v2_hdr->magic1 != 0 || v2_hdr->magic2 != 0) |
||
110 | { |
||
111 | /************************* |
||
112 | * Handle v1 relocations * |
||
113 | *************************/ |
||
114 | runtime_pseudo_reloc_item_v1 * o; |
||
115 | for (o = (runtime_pseudo_reloc_item_v1 *) v2_hdr; |
||
116 | o < (runtime_pseudo_reloc_item_v1 *)end; |
||
117 | o++) |
||
118 | { |
||
119 | uint32_t newval; |
||
120 | reloc_target = (ptrdiff_t) base + o->target; |
||
121 | newval = (*((uint32_t*) reloc_target)) + o->addend; |
||
122 | *(uint32_t*)reloc_target = newval; |
||
123 | } |
||
124 | return; |
||
125 | } |
||
126 | |||
127 | /* If we got this far, then we have relocations of version 2 or newer */ |
||
128 | |||
129 | /* Check if this is a known version. */ |
||
130 | if (v2_hdr->version != RP_VERSION_V2) |
||
131 | { |
||
132 | // printf(" Unknown pseudo relocation protocol version %d.\n", |
||
133 | // (int) v2_hdr->version); |
||
134 | return; |
||
135 | } |
||
136 | |||
137 | /************************* |
||
138 | * Handle v2 relocations * |
||
139 | *************************/ |
||
140 | |||
141 | /* Walk over header. */ |
||
142 | r = (runtime_pseudo_reloc_item_v2 *) &v2_hdr[1]; |
||
143 | |||
144 | for (; r < (runtime_pseudo_reloc_item_v2 *) end; r++) |
||
145 | { |
||
146 | /* location where new address will be written */ |
||
147 | reloc_target = (ptrdiff_t) base + r->target; |
||
148 | |||
149 | /* get sym pointer. It points either to the iat entry |
||
150 | * of the referenced element, or to the stub function. |
||
151 | */ |
||
152 | addr_imp = (ptrdiff_t) base + r->sym; |
||
153 | addr_imp = *((ptrdiff_t *) addr_imp); |
||
154 | |||
155 | /* read existing relocation value from image, casting to the |
||
156 | * bitsize indicated by the 8 LSBs of flags. If the value is |
||
157 | * negative, manually sign-extend to ptrdiff_t width. Raise an |
||
158 | * error if the bitsize indicated by the 8 LSBs of flags is not |
||
159 | * supported. |
||
160 | */ |
||
161 | switch ((r->flags & 0xff)) |
||
162 | { |
||
163 | case 8: |
||
164 | reldata = (ptrdiff_t) (*((unsigned char *)reloc_target)); |
||
165 | if ((reldata & 0x80) != 0) |
||
166 | reldata |= ~((ptrdiff_t) 0xff); |
||
167 | break; |
||
168 | case 16: |
||
169 | reldata = (ptrdiff_t) (*((unsigned short *)reloc_target)); |
||
170 | if ((reldata & 0x8000) != 0) |
||
171 | reldata |= ~((ptrdiff_t) 0xffff); |
||
172 | break; |
||
173 | case 32: |
||
174 | reldata = (ptrdiff_t) (*((unsigned int *)reloc_target)); |
||
175 | break; |
||
176 | default: |
||
177 | reldata=0; |
||
178 | // printf(" Unknown pseudo relocation bit size %d.\n", |
||
179 | // (int) (r->flags & 0xff)); |
||
180 | break; |
||
181 | } |
||
182 | |||
183 | /* Adjust the relocation value */ |
||
184 | reldata -= ((ptrdiff_t) base + r->sym); |
||
185 | reldata += addr_imp; |
||
186 | |||
187 | /* Write the new relocation value back to *reloc_target */ |
||
188 | switch ((r->flags & 0xff)) |
||
189 | { |
||
190 | case 8: |
||
191 | *(uint8_t*)reloc_target = (uint8_t)reldata; |
||
192 | break; |
||
193 | case 16: |
||
194 | *(uint16_t*)reloc_target = (uint16_t)reldata; |
||
195 | break; |
||
196 | case 32: |
||
197 | *(uint32_t*)reloc_target = (uint32_t)reldata; |
||
198 | break; |
||
199 | } |
||
200 | } |
||
201 | } |
||
202 | |||
203 | void |
||
204 | _pei386_runtime_relocator (void) |
||
205 | { |
||
206 | static int was_init = 0; |
||
207 | if (was_init) |
||
208 | return; |
||
209 | ++was_init; |
||
210 | do_pseudo_reloc (&__RUNTIME_PSEUDO_RELOC_LIST__, |
||
211 | &__RUNTIME_PSEUDO_RELOC_LIST_END__, |
||
212 | &_image_base__); |
||
213 | }>>> |