Serenity Operating System
1/*
2 * Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <LibC/elf.h>
8#include <LibELF/Relocation.h>
9
10namespace ELF {
11
12bool perform_relative_relocations(FlatPtr base_address)
13{
14
15 ElfW(Ehdr)* header = (ElfW(Ehdr)*)(base_address);
16 ElfW(Phdr)* pheader = (ElfW(Phdr)*)(base_address + header->e_phoff);
17 FlatPtr dynamic_section_addr = 0;
18 for (size_t i = 0; i < (size_t)header->e_phnum; ++i, ++pheader) {
19 if (pheader->p_type != PT_DYNAMIC)
20 continue;
21 dynamic_section_addr = pheader->p_vaddr + base_address;
22 }
23 if (!dynamic_section_addr)
24 return false;
25
26 FlatPtr relocation_section_addr = 0;
27 size_t relocation_table_size = 0;
28 size_t relocation_count = 0;
29 size_t relocation_entry_size = 0;
30 FlatPtr relr_relocation_section_addr = 0;
31 size_t relr_relocation_table_size = 0;
32 bool use_addend = false;
33 auto* dyns = reinterpret_cast<const ElfW(Dyn)*>(dynamic_section_addr);
34 for (unsigned i = 0;; ++i) {
35 auto& dyn = dyns[i];
36 if (dyn.d_tag == DT_NULL)
37 break;
38 if (dyn.d_tag == DT_RELA)
39 use_addend = true;
40 if (dyn.d_tag == DT_REL || dyn.d_tag == DT_RELA)
41 relocation_section_addr = base_address + dyn.d_un.d_ptr;
42 else if (dyn.d_tag == DT_RELCOUNT || dyn.d_tag == DT_RELACOUNT)
43 relocation_count = dyn.d_un.d_val;
44 else if (dyn.d_tag == DT_RELSZ || dyn.d_tag == DT_RELASZ)
45 relocation_table_size = dyn.d_un.d_val;
46 else if (dyn.d_tag == DT_RELENT || dyn.d_tag == DT_RELAENT)
47 relocation_entry_size = dyn.d_un.d_val;
48 else if (dyn.d_tag == DT_RELR)
49 relr_relocation_section_addr = base_address + dyn.d_un.d_ptr;
50 else if (dyn.d_tag == DT_RELRSZ)
51 relr_relocation_table_size = dyn.d_un.d_val;
52 else if (dyn.d_tag == DT_RELRENT)
53 VERIFY(dyn.d_un.d_val == sizeof(FlatPtr));
54 }
55
56 if ((!relocation_section_addr || !relocation_table_size || !relocation_count) && (!relr_relocation_section_addr || !relr_relocation_table_size))
57 return false;
58
59 for (unsigned i = 0; i < relocation_count; ++i) {
60 size_t offset_in_section = i * relocation_entry_size;
61 auto* relocation = (ElfW(Rela)*)(relocation_section_addr + offset_in_section);
62 VERIFY(ELF64_R_TYPE(relocation->r_info) == R_X86_64_RELATIVE || ELF64_R_TYPE(relocation->r_info) == R_AARCH64_RELATIVE);
63 auto* patch_address = (FlatPtr*)(base_address + relocation->r_offset);
64 FlatPtr relocated_address;
65 if (use_addend) {
66 relocated_address = base_address + relocation->r_addend;
67 } else {
68 __builtin_memcpy(&relocated_address, patch_address, sizeof(relocated_address));
69 relocated_address += base_address;
70 }
71 __builtin_memcpy(patch_address, &relocated_address, sizeof(relocated_address));
72 }
73
74 auto patch_relr = [base_address](FlatPtr* patch_ptr) {
75 FlatPtr relocated_address;
76 __builtin_memcpy(&relocated_address, patch_ptr, sizeof(FlatPtr));
77 relocated_address += base_address;
78 __builtin_memcpy(patch_ptr, &relocated_address, sizeof(FlatPtr));
79 };
80
81 auto* entries = reinterpret_cast<ElfW(Relr)*>(relr_relocation_section_addr);
82 FlatPtr* patch_ptr = nullptr;
83
84 for (unsigned i = 0; i < relr_relocation_table_size / sizeof(FlatPtr); ++i) {
85 if ((entries[i] & 1u) == 0) {
86 patch_ptr = reinterpret_cast<FlatPtr*>(base_address + entries[i]);
87 patch_relr(patch_ptr);
88 ++patch_ptr;
89 } else {
90 unsigned j = 0;
91 for (auto bitmap = entries[i]; (bitmap >>= 1u) != 0; ++j)
92 if (bitmap & 1u)
93 patch_relr(patch_ptr + j);
94
95 patch_ptr += 8 * sizeof(FlatPtr) - 1;
96 }
97 }
98 return true;
99}
100}