source: mainline/uspace/lib/c/arch/ppc32/src/rtld/reloc.c@ 98e5a1a

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 98e5a1a was eefdd7c, checked in by Jiri Svoboda <jiri@…>, 6 years ago

Dynamic linking for ppc32

  • Property mode set to 100644
File size: 10.9 KB
Line 
1/*
2 * Copyright (c) 2019 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libcppc32
30 * @brief
31 * @{
32 */
33/**
34 * @file
35 */
36
37#include <bitops.h>
38#include <smc.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <inttypes.h>
42#include <str.h>
43
44#include <libarch/rtld/elf_dyn.h>
45#include <rtld/symbol.h>
46#include <rtld/rtld.h>
47#include <rtld/rtld_debug.h>
48#include <rtld/rtld_arch.h>
49
50static void plt_farcall_init(uint32_t *plt, uint32_t *);
51static void plt_entry_init(uint32_t *, uint32_t *, uint32_t *, uintptr_t);
52static uint32_t *plt_entry_ptr(uint32_t *, size_t);
53static size_t plt_entry_index(size_t);
54static uint16_t addr_ha(uint32_t);
55static uint16_t addr_l(uint32_t);
56
57void module_process_pre_arch(module_t *m)
58{
59 /* Unused */
60}
61
62/**
63 * Process (fixup) all relocations in a relocation table.
64 */
65void rel_table_process(module_t *m, elf_rel_t *rt, size_t rt_size)
66{
67 /* Unused */
68 (void)m;
69 (void)rt;
70 (void)rt_size;
71}
72
73/**
74 * Process (fixup) all relocations in a relocation table with explicit addends.
75 */
76void rela_table_process(module_t *m, elf_rela_t *rt, size_t rt_size)
77{
78 unsigned i;
79
80 size_t rt_entries;
81 size_t r_offset;
82 size_t r_addend;
83 elf_xword r_info;
84 unsigned rel_type;
85 elf_word sym_idx;
86 uintptr_t sym_addr;
87
88 elf_symbol_t *sym_table;
89 elf_symbol_t *sym;
90 uintptr_t *r_ptr;
91 uintptr_t sym_size;
92 char *str_tab;
93
94 elf_symbol_t *sym_def;
95 module_t *dest;
96 uint32_t *plt;
97 uint32_t *plt_datawords;
98 size_t jmp_slots;
99
100 DPRINTF("Count jump slots.\n");
101
102 rt_entries = rt_size / sizeof(elf_rela_t);
103
104 jmp_slots = 0;
105 for (i = 0; i < rt_entries; ++i) {
106 r_info = rt[i].r_info;
107 rel_type = ELF32_R_TYPE(r_info);
108
109 if (rel_type == R_PPC_JMP_SLOT)
110 ++jmp_slots;
111 }
112
113 DPRINTF("Init farcall section\n");
114
115 plt = (uint32_t *)m->dyn.plt_got;
116
117 /* Table with addresses starts just after last PLT entry */
118 plt_datawords = plt_entry_ptr(plt, jmp_slots);
119
120 /* Init farcall section with reference to datawords table */
121 plt_farcall_init(plt, plt_datawords);
122
123 DPRINTF("parse relocation table\n");
124
125 sym_table = m->dyn.sym_tab;
126 str_tab = m->dyn.str_tab;
127
128 DPRINTF("rel table address: 0x%zx, entries: %zd\n", (uintptr_t)rt, rt_entries);
129
130 for (i = 0; i < rt_entries; ++i) {
131#if 0
132 DPRINTF("symbol %d: ", i);
133#endif
134 r_offset = rt[i].r_offset;
135 r_info = rt[i].r_info;
136 r_addend = rt[i].r_addend;
137
138 sym_idx = ELF32_R_SYM(r_info);
139 sym = &sym_table[sym_idx];
140
141#if 0
142 DPRINTF("name '%s', value 0x%x, size 0x%x\n",
143 str_tab + sym->st_name,
144 sym->st_value,
145 sym->st_size);
146#endif
147 rel_type = ELF32_R_TYPE(r_info);
148 r_ptr = (uintptr_t *)(r_offset + m->bias);
149
150 if (sym->st_name != 0) {
151 DPRINTF("rel_type: %x, rel_offset: 0x%zx\n", rel_type, r_offset);
152 sym_def = symbol_def_find(str_tab + sym->st_name,
153 m, ssf_none, &dest);
154 DPRINTF("dest name: '%s'\n", dest->dyn.soname);
155 DPRINTF("dest bias: 0x%zx\n", dest->bias);
156 if (sym_def) {
157 sym_addr = (uintptr_t)
158 symbol_get_addr(sym_def, dest, NULL);
159 DPRINTF("symbol definition found, value=0x%zx addr=0x%zx\n", sym_def->st_value, sym_addr);
160 } else {
161 printf("Definition of '%s' not found.\n",
162 str_tab + sym->st_name);
163 continue;
164 }
165 } else {
166 sym_addr = 0;
167 sym_def = NULL;
168
169 /*
170 * DTPMOD with null st_name should return the index
171 * of the current module.
172 */
173 dest = m;
174 }
175
176 switch (rel_type) {
177 case R_PPC_ADDR32:
178 DPRINTF("fixup R_PPC_ADDR32 (S+A)\n");
179 DPRINTF("*0x%zx = 0x%zx\n", (uintptr_t)r_ptr, sym_addr);
180 *r_ptr = sym_addr + r_addend;
181 DPRINTF("OK\n");
182 break;
183 case R_PPC_REL24:
184 DPRINTF("fixup R_PPC_REL24 ((S+A-P) >> 2)\n");
185 DPRINTF("*0x%zx = 0x%zx\n", (uintptr_t)r_ptr,
186 (sym_addr + r_addend - (uintptr_t)r_ptr) >> 2);
187 *r_ptr = (sym_addr + r_addend - (uintptr_t)r_ptr) >> 2;
188 DPRINTF("OK\n");
189 break;
190 case R_PPC_COPY:
191 /*
192 * Copy symbol data from shared object to specified
193 * location. Need to find the 'source', i.e. the
194 * other instance of the object than the one in the
195 * executable program.
196 */
197 DPRINTF("fixup R_PPC_COPY (s)\n");
198
199 sym_def = symbol_def_find(str_tab + sym->st_name,
200 m, ssf_noexec, &dest);
201
202 if (sym_def) {
203 sym_addr = (uintptr_t)
204 symbol_get_addr(sym_def, dest, NULL);
205 } else {
206 printf("Source definition of '%s' not found.\n",
207 str_tab + sym->st_name);
208 continue;
209 }
210
211 sym_size = sym->st_size;
212 if (sym_size != sym_def->st_size) {
213#if 0
214 printf("Warning: Mismatched symbol sizes.\n");
215#endif
216 /* Take the lower value. */
217 if (sym_size > sym_def->st_size)
218 sym_size = sym_def->st_size;
219 }
220
221 memcpy(r_ptr, (const void *)sym_addr, sym_size);
222 DPRINTF("OK\n");
223 break;
224
225 case R_PPC_JMP_SLOT:
226 DPRINTF("fixup R_PPC_JMP_SLOT (S)\n");
227 DPRINTF("r_offset=0x%zx r_addend=0x%zx\n",
228 r_offset, r_addend);
229
230 sym_def = symbol_def_find(str_tab + sym->st_name,
231 m, ssf_noexec, &dest);
232
233 if (sym_def) {
234 sym_addr = (uintptr_t)
235 symbol_get_addr(sym_def, dest, NULL);
236 } else {
237 printf("Source definition of '%s' not found.\n",
238 str_tab + sym->st_name);
239 continue;
240 }
241
242 sym_size = sym->st_size;
243 if (sym_size != sym_def->st_size) {
244#if 0
245 printf("Warning: Mismatched symbol sizes.\n");
246#endif
247 /* Take the lower value. */
248 if (sym_size > sym_def->st_size)
249 sym_size = sym_def->st_size;
250 }
251
252 DPRINTF("sym_addr = 0x%zx\n", sym_addr);
253 DPRINTF("r_offset=0x%zx\n", r_offset);
254
255 /*
256 * Fill PLT entry with jump to symbol address.
257 */
258 plt_entry_init(plt, (uint32_t *)r_ptr, plt_datawords,
259 sym_addr);
260
261 DPRINTF("OK\n");
262 break;
263
264 case R_PPC_RELATIVE:
265 DPRINTF("fixup R_PPC_RELATIVE (B+A)\n");
266 DPRINTF("*0x%zx = 0x%zx\n", (uintptr_t)r_ptr, m->bias + r_addend);
267 *r_ptr = m->bias + r_addend;
268 DPRINTF("OK\n");
269 break;
270
271 case R_PPC_DTPMOD32:
272 DPRINTF("fixup R_PPC_DTPMOD32\n");
273 DPRINTF("*0x%zx = 0x%zx\n", (uintptr_t)r_ptr, (size_t)dest->id);
274 *r_ptr = dest->id;
275 DPRINTF("OK\n");
276 break;
277
278 case R_PPC_DTPREL32:
279 DPRINTF("fixup R_PPC_DTPREL32\n"); /* XXXXX */
280 DPRINTF("*0x%zx = 0x%zx\n", (uintptr_t)r_ptr, sym_def->st_value);
281 *r_ptr = sym_def->st_value + r_addend;
282 DPRINTF("OK\n");
283 break;
284
285 default:
286 printf("Error: Unknown relocation type %d\n",
287 rel_type);
288 exit(1);
289 }
290
291 }
292}
293
294/** Init PLT farcall section. */
295static void plt_farcall_init(uint32_t *plt, uint32_t *plt_datawords)
296{
297 uint16_t hi;
298 uint16_t lo;
299 int i;
300
301 hi = addr_ha((uintptr_t)plt_datawords);
302 lo = addr_l((uintptr_t)plt_datawords);
303
304 plt[0] = 0x3d6b0000 | hi; /* addis %r11,% r11,. plt_datawords@ha */
305 plt[1] = 0x816b0000 | lo; /* lwz %r11, .plt_datawords@l(%r11) */
306 plt[2] = 0x7d6903a6; /* mtctr %r11 */
307 plt[3] = 0x4e800420; /* bctr */
308 plt[4] = 0x60000000; /* nop */
309 plt[5] = 0x60000000; /* nop */
310
311 smc_coherence(plt, 4 * 6);
312
313 for (i = 0; i < 6; i++)
314 DPRINTF("%p: farcall[%d] = %08zx\n", &plt[i], i, plt[i]);
315}
316
317/** Fill in PLT entry.
318 *
319 * Fill a PLT entry with PowerPC instructions to set table index and
320 * jump to the farcall section. Fill table entry with target address.
321 *
322 * @param plt Pointer to PLT
323 * @param plte Pointer to PLT entry to fill in
324 * @param datawords Address table
325 * @param ta Target address of the jump
326 */
327static void plt_entry_init(uint32_t *plt, uint32_t *plte, uint32_t *datawords,
328 uintptr_t ta)
329{
330 size_t index;
331 size_t woffset;
332 uint16_t i4index;
333 uint32_t btgt;
334
335 DPRINTF("plt_entry_init(plt=%p, plte=%p, datawords=%p, ta=0z%zx\n",
336 plt, plte, datawords, ta);
337
338 /* Entry offset in words */
339 woffset = plte - plt;
340
341 /* Entry index */
342 index = plt_entry_index(woffset);
343
344 /* This only works for the first 2048 entries */
345 assert(index * 4 < 0x8000);
346 i4index = 4 * index;
347
348 /* Relative branch offset */
349 btgt = ((uint8_t *)plt - (uint8_t *)&plte[1]) & 0x03ffffff;
350
351 /* Write target address to table */
352 datawords[index] = ta;
353 DPRINTF("%p: datawords[%zu] = %08x\n", &datawords[index], index, ta);
354
355 plte[0] = 0x39600000 | i4index; /* li %r11, 4 * index */
356 plte[1] = 0x48000000 | btgt; /* b .plt_farcall */
357
358 DPRINTF("%p: plte[0] = %08zx\n", &plte[0], plte[0]);
359 DPRINTF("%p: plte[1] = %08zx\n", &plte[1], plte[1]);
360
361 smc_coherence(plte, 4 * 2);
362}
363
364/** Determine PLT entry address.
365 *
366 * @param plt Start of PLT
367 * @param index PLT entry index
368 * @return Pointer to PLT entry
369 */
370static uint32_t *plt_entry_ptr(uint32_t *plt, size_t index)
371{
372 if (index < 8192)
373 return plt + 18 + 2 * index;
374 else
375 return plt + 18 + 2 * 8192 + 4 * (index - 8192);
376}
377
378/** Determine index of PLT entry from its word offset.
379 *
380 * @param woffset Offset of PLT entry in words
381 * @return PLT entry index
382 */
383static size_t plt_entry_index(size_t woffset)
384{
385 assert(woffset >= 18);
386 woffset -= 18;
387
388 if (woffset < 2 * 8192) {
389 assert((woffset & 0x1) == 0);
390 return woffset / 2;
391 } else {
392 assert((woffset & 0x3) == 0);
393 return (woffset - 2 * 8192) / 4;
394 }
395}
396
397/** Determine high bits of address.
398 *
399 * The lower bits are determined by @c addr_l function. The lower bits
400 * are considered to be a 16-bit signed integer.
401 *
402 * @param addr Address
403 * @return Higher bits of address
404 */
405static uint16_t addr_ha(uint32_t addr)
406{
407 int32_t la;
408
409 /* The lower part of the address is a signed 16-bit integer */
410 la = (int16_t)(addr & 0xffff);
411
412 /* Compute higher bits while compensating for the sign extension */
413 return (addr - la) >> 16;
414}
415
416/** Determine lower bits of address.
417 *
418 * The lower bits are considered to be 16-bit signed integer/immediate
419 * operand by the ISA, but we return them here as unsigned unmber so
420 * it can be easily incorporated into an instruction opcode.
421 *
422 * @param addr Address
423 * @return Lower bits of address cast as unsigned 16-bit integer
424 */
425static uint16_t addr_l(uint32_t addr)
426{
427 return (uint16_t) (addr & 0x0000ffff);
428}
429
430/** @}
431 */
Note: See TracBrowser for help on using the repository browser.