Serenity Operating System
1/*
2 * Copyright (c) 2019-2020, Andrew Kaster <akaster@serenityos.org>
3 * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
4 * Copyright (c) 2022, the SerenityOS developers.
5 *
6 * SPDX-License-Identifier: BSD-2-Clause
7 */
8
9#pragma once
10
11#include <AK/Assertions.h>
12#include <AK/Concepts.h>
13#include <AK/DeprecatedString.h>
14#include <AK/RefCounted.h>
15#include <Kernel/VirtualAddress.h>
16#include <LibC/elf.h>
17#include <LibC/link.h>
18
19namespace ELF {
20
21class DynamicObject : public RefCounted<DynamicObject> {
22public:
23 static NonnullRefPtr<DynamicObject> create(DeprecatedString const& filepath, VirtualAddress base_address, VirtualAddress dynamic_section_address);
24 static char const* name_for_dtag(ElfW(Sword) d_tag);
25
26 ~DynamicObject();
27 void dump() const;
28
29 class DynamicEntry;
30 class Section;
31 class RelocationSection;
32 class Symbol;
33 class Relocation;
34 class HashSection;
35
36 class DynamicEntry {
37 public:
38 explicit DynamicEntry(const ElfW(Dyn) & dyn)
39 : m_dyn(dyn)
40 {
41 }
42
43 ~DynamicEntry() = default;
44
45 ElfW(Sword) tag() const { return m_dyn.d_tag; }
46 ElfW(Addr) ptr() const { return m_dyn.d_un.d_ptr; }
47 ElfW(Word) val() const { return m_dyn.d_un.d_val; }
48
49 private:
50 const ElfW(Dyn) & m_dyn;
51 };
52
53 class Symbol {
54 public:
55 Symbol(DynamicObject const& dynamic, unsigned index, const ElfW(Sym) & sym)
56 : m_dynamic(dynamic)
57 , m_sym(sym)
58 , m_index(index)
59 {
60 }
61
62 StringView name() const { return m_dynamic.symbol_string_table_string(m_sym.st_name); }
63 char const* raw_name() const { return m_dynamic.raw_symbol_string_table_string(m_sym.st_name); }
64 unsigned section_index() const { return m_sym.st_shndx; }
65 FlatPtr value() const { return m_sym.st_value; }
66 size_t size() const { return m_sym.st_size; }
67 unsigned index() const { return m_index; }
68 unsigned type() const
69 {
70 return ELF64_ST_TYPE(m_sym.st_info);
71 }
72 unsigned bind() const { return ELF64_ST_BIND(m_sym.st_info); }
73
74 bool is_undefined() const
75 {
76 return section_index() == 0;
77 }
78
79 VirtualAddress address() const
80 {
81 if (m_dynamic.elf_is_dynamic())
82 return m_dynamic.base_address().offset(value());
83 return VirtualAddress { value() };
84 }
85 DynamicObject const& object() const { return m_dynamic; }
86
87 private:
88 DynamicObject const& m_dynamic;
89 const ElfW(Sym) & m_sym;
90 unsigned const m_index;
91 };
92
93 class Section {
94 public:
95 Section(DynamicObject const& dynamic, unsigned section_offset, unsigned section_size_bytes, unsigned entry_size, StringView name)
96 : m_dynamic(dynamic)
97 , m_section_offset(section_offset)
98 , m_section_size_bytes(section_size_bytes)
99 , m_entry_size(entry_size)
100 , m_name(name)
101 {
102 }
103 ~Section() = default;
104
105 StringView name() const { return m_name; }
106 unsigned offset() const { return m_section_offset; }
107 unsigned size() const { return m_section_size_bytes; }
108 unsigned entry_size() const { return m_entry_size; }
109 unsigned entry_count() const
110 {
111 return !entry_size() ? 0 : size() / entry_size();
112 }
113 VirtualAddress address() const
114 {
115 return m_dynamic.base_address().offset(m_section_offset);
116 }
117
118 protected:
119 friend class RelocationSection;
120 friend class HashSection;
121 DynamicObject const& m_dynamic;
122 unsigned m_section_offset;
123 unsigned m_section_size_bytes;
124 unsigned m_entry_size;
125 StringView m_name;
126 };
127
128 class RelocationSection : public Section {
129 public:
130 explicit RelocationSection(Section const& section, bool addend_used)
131 : Section(section.m_dynamic, section.m_section_offset, section.m_section_size_bytes, section.m_entry_size, section.m_name)
132 , m_addend_used(addend_used)
133 {
134 }
135 unsigned relocation_count() const { return entry_count(); }
136 Relocation relocation(unsigned index) const;
137 Relocation relocation_at_offset(unsigned offset) const;
138
139 template<IteratorFunction<DynamicObject::Relocation&> F>
140 void for_each_relocation(F) const;
141 template<VoidFunction<DynamicObject::Relocation&> F>
142 void for_each_relocation(F func) const;
143
144 private:
145 bool const m_addend_used;
146 };
147
148 class Relocation {
149 public:
150 Relocation(DynamicObject const& dynamic, const ElfW(Rela) & rel, unsigned offset_in_section, bool addend_used)
151 : m_dynamic(dynamic)
152 , m_rel(rel)
153 , m_offset_in_section(offset_in_section)
154 , m_addend_used(addend_used)
155 {
156 }
157
158 ~Relocation() = default;
159
160 unsigned offset_in_section() const { return m_offset_in_section; }
161 unsigned offset() const { return m_rel.r_offset; }
162 unsigned type() const
163 {
164 return ELF64_R_TYPE(m_rel.r_info);
165 }
166 unsigned symbol_index() const { return ELF64_R_SYM(m_rel.r_info); }
167 unsigned addend() const
168 {
169 VERIFY(m_addend_used);
170 return m_rel.r_addend;
171 }
172 bool addend_used() const { return m_addend_used; }
173
174 Symbol symbol() const
175 {
176 return m_dynamic.symbol(symbol_index());
177 }
178 VirtualAddress address() const
179 {
180 if (m_dynamic.elf_is_dynamic())
181 return m_dynamic.base_address().offset(offset());
182 return VirtualAddress { offset() };
183 }
184 [[nodiscard]] DynamicObject const& dynamic_object() const { return m_dynamic; }
185
186 private:
187 DynamicObject const& m_dynamic;
188 const ElfW(Rela) & m_rel;
189 unsigned const m_offset_in_section;
190 bool const m_addend_used;
191 };
192
193 enum class HashType {
194 SYSV,
195 GNU
196 };
197
198 class HashSymbol {
199 public:
200 HashSymbol(StringView name)
201 : m_name(name)
202 {
203 }
204
205 StringView name() const { return m_name; }
206 u32 gnu_hash() const;
207 u32 sysv_hash() const;
208
209 private:
210 StringView m_name;
211 mutable Optional<u32> m_gnu_hash;
212 mutable Optional<u32> m_sysv_hash;
213 };
214
215 class HashSection : public Section {
216 public:
217 HashSection(Section const& section, HashType hash_type)
218 : Section(section.m_dynamic, section.m_section_offset, section.m_section_size_bytes, section.m_entry_size, section.m_name)
219 , m_hash_type(hash_type)
220 {
221 }
222
223 Optional<Symbol> lookup_symbol(HashSymbol const& symbol) const
224 {
225 if (m_hash_type == HashType::SYSV)
226 return lookup_sysv_symbol(symbol.name(), symbol.sysv_hash());
227 return lookup_gnu_symbol(symbol.name(), symbol.gnu_hash());
228 }
229
230 private:
231 Optional<Symbol> lookup_sysv_symbol(StringView name, u32 hash_value) const;
232 Optional<Symbol> lookup_gnu_symbol(StringView name, u32 hash) const;
233
234 HashType m_hash_type {};
235 };
236
237 unsigned symbol_count() const { return m_symbol_count; }
238
239 Symbol symbol(unsigned) const;
240
241 typedef void (*InitializationFunction)();
242 typedef ElfW(Addr) (*IfuncResolver)();
243
244 bool has_init_section() const { return m_init_offset != 0; }
245 bool has_init_array_section() const { return m_init_array_offset != 0; }
246 Section init_section() const;
247 InitializationFunction init_section_function() const;
248 Section fini_section() const;
249 Section init_array_section() const;
250 Section fini_array_section() const;
251
252 HashSection hash_section() const
253 {
254 auto section_name = m_hash_type == HashType::SYSV ? "DT_HASH"sv : "DT_GNU_HASH"sv;
255
256 return HashSection(Section(*this, m_hash_table_offset, 0, 0, section_name), m_hash_type);
257 }
258
259 RelocationSection relocation_section() const;
260 RelocationSection plt_relocation_section() const;
261 Section relr_relocation_section() const;
262
263 bool should_process_origin() const { return m_dt_flags & DF_ORIGIN; }
264 bool requires_symbolic_symbol_resolution() const { return m_dt_flags & DF_SYMBOLIC; }
265 // Text relocations meaning: we need to edit the .text section which is normally mapped PROT_READ
266 bool has_text_relocations() const { return m_dt_flags & DF_TEXTREL; }
267 bool must_bind_now() const { return m_dt_flags & DF_BIND_NOW; }
268 bool has_static_thread_local_storage() const { return m_dt_flags & DF_STATIC_TLS; }
269
270 bool has_plt() const { return m_procedure_linkage_table_offset.has_value(); }
271 VirtualAddress plt_got_base_address() const { return m_base_address.offset(m_procedure_linkage_table_offset.value()); }
272 VirtualAddress base_address() const { return m_base_address; }
273
274 DeprecatedString const& filepath() const { return m_filepath; }
275
276 StringView rpath() const { return m_has_rpath ? symbol_string_table_string(m_rpath_index) : StringView {}; }
277 StringView runpath() const { return m_has_runpath ? symbol_string_table_string(m_runpath_index) : StringView {}; }
278 StringView soname() const { return m_has_soname ? symbol_string_table_string(m_soname_index) : StringView {}; }
279
280 Optional<FlatPtr> tls_offset() const { return m_tls_offset; }
281 Optional<FlatPtr> tls_size() const { return m_tls_size; }
282 void set_tls_offset(FlatPtr offset) { m_tls_offset = offset; }
283 void set_tls_size(FlatPtr size) { m_tls_size = size; }
284
285 ElfW(Half) program_header_count() const;
286 const ElfW(Phdr) * program_headers() const;
287
288 template<VoidFunction<StringView> F>
289 void for_each_needed_library(F) const;
290
291 template<VoidFunction<InitializationFunction&> F>
292 void for_each_initialization_array_function(F f) const;
293
294 template<IteratorFunction<DynamicEntry&> F>
295 void for_each_dynamic_entry(F) const;
296 template<VoidFunction<DynamicEntry&> F>
297 void for_each_dynamic_entry(F func) const;
298
299 template<VoidFunction<Symbol&> F>
300 void for_each_symbol(F) const;
301
302 template<typename F>
303 void for_each_relr_relocation(F) const;
304
305 struct SymbolLookupResult {
306 FlatPtr value { 0 };
307 size_t size { 0 };
308 VirtualAddress address;
309 unsigned bind { STB_LOCAL };
310 unsigned type { STT_FUNC };
311 const ELF::DynamicObject* dynamic_object { nullptr }; // The object in which the symbol is defined
312 };
313
314 Optional<SymbolLookupResult> lookup_symbol(StringView name) const;
315 Optional<SymbolLookupResult> lookup_symbol(HashSymbol const& symbol) const;
316
317 // Will be called from _fixup_plt_entry, as part of the PLT trampoline
318 VirtualAddress patch_plt_entry(u32 relocation_offset);
319
320 bool elf_is_dynamic() const { return m_is_elf_dynamic; }
321
322 void* symbol_for_name(StringView name);
323
324private:
325 explicit DynamicObject(DeprecatedString const& filepath, VirtualAddress base_address, VirtualAddress dynamic_section_address);
326
327 StringView symbol_string_table_string(ElfW(Word)) const;
328 char const* raw_symbol_string_table_string(ElfW(Word)) const;
329 void parse();
330
331 DeprecatedString m_filepath;
332
333 VirtualAddress m_base_address;
334 VirtualAddress m_dynamic_address;
335 VirtualAddress m_elf_base_address;
336
337 unsigned m_symbol_count { 0 };
338
339 // Begin Section information collected from DT_* entries
340 FlatPtr m_init_offset { 0 };
341 FlatPtr m_fini_offset { 0 };
342
343 FlatPtr m_init_array_offset { 0 };
344 size_t m_init_array_size { 0 };
345 FlatPtr m_fini_array_offset { 0 };
346 size_t m_fini_array_size { 0 };
347
348 FlatPtr m_hash_table_offset { 0 };
349 HashType m_hash_type { HashType::SYSV };
350
351 FlatPtr m_string_table_offset { 0 };
352 size_t m_size_of_string_table { 0 };
353 FlatPtr m_symbol_table_offset { 0 };
354 size_t m_size_of_symbol_table_entry { 0 };
355
356 ElfW(Sword) m_procedure_linkage_table_relocation_type { -1 };
357 FlatPtr m_plt_relocation_offset_location { 0 }; // offset of PLT relocations, at end of relocations
358 size_t m_size_of_plt_relocation_entry_list { 0 };
359 Optional<FlatPtr> m_procedure_linkage_table_offset;
360
361 // NOTE: We'll only ever either RELA or REL entries, not both (thank god)
362 // NOTE: The x86 ABI will only ever genrerate REL entries.
363 size_t m_number_of_relocations { 0 };
364 size_t m_size_of_relocation_entry { 0 };
365 size_t m_size_of_relocation_table { 0 };
366 bool m_addend_used { false };
367 FlatPtr m_relocation_table_offset { 0 };
368 size_t m_size_of_relr_relocations_entry { 0 };
369 size_t m_size_of_relr_relocation_table { 0 };
370 FlatPtr m_relr_relocation_table_offset { 0 };
371 bool m_is_elf_dynamic { false };
372
373 // DT_FLAGS
374 ElfW(Word) m_dt_flags { 0 };
375
376 bool m_has_soname { false };
377 ElfW(Word) m_soname_index { 0 }; // Index into dynstr table for SONAME
378 bool m_has_rpath { false };
379 ElfW(Word) m_rpath_index { 0 }; // Index into dynstr table for RPATH
380 bool m_has_runpath { false };
381 ElfW(Word) m_runpath_index { 0 }; // Index into dynstr table for RUNPATH
382
383 Optional<FlatPtr> m_tls_offset;
384 Optional<FlatPtr> m_tls_size;
385 // End Section information from DT_* entries
386};
387
388template<IteratorFunction<DynamicObject::Relocation&> F>
389inline void DynamicObject::RelocationSection::for_each_relocation(F func) const
390{
391 for (unsigned i = 0; i < relocation_count(); ++i) {
392 auto const reloc = relocation(i);
393 if (reloc.type() == 0)
394 continue;
395 if (func(reloc) == IterationDecision::Break)
396 break;
397 }
398}
399
400template<VoidFunction<DynamicObject::Relocation&> F>
401inline void DynamicObject::RelocationSection::for_each_relocation(F func) const
402{
403 for_each_relocation([&](auto& reloc) {
404 func(reloc);
405 return IterationDecision::Continue;
406 });
407}
408
409template<typename F>
410inline void DynamicObject::for_each_relr_relocation(F f) const
411{
412 auto section = relr_relocation_section();
413 if (section.entry_count() == 0)
414 return;
415
416 VERIFY(section.entry_size() == sizeof(FlatPtr));
417 VERIFY(section.size() >= section.entry_size() * section.entry_count());
418
419 auto* entries = reinterpret_cast<ElfW(Relr)*>(section.address().get());
420 auto base = base_address().get();
421 FlatPtr patch_addr = 0;
422 for (unsigned i = 0; i < section.entry_count(); ++i) {
423 if ((entries[i] & 1u) == 0) {
424 patch_addr = base + entries[i];
425 f(patch_addr);
426 patch_addr += sizeof(FlatPtr);
427 } else {
428 unsigned j = 0;
429 for (auto bitmap = entries[i]; (bitmap >>= 1u) != 0; ++j)
430 if (bitmap & 1u)
431 f(patch_addr + j * sizeof(FlatPtr));
432
433 patch_addr += (8 * sizeof(FlatPtr) - 1) * sizeof(FlatPtr);
434 }
435 }
436}
437
438template<VoidFunction<DynamicObject::Symbol&> F>
439inline void DynamicObject::for_each_symbol(F func) const
440{
441 for (unsigned i = 0; i < symbol_count(); ++i) {
442 func(symbol(i));
443 }
444}
445
446template<IteratorFunction<DynamicObject::DynamicEntry&> F>
447inline void DynamicObject::for_each_dynamic_entry(F func) const
448{
449 auto* dyns = reinterpret_cast<const ElfW(Dyn)*>(m_dynamic_address.as_ptr());
450 for (unsigned i = 0;; ++i) {
451 auto&& dyn = DynamicEntry(dyns[i]);
452 if (dyn.tag() == DT_NULL)
453 break;
454 if (func(dyn) == IterationDecision::Break)
455 break;
456 }
457}
458
459template<VoidFunction<DynamicObject::DynamicEntry&> F>
460inline void DynamicObject::for_each_dynamic_entry(F func) const
461{
462 for_each_dynamic_entry([&](auto& dyn) {
463 func(dyn);
464 return IterationDecision::Continue;
465 });
466}
467
468template<VoidFunction<StringView> F>
469inline void DynamicObject::for_each_needed_library(F func) const
470{
471 for_each_dynamic_entry([func, this](auto entry) {
472 if (entry.tag() != DT_NEEDED)
473 return;
474 ElfW(Word) offset = entry.val();
475 func(symbol_string_table_string(offset));
476 });
477}
478
479template<VoidFunction<DynamicObject::InitializationFunction&> F>
480void DynamicObject::for_each_initialization_array_function(F f) const
481{
482 if (!has_init_array_section())
483 return;
484 FlatPtr init_array = (FlatPtr)init_array_section().address().as_ptr();
485 for (size_t i = 0; i < (m_init_array_size / sizeof(void*)); ++i) {
486 InitializationFunction current = ((InitializationFunction*)(init_array))[i];
487 f(current);
488 }
489}
490
491} // end namespace ELF