Serenity Operating System
at hosted 218 lines 8.3 kB view raw
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}