xref: /kvm-unit-tests/powerpc/reloc64.c (revision 805a69540baa1e31706704c8b4967e4927138e4f)
1 /*
2  * relocate R_PPC_RELATIVE RELA entries. Normally this is done in
3  * assembly code to avoid the risk of using absolute addresses before
4  * they're relocated. We use C, but cautiously (no global references).
5  *
6  * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com>
7  *
8  * This work is licensed under the terms of the GNU LGPL, version 2.
9  */
10 #define DT_NULL		0
11 #define DT_RELA 	7
12 #define DT_RELACOUNT	0x6ffffff9
13 #define R_PPC_RELATIVE	22
14 
15 struct elf64_dyn {
16 	signed long long tag;
17 	unsigned long long val;
18 };
19 
20 #define RELA_GET_TYPE(rela_ptr) ((rela_ptr)->info & 0xffffffff)
21 struct elf64_rela {
22 	unsigned long long offset;
23 	unsigned long long info;
24 	signed long long addend;
25 };
26 
27 void relocate(unsigned long load_addr, struct elf64_dyn *dyn_table);
28 
relocate(unsigned long load_addr,struct elf64_dyn * dyn_table)29 void relocate(unsigned long load_addr, struct elf64_dyn *dyn_table)
30 {
31 	unsigned long long rela_addr = 0, rela_count = 0, *addr;
32 	struct elf64_dyn *d = dyn_table;
33 	struct elf64_rela *r;
34 
35 	while (d && d->tag != DT_NULL) {
36 		if (d->tag == DT_RELA)
37 			rela_addr = d->val;
38 		else if (d->tag == DT_RELACOUNT)
39 			rela_count = d->val;
40 		if (rela_addr && rela_count)
41 			break;
42 		++d;
43 	}
44 
45 	if (!rela_addr || !rela_count)
46 		return;
47 
48 	r = (void *)(rela_addr + load_addr);
49 
50 	while (rela_count--) {
51 		if (RELA_GET_TYPE(r) == R_PPC_RELATIVE) {
52 			addr = (void *)(r->offset + load_addr);
53 			*addr = r->addend + load_addr;
54 		}
55 		++r;
56 	}
57 }
58