/src/serenity/Userland/Libraries/LibELF/Image.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> |
3 | | * Copyright (c) 2022, the SerenityOS developers. |
4 | | * |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #include <AK/BinarySearch.h> |
9 | | #include <AK/Debug.h> |
10 | | #include <AK/Demangle.h> |
11 | | #include <AK/QuickSort.h> |
12 | | #include <AK/StringBuilder.h> |
13 | | #include <AK/StringView.h> |
14 | | #include <Kernel/API/serenity_limits.h> |
15 | | #include <LibELF/Image.h> |
16 | | #include <LibELF/Validation.h> |
17 | | |
18 | | #ifdef KERNEL |
19 | | # include <Kernel/Library/StdLib.h> |
20 | | #else |
21 | | # include <string.h> |
22 | | #endif |
23 | | |
24 | | namespace ELF { |
25 | | |
26 | | Image::Image(ReadonlyBytes bytes, bool verbose_logging) |
27 | 1.91k | : m_buffer(bytes.data()) |
28 | 1.91k | , m_size(bytes.size()) |
29 | 1.91k | , m_verbose_logging(verbose_logging) |
30 | 1.91k | { |
31 | 1.91k | parse(); |
32 | 1.91k | } |
33 | | |
34 | | Image::Image(u8 const* buffer, size_t size, bool verbose_logging) |
35 | 1.91k | : Image(ReadonlyBytes { buffer, size }, verbose_logging) |
36 | 1.91k | { |
37 | 1.91k | } |
38 | | |
39 | | StringView Image::section_index_to_string(unsigned index) const |
40 | 0 | { |
41 | 0 | VERIFY(m_valid); |
42 | 0 | if (index == SHN_UNDEF) |
43 | 0 | return "Undefined"sv; |
44 | 0 | if (index >= SHN_LORESERVE) |
45 | 0 | return "Reserved"sv; |
46 | 0 | return section(index).name(); |
47 | 0 | } |
48 | | |
49 | | unsigned Image::symbol_count() const |
50 | 0 | { |
51 | 0 | VERIFY(m_valid); |
52 | 0 | if (!section_count()) |
53 | 0 | return 0; |
54 | 0 | return section(m_symbol_table_section_index).entry_count(); |
55 | 0 | } |
56 | | |
57 | | void Image::dump() const |
58 | 0 | { |
59 | | #if ELF_IMAGE_DEBUG |
60 | | dbgln("ELF::Image({:p}) {{", this); |
61 | | dbgln(" is_valid: {}", is_valid()); |
62 | | |
63 | | if (!is_valid()) { |
64 | | dbgln("}}"); |
65 | | return; |
66 | | } |
67 | | |
68 | | dbgln(" type: {}", ELF::Image::object_file_type_to_string(header().e_type).value_or("(?)"sv)); |
69 | | dbgln(" machine: {}", header().e_machine); |
70 | | dbgln(" entry: {:x}", header().e_entry); |
71 | | dbgln(" shoff: {}", header().e_shoff); |
72 | | dbgln(" shnum: {}", header().e_shnum); |
73 | | dbgln(" phoff: {}", header().e_phoff); |
74 | | dbgln(" phnum: {}", header().e_phnum); |
75 | | dbgln(" shstrndx: {}", header().e_shstrndx); |
76 | | |
77 | | for_each_program_header([&](ProgramHeader const& program_header) { |
78 | | dbgln(" Program Header {}: {{", program_header.index()); |
79 | | dbgln(" type: {:x}", program_header.type()); |
80 | | dbgln(" offset: {:x}", program_header.offset()); |
81 | | dbgln(" flags: {:x}", program_header.flags()); |
82 | | dbgln(" }}"); |
83 | | }); |
84 | | |
85 | | for (unsigned i = 0; i < header().e_shnum; ++i) { |
86 | | auto const& section = this->section(i); |
87 | | dbgln(" Section {}: {{", i); |
88 | | dbgln(" name: {}", section.name()); |
89 | | dbgln(" type: {:x}", section.type()); |
90 | | dbgln(" offset: {:x}", section.offset()); |
91 | | dbgln(" size: {}", section.size()); |
92 | | dbgln(" "); |
93 | | dbgln(" }}"); |
94 | | } |
95 | | |
96 | | dbgln("Symbol count: {} (table is {})", symbol_count(), m_symbol_table_section_index); |
97 | | for (unsigned i = 1; i < symbol_count(); ++i) { |
98 | | auto const& sym = symbol(i); |
99 | | dbgln("Symbol @{}:", i); |
100 | | dbgln(" Name: {}", sym.name()); |
101 | | dbgln(" In section: {}", section_index_to_string(sym.section_index())); |
102 | | dbgln(" Value: {}", sym.value()); |
103 | | dbgln(" Size: {}", sym.size()); |
104 | | } |
105 | | |
106 | | dbgln("}}"); |
107 | | #endif |
108 | 0 | } |
109 | | |
110 | | unsigned Image::section_count() const |
111 | 145k | { |
112 | 145k | VERIFY(m_valid); |
113 | 145k | return header().e_shnum; |
114 | 145k | } |
115 | | |
116 | | unsigned Image::program_header_count() const |
117 | 0 | { |
118 | 0 | VERIFY(m_valid); |
119 | 0 | return header().e_phnum; |
120 | 0 | } |
121 | | |
122 | | bool Image::parse() |
123 | 1.91k | { |
124 | 1.91k | if (m_size < sizeof(Elf_Ehdr) || !validate_elf_header(header(), m_size, m_verbose_logging)) { |
125 | 668 | if (m_verbose_logging) |
126 | 0 | dbgln("ELF::Image::parse(): ELF Header not valid"); |
127 | 668 | m_valid = false; |
128 | 668 | return false; |
129 | 668 | } |
130 | | |
131 | 1.24k | [[maybe_unused]] Optional<Elf_Phdr> interpreter_path_program_header {}; |
132 | 1.24k | if (!validate_program_headers(header(), m_size, { m_buffer, m_size }, interpreter_path_program_header, nullptr, m_verbose_logging)) { |
133 | 622 | if (m_verbose_logging) |
134 | 0 | dbgln("ELF::Image::parse(): ELF Program Headers not valid"); |
135 | 622 | m_valid = false; |
136 | 622 | return false; |
137 | 622 | } |
138 | | |
139 | 624 | m_valid = true; |
140 | | |
141 | | // First locate the string tables. |
142 | 145k | for (unsigned i = 0; i < section_count(); ++i) { |
143 | 145k | auto& sh = section_header(i); |
144 | 145k | if (sh.sh_type == SHT_SYMTAB) { |
145 | 74 | if (m_symbol_table_section_index && m_symbol_table_section_index != i) { |
146 | 22 | m_valid = false; |
147 | 22 | return false; |
148 | 22 | } |
149 | 52 | m_symbol_table_section_index = i; |
150 | 52 | } |
151 | 145k | if (sh.sh_type == SHT_STRTAB && i != header().e_shstrndx) { |
152 | 5.43k | if (section_header_table_string(sh.sh_name) == ELF_STRTAB) |
153 | 194 | m_string_table_section_index = i; |
154 | 5.43k | } |
155 | 145k | } |
156 | | |
157 | 602 | return m_valid; |
158 | 624 | } |
159 | | |
160 | | StringView Image::table_string(unsigned table_index, unsigned offset) const |
161 | 5.43k | { |
162 | 5.43k | VERIFY(m_valid); |
163 | 5.43k | auto& sh = section_header(table_index); |
164 | 5.43k | if (sh.sh_type != SHT_STRTAB) |
165 | 854 | return {}; |
166 | 4.58k | size_t computed_offset = sh.sh_offset + offset; |
167 | 4.58k | if (computed_offset >= m_size) { |
168 | 622 | if (m_verbose_logging) |
169 | 0 | dbgln("SHENANIGANS! Image::table_string() computed offset outside image."); |
170 | 622 | return {}; |
171 | 622 | } |
172 | 3.96k | size_t max_length = min(m_size - computed_offset, (size_t)SERENITY_PAGE_SIZE); |
173 | 3.96k | size_t length = strnlen(raw_data(sh.sh_offset + offset), max_length); |
174 | 3.96k | return { raw_data(sh.sh_offset + offset), length }; |
175 | 4.58k | } |
176 | | |
177 | | StringView Image::section_header_table_string(unsigned offset) const |
178 | 5.43k | { |
179 | 5.43k | VERIFY(m_valid); |
180 | 5.43k | return table_string(header().e_shstrndx, offset); |
181 | 5.43k | } |
182 | | |
183 | | StringView Image::table_string(unsigned offset) const |
184 | 0 | { |
185 | 0 | VERIFY(m_valid); |
186 | 0 | return table_string(m_string_table_section_index, offset); |
187 | 0 | } |
188 | | |
189 | | char const* Image::raw_data(unsigned offset) const |
190 | 771k | { |
191 | 771k | VERIFY(offset < m_size); // Callers must check indices into raw_data()'s result are also in bounds. |
192 | 771k | return reinterpret_cast<char const*>(m_buffer) + offset; |
193 | 771k | } |
194 | | |
195 | | Elf_Ehdr const& Image::header() const |
196 | 612k | { |
197 | 612k | VERIFY(m_size >= sizeof(Elf_Ehdr)); |
198 | 612k | return *reinterpret_cast<Elf_Ehdr const*>(raw_data(0)); |
199 | 612k | } |
200 | | |
201 | | Elf_Phdr const& Image::program_header_internal(unsigned index) const |
202 | 0 | { |
203 | 0 | VERIFY(m_valid); |
204 | 0 | VERIFY(index < header().e_phnum); |
205 | 0 | return *reinterpret_cast<Elf_Phdr const*>(raw_data(header().e_phoff + (index * sizeof(Elf_Phdr)))); |
206 | 0 | } |
207 | | |
208 | | Elf_Shdr const& Image::section_header(unsigned index) const |
209 | 150k | { |
210 | 150k | VERIFY(m_valid); |
211 | 150k | VERIFY(index < header().e_shnum); |
212 | 150k | return *reinterpret_cast<Elf_Shdr const*>(raw_data(header().e_shoff + (index * header().e_shentsize))); |
213 | 150k | } |
214 | | |
215 | | Image::Symbol Image::symbol(unsigned index) const |
216 | 0 | { |
217 | 0 | VERIFY(m_valid); |
218 | 0 | VERIFY(index < symbol_count()); |
219 | 0 | auto* raw_syms = reinterpret_cast<Elf_Sym const*>(raw_data(section(m_symbol_table_section_index).offset())); |
220 | 0 | return Symbol(*this, index, raw_syms[index]); |
221 | 0 | } |
222 | | |
223 | | Image::Section Image::section(unsigned index) const |
224 | 0 | { |
225 | 0 | VERIFY(m_valid); |
226 | 0 | VERIFY(index < section_count()); |
227 | 0 | return Section(*this, index); |
228 | 0 | } |
229 | | |
230 | | Image::ProgramHeader Image::program_header(unsigned index) const |
231 | 0 | { |
232 | 0 | VERIFY(m_valid); |
233 | 0 | VERIFY(index < program_header_count()); |
234 | 0 | return ProgramHeader(*this, index); |
235 | 0 | } |
236 | | |
237 | | Image::Relocation Image::RelocationSection::relocation(unsigned index) const |
238 | 0 | { |
239 | 0 | VERIFY(index < relocation_count()); |
240 | 0 | unsigned offset_in_section = index * entry_size(); |
241 | 0 | auto relocation_address = bit_cast<Elf_Rela*>(m_image.raw_data(offset() + offset_in_section)); |
242 | 0 | return Relocation(m_image, *relocation_address, addend_used()); |
243 | 0 | } |
244 | | |
245 | | Optional<Image::Section> Image::lookup_section(StringView name) const |
246 | 0 | { |
247 | 0 | VERIFY(m_valid); |
248 | 0 | for (unsigned i = 0; i < section_count(); ++i) { |
249 | 0 | auto section = this->section(i); |
250 | 0 | if (section.name() == name) |
251 | 0 | return section; |
252 | 0 | } |
253 | 0 | return {}; |
254 | 0 | } |
255 | | |
256 | | Optional<StringView> Image::object_file_type_to_string(Elf_Half type) |
257 | 0 | { |
258 | 0 | switch (type) { |
259 | 0 | case ET_NONE: |
260 | 0 | return "None"sv; |
261 | 0 | case ET_REL: |
262 | 0 | return "Relocatable"sv; |
263 | 0 | case ET_EXEC: |
264 | 0 | return "Executable"sv; |
265 | 0 | case ET_DYN: |
266 | 0 | return "Shared object"sv; |
267 | 0 | case ET_CORE: |
268 | 0 | return "Core"sv; |
269 | 0 | default: |
270 | 0 | return {}; |
271 | 0 | } |
272 | 0 | } |
273 | | |
274 | | Optional<StringView> Image::object_machine_type_to_string(Elf_Half type) |
275 | 0 | { |
276 | 0 | switch (type) { |
277 | 0 | case ET_NONE: |
278 | 0 | return "None"sv; |
279 | 0 | case EM_M32: |
280 | 0 | return "AT&T WE 32100"sv; |
281 | 0 | case EM_SPARC: |
282 | 0 | return "SPARC"sv; |
283 | 0 | case EM_386: |
284 | 0 | return "Intel 80386"sv; |
285 | 0 | case EM_68K: |
286 | 0 | return "Motorola 68000"sv; |
287 | 0 | case EM_88K: |
288 | 0 | return "Motorola 88000"sv; |
289 | 0 | case EM_486: |
290 | 0 | return "Intel 80486"sv; |
291 | 0 | case EM_860: |
292 | 0 | return "Intel 80860"sv; |
293 | 0 | case EM_MIPS: |
294 | 0 | return "MIPS R3000 Big-Endian only"sv; |
295 | 0 | case EM_X86_64: |
296 | 0 | return "x86_64"sv; |
297 | 0 | default: |
298 | 0 | return {}; |
299 | 0 | } |
300 | 0 | } |
301 | | |
302 | | Optional<StringView> Image::object_abi_type_to_string(Elf_Byte type) |
303 | 0 | { |
304 | 0 | switch (type) { |
305 | 0 | case ELFOSABI_SYSV: |
306 | 0 | return "SYSV"sv; |
307 | 0 | case ELFOSABI_HPUX: |
308 | 0 | return "HP-UX"sv; |
309 | 0 | case ELFOSABI_NETBSD: |
310 | 0 | return "NetBSD"sv; |
311 | 0 | case ELFOSABI_LINUX: |
312 | 0 | return "Linux"sv; |
313 | 0 | case ELFOSABI_HURD: |
314 | 0 | return "GNU Hurd"sv; |
315 | 0 | case ELFOSABI_86OPEN: |
316 | 0 | return "86Open"sv; |
317 | 0 | case ELFOSABI_SOLARIS: |
318 | 0 | return "Solaris"sv; |
319 | 0 | case ELFOSABI_MONTEREY: |
320 | 0 | return "AIX"sv; |
321 | 0 | case ELFOSABI_IRIX: |
322 | 0 | return "IRIX"sv; |
323 | 0 | case ELFOSABI_FREEBSD: |
324 | 0 | return "FreeBSD"sv; |
325 | 0 | case ELFOSABI_TRU64: |
326 | 0 | return "Tru64"sv; |
327 | 0 | case ELFOSABI_MODESTO: |
328 | 0 | return "Novell Modesto"sv; |
329 | 0 | case ELFOSABI_OPENBSD: |
330 | 0 | return "OpenBSD"sv; |
331 | 0 | case ELFOSABI_ARM: |
332 | 0 | return "ARM"sv; |
333 | 0 | case ELFOSABI_STANDALONE: |
334 | 0 | return "Standalone"sv; |
335 | 0 | default: |
336 | 0 | return {}; |
337 | 0 | } |
338 | 0 | } |
339 | | |
340 | | StringView Image::Symbol::raw_data() const |
341 | 0 | { |
342 | 0 | auto section = this->section(); |
343 | 0 | return { section.raw_data() + (value() - section.address()), size() }; |
344 | 0 | } |
345 | | |
346 | | #ifndef KERNEL |
347 | | Optional<Image::Symbol> Image::find_demangled_function(StringView name) const |
348 | 0 | { |
349 | 0 | Optional<Image::Symbol> found; |
350 | 0 | for_each_symbol([&](Image::Symbol const& symbol) { |
351 | 0 | if (symbol.type() != STT_FUNC && symbol.type() != STT_GNU_IFUNC) |
352 | 0 | return IterationDecision::Continue; |
353 | 0 | if (symbol.is_undefined()) |
354 | 0 | return IterationDecision::Continue; |
355 | 0 | auto demangled = demangle(symbol.name()); |
356 | 0 | auto index_of_paren = demangled.find('('); |
357 | 0 | if (index_of_paren.has_value()) { |
358 | 0 | demangled = demangled.substring(0, index_of_paren.value()); |
359 | 0 | } |
360 | 0 | if (demangled != name) |
361 | 0 | return IterationDecision::Continue; |
362 | 0 | found = symbol; |
363 | 0 | return IterationDecision::Break; |
364 | 0 | }); |
365 | 0 | return found; |
366 | 0 | } |
367 | | |
368 | | Image::SortedSymbol* Image::find_sorted_symbol(FlatPtr address) const |
369 | 0 | { |
370 | 0 | if (symbol_count() == 0) |
371 | 0 | return nullptr; |
372 | | |
373 | 0 | if (m_sorted_symbols.is_empty()) |
374 | 0 | sort_symbols(); |
375 | |
|
376 | 0 | auto maybe_index = upper_bound(m_sorted_symbols, address, [address](auto, auto const& candidate) { |
377 | 0 | if (address >= candidate.address) |
378 | 0 | return 1; |
379 | 0 | return -1; |
380 | 0 | }); |
381 | |
|
382 | 0 | if (!maybe_index.has_value()) |
383 | 0 | return &m_sorted_symbols.last(); |
384 | | |
385 | 0 | if (maybe_index.value() == 0) |
386 | 0 | return nullptr; |
387 | | |
388 | 0 | return &m_sorted_symbols[maybe_index.value() - 1]; |
389 | 0 | } |
390 | | |
391 | | Optional<Image::Symbol> Image::find_symbol(FlatPtr address, u32* out_offset) const |
392 | 0 | { |
393 | 0 | auto symbol_count = this->symbol_count(); |
394 | 0 | if (!symbol_count) |
395 | 0 | return {}; |
396 | | |
397 | 0 | auto* symbol = find_sorted_symbol(address); |
398 | 0 | if (!symbol) |
399 | 0 | return {}; |
400 | 0 | if (out_offset) |
401 | 0 | *out_offset = address - symbol->address; |
402 | 0 | return symbol->symbol; |
403 | 0 | } |
404 | | |
405 | | NEVER_INLINE void Image::sort_symbols() const |
406 | 0 | { |
407 | 0 | m_sorted_symbols.ensure_capacity(symbol_count()); |
408 | 0 | bool const is_aarch64_or_riscv = header().e_machine == EM_AARCH64 || header().e_machine == EM_RISCV; |
409 | 0 | for_each_symbol([this, is_aarch64_or_riscv](auto const& symbol) { |
410 | | // The AArch64 and RISC-V ABIs mark the boundaries of literal pools in a function with $x/$d. |
411 | | // https://github.com/ARM-software/abi-aa/blob/2023q1-release/aaelf64/aaelf64.rst#mapping-symbols |
412 | | // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#mapping-symbol |
413 | | // Skip them so we don't accidentally print these instead of function names. |
414 | 0 | if (is_aarch64_or_riscv && (symbol.name().starts_with("$x"sv) || symbol.name().starts_with("$d"sv))) |
415 | 0 | return; |
416 | | // STT_SECTION has the same address as the first function in the section, but shows up as the empty string. |
417 | 0 | if (symbol.type() == STT_SECTION) |
418 | 0 | return; |
419 | 0 | m_sorted_symbols.append({ symbol.value(), symbol.name(), {}, symbol }); |
420 | 0 | }); |
421 | 0 | quick_sort(m_sorted_symbols, [](auto& a, auto& b) { |
422 | 0 | return a.address < b.address; |
423 | 0 | }); |
424 | 0 | } |
425 | | |
426 | | ByteString Image::symbolicate(FlatPtr address, u32* out_offset) const |
427 | 0 | { |
428 | 0 | auto symbol_count = this->symbol_count(); |
429 | 0 | if (!symbol_count) { |
430 | 0 | if (out_offset) |
431 | 0 | *out_offset = 0; |
432 | 0 | return "??"; |
433 | 0 | } |
434 | | |
435 | 0 | auto* symbol = find_sorted_symbol(address); |
436 | 0 | if (!symbol) { |
437 | 0 | if (out_offset) |
438 | 0 | *out_offset = 0; |
439 | 0 | return "??"; |
440 | 0 | } |
441 | | |
442 | 0 | auto& demangled_name = symbol->demangled_name; |
443 | 0 | if (demangled_name.is_empty()) |
444 | 0 | demangled_name = demangle(symbol->name); |
445 | |
|
446 | 0 | if (out_offset) { |
447 | 0 | *out_offset = address - symbol->address; |
448 | 0 | return demangled_name; |
449 | 0 | } |
450 | 0 | return ByteString::formatted("{} +{:#x}", demangled_name, address - symbol->address); |
451 | 0 | } |
452 | | #endif |
453 | | |
454 | | } // end namespace ELF |