1/*
2 * Copyright 2004-2018, Haiku Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT license.
4 *
5 * Copyright 2002, Travis Geiselbrecht. All rights reserved.
6 * Distributed under the terms of the NewOS License.
7 */
8
9
10#ifdef _BOOT_MODE
11#	include <boot/arch.h>
12#endif
13
14#include <KernelExport.h>
15
16#include <elf_priv.h>
17#include <arch/elf.h>
18
19
20//#define TRACE_ARCH_ELF
21#ifdef TRACE_ARCH_ELF
22#	define TRACE(x) dprintf x
23#else
24#	define TRACE(x) ;
25#endif
26
27
28#ifndef _BOOT_MODE
29static bool
30is_in_image(struct elf_image_info *image, addr_t address)
31{
32	return (address >= image->text_region.start
33			&& address < image->text_region.start + image->text_region.size)
34		|| (address >= image->data_region.start
35			&& address < image->data_region.start + image->data_region.size);
36}
37#endif	// !_BOOT_MODE
38
39
40#if !defined(__x86_64__) || defined(ELF32_COMPAT)	\
41	 || (defined(_BOOT_MODE) && defined(BOOT_SUPPORT_ELF32))
42
43
44#ifdef TRACE_ARCH_ELF
45static const char *kRelocations[] = {
46	"R_386_NONE",
47	"R_386_32",			/* add symbol value */
48	"R_386_PC32",		/* add PC relative symbol value */
49	"R_386_GOT32",		/* add PC relative GOT offset */
50	"R_386_PLT32",		/* add PC relative PLT offset */
51	"R_386_COPY",		/* copy data from shared object */
52	"R_386_GLOB_DAT",	/* set GOT entry to data address */
53	"R_386_JMP_SLOT",	/* set GOT entry to code address */
54	"R_386_RELATIVE",	/* add load address of shared object */
55	"R_386_GOTOFF",		/* add GOT relative symbol address */
56	"R_386_GOTPC",		/* add PC relative GOT table address */
57};
58#endif
59
60
61#ifdef _BOOT_MODE
62status_t
63boot_arch_elf_relocate_rel(struct preloaded_elf32_image *image, Elf32_Rel *rel,
64	int relLength)
65#else
66int
67arch_elf_relocate_rel(struct elf_image_info *image,
68	struct elf_image_info *resolveImage, Elf32_Rel *rel, int relLength)
69#endif
70{
71	Elf32_Addr S;
72	uint32 A;
73	uint32 P;
74	uint32 finalAddress;
75	uint32 *resolveAddress;
76	int i;
77
78	S = A = P = 0;
79
80	for (i = 0; i * (int)sizeof(Elf32_Rel) < relLength; i++) {
81		TRACE(("looking at rel type %s, offset 0x%" B_PRIx32 "\n",
82			kRelocations[ELF32_R_TYPE(rel[i].r_info)], rel[i].r_offset));
83
84		// calc S
85		switch (ELF32_R_TYPE(rel[i].r_info)) {
86			case R_386_32:
87			case R_386_PC32:
88			case R_386_GLOB_DAT:
89			case R_386_JMP_SLOT:
90			case R_386_GOTOFF:
91			{
92				Elf32_Sym *symbol;
93				status_t status;
94
95				symbol = SYMBOL(image, ELF32_R_SYM(rel[i].r_info));
96
97#ifdef _BOOT_MODE
98				status = boot_elf_resolve_symbol(image, symbol, &S);
99#else
100				status = elf_resolve_symbol(image, symbol, resolveImage, &S);
101#endif
102				if (status < B_OK)
103					return status;
104#ifndef _BOOT_MODE
105				TRACE(("S 0x%08" B_PRIx32 " (%s)\n", S, SYMNAME(image, symbol)));
106#endif
107			}
108		}
109		// calc A
110		switch (ELF32_R_TYPE(rel[i].r_info)) {
111			case R_386_32:
112			case R_386_PC32:
113			case R_386_GOT32:
114			case R_386_PLT32:
115			case R_386_RELATIVE:
116			case R_386_GOTOFF:
117			case R_386_GOTPC:
118#ifndef _BOOT_MODE
119				A = *(uint32 *)(image->text_region.delta + rel[i].r_offset);
120#else
121				A = boot_elf32_get_relocation(image->text_region.delta + rel[i].r_offset);
122#endif
123				TRACE(("A 0x%08" B_PRIx32 "\n", A));
124				break;
125		}
126		// calc P
127		switch (ELF32_R_TYPE(rel[i].r_info)) {
128			case R_386_PC32:
129			case R_386_GOT32:
130			case R_386_PLT32:
131			case R_386_GOTPC:
132				P = image->text_region.delta + rel[i].r_offset;
133				TRACE(("P 0x%08" B_PRIx32 "\n", P));
134				break;
135		}
136
137		switch (ELF32_R_TYPE(rel[i].r_info)) {
138			case R_386_NONE:
139				continue;
140			case R_386_32:
141				finalAddress = S + A;
142				break;
143			case R_386_PC32:
144				finalAddress = S + A - P;
145				break;
146			case R_386_RELATIVE:
147				// B + A;
148				finalAddress = image->text_region.delta + A;
149				break;
150			case R_386_JMP_SLOT:
151			case R_386_GLOB_DAT:
152				finalAddress = S;
153				break;
154
155			default:
156				dprintf("arch_elf_relocate_rel: unhandled relocation type %d\n",
157					ELF32_R_TYPE(rel[i].r_info));
158				return B_BAD_DATA;
159		}
160
161		resolveAddress = (uint32 *)(addr_t)(image->text_region.delta + rel[i].r_offset);
162#ifndef _BOOT_MODE
163		if (!is_in_image(image, (addr_t)resolveAddress)) {
164			dprintf("arch_elf_relocate_rel: invalid offset 0x%" B_PRIx32 "\n",
165				rel[i].r_offset);
166			return B_BAD_ADDRESS;
167		}
168		*resolveAddress = finalAddress;
169#else
170		boot_elf32_set_relocation((Elf32_Addr)(addr_t)resolveAddress, finalAddress);
171#endif
172		TRACE(("-> offset 0x%08" B_PRIx32 "x (0x%08" B_PRIx32 ") = 0x%08" B_PRIx32 "\n",
173			image->text_region.delta + rel[i].r_offset, rel[i].r_offset, finalAddress));
174	}
175
176	return B_NO_ERROR;
177}
178
179
180#ifdef _BOOT_MODE
181status_t
182boot_arch_elf_relocate_rela(struct preloaded_elf32_image *image,
183	Elf32_Rela *rel, int relLength)
184#else
185int
186arch_elf_relocate_rela(struct elf_image_info *image,
187	struct elf_image_info *resolveImage, Elf32_Rela *rel, int relLength)
188#endif
189{
190	dprintf("arch_elf_relocate_rela: not supported on x86\n");
191	return B_ERROR;
192}
193
194
195#endif	// !defined(__x86_64__) || defined(ELF32_COMPAT) ||
196	// (defined(_BOOT_MODE) && defined(BOOT_SUPPORT_ELF32))
197
198
199#if (defined(__x86_64__) && !defined(ELF32_COMPAT)) || \
200	(defined(_BOOT_MODE) && defined(BOOT_SUPPORT_ELF64))
201
202
203#ifdef _BOOT_MODE
204status_t
205boot_arch_elf_relocate_rel(preloaded_elf64_image* image, Elf64_Rel* rel,
206	int relLength)
207#else
208int
209arch_elf_relocate_rel(struct elf_image_info *image,
210	struct elf_image_info *resolveImage, Elf64_Rel *rel, int relLength)
211#endif
212{
213	dprintf("arch_elf_relocate_rel: not supported on x86_64\n");
214	return B_ERROR;
215}
216
217
218#ifdef _BOOT_MODE
219status_t
220boot_arch_elf_relocate_rela(preloaded_elf64_image* image, Elf64_Rela* rel,
221	int relLength)
222#else
223int
224arch_elf_relocate_rela(struct elf_image_info *image,
225	struct elf_image_info *resolveImage, Elf64_Rela *rel, int relLength)
226#endif
227{
228	for (int i = 0; i < relLength / (int)sizeof(Elf64_Rela); i++) {
229		int type = ELF64_R_TYPE(rel[i].r_info);
230		int symIndex = ELF64_R_SYM(rel[i].r_info);
231		Elf64_Addr symAddr = 0;
232
233		// Resolve the symbol, if any.
234		if (symIndex != 0) {
235			Elf64_Sym* symbol = SYMBOL(image, symIndex);
236
237			status_t status;
238#ifdef _BOOT_MODE
239			status = boot_elf_resolve_symbol(image, symbol, &symAddr);
240#else
241			status = elf_resolve_symbol(image, symbol, resolveImage, &symAddr);
242#endif
243			if (status < B_OK)
244				return status;
245		}
246
247		// Address of the relocation.
248		Elf64_Addr relocAddr = image->text_region.delta + rel[i].r_offset;
249
250		// Calculate the relocation value.
251		Elf64_Addr relocValue;
252		switch (type) {
253			case R_X86_64_NONE:
254				continue;
255			case R_X86_64_64:
256				relocValue = symAddr + rel[i].r_addend;
257				break;
258			case R_X86_64_PC32:
259				relocValue = symAddr + rel[i].r_addend - rel[i].r_offset;
260				break;
261			case R_X86_64_GLOB_DAT:
262			case R_X86_64_JUMP_SLOT:
263				relocValue = symAddr + rel[i].r_addend;
264				break;
265			case R_X86_64_RELATIVE:
266				relocValue = image->text_region.delta + rel[i].r_addend;
267				break;
268			default:
269				dprintf("arch_elf_relocate_rela: unhandled relocation type %d\n",
270					type);
271				return B_BAD_DATA;
272		}
273#ifdef _BOOT_MODE
274		boot_elf64_set_relocation(relocAddr, relocValue);
275#else
276		if (!is_in_image(image, relocAddr)) {
277			dprintf("arch_elf_relocate_rela: invalid offset %#lx\n",
278				rel[i].r_offset);
279			return B_BAD_ADDRESS;
280		}
281
282		if (type == R_X86_64_PC32)
283			*(Elf32_Addr *)relocAddr = relocValue;
284		else
285			*(Elf64_Addr *)relocAddr = relocValue;
286#endif
287	}
288
289	return B_OK;
290}
291
292
293#endif	// (defined(__x86_64__) && !defined(ELF32_COMPAT)) ||
294	// (defined(_BOOT_MODE) && defined(BOOT_SUPPORT_ELF64))
295