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/QuickSort.h>
30
31#ifdef KERNEL
32#include <Kernel/VM/MemoryManager.h>
33#define do_memcpy copy_to_user
34#else
35#define do_memcpy memcpy
36#endif
37
38//#define ELFLOADER_DEBUG
39
40ELFLoader::ELFLoader(const u8* buffer, size_t size)
41 : m_image(buffer, size)
42{
43}
44
45ELFLoader::~ELFLoader()
46{
47}
48
49bool ELFLoader::load()
50{
51#ifdef ELFLOADER_DEBUG
52 m_image.dump();
53#endif
54 if (!m_image.is_valid())
55 return false;
56
57 if (!layout())
58 return false;
59
60 return true;
61}
62
63bool ELFLoader::layout()
64{
65 bool failed = false;
66 m_image.for_each_program_header([&](const ELFImage::ProgramHeader& program_header) {
67 if (program_header.type() == PT_TLS) {
68#ifdef KERNEL
69 auto* tls_image = tls_section_hook(program_header.size_in_memory(), program_header.alignment());
70 if (!tls_image) {
71 failed = true;
72 return;
73 }
74 if (!m_image.is_within_image(program_header.raw_data(), program_header.size_in_image())) {
75 dbg() << "Shenanigans! ELF PT_TLS header sneaks outside of executable.";
76 failed = true;
77 return;
78 }
79 do_memcpy(tls_image, program_header.raw_data(), program_header.size_in_image());
80#endif
81 return;
82 }
83 if (program_header.type() != PT_LOAD)
84 return;
85#ifdef ELFLOADER_DEBUG
86 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());
87#endif
88#ifdef KERNEL
89 if (program_header.is_writable()) {
90 auto* allocated_section = alloc_section_hook(
91 program_header.vaddr(),
92 program_header.size_in_memory(),
93 program_header.alignment(),
94 program_header.is_readable(),
95 program_header.is_writable(),
96 String::format("elf-alloc-%s%s", program_header.is_readable() ? "r" : "", program_header.is_writable() ? "w" : ""));
97 if (!allocated_section) {
98 failed = true;
99 return;
100 }
101 if (!m_image.is_within_image(program_header.raw_data(), program_header.size_in_image())) {
102 dbg() << "Shenanigans! Writable ELF PT_LOAD header sneaks outside of executable.";
103 failed = true;
104 return;
105 }
106 // It's not always the case with PIE executables (and very well shouldn't be) that the
107 // virtual address in the program header matches the one we end up giving the process.
108 // In order to copy the data image correctly into memory, we need to copy the data starting at
109 // the right initial page offset into the pages allocated for the elf_alloc-XX section.
110 // FIXME: There's an opportunity to munmap, or at least mprotect, the padding space between
111 // the .text and .data PT_LOAD sections of the executable.
112 // Accessing it would definitely be a bug.
113 auto page_offset = program_header.vaddr();
114 page_offset.mask(~PAGE_MASK);
115 do_memcpy((u8*)allocated_section + page_offset.get(), program_header.raw_data(), program_header.size_in_image());
116 } else {
117 auto* mapped_section = map_section_hook(
118 program_header.vaddr(),
119 program_header.size_in_memory(),
120 program_header.alignment(),
121 program_header.offset(),
122 program_header.is_readable(),
123 program_header.is_writable(),
124 program_header.is_executable(),
125 String::format("elf-map-%s%s%s", program_header.is_readable() ? "r" : "", program_header.is_writable() ? "w" : "", program_header.is_executable() ? "x" : ""));
126 if (!mapped_section) {
127 failed = true;
128 }
129 }
130#endif
131 });
132 return !failed;
133}
134
135char* ELFLoader::symbol_ptr(const char* name)
136{
137 char* found_ptr = nullptr;
138 m_image.for_each_symbol([&](const ELFImage::Symbol symbol) {
139 if (symbol.type() != STT_FUNC)
140 return IterationDecision::Continue;
141 if (symbol.name() == name)
142 return IterationDecision::Continue;
143 if (m_image.is_executable())
144 found_ptr = (char*)(size_t)symbol.value();
145 else
146 ASSERT_NOT_REACHED();
147 return IterationDecision::Break;
148 });
149 return found_ptr;
150}
151
152String ELFLoader::symbolicate(u32 address, u32* out_offset) const
153{
154 if (!m_image.symbol_count()) {
155 if (out_offset)
156 *out_offset = 0;
157 return "??";
158 }
159 SortedSymbol* sorted_symbols = nullptr;
160#ifdef KERNEL
161 if (!m_sorted_symbols_region) {
162 m_sorted_symbols_region = MM.allocate_kernel_region(PAGE_ROUND_UP(m_image.symbol_count() * sizeof(SortedSymbol)), "Sorted symbols", Kernel::Region::Access::Read | Kernel::Region::Access::Write);
163 sorted_symbols = (SortedSymbol*)m_sorted_symbols_region->vaddr().as_ptr();
164 size_t index = 0;
165 m_image.for_each_symbol([&](auto& symbol) {
166 sorted_symbols[index++] = { symbol.value(), symbol.name() };
167 return IterationDecision::Continue;
168 });
169 quick_sort(sorted_symbols, sorted_symbols + m_image.symbol_count(), [](auto& a, auto& b) {
170 return a.address < b.address;
171 });
172 } else {
173 sorted_symbols = (SortedSymbol*)m_sorted_symbols_region->vaddr().as_ptr();
174 }
175#else
176 if (m_sorted_symbols.is_empty()) {
177 m_sorted_symbols.ensure_capacity(m_image.symbol_count());
178 m_image.for_each_symbol([this](auto& symbol) {
179 m_sorted_symbols.append({ symbol.value(), symbol.name() });
180 return IterationDecision::Continue;
181 });
182 quick_sort(m_sorted_symbols.begin(), m_sorted_symbols.end(), [](auto& a, auto& b) {
183 return a.address < b.address;
184 });
185 }
186 sorted_symbols = m_sorted_symbols.data();
187#endif
188
189 for (size_t i = 0; i < m_image.symbol_count(); ++i) {
190 if (sorted_symbols[i].address > address) {
191 if (i == 0) {
192 if (out_offset)
193 *out_offset = 0;
194 return "!!";
195 }
196 auto& symbol = sorted_symbols[i - 1];
197 if (out_offset) {
198 *out_offset = address - symbol.address;
199 return demangle(symbol.name);
200 }
201 return String::format("%s +%u", demangle(symbol.name).characters(), address - symbol.address);
202 }
203 }
204 if (out_offset)
205 *out_offset = 0;
206 return "??";
207}