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