xref: /aosp_15_r20/bootable/libbootloader/gbl/libelf/relocation.cpp (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
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