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