1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18 // The code in this file is responsible for applying dynamic relocation
19 // for position independent ELF. It itself has to work without any relocation.
20 // That means all dependencies of this file need to be statically linked. There
21 // shall not be any reference to any symbol from dynamically linked library
22 // before relocation is done.
23 //
24 // It would be worth to investigate if this can be implemented in rust.
25
26 #include <stddef.h>
27 #include <stdint.h>
28
29 #include "libelf/elf.h"
30
31 #include "elf/relocation.h"
32
33 #ifdef HOST_TOOLING
34 #include <stdio.h>
35 #include <stdlib.h>
36 #define RELOCATION_PRINTF(...) fprintf(stderr, __VA_ARGS__)
37 #else
38 #define RELOCATION_PRINTF(...)
39 #endif
40
41 namespace {
42
43 struct DynamicSectionInfo {
44 const Elf64_Dyn* rel;
45 const Elf64_Dyn* rel_size;
46 const Elf64_Dyn* rela;
47 const Elf64_Dyn* rela_size;
48 const Elf64_Sym* symtab;
49 };
50
GetRelxAddend(const Elf64_Rela & entry,uint64_t *)51 Elf64_Sxword GetRelxAddend(const Elf64_Rela& entry, uint64_t*) {
52 return entry.r_addend;
53 }
GetRelxAddend(const Elf64_Rel &,uint64_t * addr)54 Elf64_Sxword GetRelxAddend(const Elf64_Rel&, uint64_t* addr) {
55 return *addr;
56 }
57
58 // Perform relocation fixup for the given RELA/REL relocation table.
59 //
60 // For more information about REL/RELA relocation, see the Relocation and Dynamic linking
61 // related sections in System V ABI (https://www.sco.com/developers/gabi/latest/contents.html)
62 template <typename T>
FixUpRelxTable(uintptr_t program_base,const Elf64_Sym * symtab,uintptr_t table_addr,size_t size)63 bool FixUpRelxTable(uintptr_t program_base, [[maybe_unused]] const Elf64_Sym* symtab,
64 uintptr_t table_addr, size_t size) {
65 size_t num_entries = size / sizeof(T);
66 const T* table = reinterpret_cast<const T*>(table_addr);
67 for (size_t i = 0; i < num_entries; i++) {
68 const T& entry = table[i];
69 // Type of the relocation. It determines the calculation.
70 uint64_t reloc_type = entry.r_info & 0xffffffff;
71 // Address for storing the new calculated address.
72 // TODO(294059825): Add support for overflow checking.
73 uint64_t* addr = reinterpret_cast<uint64_t*>(program_base + entry.r_offset);
74 // Addend value. For RELA, it comes from the entry. For REL, it's the current
75 // value at address `addr`.
76 Elf64_Sxword addend = GetRelxAddend(entry, addr);
77 // Index of the corresponding symbol in the symbol table.
78 // Certain type of relocation requires to modify symbol table. Keep this here
79 // for reference.
80 [[maybe_unused]] size_t symbol_index = entry.r_info >> 32;
81
82 // Now process the various type of relocation.
83 //
84 // For documents about AARCH64 relocation, see
85 // https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst#5712dynamic-relocations
86 //
87 // For document about RISC-V relocation, see
88 // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#relocations
89 //
90 // GBL's usage of ELF is restricted. We only implement relocations that we expect to see.
91 if (reloc_type == R_RISCV_RELATIVE) {
92 // This is the most common type of relocation. For position independent executable,
93 // symbol address is typically the relative offset to program start. Relocated absolute
94 // address is obtained by adding the address the program is loaded to.
95 *addr = program_base + addend;
96 } else {
97 RELOCATION_PRINTF("Unhandled relocation type: %lx\n", reloc_type);
98 return false;
99 }
100 }
101
102 return true;
103 }
104
105 // Search for RELA type relocation table from the .dynamic section and apply relocation fix up if
106 // there is one.
ApplyRelaRelocation(uintptr_t program_base,const DynamicSectionInfo & info)107 bool ApplyRelaRelocation(uintptr_t program_base, const DynamicSectionInfo& info) {
108 if ((info.rela == NULL) != (info.rela_size == NULL)) {
109 return false;
110 } else if (!info.rela) {
111 return true;
112 }
113 return FixUpRelxTable<Elf64_Rela>(program_base, info.symtab, info.rela->d_un.d_ptr + program_base,
114 info.rela_size->d_un.d_val);
115 }
116
117 // Search for REL type relocation table from the .dynamic section and apply relocation fix up if
118 // there is one.
ApplyRelRelocation(uintptr_t program_base,const DynamicSectionInfo & info)119 bool ApplyRelRelocation(uintptr_t program_base, const DynamicSectionInfo& info) {
120 if ((info.rel == NULL) != (info.rel_size == NULL)) {
121 return false;
122 } else if (!info.rel) {
123 return true;
124 }
125 return FixUpRelxTable<Elf64_Rel>(program_base, info.symtab, info.rel->d_un.d_ptr + program_base,
126 info.rel_size->d_un.d_val);
127 }
128
129 } // namespace
130
ApplyRelocation(uintptr_t program_base,uintptr_t dynamic_section)131 extern "C" bool ApplyRelocation(uintptr_t program_base, uintptr_t dynamic_section) {
132 DynamicSectionInfo dynamic_section_info = {
133 .rel = NULL,
134 .rel_size = NULL,
135 .rela = NULL,
136 .rela_size = NULL,
137 .symtab = NULL,
138 };
139
140 const Elf64_Dyn* dynamic_section_table = reinterpret_cast<const Elf64_Dyn*>(dynamic_section);
141 for (size_t i = 0; dynamic_section_table[i].d_tag != DT_NULL; i++) {
142 switch (dynamic_section_table[i].d_tag) {
143 // RELA type relocation entries
144 case DT_RELA:
145 dynamic_section_info.rela = &dynamic_section_table[i];
146 break;
147 // RELA type relocation entries total size
148 case DT_RELASZ:
149 dynamic_section_info.rela_size = &dynamic_section_table[i];
150 break;
151 // REL type relocation entries
152 case DT_REL:
153 dynamic_section_info.rel = &dynamic_section_table[i];
154 break;
155 // RELA type relocation entries total size
156 case DT_RELSZ:
157 dynamic_section_info.rel_size = &dynamic_section_table[i];
158 break;
159 // Symbol table
160 case DT_SYMTAB:
161 dynamic_section_info.symtab =
162 (const Elf64_Sym*)(dynamic_section_table[i].d_un.d_ptr + program_base);
163 break;
164 // We shouldn't see RELR relocation unless `-Wl,--pack-dyn-relocs=relr` is used.
165 case DT_RELR:
166 // We shouldn't see PLT relocation unless we are loading a shared library.
167 case DT_JMPREL:
168 return false;
169 default:
170 break;
171 }
172 }
173
174 if (!dynamic_section_info.symtab) {
175 return false;
176 }
177
178 return ApplyRelRelocation(program_base, dynamic_section_info) &&
179 ApplyRelaRelocation(program_base, dynamic_section_info);
180 }
181