xref: /kvm-unit-tests/powerpc/reloc64.c (revision 0c4e631ecaf30f8065dec5cc589d5894c961e6ee)
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 {
29 	unsigned long long rela_addr = 0, rela_count = 0, *addr;
30 	struct elf64_dyn *d = dyn_table;
31 	struct elf64_rela *r;
32 
33 	while (d && d->tag != DT_NULL) {
34 		if (d->tag == DT_RELA)
35 			rela_addr = d->val;
36 		else if (d->tag == DT_RELACOUNT)
37 			rela_count = d->val;
38 		if (rela_addr && rela_count)
39 			break;
40 		++d;
41 	}
42 
43 	if (!rela_addr || !rela_count)
44 		return;
45 
46 	r = (void *)(rela_addr + load_addr);
47 
48 	while (rela_count--) {
49 		if (RELA_GET_TYPE(r) == R_PPC_RELATIVE) {
50 			addr = (void *)(r->offset + load_addr);
51 			*addr = r->addend + load_addr;
52 		}
53 		++r;
54 	}
55 }
56