1/*
2 * Copyright 2002-2008, Axel D��rfler, axeld@pinc-software.de. All rights reserved.
3 * Copyright 2012, Alex Smith, alex@alex-smith.me.uk.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "elf.h"
9
10#include <boot/arch.h>
11#include <boot/platform.h>
12#include <boot/stage2.h>
13#include <driver_settings.h>
14#include <elf_private.h>
15#include <kernel.h>
16#include <SupportDefs.h>
17
18#include <errno.h>
19#include <unistd.h>
20#include <string.h>
21#include <stdlib.h>
22
23//#define TRACE_ELF
24#ifdef TRACE_ELF
25#	define TRACE(x) dprintf x
26#else
27#	define TRACE(x) ;
28#endif
29
30
31static bool sLoadElfSymbols = true;
32
33
34// #pragma mark - Generic ELF loader
35
36
37template<typename Class>
38class ELFLoader {
39private:
40	typedef typename Class::ImageType	ImageType;
41	typedef typename Class::RegionType	RegionType;
42	typedef typename Class::AddrType	AddrType;
43	typedef typename Class::EhdrType	EhdrType;
44	typedef typename Class::PhdrType	PhdrType;
45	typedef typename Class::ShdrType	ShdrType;
46	typedef typename Class::DynType		DynType;
47	typedef typename Class::SymType		SymType;
48	typedef typename Class::RelType		RelType;
49	typedef typename Class::RelaType	RelaType;
50
51public:
52	static	status_t	Create(int fd, preloaded_image** _image);
53	static	status_t	Load(int fd, preloaded_image* image);
54	static	status_t	Relocate(preloaded_image* image);
55	static	status_t	Resolve(ImageType* image, SymType* symbol,
56							AddrType* symbolAddress);
57
58private:
59	static	status_t	_LoadSymbolTable(int fd, ImageType* image);
60	static	status_t	_ParseDynamicSection(ImageType* image);
61};
62
63
64#ifdef BOOT_SUPPORT_ELF32
65struct ELF32Class {
66	static const uint8 kIdentClass = ELFCLASS32;
67
68	typedef preloaded_elf32_image	ImageType;
69	typedef elf32_region			RegionType;
70	typedef Elf32_Addr				AddrType;
71	typedef Elf32_Ehdr				EhdrType;
72	typedef Elf32_Phdr				PhdrType;
73	typedef Elf32_Shdr				ShdrType;
74	typedef Elf32_Dyn				DynType;
75	typedef Elf32_Sym				SymType;
76	typedef Elf32_Rel				RelType;
77	typedef Elf32_Rela				RelaType;
78
79	static inline status_t
80	AllocateRegion(AddrType* _address, AddrType size, uint8 protection,
81		void** _mappedAddress)
82	{
83		status_t status = platform_allocate_region((void**)_address, size,
84			protection, false);
85		if (status != B_OK)
86			return status;
87
88		*_mappedAddress = (void*)*_address;
89
90		addr_t res;
91		platform_bootloader_address_to_kernel_address((void*)*_address, &res);
92
93		*_address = res;
94
95		return B_OK;
96	}
97
98	static inline void*
99	Map(AddrType address)
100	{
101		void *result = NULL;
102		if (platform_kernel_address_to_bootloader_address(address, &result) != B_OK) {
103			panic("Couldn't convert address 0x%08x", (uint32_t)address);
104		}
105
106		return result;
107	}
108};
109
110typedef ELFLoader<ELF32Class> ELF32Loader;
111#endif
112
113
114#ifdef BOOT_SUPPORT_ELF64
115struct ELF64Class {
116	static const uint8 kIdentClass = ELFCLASS64;
117
118	typedef preloaded_elf64_image	ImageType;
119	typedef elf64_region			RegionType;
120	typedef Elf64_Addr				AddrType;
121	typedef Elf64_Ehdr				EhdrType;
122	typedef Elf64_Phdr				PhdrType;
123	typedef Elf64_Shdr				ShdrType;
124	typedef Elf64_Dyn				DynType;
125	typedef Elf64_Sym				SymType;
126	typedef Elf64_Rel				RelType;
127	typedef Elf64_Rela				RelaType;
128
129	static inline status_t
130	AllocateRegion(AddrType* _address, AddrType size, uint8 protection,
131		void **_mappedAddress)
132	{
133#if defined(_BOOT_PLATFORM_BIOS)
134		// Assume the real 64-bit base address is KERNEL_LOAD_BASE_64_BIT and
135		// the mappings in the loader address space are at KERNEL_LOAD_BASE.
136
137		void* address = (void*)(addr_t)(*_address & 0xffffffff);
138#else
139		void* address = (void*)*_address;
140#endif
141
142		status_t status = platform_allocate_region(&address, size, protection,
143			false);
144		if (status != B_OK)
145			return status;
146
147		*_mappedAddress = address;
148#if defined(_BOOT_PLATFORM_BIOS)
149		*_address = (AddrType)(addr_t)address + KERNEL_FIXUP_FOR_LONG_MODE;
150#else
151		platform_bootloader_address_to_kernel_address(address, _address);
152#endif
153		return B_OK;
154	}
155
156	static inline void*
157	Map(AddrType address)
158	{
159#ifdef _BOOT_PLATFORM_BIOS
160		return (void*)(addr_t)(address - KERNEL_FIXUP_FOR_LONG_MODE);
161#else
162		void *result;
163		if (platform_kernel_address_to_bootloader_address(address, &result) != B_OK) {
164			panic("Couldn't convert address %#" PRIx64, address);
165		}
166		return result;
167#endif
168	}
169};
170
171typedef ELFLoader<ELF64Class> ELF64Loader;
172#endif
173
174
175template<typename Class>
176/*static*/ status_t
177ELFLoader<Class>::Create(int fd, preloaded_image** _image)
178{
179	ImageType* image = (ImageType*)kernel_args_malloc(sizeof(ImageType));
180	if (image == NULL)
181		return B_NO_MEMORY;
182
183	ssize_t length = read_pos(fd, 0, &image->elf_header, sizeof(EhdrType));
184	if (length < (ssize_t)sizeof(EhdrType)) {
185		kernel_args_free(image);
186		return B_BAD_TYPE;
187	}
188
189	const EhdrType& elfHeader = image->elf_header;
190
191	if (memcmp(elfHeader.e_ident, ELFMAG, 4) != 0
192		|| elfHeader.e_ident[4] != Class::kIdentClass
193		|| elfHeader.e_phoff == 0
194		|| !elfHeader.IsHostEndian()
195		|| elfHeader.e_phentsize != sizeof(PhdrType)) {
196		kernel_args_free(image);
197		return B_BAD_TYPE;
198	}
199
200	image->elf_class = elfHeader.e_ident[EI_CLASS];
201
202	*_image = image;
203	return B_OK;
204}
205
206
207template<typename Class>
208/*static*/ status_t
209ELFLoader<Class>::Load(int fd, preloaded_image* _image)
210{
211	size_t totalSize;
212	ssize_t length;
213	status_t status;
214	void* mappedRegion = NULL;
215
216	ImageType* image = static_cast<ImageType*>(_image);
217	const EhdrType& elfHeader = image->elf_header;
218
219	ssize_t size = elfHeader.e_phnum * elfHeader.e_phentsize;
220	PhdrType* programHeaders = (PhdrType*)malloc(size);
221	if (programHeaders == NULL) {
222		dprintf("error allocating space for program headers\n");
223		status = B_NO_MEMORY;
224		goto error1;
225	}
226
227	length = read_pos(fd, elfHeader.e_phoff, programHeaders, size);
228	if (length < size) {
229		TRACE(("error reading in program headers\n"));
230		status = B_ERROR;
231		goto error1;
232	}
233
234	// create an area large enough to hold the image
235
236	image->data_region.size = 0;
237	image->text_region.size = 0;
238
239	for (int32 i = 0; i < elfHeader.e_phnum; i++) {
240		PhdrType& header = programHeaders[i];
241
242		switch (header.p_type) {
243			case PT_LOAD:
244				break;
245			case PT_DYNAMIC:
246				image->dynamic_section.start = header.p_vaddr;
247				image->dynamic_section.size = header.p_memsz;
248				continue;
249			case PT_INTERP:
250			case PT_PHDR:
251			case PT_ARM_UNWIND:
252			case PT_RISCV_ATTRIBUTES:
253				// known but unused type
254				continue;
255			case PT_EH_FRAME:
256				// not implemented yet, but can be ignored
257				continue;
258			default:
259				dprintf("unhandled pheader type 0x%" B_PRIx32 "\n", header.p_type);
260				continue;
261		}
262
263		RegionType* region;
264		if (header.IsReadWrite()) {
265			if (image->data_region.size != 0) {
266				dprintf("elf: rw already handled!\n");
267				continue;
268			}
269			region = &image->data_region;
270		} else if (header.IsExecutable()) {
271			if (image->text_region.size != 0) {
272				dprintf("elf: ro already handled!\n");
273				continue;
274			}
275			region = &image->text_region;
276		} else
277			continue;
278
279		region->start = ROUNDDOWN(header.p_vaddr, B_PAGE_SIZE);
280		region->size = ROUNDUP(header.p_memsz + (header.p_vaddr % B_PAGE_SIZE),
281			B_PAGE_SIZE);
282		region->delta = -region->start;
283
284		TRACE(("segment %" B_PRId32 ": start = 0x%" B_PRIx64 ", size = %"
285			B_PRIu64 ", delta = %" B_PRIx64 "\n", i, (uint64)region->start,
286			(uint64)region->size, (int64)(AddrType)region->delta));
287	}
288
289
290	// found both, text and data?
291	if (image->data_region.size == 0 || image->text_region.size == 0) {
292		dprintf("Couldn't find both text and data segment!\n");
293		status = B_BAD_DATA;
294		goto error1;
295	}
296
297	// get the segment order
298	RegionType* firstRegion;
299	RegionType* secondRegion;
300	if (image->text_region.start < image->data_region.start) {
301		firstRegion = &image->text_region;
302		secondRegion = &image->data_region;
303	} else {
304		firstRegion = &image->data_region;
305		secondRegion = &image->text_region;
306	}
307
308	// The kernel and the modules are relocatable, thus AllocateRegion()
309	// can automatically allocate an address, but shall prefer the specified
310	// base address.
311	totalSize = secondRegion->start + secondRegion->size - firstRegion->start;
312	{
313		AddrType address = firstRegion->start;
314		if (Class::AllocateRegion(&address, totalSize,
315				B_READ_AREA | B_WRITE_AREA | B_EXECUTE_AREA, &mappedRegion)
316				!= B_OK) {
317			status = B_NO_MEMORY;
318			goto error1;
319		}
320		firstRegion->start = address;
321	}
322
323	// initialize the region pointers to the allocated region
324	secondRegion->start += firstRegion->start + firstRegion->delta;
325
326	image->data_region.delta += image->data_region.start;
327	image->text_region.delta += image->text_region.start;
328
329	TRACE(("text: start 0x%" B_PRIx64 ", size 0x%" B_PRIx64 ", delta 0x%"
330		B_PRIx64 "\n", (uint64)image->text_region.start,
331		(uint64)image->text_region.size,
332		(int64)(AddrType)image->text_region.delta));
333	TRACE(("data: start 0x%" B_PRIx64 ", size 0x%" B_PRIx64 ", delta 0x%"
334		B_PRIx64 "\n", (uint64)image->data_region.start,
335		(uint64)image->data_region.size,
336		(int64)(AddrType)image->data_region.delta));
337
338	// load program data
339
340	for (int32 i = 0; i < elfHeader.e_phnum; i++) {
341		PhdrType& header = programHeaders[i];
342
343		if (header.p_type != PT_LOAD)
344			continue;
345
346		RegionType* region;
347		if (header.IsReadWrite())
348			region = &image->data_region;
349		else if (header.IsExecutable())
350			region = &image->text_region;
351		else
352			continue;
353
354		TRACE(("load segment %" PRId32 " (%" PRIu64 " bytes) mapped at %p...\n",
355			i, (uint64)header.p_filesz, Class::Map(region->start)));
356
357		length = read_pos(fd, header.p_offset,
358			Class::Map(region->start + (header.p_vaddr % B_PAGE_SIZE)),
359			header.p_filesz);
360		if (length < (ssize_t)header.p_filesz) {
361			status = B_BAD_DATA;
362			dprintf("error reading in seg %" B_PRId32 "\n", i);
363			goto error2;
364		}
365
366		// Clear anything above the file size (that may also contain the BSS
367		// area)
368
369		uint32 offset = (header.p_vaddr % B_PAGE_SIZE) + header.p_filesz;
370		if (offset < region->size)
371			memset(Class::Map(region->start + offset), 0, region->size - offset);
372	}
373
374	// offset dynamic section, and program entry addresses by the delta of the
375	// regions
376	image->dynamic_section.start += image->text_region.delta;
377	image->elf_header.e_entry += image->text_region.delta;
378
379	image->num_debug_symbols = 0;
380	image->debug_symbols = NULL;
381	image->debug_string_table = NULL;
382
383	if (sLoadElfSymbols)
384		_LoadSymbolTable(fd, image);
385
386	free(programHeaders);
387
388	return B_OK;
389
390error2:
391	if (mappedRegion != NULL)
392		platform_free_region(mappedRegion, totalSize);
393error1:
394	free(programHeaders);
395	kernel_args_free(image);
396
397	return status;
398}
399
400
401template<typename Class>
402/*static*/ status_t
403ELFLoader<Class>::Relocate(preloaded_image* _image)
404{
405	ImageType* image = static_cast<ImageType*>(_image);
406
407	status_t status = _ParseDynamicSection(image);
408	if (status != B_OK)
409		return status;
410
411	// deal with the rels first
412	if (image->rel) {
413		TRACE(("total %i relocs\n",
414			(int)image->rel_len / (int)sizeof(RelType)));
415
416		status = boot_arch_elf_relocate_rel(image, image->rel, image->rel_len);
417		if (status != B_OK)
418			return status;
419	}
420
421	if (image->pltrel) {
422		RelType* pltrel = image->pltrel;
423		if (image->pltrel_type == DT_REL) {
424			TRACE(("total %i plt-relocs\n",
425				(int)image->pltrel_len / (int)sizeof(RelType)));
426
427			status = boot_arch_elf_relocate_rel(image, pltrel,
428				image->pltrel_len);
429		} else {
430			TRACE(("total %i plt-relocs\n",
431				(int)image->pltrel_len / (int)sizeof(RelaType)));
432
433			status = boot_arch_elf_relocate_rela(image, (RelaType*)pltrel,
434				image->pltrel_len);
435		}
436		if (status != B_OK)
437			return status;
438	}
439
440	if (image->rela) {
441		TRACE(("total %i rela relocs\n",
442			(int)image->rela_len / (int)sizeof(RelaType)));
443		status = boot_arch_elf_relocate_rela(image, image->rela,
444			image->rela_len);
445		if (status != B_OK)
446			return status;
447	}
448
449	return B_OK;
450}
451
452template<typename Class>
453/*static*/ status_t
454ELFLoader<Class>::Resolve(ImageType* image, SymType* symbol,
455	AddrType* symbolAddress)
456{
457	switch (symbol->st_shndx) {
458		case SHN_UNDEF:
459			// Since we do that only for the kernel, there shouldn't be
460			// undefined symbols.
461			TRACE(("elf_resolve_symbol: undefined symbol\n"));
462			return B_MISSING_SYMBOL;
463		case SHN_ABS:
464			*symbolAddress = symbol->st_value;
465			return B_NO_ERROR;
466		case SHN_COMMON:
467			// ToDo: finish this
468			TRACE(("elf_resolve_symbol: COMMON symbol, finish me!\n"));
469			return B_ERROR;
470		default:
471			// standard symbol
472			*symbolAddress = symbol->st_value + image->text_region.delta;
473			return B_OK;
474	}
475}
476
477
478template<typename Class>
479/*static*/ status_t
480ELFLoader<Class>::_LoadSymbolTable(int fd, ImageType* image)
481{
482	const EhdrType& elfHeader = image->elf_header;
483	SymType* symbolTable = NULL;
484	ShdrType* stringHeader = NULL;
485	uint32 numSymbols = 0;
486	char* stringTable;
487	status_t status;
488
489	// get section headers
490
491	ssize_t size = elfHeader.e_shnum * elfHeader.e_shentsize;
492	ShdrType* sectionHeaders = (ShdrType*)malloc(size);
493	if (sectionHeaders == NULL) {
494		dprintf("error allocating space for section headers\n");
495		return B_NO_MEMORY;
496	}
497
498	ssize_t length = read_pos(fd, elfHeader.e_shoff, sectionHeaders, size);
499	if (length < size) {
500		TRACE(("error reading in program headers\n"));
501		status = B_ERROR;
502		goto error1;
503	}
504
505	// find symbol table in section headers
506
507	for (int32 i = 0; i < elfHeader.e_shnum; i++) {
508		if (sectionHeaders[i].sh_type == SHT_SYMTAB) {
509			stringHeader = &sectionHeaders[sectionHeaders[i].sh_link];
510
511			if (stringHeader->sh_type != SHT_STRTAB) {
512				TRACE(("doesn't link to string table\n"));
513				status = B_BAD_DATA;
514				goto error1;
515			}
516
517			// read in symbol table
518			size = sectionHeaders[i].sh_size;
519			symbolTable = (SymType*)kernel_args_malloc(size);
520			if (symbolTable == NULL) {
521				status = B_NO_MEMORY;
522				goto error1;
523			}
524
525			length = read_pos(fd, sectionHeaders[i].sh_offset, symbolTable,
526				size);
527			if (length < size) {
528				TRACE(("error reading in symbol table\n"));
529				status = B_ERROR;
530				goto error1;
531			}
532
533			numSymbols = size / sizeof(SymType);
534			break;
535		}
536	}
537
538	if (symbolTable == NULL) {
539		TRACE(("no symbol table\n"));
540		status = B_BAD_VALUE;
541		goto error1;
542	}
543
544	// read in string table
545
546	size = stringHeader->sh_size;
547	stringTable = (char*)kernel_args_malloc(size);
548	if (stringTable == NULL) {
549		status = B_NO_MEMORY;
550		goto error2;
551	}
552
553	length = read_pos(fd, stringHeader->sh_offset, stringTable, size);
554	if (length < size) {
555		TRACE(("error reading in string table\n"));
556		status = B_ERROR;
557		goto error3;
558	}
559
560	TRACE(("loaded %" B_PRIu32 " debug symbols\n", numSymbols));
561
562	// insert tables into image
563	image->debug_symbols = symbolTable;
564	image->num_debug_symbols = numSymbols;
565	image->debug_string_table = stringTable;
566	image->debug_string_table_size = size;
567
568	free(sectionHeaders);
569	return B_OK;
570
571error3:
572	kernel_args_free(stringTable);
573error2:
574	kernel_args_free(symbolTable);
575error1:
576	free(sectionHeaders);
577
578	return status;
579}
580
581
582template<typename Class>
583/*static*/ status_t
584ELFLoader<Class>::_ParseDynamicSection(ImageType* image)
585{
586	image->syms = 0;
587	image->rel = 0;
588	image->rel_len = 0;
589	image->rela = 0;
590	image->rela_len = 0;
591	image->pltrel = 0;
592	image->pltrel_len = 0;
593	image->pltrel_type = 0;
594
595	if(image->dynamic_section.start == 0)
596		return B_ERROR;
597
598	DynType* d = (DynType*)Class::Map(image->dynamic_section.start);
599
600	for (int i = 0; d[i].d_tag != DT_NULL; i++) {
601		switch (d[i].d_tag) {
602			case DT_HASH:
603			case DT_STRTAB:
604				break;
605			case DT_SYMTAB:
606				image->syms = (SymType*)Class::Map(d[i].d_un.d_ptr
607					+ image->text_region.delta);
608				break;
609			case DT_REL:
610				image->rel = (RelType*)Class::Map(d[i].d_un.d_ptr
611					+ image->text_region.delta);
612				break;
613			case DT_RELSZ:
614				image->rel_len = d[i].d_un.d_val;
615				break;
616			case DT_RELA:
617				image->rela = (RelaType*)Class::Map(d[i].d_un.d_ptr
618					+ image->text_region.delta);
619				break;
620			case DT_RELASZ:
621				image->rela_len = d[i].d_un.d_val;
622				break;
623			case DT_JMPREL:
624				image->pltrel = (RelType*)Class::Map(d[i].d_un.d_ptr
625					+ image->text_region.delta);
626				break;
627			case DT_PLTRELSZ:
628				image->pltrel_len = d[i].d_un.d_val;
629				break;
630			case DT_PLTREL:
631				image->pltrel_type = d[i].d_un.d_val;
632				break;
633
634			default:
635				continue;
636		}
637	}
638
639	// lets make sure we found all the required sections
640	if (image->syms == NULL)
641		return B_ERROR;
642
643	return B_OK;
644}
645
646
647// #pragma mark -
648
649
650void
651elf_init()
652{
653	void* settings = load_driver_settings("kernel");
654	if (settings == NULL)
655		return;
656
657	sLoadElfSymbols = get_driver_boolean_parameter(settings, "load_symbols",
658		false, false);
659
660	unload_driver_settings(settings);
661}
662
663
664status_t
665elf_load_image(int fd, preloaded_image** _image)
666{
667	status_t status = B_ERROR;
668
669	TRACE(("elf_load_image(fd = %d, _image = %p)\n", fd, _image));
670
671#if BOOT_SUPPORT_ELF64
672	if (gKernelArgs.kernel_image == NULL
673		|| gKernelArgs.kernel_image->elf_class == ELFCLASS64) {
674		status = ELF64Loader::Create(fd, _image);
675		if (status == B_OK)
676			return ELF64Loader::Load(fd, *_image);
677		else if (status != B_BAD_TYPE)
678			return status;
679	}
680#endif
681#if BOOT_SUPPORT_ELF32
682	if (gKernelArgs.kernel_image == NULL
683		|| gKernelArgs.kernel_image->elf_class == ELFCLASS32) {
684		status = ELF32Loader::Create(fd, _image);
685		if (status == B_OK)
686			return ELF32Loader::Load(fd, *_image);
687	}
688#endif
689
690	return status;
691}
692
693
694status_t
695elf_load_image(Directory* directory, const char* path)
696{
697	preloaded_image* image;
698
699	TRACE(("elf_load_image(directory = %p, \"%s\")\n", directory, path));
700
701	int fd = open_from(directory, path, O_RDONLY);
702	if (fd < 0)
703		return fd;
704
705	// check if this file has already been loaded
706
707	struct stat stat;
708	if (fstat(fd, &stat) < 0)
709		return errno;
710
711	image = gKernelArgs.preloaded_images;
712	for (; image != NULL; image = image->next) {
713		if (image->inode == stat.st_ino) {
714			// file has already been loaded, no need to load it twice!
715			close(fd);
716			return B_OK;
717		}
718	}
719
720	// we still need to load it, so do it
721
722	status_t status = elf_load_image(fd, &image);
723	if (status == B_OK) {
724		image->name = kernel_args_strdup(path);
725		image->inode = stat.st_ino;
726
727		// insert to kernel args
728		image->next = gKernelArgs.preloaded_images;
729		gKernelArgs.preloaded_images = image;
730	} else
731		kernel_args_free(image);
732
733	close(fd);
734	return status;
735}
736
737
738status_t
739elf_relocate_image(preloaded_image* image)
740{
741#ifdef BOOT_SUPPORT_ELF64
742	if (image->elf_class == ELFCLASS64)
743		return ELF64Loader::Relocate(image);
744#endif
745#ifdef BOOT_SUPPORT_ELF32
746	if (image->elf_class == ELFCLASS32)
747		return ELF32Loader::Relocate(image);
748#endif
749	return B_ERROR;
750}
751
752
753#ifdef BOOT_SUPPORT_ELF32
754status_t
755boot_elf_resolve_symbol(preloaded_elf32_image* image, Elf32_Sym* symbol,
756	Elf32_Addr* symbolAddress)
757{
758	return ELF32Loader::Resolve(image, symbol, symbolAddress);
759}
760
761Elf32_Addr
762boot_elf32_get_relocation(Elf32_Addr resolveAddress)
763{
764	Elf32_Addr* src = (Elf32_Addr*)ELF32Class::Map(resolveAddress);
765	return *src;
766}
767
768void
769boot_elf32_set_relocation(Elf32_Addr resolveAddress, Elf32_Addr finalAddress)
770{
771	Elf32_Addr* dest = (Elf32_Addr*)ELF32Class::Map(resolveAddress);
772	*dest = finalAddress;
773}
774#endif
775
776
777#ifdef BOOT_SUPPORT_ELF64
778status_t
779boot_elf_resolve_symbol(preloaded_elf64_image* image, Elf64_Sym* symbol,
780	Elf64_Addr* symbolAddress)
781{
782	return ELF64Loader::Resolve(image, symbol, symbolAddress);
783}
784
785void
786boot_elf64_set_relocation(Elf64_Addr resolveAddress, Elf64_Addr finalAddress)
787{
788	Elf64_Addr* dest = (Elf64_Addr*)ELF64Class::Map(resolveAddress);
789	*dest = finalAddress;
790}
791#endif
792