Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "ELFLoader.h"
28#include <AK/Demangle.h>
29#include <AK/Memory.h>
30#include <AK/QuickSort.h>
31
32#ifdef KERNEL
33# include <Kernel/VM/MemoryManager.h>
34# define do_memcpy copy_to_user
35#else
36# define do_memcpy memcpy
37#endif
38
39//#define ELFLOADER_DEBUG
40
41ELFLoader::ELFLoader(const u8* buffer, size_t size)
42 : m_image(buffer, size)
43{
44 m_symbol_count = m_image.symbol_count();
45}
46
47ELFLoader::~ELFLoader()
48{
49}
50
51bool ELFLoader::load()
52{
53#ifdef ELFLOADER_DEBUG
54 m_image.dump();
55#endif
56 if (!m_image.is_valid())
57 return false;
58
59 if (!layout())
60 return false;
61
62 return true;
63}
64
65bool ELFLoader::layout()
66{
67 bool failed = false;
68 m_image.for_each_program_header([&](const ELFImage::ProgramHeader& program_header) {
69 if (program_header.type() == PT_TLS) {
70#ifdef KERNEL
71 auto* tls_image = tls_section_hook(program_header.size_in_memory(), program_header.alignment());
72 if (!tls_image) {
73 failed = true;
74 return;
75 }
76 if (!m_image.is_within_image(program_header.raw_data(), program_header.size_in_image())) {
77 dbg() << "Shenanigans! ELF PT_TLS header sneaks outside of executable.";
78 failed = true;
79 return;
80 }
81 do_memcpy(tls_image, program_header.raw_data(), program_header.size_in_image());
82#endif
83 return;
84 }
85 if (program_header.type() != PT_LOAD)
86 return;
87#ifdef ELFLOADER_DEBUG
88 kprintf("PH: V%p %u r:%u w:%u\n", program_header.vaddr().get(), program_header.size_in_memory(), program_header.is_readable(), program_header.is_writable());
89#endif
90#ifdef KERNEL
91 if (program_header.is_writable()) {
92 auto* allocated_section = alloc_section_hook(
93 program_header.vaddr(),
94 program_header.size_in_memory(),
95 program_header.alignment(),
96 program_header.is_readable(),
97 program_header.is_writable(),
98 String::format("elf-alloc-%s%s", program_header.is_readable() ? "r" : "", program_header.is_writable() ? "w" : ""));
99 if (!allocated_section) {
100 failed = true;
101 return;
102 }
103 if (!m_image.is_within_image(program_header.raw_data(), program_header.size_in_image())) {
104 dbg() << "Shenanigans! Writable ELF PT_LOAD header sneaks outside of executable.";
105 failed = true;
106 return;
107 }
108 // It's not always the case with PIE executables (and very well shouldn't be) that the
109 // virtual address in the program header matches the one we end up giving the process.
110 // In order to copy the data image correctly into memory, we need to copy the data starting at
111 // the right initial page offset into the pages allocated for the elf_alloc-XX section.
112 // FIXME: There's an opportunity to munmap, or at least mprotect, the padding space between
113 // the .text and .data PT_LOAD sections of the executable.
114 // Accessing it would definitely be a bug.
115 auto page_offset = program_header.vaddr();
116 page_offset.mask(~PAGE_MASK);
117 do_memcpy((u8*)allocated_section + page_offset.get(), program_header.raw_data(), program_header.size_in_image());
118 } else {
119 auto* mapped_section = map_section_hook(
120 program_header.vaddr(),
121 program_header.size_in_memory(),
122 program_header.alignment(),
123 program_header.offset(),
124 program_header.is_readable(),
125 program_header.is_writable(),
126 program_header.is_executable(),
127 String::format("elf-map-%s%s%s", program_header.is_readable() ? "r" : "", program_header.is_writable() ? "w" : "", program_header.is_executable() ? "x" : ""));
128 if (!mapped_section) {
129 failed = true;
130 }
131 }
132#endif
133 });
134 return !failed;
135}
136
137char* ELFLoader::symbol_ptr(const char* name)
138{
139 char* found_ptr = nullptr;
140 m_image.for_each_symbol([&](const ELFImage::Symbol symbol) {
141 if (symbol.type() != STT_FUNC)
142 return IterationDecision::Continue;
143 if (symbol.name() == name)
144 return IterationDecision::Continue;
145 if (m_image.is_executable())
146 found_ptr = (char*)(size_t)symbol.value();
147 else
148 ASSERT_NOT_REACHED();
149 return IterationDecision::Break;
150 });
151 return found_ptr;
152}
153
154String ELFLoader::symbolicate(u32 address, u32* out_offset) const
155{
156 if (!m_symbol_count) {
157 if (out_offset)
158 *out_offset = 0;
159 return "??";
160 }
161 SortedSymbol* sorted_symbols = nullptr;
162#ifdef KERNEL
163 if (!m_sorted_symbols_region) {
164 m_sorted_symbols_region = MM.allocate_kernel_region(PAGE_ROUND_UP(m_symbol_count * sizeof(SortedSymbol)), "Sorted symbols", Kernel::Region::Access::Read | Kernel::Region::Access::Write);
165 sorted_symbols = (SortedSymbol*)m_sorted_symbols_region->vaddr().as_ptr();
166 size_t index = 0;
167 m_image.for_each_symbol([&](auto& symbol) {
168 sorted_symbols[index++] = { symbol.value(), symbol.name() };
169 return IterationDecision::Continue;
170 });
171 quick_sort(sorted_symbols, sorted_symbols + m_symbol_count, [](auto& a, auto& b) {
172 return a.address < b.address;
173 });
174 } else {
175 sorted_symbols = (SortedSymbol*)m_sorted_symbols_region->vaddr().as_ptr();
176 }
177#else
178 if (m_sorted_symbols.is_empty()) {
179 m_sorted_symbols.ensure_capacity(m_symbol_count);
180 m_image.for_each_symbol([this](auto& symbol) {
181 m_sorted_symbols.append({ symbol.value(), symbol.name(), {} });
182 return IterationDecision::Continue;
183 });
184 quick_sort(m_sorted_symbols, [](auto& a, auto& b) {
185 return a.address < b.address;
186 });
187 }
188 sorted_symbols = m_sorted_symbols.data();
189#endif
190
191 for (size_t i = 0; i < m_symbol_count; ++i) {
192 if (sorted_symbols[i].address > address) {
193 if (i == 0) {
194 if (out_offset)
195 *out_offset = 0;
196 return "!!";
197 }
198 auto& symbol = sorted_symbols[i - 1];
199
200#ifdef KERNEL
201 auto demangled_name = demangle(symbol.name);
202#else
203 auto& demangled_name = symbol.demangled_name;
204 if (demangled_name.is_null())
205 demangled_name = demangle(symbol.name);
206#endif
207
208 if (out_offset) {
209 *out_offset = address - symbol.address;
210 return demangled_name;
211 }
212 return String::format("%s +%u", demangled_name.characters(), address - symbol.address);
213 }
214 }
215 if (out_offset)
216 *out_offset = 0;
217 return "??";
218}