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