mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 20:17:44 +00:00
Libraries: Move to Userland/Libraries/
This commit is contained in:
parent
dc28c07fa5
commit
13d7c09125
1857 changed files with 266 additions and 274 deletions
58
Userland/Libraries/LibELF/Arch/i386/plt_trampoline.S
Normal file
58
Userland/Libraries/LibELF/Arch/i386/plt_trampoline.S
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*-
|
||||
* Copyright (c) 1998, 2002 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Christos Zoulas and by Charles M. Hannum.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This asm method is copied from NetBSD. We changed the internal method that
|
||||
* gets called and the name, but it's still essentially the same as
|
||||
* _rtld_bind_start from libexec/ld.elf_so/arch/i386/rtld_start.S
|
||||
*/
|
||||
|
||||
.align 4
|
||||
.globl _plt_trampoline
|
||||
.hidden _plt_trampoline
|
||||
.type _plt_trampoline,@function
|
||||
_plt_trampoline: # (obj, reloff)
|
||||
pushf # save registers
|
||||
pushl %eax
|
||||
pushl %ecx
|
||||
pushl %edx
|
||||
|
||||
pushl 20(%esp) # Copy of reloff
|
||||
pushl 20(%esp) # Copy of obj
|
||||
call _fixup_plt_entry # Call the binder
|
||||
addl $8,%esp # pop binder args
|
||||
movl %eax,20(%esp) # Store function to be called in obj
|
||||
|
||||
popl %edx
|
||||
popl %ecx
|
||||
popl %eax
|
||||
popf
|
||||
|
||||
leal 4(%esp),%esp # Discard reloff, do not change eflags
|
||||
ret
|
123
Userland/Libraries/LibELF/AuxiliaryVector.h
Normal file
123
Userland/Libraries/LibELF/AuxiliaryVector.h
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright (c) 2020, The SerenityOS developers.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
/* Auxiliary Vector types, from Intel386 ABI ver 1.0 section 2.3.3 */
|
||||
typedef struct
|
||||
{
|
||||
long a_type; /* Note: Extended to long from int, for ease of comaptibility w/64 bit */
|
||||
union {
|
||||
long a_val;
|
||||
void* a_ptr;
|
||||
void (*a_fnc)(); /* In spec, not used */
|
||||
} a_un;
|
||||
} auxv_t;
|
||||
|
||||
// clang-format off
|
||||
#define AT_NULL 0 /* No length, last entry's a_type has this value */
|
||||
#define AT_IGNORE 1 /* Entry has no meaning, a_un undefined */
|
||||
#define AT_EXECFD 2 /* a_val contains a file descriptor of the main program image */
|
||||
#define AT_PHDR 3 /* a_ptr contains pointer to program header table of main program image */
|
||||
#define AT_PHENT 4 /* a_val holds size of program header table entries */
|
||||
#define AT_PHNUM 5 /* a_val holds number of program header table entries */
|
||||
#define AT_PAGESZ 6 /* a_val gives system page size in bytes */
|
||||
#define AT_BASE 7 /* a_ptr holds base address that Loader was loaded into memory */
|
||||
#define AT_FLAGS 8 /* a_val holds 1 bit flags. Undefined flags are 0 */
|
||||
#define AT_ENTRY 9 /* a_ptr holds entry point of the main program */
|
||||
#define AT_NOTELF 10 /* a_val non-zero if the program is not ELF */
|
||||
#define AT_UID 11 /* a_val holds real user id of process */
|
||||
#define AT_EUID 12 /* a_val holds effective user id of process */
|
||||
#define AT_GID 13 /* a_val holds real group id of process */
|
||||
#define AT_EGID 14 /* a_val holds effective group id of process */
|
||||
#define AT_PLATFORM 15 /* a_val points to a string containing platform name */
|
||||
#define AT_HWCAP 16 /* a_val contains bitmask of CPU features. Equivalent to CPUID 1.EDX*/
|
||||
#define AT_CLKTCK 17 /* a_val contains frequence at which times() increments. (Re: Spec. What is times()?) */
|
||||
#define AT_SECURE 23 /* a_val holds 1 if program in secure mode (e.g. suid). Otherwise 0 */
|
||||
#define AT_BASE_PLATFORM 24 /* a_ptr points to a string identifying base platform name, which might be different from platform (e.g x86_64 when in i386 compat) */
|
||||
#define AT_RANDOM 25 /* a_ptr points to 16 securely generated random bytes */
|
||||
#define AT_HWCAP2 26 /* a_val holds extended hw feature mask. Currently 0 */
|
||||
#define AT_EXECFN 31 /* a_ptr points to file name of executed program */
|
||||
#define AT_EXE_BASE 32 /* a_ptr holds base address where main program was loaded into memory */
|
||||
#define AT_EXE_SIZE 33 /* a_val holds the size of the main program in memory */
|
||||
// clang-format on
|
||||
|
||||
namespace ELF {
|
||||
|
||||
struct AuxiliaryValue {
|
||||
enum Type {
|
||||
Null = AT_NULL,
|
||||
Ignore = AT_IGNORE,
|
||||
ExecFileDescriptor = AT_EXECFD,
|
||||
Phdr = AT_PHDR,
|
||||
Phent = AT_PHENT,
|
||||
Phnum = AT_PHNUM,
|
||||
PageSize = AT_PAGESZ,
|
||||
BaseAddress = AT_BASE,
|
||||
Flags = AT_FLAGS,
|
||||
Entry = AT_ENTRY,
|
||||
NotELF = AT_NOTELF,
|
||||
Uid = AT_UID,
|
||||
EUid = AT_EUID,
|
||||
Gid = AT_GID,
|
||||
EGid = AT_EGID,
|
||||
Platform = AT_PLATFORM,
|
||||
HwCap = AT_HWCAP,
|
||||
ClockTick = AT_CLKTCK,
|
||||
Secure = AT_SECURE,
|
||||
BasePlatform = AT_BASE_PLATFORM,
|
||||
Random = AT_RANDOM,
|
||||
HwCap2 = AT_HWCAP2,
|
||||
ExecFilename = AT_EXECFN,
|
||||
ExeBaseAddress = AT_EXE_BASE,
|
||||
ExeSize = AT_EXE_SIZE
|
||||
};
|
||||
|
||||
AuxiliaryValue(Type type, long val)
|
||||
{
|
||||
auxv.a_type = type;
|
||||
auxv.a_un.a_val = val;
|
||||
}
|
||||
AuxiliaryValue(Type type, void* ptr)
|
||||
{
|
||||
auxv.a_type = type;
|
||||
auxv.a_un.a_ptr = (void*)ptr;
|
||||
}
|
||||
AuxiliaryValue(Type type, String string)
|
||||
{
|
||||
auxv.a_type = type;
|
||||
auxv.a_un.a_ptr = nullptr;
|
||||
optional_string = string;
|
||||
}
|
||||
|
||||
auxv_t auxv {};
|
||||
String optional_string;
|
||||
};
|
||||
|
||||
}
|
1
Userland/Libraries/LibELF/CMakeLists.txt
Normal file
1
Userland/Libraries/LibELF/CMakeLists.txt
Normal file
|
@ -0,0 +1 @@
|
|||
serenity_install_sources(Libraries/LibELF)
|
87
Userland/Libraries/LibELF/CoreDump.h
Normal file
87
Userland/Libraries/LibELF/CoreDump.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2020, The SerenityOS developers.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibC/sys/arch/i386/regs.h>
|
||||
|
||||
namespace ELF::Core {
|
||||
|
||||
struct [[gnu::packed]] NotesEntryHeader {
|
||||
enum Type : u8 {
|
||||
Null = 0, // Terminates segment
|
||||
ProcessInfo,
|
||||
ThreadInfo,
|
||||
MemoryRegionInfo,
|
||||
Metadata,
|
||||
};
|
||||
Type type;
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] NotesEntry {
|
||||
NotesEntryHeader header;
|
||||
char data[];
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] ProcessInfo {
|
||||
NotesEntryHeader header;
|
||||
int pid;
|
||||
u8 termination_signal;
|
||||
char executable_path[]; // Null terminated
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] ThreadInfo {
|
||||
NotesEntryHeader header;
|
||||
int tid;
|
||||
PtraceRegisters regs;
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] MemoryRegionInfo {
|
||||
NotesEntryHeader header;
|
||||
uint32_t region_start;
|
||||
uint32_t region_end;
|
||||
uint16_t program_header_index;
|
||||
char region_name[]; // Null terminated
|
||||
|
||||
String object_name() const
|
||||
{
|
||||
StringView memory_region_name { region_name };
|
||||
if (memory_region_name.contains("Loader.so"))
|
||||
return "Loader.so";
|
||||
if (!memory_region_name.contains(":"))
|
||||
return {};
|
||||
return memory_region_name.substring_view(0, memory_region_name.find_first_of(":").value()).to_string();
|
||||
}
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] Metadata {
|
||||
NotesEntryHeader header;
|
||||
char json_data[]; // Null terminated
|
||||
};
|
||||
|
||||
}
|
287
Userland/Libraries/LibELF/DynamicLinker.cpp
Normal file
287
Userland/Libraries/LibELF/DynamicLinker.cpp
Normal file
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <AK/LogStream.h>
|
||||
#include <AK/ScopeGuard.h>
|
||||
#include <LibC/mman.h>
|
||||
#include <LibC/stdio.h>
|
||||
#include <LibC/sys/internals.h>
|
||||
#include <LibC/unistd.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibELF/AuxiliaryVector.h>
|
||||
#include <LibELF/DynamicLinker.h>
|
||||
#include <LibELF/DynamicLoader.h>
|
||||
#include <LibELF/DynamicObject.h>
|
||||
#include <LibELF/Image.h>
|
||||
#include <LibELF/exec_elf.h>
|
||||
#include <dlfcn.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
// #define DYNAMIC_LOAD_VERBOSE
|
||||
|
||||
#ifdef DYNAMIC_LOAD_VERBOSE
|
||||
# define VERBOSE(fmt, ...) dbgprintf(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
# define VERBOSE(fmt, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
#define TLS_VERBOSE(fmt, ...) dbgprintf(fmt, ##__VA_ARGS__)
|
||||
|
||||
namespace ELF {
|
||||
|
||||
namespace {
|
||||
HashMap<String, NonnullRefPtr<ELF::DynamicLoader>> g_loaders;
|
||||
HashMap<String, NonnullRefPtr<ELF::DynamicObject>> g_loaded_objects;
|
||||
Vector<NonnullRefPtr<ELF::DynamicObject>> g_global_objects;
|
||||
|
||||
using MainFunction = int (*)(int, char**, char**);
|
||||
using LibCExitFunction = void (*)(int);
|
||||
|
||||
size_t g_current_tls_offset = 0;
|
||||
size_t g_total_tls_size = 0;
|
||||
char** g_envp = nullptr;
|
||||
LibCExitFunction g_libc_exit = nullptr;
|
||||
|
||||
bool g_allowed_to_check_environment_variables { false };
|
||||
bool g_do_breakpoint_trap_before_entry { false };
|
||||
}
|
||||
|
||||
DynamicObject::SymbolLookupResult DynamicLinker::lookup_global_symbol(const char* symbol_name)
|
||||
{
|
||||
DynamicObject::SymbolLookupResult weak_result = {};
|
||||
for (auto& lib : g_global_objects) {
|
||||
auto res = lib->lookup_symbol(symbol_name);
|
||||
if (res.found) {
|
||||
if (res.bind == STB_GLOBAL) {
|
||||
return res;
|
||||
} else if (res.bind == STB_WEAK && !weak_result.found) {
|
||||
weak_result = res;
|
||||
}
|
||||
// We don't want to allow local symbols to be pulled in to other modules
|
||||
}
|
||||
}
|
||||
return weak_result;
|
||||
}
|
||||
|
||||
static void map_library(const String& name, int fd)
|
||||
{
|
||||
struct stat lib_stat;
|
||||
int rc = fstat(fd, &lib_stat);
|
||||
ASSERT(!rc);
|
||||
|
||||
auto loader = ELF::DynamicLoader::construct(name.characters(), fd, lib_stat.st_size);
|
||||
loader->set_tls_offset(g_current_tls_offset);
|
||||
|
||||
g_loaders.set(name, loader);
|
||||
|
||||
g_current_tls_offset += loader->tls_size();
|
||||
}
|
||||
|
||||
static void map_library(const String& name)
|
||||
{
|
||||
// TODO: Do we want to also look for libs in other paths too?
|
||||
String path = String::format("/usr/lib/%s", name.characters());
|
||||
int fd = open(path.characters(), O_RDONLY);
|
||||
ASSERT(fd >= 0);
|
||||
map_library(name, fd);
|
||||
}
|
||||
|
||||
static String get_library_name(const StringView& path)
|
||||
{
|
||||
return LexicalPath(path).basename();
|
||||
}
|
||||
|
||||
static Vector<String> get_dependencies(const String& name)
|
||||
{
|
||||
auto lib = g_loaders.get(name).value();
|
||||
Vector<String> dependencies;
|
||||
|
||||
lib->for_each_needed_library([&dependencies, &name](auto needed_name) {
|
||||
if (name == needed_name)
|
||||
return IterationDecision::Continue;
|
||||
dependencies.append(needed_name);
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
static void map_dependencies(const String& name)
|
||||
{
|
||||
VERBOSE("mapping dependencies for: %s\n", name.characters());
|
||||
|
||||
for (const auto& needed_name : get_dependencies(name)) {
|
||||
VERBOSE("needed library: %s\n", needed_name.characters());
|
||||
String library_name = get_library_name(needed_name);
|
||||
|
||||
if (!g_loaders.contains(library_name)) {
|
||||
map_library(library_name);
|
||||
map_dependencies(library_name);
|
||||
}
|
||||
}
|
||||
VERBOSE("mapped dependencies for %s\n", name.characters());
|
||||
}
|
||||
|
||||
static void allocate_tls()
|
||||
{
|
||||
size_t total_tls_size = 0;
|
||||
for (const auto& data : g_loaders) {
|
||||
VERBOSE("%s: TLS Size: %zu\n", data.key.characters(), data.value->tls_size());
|
||||
total_tls_size += data.value->tls_size();
|
||||
}
|
||||
if (total_tls_size) {
|
||||
[[maybe_unused]] void* tls_address = ::allocate_tls(total_tls_size);
|
||||
VERBOSE("from userspace, tls_address: %p\n", tls_address);
|
||||
}
|
||||
g_total_tls_size = total_tls_size;
|
||||
}
|
||||
|
||||
static void initialize_libc(DynamicObject& libc)
|
||||
{
|
||||
// Traditionally, `_start` of the main program initializes libc.
|
||||
// However, since some libs use malloc() and getenv() in global constructors,
|
||||
// we have to initialize libc just after it is loaded.
|
||||
// Also, we can't just mark `__libc_init` with "__attribute__((constructor))"
|
||||
// because it uses getenv() internally, so `environ` has to be initialized before we call `__libc_init`.
|
||||
auto res = libc.lookup_symbol("environ");
|
||||
ASSERT(res.found);
|
||||
*((char***)res.address) = g_envp;
|
||||
|
||||
res = libc.lookup_symbol("__environ_is_malloced");
|
||||
ASSERT(res.found);
|
||||
*((bool*)res.address) = false;
|
||||
|
||||
res = libc.lookup_symbol("exit");
|
||||
ASSERT(res.found);
|
||||
g_libc_exit = (LibCExitFunction)res.address;
|
||||
|
||||
res = libc.lookup_symbol("__libc_init");
|
||||
ASSERT(res.found);
|
||||
typedef void libc_init_func();
|
||||
((libc_init_func*)res.address)();
|
||||
}
|
||||
|
||||
static void load_elf(const String& name)
|
||||
{
|
||||
VERBOSE("load_elf: %s\n", name.characters());
|
||||
auto loader = g_loaders.get(name).value();
|
||||
VERBOSE("a1\n");
|
||||
for (const auto& needed_name : get_dependencies(name)) {
|
||||
VERBOSE("needed library: %s\n", needed_name.characters());
|
||||
String library_name = get_library_name(needed_name);
|
||||
if (!g_loaded_objects.contains(library_name)) {
|
||||
load_elf(library_name);
|
||||
}
|
||||
}
|
||||
|
||||
auto dynamic_object = loader->load_from_image(RTLD_GLOBAL | RTLD_LAZY, g_total_tls_size);
|
||||
ASSERT(dynamic_object);
|
||||
|
||||
g_loaded_objects.set(name, *dynamic_object);
|
||||
g_global_objects.append(*dynamic_object);
|
||||
|
||||
VERBOSE("load_elf: done %s\n", name.characters());
|
||||
}
|
||||
|
||||
static NonnullRefPtr<DynamicLoader> commit_elf(const String& name)
|
||||
{
|
||||
auto loader = g_loaders.get(name).value();
|
||||
for (const auto& needed_name : get_dependencies(name)) {
|
||||
String library_name = get_library_name(needed_name);
|
||||
if (g_loaders.contains(library_name)) {
|
||||
commit_elf(library_name);
|
||||
}
|
||||
}
|
||||
|
||||
auto object = loader->load_stage_3(RTLD_GLOBAL | RTLD_LAZY, g_total_tls_size);
|
||||
ASSERT(object);
|
||||
if (name == "libc.so") {
|
||||
initialize_libc(*object);
|
||||
}
|
||||
g_loaders.remove(name);
|
||||
return loader;
|
||||
}
|
||||
|
||||
static void read_environment_variables()
|
||||
{
|
||||
for (char** env = g_envp; *env; ++env) {
|
||||
if (StringView { *env } == "_LOADER_BREAKPOINT=1") {
|
||||
g_do_breakpoint_trap_before_entry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ELF::DynamicLinker::linker_main(String&& main_program_name, int main_program_fd, bool is_secure, int argc, char** argv, char** envp)
|
||||
{
|
||||
g_envp = envp;
|
||||
|
||||
g_allowed_to_check_environment_variables = !is_secure;
|
||||
if (g_allowed_to_check_environment_variables)
|
||||
read_environment_variables();
|
||||
|
||||
map_library(main_program_name, main_program_fd);
|
||||
map_dependencies(main_program_name);
|
||||
|
||||
VERBOSE("loaded all dependencies");
|
||||
for ([[maybe_unused]] auto& lib : g_loaders) {
|
||||
VERBOSE("%s - tls size: %zu, tls offset: %zu\n", lib.key.characters(), lib.value->tls_size(), lib.value->tls_offset());
|
||||
}
|
||||
|
||||
allocate_tls();
|
||||
|
||||
load_elf(main_program_name);
|
||||
auto main_program_lib = commit_elf(main_program_name);
|
||||
|
||||
FlatPtr entry_point = reinterpret_cast<FlatPtr>(main_program_lib->image().entry().as_ptr());
|
||||
if (main_program_lib->is_dynamic())
|
||||
entry_point += reinterpret_cast<FlatPtr>(main_program_lib->text_segment_load_address().as_ptr());
|
||||
|
||||
VERBOSE("entry point: %p\n", (void*)entry_point);
|
||||
g_loaders.clear();
|
||||
|
||||
MainFunction main_function = (MainFunction)(entry_point);
|
||||
VERBOSE("jumping to main program entry point: %p\n", main_function);
|
||||
if (g_do_breakpoint_trap_before_entry) {
|
||||
asm("int3");
|
||||
}
|
||||
int rc = main_function(argc, argv, envp);
|
||||
VERBOSE("rc: %d\n", rc);
|
||||
if (g_libc_exit != nullptr) {
|
||||
g_libc_exit(rc);
|
||||
} else {
|
||||
_exit(rc);
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
}
|
45
Userland/Libraries/LibELF/DynamicLinker.h
Normal file
45
Userland/Libraries/LibELF/DynamicLinker.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2021 (c), the SerenityOS developers.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Result.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibELF/DynamicObject.h>
|
||||
|
||||
namespace ELF {
|
||||
|
||||
class DynamicLinker {
|
||||
public:
|
||||
static DynamicObject::SymbolLookupResult lookup_global_symbol(const char* symbol);
|
||||
[[noreturn]] static void linker_main(String&& main_program_name, int fd, bool is_secure, int argc, char** argv, char** envp);
|
||||
|
||||
private:
|
||||
DynamicLinker() = delete;
|
||||
~DynamicLinker() = delete;
|
||||
};
|
||||
|
||||
}
|
530
Userland/Libraries/LibELF/DynamicLoader.cpp
Normal file
530
Userland/Libraries/LibELF/DynamicLoader.cpp
Normal file
|
@ -0,0 +1,530 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020, Andrew Kaster <andrewdkaster@gmail.com>
|
||||
* Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibELF/DynamicLoader.h>
|
||||
#include <LibELF/Validation.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#ifndef DYNAMIC_LOAD_DEBUG
|
||||
# define DYNAMIC_LOAD_DEBUG
|
||||
#endif
|
||||
|
||||
// #define DYNAMIC_LOAD_VERBOSE
|
||||
|
||||
#ifdef DYNAMIC_LOAD_VERBOSE
|
||||
# define VERBOSE(fmt, ...) dbgprintf(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
# define VERBOSE(fmt, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef __serenity__
|
||||
static void* mmap_with_name(void* addr, size_t length, int prot, int flags, int fd, off_t offset, const char*)
|
||||
{
|
||||
return mmap(addr, length, prot, flags, fd, offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace ELF {
|
||||
|
||||
static bool s_always_bind_now = false;
|
||||
|
||||
NonnullRefPtr<DynamicLoader> DynamicLoader::construct(const char* filename, int fd, size_t size)
|
||||
{
|
||||
return adopt(*new DynamicLoader(filename, fd, size));
|
||||
}
|
||||
|
||||
void* DynamicLoader::do_mmap(int fd, size_t size, const String& name)
|
||||
{
|
||||
if (size < sizeof(Elf32_Ehdr))
|
||||
return MAP_FAILED;
|
||||
|
||||
String file_mmap_name = String::format("ELF_DYN: %s", name.characters());
|
||||
return mmap_with_name(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0, file_mmap_name.characters());
|
||||
}
|
||||
|
||||
DynamicLoader::DynamicLoader(const char* filename, int fd, size_t size)
|
||||
: m_filename(filename)
|
||||
, m_file_size(size)
|
||||
, m_image_fd(fd)
|
||||
, m_file_mapping(do_mmap(m_image_fd, m_file_size, m_filename))
|
||||
, m_elf_image((u8*)m_file_mapping, m_file_size)
|
||||
{
|
||||
|
||||
if (m_file_mapping == MAP_FAILED) {
|
||||
m_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
m_tls_size = calculate_tls_size();
|
||||
|
||||
m_valid = validate();
|
||||
}
|
||||
|
||||
RefPtr<DynamicObject> DynamicLoader::dynamic_object_from_image() const
|
||||
{
|
||||
VirtualAddress dynamic_section_address;
|
||||
|
||||
m_elf_image.for_each_program_header([&dynamic_section_address](auto program_header) {
|
||||
if (program_header.type() == PT_DYNAMIC) {
|
||||
dynamic_section_address = VirtualAddress(program_header.raw_data());
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
ASSERT(!dynamic_section_address.is_null());
|
||||
|
||||
return ELF::DynamicObject::construct(VirtualAddress(m_elf_image.base_address()), dynamic_section_address);
|
||||
}
|
||||
|
||||
size_t DynamicLoader::calculate_tls_size() const
|
||||
{
|
||||
size_t tls_size = 0;
|
||||
m_elf_image.for_each_program_header([&tls_size](auto program_header) {
|
||||
if (program_header.type() == PT_TLS) {
|
||||
tls_size = program_header.size_in_memory();
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
return tls_size;
|
||||
}
|
||||
|
||||
bool DynamicLoader::validate()
|
||||
{
|
||||
auto* elf_header = (Elf32_Ehdr*)m_file_mapping;
|
||||
return validate_elf_header(*elf_header, m_file_size) && validate_program_headers(*elf_header, m_file_size, (u8*)m_file_mapping, m_file_size, &m_program_interpreter);
|
||||
}
|
||||
|
||||
DynamicLoader::~DynamicLoader()
|
||||
{
|
||||
if (MAP_FAILED != m_file_mapping)
|
||||
munmap(m_file_mapping, m_file_size);
|
||||
close(m_image_fd);
|
||||
}
|
||||
|
||||
void* DynamicLoader::symbol_for_name(const char* name)
|
||||
{
|
||||
auto symbol = m_dynamic_object->hash_section().lookup_symbol(name);
|
||||
|
||||
if (symbol.is_undefined())
|
||||
return nullptr;
|
||||
|
||||
return m_dynamic_object->base_address().offset(symbol.value()).as_ptr();
|
||||
}
|
||||
|
||||
RefPtr<DynamicObject> DynamicLoader::load_from_image(unsigned flags, size_t total_tls_size)
|
||||
{
|
||||
|
||||
m_valid = m_elf_image.is_valid();
|
||||
|
||||
if (!m_valid) {
|
||||
dbgprintf("DynamicLoader::load_from_image failed: image is invalid\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef DYNAMIC_LOAD_VERBOSE
|
||||
// m_image->dump();
|
||||
#endif
|
||||
|
||||
load_program_headers();
|
||||
|
||||
m_dynamic_object = DynamicObject::construct(m_text_segment_load_address, m_dynamic_section_address);
|
||||
m_dynamic_object->set_tls_offset(m_tls_offset);
|
||||
m_dynamic_object->set_tls_size(m_tls_size);
|
||||
|
||||
auto rc = load_stage_2(flags, total_tls_size);
|
||||
if (!rc) {
|
||||
dbgprintf("DynamicLoader::load_from_image failed at load_stage_2\n");
|
||||
return nullptr;
|
||||
}
|
||||
return m_dynamic_object;
|
||||
}
|
||||
|
||||
bool DynamicLoader::load_stage_2(unsigned flags, size_t total_tls_size)
|
||||
{
|
||||
ASSERT(flags & RTLD_GLOBAL);
|
||||
|
||||
#ifdef DYNAMIC_LOAD_DEBUG
|
||||
m_dynamic_object->dump();
|
||||
#endif
|
||||
|
||||
if (m_dynamic_object->has_text_relocations()) {
|
||||
ASSERT(m_text_segment_load_address.get() != 0);
|
||||
|
||||
#ifndef AK_OS_MACOS
|
||||
// Remap this text region as private.
|
||||
if (mremap(m_text_segment_load_address.as_ptr(), m_text_segment_size, m_text_segment_size, MAP_PRIVATE) == MAP_FAILED) {
|
||||
perror("mremap .text: MAP_PRIVATE");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (0 > mprotect(m_text_segment_load_address.as_ptr(), m_text_segment_size, PROT_READ | PROT_WRITE)) {
|
||||
perror("mprotect .text: PROT_READ | PROT_WRITE"); // FIXME: dlerror?
|
||||
return false;
|
||||
}
|
||||
}
|
||||
do_main_relocations(total_tls_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DynamicLoader::do_main_relocations(size_t total_tls_size)
|
||||
{
|
||||
auto do_single_relocation = [&](ELF::DynamicObject::Relocation relocation) {
|
||||
switch (do_relocation(total_tls_size, relocation)) {
|
||||
case RelocationResult::Failed:
|
||||
dbgln("Loader.so: {} unresolved symbol '{}'", m_filename, relocation.symbol().name());
|
||||
ASSERT_NOT_REACHED();
|
||||
break;
|
||||
case RelocationResult::ResolveLater:
|
||||
m_unresolved_relocations.append(relocation);
|
||||
break;
|
||||
case RelocationResult::Success:
|
||||
break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
};
|
||||
m_dynamic_object->relocation_section().for_each_relocation(do_single_relocation);
|
||||
m_dynamic_object->plt_relocation_section().for_each_relocation(do_single_relocation);
|
||||
}
|
||||
|
||||
RefPtr<DynamicObject> DynamicLoader::load_stage_3(unsigned flags, size_t total_tls_size)
|
||||
{
|
||||
|
||||
do_lazy_relocations(total_tls_size);
|
||||
if (flags & RTLD_LAZY) {
|
||||
setup_plt_trampoline();
|
||||
}
|
||||
|
||||
// Clean up our setting of .text to PROT_READ | PROT_WRITE
|
||||
if (m_dynamic_object->has_text_relocations()) {
|
||||
if (0 > mprotect(m_text_segment_load_address.as_ptr(), m_text_segment_size, PROT_READ | PROT_EXEC)) {
|
||||
perror("mprotect .text: PROT_READ | PROT_EXEC"); // FIXME: dlerror?
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
call_object_init_functions();
|
||||
|
||||
VERBOSE("Loaded %s\n", m_filename.characters());
|
||||
return m_dynamic_object;
|
||||
}
|
||||
|
||||
void DynamicLoader::do_lazy_relocations(size_t total_tls_size)
|
||||
{
|
||||
for (const auto& relocation : m_unresolved_relocations) {
|
||||
if (auto res = do_relocation(total_tls_size, relocation); res != RelocationResult::Success) {
|
||||
dbgln("Loader.so: {} unresolved symbol '{}'", m_filename, relocation.symbol().name());
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicLoader::load_program_headers()
|
||||
{
|
||||
Vector<ProgramHeaderRegion> program_headers;
|
||||
|
||||
ProgramHeaderRegion* text_region_ptr = nullptr;
|
||||
ProgramHeaderRegion* data_region_ptr = nullptr;
|
||||
ProgramHeaderRegion* tls_region_ptr = nullptr;
|
||||
VirtualAddress dynamic_region_desired_vaddr;
|
||||
|
||||
m_elf_image.for_each_program_header([&](const Image::ProgramHeader& program_header) {
|
||||
ProgramHeaderRegion new_region;
|
||||
new_region.set_program_header(program_header.raw_header());
|
||||
program_headers.append(move(new_region));
|
||||
auto& region = program_headers.last();
|
||||
if (region.is_tls_template())
|
||||
tls_region_ptr = ®ion;
|
||||
else if (region.is_load()) {
|
||||
if (region.is_executable())
|
||||
text_region_ptr = ®ion;
|
||||
else
|
||||
data_region_ptr = ®ion;
|
||||
} else if (region.is_dynamic()) {
|
||||
dynamic_region_desired_vaddr = region.desired_load_address();
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
ASSERT(text_region_ptr && data_region_ptr);
|
||||
|
||||
// Process regions in order: .text, .data, .tls
|
||||
auto* region = text_region_ptr;
|
||||
void* requested_load_address = m_elf_image.is_dynamic() ? nullptr : region->desired_load_address().as_ptr();
|
||||
|
||||
ASSERT(!region->is_writable());
|
||||
|
||||
void* text_segment_begin = mmap_with_name(
|
||||
requested_load_address,
|
||||
region->required_load_size(),
|
||||
region->mmap_prot(),
|
||||
MAP_SHARED,
|
||||
m_image_fd,
|
||||
region->offset(),
|
||||
String::format("%s: .text", m_filename.characters()).characters());
|
||||
if (MAP_FAILED == text_segment_begin) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
ASSERT(requested_load_address == nullptr || requested_load_address == text_segment_begin);
|
||||
m_text_segment_size = region->required_load_size();
|
||||
m_text_segment_load_address = VirtualAddress { (FlatPtr)text_segment_begin };
|
||||
|
||||
if (m_elf_image.is_dynamic())
|
||||
m_dynamic_section_address = dynamic_region_desired_vaddr.offset(m_text_segment_load_address.get());
|
||||
else
|
||||
m_dynamic_section_address = dynamic_region_desired_vaddr;
|
||||
|
||||
region = data_region_ptr;
|
||||
void* data_segment_begin = mmap_with_name(
|
||||
(u8*)text_segment_begin + m_text_segment_size,
|
||||
region->required_load_size(),
|
||||
region->mmap_prot(),
|
||||
MAP_ANONYMOUS | MAP_PRIVATE,
|
||||
0,
|
||||
0,
|
||||
String::format("%s: .data", m_filename.characters()).characters());
|
||||
if (MAP_FAILED == data_segment_begin) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
VirtualAddress data_segment_actual_addr;
|
||||
if (m_elf_image.is_dynamic()) {
|
||||
data_segment_actual_addr = region->desired_load_address().offset((FlatPtr)text_segment_begin);
|
||||
} else {
|
||||
data_segment_actual_addr = region->desired_load_address();
|
||||
}
|
||||
memcpy(data_segment_actual_addr.as_ptr(), (u8*)m_file_mapping + region->offset(), region->size_in_image());
|
||||
// FIXME: Initialize the values in the TLS section. Currently, it is zeroed.
|
||||
}
|
||||
|
||||
DynamicLoader::RelocationResult DynamicLoader::do_relocation(size_t total_tls_size, ELF::DynamicObject::Relocation relocation)
|
||||
{
|
||||
VERBOSE("Relocation symbol: %s, type: %d\n", relocation.symbol().name(), relocation.type());
|
||||
FlatPtr* patch_ptr = nullptr;
|
||||
if (is_dynamic())
|
||||
patch_ptr = (FlatPtr*)(m_dynamic_object->base_address().as_ptr() + relocation.offset());
|
||||
else
|
||||
patch_ptr = (FlatPtr*)(FlatPtr)relocation.offset();
|
||||
|
||||
// VERBOSE("dynamic object name: %s\n", dynamic_object.object_name());
|
||||
VERBOSE("dynamic object base address: %p\n", m_dynamic_object->base_address());
|
||||
VERBOSE("relocation offset: 0x%x\n", relocation.offset());
|
||||
VERBOSE("patch_ptr: %p\n", patch_ptr);
|
||||
switch (relocation.type()) {
|
||||
case R_386_NONE:
|
||||
// Apparently most loaders will just skip these?
|
||||
// Seems if the 'link editor' generates one something is funky with your code
|
||||
VERBOSE("None relocation. No symbol, no nothing.\n");
|
||||
break;
|
||||
case R_386_32: {
|
||||
auto symbol = relocation.symbol();
|
||||
VERBOSE("Absolute relocation: name: '%s', value: %p\n", symbol.name(), symbol.value());
|
||||
auto res = lookup_symbol(symbol);
|
||||
if (!res.found) {
|
||||
if (symbol.bind() == STB_WEAK) {
|
||||
return RelocationResult::ResolveLater;
|
||||
}
|
||||
dbgln("ERROR: symbol not found: {}.", symbol.name());
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
u32 symbol_address = res.address;
|
||||
*patch_ptr += symbol_address;
|
||||
VERBOSE(" Symbol address: %p\n", *patch_ptr);
|
||||
break;
|
||||
}
|
||||
case R_386_PC32: {
|
||||
auto symbol = relocation.symbol();
|
||||
VERBOSE("PC-relative relocation: '%s', value: %p\n", symbol.name(), symbol.value());
|
||||
auto res = lookup_symbol(symbol);
|
||||
ASSERT(res.found);
|
||||
u32 relative_offset = (res.address - (FlatPtr)(m_dynamic_object->base_address().as_ptr() + relocation.offset()));
|
||||
*patch_ptr += relative_offset;
|
||||
VERBOSE(" Symbol address: %p\n", *patch_ptr);
|
||||
break;
|
||||
}
|
||||
case R_386_GLOB_DAT: {
|
||||
auto symbol = relocation.symbol();
|
||||
VERBOSE("Global data relocation: '%s', value: %p\n", symbol.name(), symbol.value());
|
||||
auto res = lookup_symbol(symbol);
|
||||
if (!res.found) {
|
||||
// We do not support these
|
||||
// TODO: Can we tell gcc not to generate the piece of code that uses these?
|
||||
// (--disable-tm-clone-registry flag in gcc conifugraion?)
|
||||
if (!strcmp(symbol.name(), "__deregister_frame_info") || !strcmp(symbol.name(), "_ITM_registerTMCloneTable")
|
||||
|| !strcmp(symbol.name(), "_ITM_deregisterTMCloneTable") || !strcmp(symbol.name(), "__register_frame_info")) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (symbol.bind() == STB_WEAK) {
|
||||
return RelocationResult::ResolveLater;
|
||||
}
|
||||
|
||||
// Symbol not found
|
||||
return RelocationResult::Failed;
|
||||
}
|
||||
VERBOSE("was symbol found? %d, address: 0x%x\n", res.found, res.address);
|
||||
VERBOSE("object: %s\n", m_filename.characters());
|
||||
|
||||
u32 symbol_location = res.address;
|
||||
ASSERT(symbol_location != (FlatPtr)m_dynamic_object->base_address().as_ptr());
|
||||
*patch_ptr = symbol_location;
|
||||
VERBOSE(" Symbol address: %p\n", *patch_ptr);
|
||||
break;
|
||||
}
|
||||
case R_386_RELATIVE: {
|
||||
// FIXME: According to the spec, R_386_relative ones must be done first.
|
||||
// We could explicitly do them first using m_number_of_relocatoins from DT_RELCOUNT
|
||||
// However, our compiler is nice enough to put them at the front of the relocations for us :)
|
||||
VERBOSE("Load address relocation at offset %X\n", relocation.offset());
|
||||
VERBOSE(" patch ptr == %p, adding load base address (%p) to it and storing %p\n", *patch_ptr, m_dynamic_object->base_address().as_ptr(), *patch_ptr + m_dynamic_object->base_address().as_ptr());
|
||||
*patch_ptr += (FlatPtr)m_dynamic_object->base_address().as_ptr(); // + addend for RelA (addend for Rel is stored at addr)
|
||||
break;
|
||||
}
|
||||
case R_386_TLS_TPOFF32:
|
||||
case R_386_TLS_TPOFF: {
|
||||
VERBOSE("Relocation type: R_386_TLS_TPOFF at offset %X\n", relocation.offset());
|
||||
auto symbol = relocation.symbol();
|
||||
// For some reason, LibC has a R_386_TLS_TPOFF that refers to the undefined symbol.. huh
|
||||
if (relocation.symbol_index() == 0)
|
||||
break;
|
||||
VERBOSE("Symbol index: %d\n", symbol.index());
|
||||
VERBOSE("Symbol is_undefined?: %d\n", symbol.is_undefined());
|
||||
VERBOSE("TLS relocation: '%s', value: %p\n", symbol.name(), symbol.value());
|
||||
auto res = lookup_symbol(symbol);
|
||||
if (!res.found)
|
||||
break;
|
||||
ASSERT(res.found);
|
||||
u32 symbol_value = res.value;
|
||||
VERBOSE("symbol value: %d\n", symbol_value);
|
||||
const auto dynamic_object_of_symbol = res.dynamic_object;
|
||||
ASSERT(dynamic_object_of_symbol);
|
||||
size_t offset_of_tls_end = dynamic_object_of_symbol->tls_offset().value() + dynamic_object_of_symbol->tls_size().value();
|
||||
// size_t offset_of_tls_end = tls_offset() + tls_size();
|
||||
VERBOSE("patch ptr: 0x%x\n", patch_ptr);
|
||||
VERBOSE("tls end offset: %d, total tls size: %d\n", offset_of_tls_end, total_tls_size);
|
||||
*patch_ptr = (offset_of_tls_end - total_tls_size - symbol_value - sizeof(Elf32_Addr));
|
||||
VERBOSE("*patch ptr: %d\n", (i32)*patch_ptr);
|
||||
break;
|
||||
}
|
||||
case R_386_JMP_SLOT: {
|
||||
// FIXME: Or BIND_NOW flag passed in?
|
||||
if (m_dynamic_object->must_bind_now() || s_always_bind_now) {
|
||||
// Eagerly BIND_NOW the PLT entries, doing all the symbol looking goodness
|
||||
// The patch method returns the address for the LAZY fixup path, but we don't need it here
|
||||
VERBOSE("patching plt reloaction: 0x%x\n", relocation.offset_in_section());
|
||||
[[maybe_unused]] auto rc = m_dynamic_object->patch_plt_entry(relocation.offset_in_section());
|
||||
} else {
|
||||
u8* relocation_address = relocation.address().as_ptr();
|
||||
|
||||
if (m_elf_image.is_dynamic())
|
||||
*(u32*)relocation_address += (FlatPtr)m_dynamic_object->base_address().as_ptr();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Raise the alarm! Someone needs to implement this relocation type
|
||||
VERBOSE("Found a new exciting relocation type %d\n", relocation.type());
|
||||
// printf("DynamicLoader: Found unknown relocation type %d\n", relocation.type());
|
||||
ASSERT_NOT_REACHED();
|
||||
break;
|
||||
}
|
||||
return RelocationResult::Success;
|
||||
}
|
||||
|
||||
// Defined in <arch>/plt_trampoline.S
|
||||
extern "C" void _plt_trampoline(void) __attribute__((visibility("hidden")));
|
||||
|
||||
void DynamicLoader::setup_plt_trampoline()
|
||||
{
|
||||
ASSERT(m_dynamic_object);
|
||||
VirtualAddress got_address = m_dynamic_object->plt_got_base_address();
|
||||
|
||||
FlatPtr* got_ptr = (FlatPtr*)got_address.as_ptr();
|
||||
got_ptr[1] = (FlatPtr)m_dynamic_object.ptr();
|
||||
got_ptr[2] = (FlatPtr)&_plt_trampoline;
|
||||
|
||||
VERBOSE("Set GOT PLT entries at %p: [0] = %p [1] = %p, [2] = %p\n", got_ptr, (void*)got_ptr[0], (void*)got_ptr[1], (void*)got_ptr[2]);
|
||||
}
|
||||
|
||||
// Called from our ASM routine _plt_trampoline.
|
||||
// Tell the compiler that it might be called from other places:
|
||||
extern "C" Elf32_Addr _fixup_plt_entry(DynamicObject* object, u32 relocation_offset);
|
||||
extern "C" Elf32_Addr _fixup_plt_entry(DynamicObject* object, u32 relocation_offset)
|
||||
{
|
||||
return object->patch_plt_entry(relocation_offset);
|
||||
}
|
||||
|
||||
void DynamicLoader::call_object_init_functions()
|
||||
{
|
||||
typedef void (*InitFunc)();
|
||||
|
||||
if (m_dynamic_object->has_init_section()) {
|
||||
auto init_function = (InitFunc)(m_dynamic_object->init_section().address().as_ptr());
|
||||
|
||||
VERBOSE("Calling DT_INIT at %p\n", init_function);
|
||||
(init_function)();
|
||||
}
|
||||
|
||||
if (m_dynamic_object->has_init_array_section()) {
|
||||
auto init_array_section = m_dynamic_object->init_array_section();
|
||||
|
||||
InitFunc* init_begin = (InitFunc*)(init_array_section.address().as_ptr());
|
||||
InitFunc* init_end = init_begin + init_array_section.entry_count();
|
||||
while (init_begin != init_end) {
|
||||
// Android sources claim that these can be -1, to be ignored.
|
||||
// 0 definitely shows up. Apparently 0/-1 are valid? Confusing.
|
||||
if (!*init_begin || ((FlatPtr)*init_begin == (FlatPtr)-1))
|
||||
continue;
|
||||
VERBOSE("Calling DT_INITARRAY entry at %p\n", *init_begin);
|
||||
(*init_begin)();
|
||||
++init_begin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 DynamicLoader::ProgramHeaderRegion::mmap_prot() const
|
||||
{
|
||||
int prot = 0;
|
||||
prot |= is_executable() ? PROT_EXEC : 0;
|
||||
prot |= is_readable() ? PROT_READ : 0;
|
||||
prot |= is_writable() ? PROT_WRITE : 0;
|
||||
return prot;
|
||||
}
|
||||
|
||||
DynamicObject::SymbolLookupResult DynamicLoader::lookup_symbol(const ELF::DynamicObject::Symbol& symbol) const
|
||||
{
|
||||
return m_dynamic_object->lookup_symbol(symbol);
|
||||
}
|
||||
|
||||
} // end namespace ELF
|
164
Userland/Libraries/LibELF/DynamicLoader.h
Normal file
164
Userland/Libraries/LibELF/DynamicLoader.h
Normal file
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020, Andrew Kaster <andrewdkaster@gmail.com>
|
||||
* Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibELF/DynamicObject.h>
|
||||
#include <LibELF/Image.h>
|
||||
#include <LibELF/exec_elf.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
namespace ELF {
|
||||
|
||||
#define ALIGN_ROUND_UP(x, align) ((((size_t)(x)) + align - 1) & (~(align - 1)))
|
||||
|
||||
class DynamicLoader : public RefCounted<DynamicLoader> {
|
||||
public:
|
||||
static NonnullRefPtr<DynamicLoader> construct(const char* filename, int fd, size_t file_size);
|
||||
|
||||
~DynamicLoader();
|
||||
|
||||
bool is_valid() const { return m_valid; }
|
||||
|
||||
// Load a full ELF image from file into the current process and create an DynamicObject
|
||||
// from the SHT_DYNAMIC in the file.
|
||||
RefPtr<DynamicObject> load_from_image(unsigned flags, size_t total_tls_size);
|
||||
|
||||
// Stage 2 of loading: dynamic object loading and primary relocations
|
||||
bool load_stage_2(unsigned flags, size_t total_tls_size);
|
||||
|
||||
// Stage 3 of loading: lazy relocations and initializers
|
||||
RefPtr<DynamicObject> load_stage_3(unsigned flags, size_t total_tls_size);
|
||||
// Intended for use by dlsym or other internal methods
|
||||
void* symbol_for_name(const char*);
|
||||
|
||||
void dump();
|
||||
|
||||
// Requested program interpreter from program headers. May be empty string
|
||||
StringView program_interpreter() const { return m_program_interpreter; }
|
||||
|
||||
VirtualAddress text_segment_load_addresss() const { return m_text_segment_load_address; }
|
||||
|
||||
void set_tls_offset(size_t offset) { m_tls_offset = offset; };
|
||||
size_t tls_size() const { return m_tls_size; }
|
||||
size_t tls_offset() const { return m_tls_offset; }
|
||||
const ELF::Image& image() const { return m_elf_image; }
|
||||
|
||||
template<typename F>
|
||||
void for_each_needed_library(F) const;
|
||||
|
||||
VirtualAddress text_segment_load_address() const { return m_text_segment_load_address; }
|
||||
bool is_dynamic() const { return m_elf_image.is_dynamic(); }
|
||||
|
||||
private:
|
||||
class ProgramHeaderRegion {
|
||||
public:
|
||||
void set_program_header(const Elf32_Phdr& header) { m_program_header = header; }
|
||||
|
||||
// Information from ELF Program header
|
||||
u32 type() const { return m_program_header.p_type; }
|
||||
u32 flags() const { return m_program_header.p_flags; }
|
||||
u32 offset() const { return m_program_header.p_offset; }
|
||||
VirtualAddress desired_load_address() const { return VirtualAddress(m_program_header.p_vaddr); }
|
||||
u32 size_in_memory() const { return m_program_header.p_memsz; }
|
||||
u32 size_in_image() const { return m_program_header.p_filesz; }
|
||||
u32 alignment() const { return m_program_header.p_align; }
|
||||
u32 mmap_prot() const;
|
||||
bool is_readable() const { return flags() & PF_R; }
|
||||
bool is_writable() const { return flags() & PF_W; }
|
||||
bool is_executable() const { return flags() & PF_X; }
|
||||
bool is_tls_template() const { return type() == PT_TLS; }
|
||||
bool is_load() const { return type() == PT_LOAD; }
|
||||
bool is_dynamic() const { return type() == PT_DYNAMIC; }
|
||||
|
||||
u32 required_load_size() { return ALIGN_ROUND_UP(m_program_header.p_memsz, m_program_header.p_align); }
|
||||
|
||||
private:
|
||||
Elf32_Phdr m_program_header; // Explicitly a copy of the PHDR in the image
|
||||
};
|
||||
|
||||
static void* do_mmap(int fd, size_t size, const String& name);
|
||||
RefPtr<DynamicObject> dynamic_object_from_image() const;
|
||||
|
||||
explicit DynamicLoader(const char* filename, int fd, size_t file_size);
|
||||
|
||||
// Stage 1
|
||||
void load_program_headers();
|
||||
|
||||
// Stage 2
|
||||
void do_main_relocations(size_t total_tls_size);
|
||||
|
||||
// Stage 3
|
||||
void do_lazy_relocations(size_t total_tls_size);
|
||||
void setup_plt_trampoline();
|
||||
void call_object_init_functions();
|
||||
|
||||
bool validate();
|
||||
|
||||
enum class RelocationResult : uint8_t {
|
||||
Failed = 0,
|
||||
Success = 1,
|
||||
ResolveLater = 2,
|
||||
};
|
||||
RelocationResult do_relocation(size_t total_tls_size, DynamicObject::Relocation relocation);
|
||||
size_t calculate_tls_size() const;
|
||||
|
||||
DynamicObject::SymbolLookupResult lookup_symbol(const ELF::DynamicObject::Symbol& symbol) const;
|
||||
|
||||
String m_filename;
|
||||
String m_program_interpreter;
|
||||
size_t m_file_size { 0 };
|
||||
int m_image_fd { -1 };
|
||||
void* m_file_mapping { MAP_FAILED };
|
||||
ELF::Image m_elf_image;
|
||||
bool m_valid { true };
|
||||
|
||||
RefPtr<DynamicObject> m_dynamic_object;
|
||||
|
||||
VirtualAddress m_text_segment_load_address;
|
||||
size_t m_text_segment_size { 0 };
|
||||
|
||||
VirtualAddress m_tls_segment_address;
|
||||
VirtualAddress m_dynamic_section_address;
|
||||
|
||||
size_t m_tls_offset { 0 };
|
||||
size_t m_tls_size { 0 };
|
||||
|
||||
Vector<DynamicObject::Relocation> m_unresolved_relocations;
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
void DynamicLoader::for_each_needed_library(F func) const
|
||||
{
|
||||
dynamic_object_from_image()->for_each_needed_library(move(func));
|
||||
}
|
||||
|
||||
} // end namespace ELF
|
527
Userland/Libraries/LibELF/DynamicObject.cpp
Normal file
527
Userland/Libraries/LibELF/DynamicObject.cpp
Normal file
|
@ -0,0 +1,527 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020, Andrew Kaster <andrewdkaster@gmail.com>
|
||||
* Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibELF/DynamicLinker.h>
|
||||
#include <LibELF/DynamicObject.h>
|
||||
#include <LibELF/exec_elf.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
// #define DYNAMIC_OBJECT_VERBOSE
|
||||
|
||||
#ifdef DYNAMIC_OBJECT_VERBOSE
|
||||
# define VERBOSE(fmt, ...) dbgprintf(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
# define VERBOSE(fmt, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
namespace ELF {
|
||||
|
||||
static const char* name_for_dtag(Elf32_Sword d_tag);
|
||||
|
||||
DynamicObject::DynamicObject(VirtualAddress base_address, VirtualAddress dynamic_section_addresss)
|
||||
: m_base_address(base_address)
|
||||
, m_dynamic_address(dynamic_section_addresss)
|
||||
{
|
||||
Elf32_Ehdr* header = (Elf32_Ehdr*)base_address.as_ptr();
|
||||
Elf32_Phdr* pheader = (Elf32_Phdr*)(base_address.as_ptr() + header->e_phoff);
|
||||
m_elf_base_address = VirtualAddress(pheader->p_vaddr - pheader->p_offset);
|
||||
if (header->e_type == ET_DYN)
|
||||
m_is_elf_dynamic = true;
|
||||
else
|
||||
m_is_elf_dynamic = false;
|
||||
|
||||
parse();
|
||||
}
|
||||
|
||||
DynamicObject::~DynamicObject()
|
||||
{
|
||||
}
|
||||
|
||||
void DynamicObject::dump() const
|
||||
{
|
||||
StringBuilder builder;
|
||||
builder.append("\nd_tag tag_name value\n");
|
||||
size_t num_dynamic_sections = 0;
|
||||
|
||||
for_each_dynamic_entry([&](const DynamicObject::DynamicEntry& entry) {
|
||||
String name_field = String::format("(%s)", name_for_dtag(entry.tag()));
|
||||
builder.appendf("0x%08X %-17s0x%X\n", entry.tag(), name_field.characters(), entry.val());
|
||||
num_dynamic_sections++;
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
if (m_has_soname)
|
||||
builder.appendf("DT_SONAME: %s\n", soname()); // FIXME: Valdidate that this string is null terminated?
|
||||
|
||||
VERBOSE("Dynamic section at address %p contains %zu entries:\n", m_dynamic_address.as_ptr(), num_dynamic_sections);
|
||||
VERBOSE("%s", builder.to_string().characters());
|
||||
}
|
||||
|
||||
void DynamicObject::parse()
|
||||
{
|
||||
for_each_dynamic_entry([&](const DynamicEntry& entry) {
|
||||
switch (entry.tag()) {
|
||||
case DT_INIT:
|
||||
m_init_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
|
||||
break;
|
||||
case DT_FINI:
|
||||
m_fini_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
|
||||
break;
|
||||
case DT_INIT_ARRAY:
|
||||
m_init_array_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
|
||||
break;
|
||||
case DT_INIT_ARRAYSZ:
|
||||
m_init_array_size = entry.val();
|
||||
break;
|
||||
case DT_FINI_ARRAY:
|
||||
m_fini_array_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
|
||||
break;
|
||||
case DT_FINI_ARRAYSZ:
|
||||
m_fini_array_size = entry.val();
|
||||
break;
|
||||
case DT_HASH:
|
||||
// Use SYSV hash only if GNU hash is not available
|
||||
if (m_hash_type == HashType::SYSV) {
|
||||
m_hash_table_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
|
||||
}
|
||||
break;
|
||||
case DT_GNU_HASH:
|
||||
m_hash_type = HashType::GNU;
|
||||
m_hash_table_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
|
||||
break;
|
||||
case DT_SYMTAB:
|
||||
m_symbol_table_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
|
||||
break;
|
||||
case DT_STRTAB:
|
||||
m_string_table_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
|
||||
break;
|
||||
case DT_STRSZ:
|
||||
m_size_of_string_table = entry.val();
|
||||
break;
|
||||
case DT_SYMENT:
|
||||
m_size_of_symbol_table_entry = entry.val();
|
||||
break;
|
||||
case DT_PLTGOT:
|
||||
m_procedure_linkage_table_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
|
||||
break;
|
||||
case DT_PLTRELSZ:
|
||||
m_size_of_plt_relocation_entry_list = entry.val();
|
||||
break;
|
||||
case DT_PLTREL:
|
||||
m_procedure_linkage_table_relocation_type = entry.val();
|
||||
ASSERT(m_procedure_linkage_table_relocation_type & (DT_REL | DT_RELA));
|
||||
break;
|
||||
case DT_JMPREL:
|
||||
m_plt_relocation_offset_location = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
|
||||
break;
|
||||
case DT_RELA:
|
||||
case DT_REL:
|
||||
m_relocation_table_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
|
||||
break;
|
||||
case DT_RELASZ:
|
||||
case DT_RELSZ:
|
||||
m_size_of_relocation_table = entry.val();
|
||||
break;
|
||||
case DT_RELAENT:
|
||||
case DT_RELENT:
|
||||
m_size_of_relocation_entry = entry.val();
|
||||
break;
|
||||
case DT_RELACOUNT:
|
||||
case DT_RELCOUNT:
|
||||
m_number_of_relocations = entry.val();
|
||||
break;
|
||||
case DT_FLAGS:
|
||||
m_dt_flags = entry.val();
|
||||
break;
|
||||
case DT_TEXTREL:
|
||||
m_dt_flags |= DF_TEXTREL; // This tag seems to exist for legacy reasons only?
|
||||
break;
|
||||
case DT_SONAME:
|
||||
m_soname_index = entry.val();
|
||||
m_has_soname = true;
|
||||
break;
|
||||
case DT_DEBUG:
|
||||
break;
|
||||
case DT_FLAGS_1:
|
||||
break;
|
||||
case DT_NEEDED:
|
||||
// We handle these in for_each_needed_library
|
||||
break;
|
||||
default:
|
||||
dbgprintf("DynamicObject: DYNAMIC tag handling not implemented for DT_%s\n", name_for_dtag(entry.tag()));
|
||||
printf("DynamicObject: DYNAMIC tag handling not implemented for DT_%s\n", name_for_dtag(entry.tag()));
|
||||
ASSERT_NOT_REACHED(); // FIXME: Maybe just break out here and return false?
|
||||
break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
if (!m_size_of_relocation_entry) {
|
||||
// TODO: FIXME, this shouldn't be hardcoded
|
||||
// The reason we need this here is that for some reason, when there only PLT relocations, the compiler
|
||||
// doesn't insert a 'PLTRELSZ' entry to the dynamic section
|
||||
m_size_of_relocation_entry = sizeof(Elf32_Rel);
|
||||
}
|
||||
|
||||
auto hash_section_address = hash_section().address().as_ptr();
|
||||
// TODO: consider base address - it might not be zero
|
||||
auto num_hash_chains = ((u32*)hash_section_address)[1];
|
||||
m_symbol_count = num_hash_chains;
|
||||
}
|
||||
|
||||
const DynamicObject::Relocation DynamicObject::RelocationSection::relocation(unsigned index) const
|
||||
{
|
||||
ASSERT(index < entry_count());
|
||||
unsigned offset_in_section = index * entry_size();
|
||||
auto relocation_address = (Elf32_Rel*)address().offset(offset_in_section).as_ptr();
|
||||
return Relocation(m_dynamic, *relocation_address, offset_in_section);
|
||||
}
|
||||
|
||||
const DynamicObject::Relocation DynamicObject::RelocationSection::relocation_at_offset(unsigned offset) const
|
||||
{
|
||||
ASSERT(offset <= (m_section_size_bytes - m_entry_size));
|
||||
auto relocation_address = (Elf32_Rel*)address().offset(offset).as_ptr();
|
||||
return Relocation(m_dynamic, *relocation_address, offset);
|
||||
}
|
||||
|
||||
const DynamicObject::Symbol DynamicObject::symbol(unsigned index) const
|
||||
{
|
||||
auto symbol_section = Section(*this, m_symbol_table_offset, (m_symbol_count * m_size_of_symbol_table_entry), m_size_of_symbol_table_entry, "DT_SYMTAB");
|
||||
auto symbol_entry = (Elf32_Sym*)symbol_section.address().offset(index * symbol_section.entry_size()).as_ptr();
|
||||
return Symbol(*this, index, *symbol_entry);
|
||||
}
|
||||
|
||||
const DynamicObject::Section DynamicObject::init_section() const
|
||||
{
|
||||
return Section(*this, m_init_offset, sizeof(void (*)()), sizeof(void (*)()), "DT_INIT");
|
||||
}
|
||||
|
||||
const DynamicObject::Section DynamicObject::fini_section() const
|
||||
{
|
||||
return Section(*this, m_fini_offset, sizeof(void (*)()), sizeof(void (*)()), "DT_FINI");
|
||||
}
|
||||
|
||||
const DynamicObject::Section DynamicObject::init_array_section() const
|
||||
{
|
||||
return Section(*this, m_init_array_offset, m_init_array_size, sizeof(void (*)()), "DT_INIT_ARRAY");
|
||||
}
|
||||
|
||||
const DynamicObject::Section DynamicObject::fini_array_section() const
|
||||
{
|
||||
return Section(*this, m_fini_array_offset, m_fini_array_size, sizeof(void (*)()), "DT_FINI_ARRAY");
|
||||
}
|
||||
|
||||
const DynamicObject::HashSection DynamicObject::hash_section() const
|
||||
{
|
||||
const char* section_name = m_hash_type == HashType::SYSV ? "DT_HASH" : "DT_GNU_HASH";
|
||||
return HashSection(Section(*this, m_hash_table_offset, 0, 0, section_name), m_hash_type);
|
||||
}
|
||||
|
||||
const DynamicObject::RelocationSection DynamicObject::relocation_section() const
|
||||
{
|
||||
return RelocationSection(Section(*this, m_relocation_table_offset, m_size_of_relocation_table, m_size_of_relocation_entry, "DT_REL"));
|
||||
}
|
||||
|
||||
const DynamicObject::RelocationSection DynamicObject::plt_relocation_section() const
|
||||
{
|
||||
return RelocationSection(Section(*this, m_plt_relocation_offset_location, m_size_of_plt_relocation_entry_list, m_size_of_relocation_entry, "DT_JMPREL"));
|
||||
}
|
||||
|
||||
u32 DynamicObject::HashSection::calculate_elf_hash(const char* name) const
|
||||
{
|
||||
// SYSV ELF hash algorithm
|
||||
// Note that the GNU HASH algorithm has less collisions
|
||||
|
||||
uint32_t hash = 0;
|
||||
|
||||
while (*name != '\0') {
|
||||
hash = hash << 4;
|
||||
hash += *name;
|
||||
name++;
|
||||
|
||||
const uint32_t top_nibble_of_hash = hash & 0xF0000000U;
|
||||
hash ^= top_nibble_of_hash >> 24;
|
||||
hash &= ~top_nibble_of_hash;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
u32 DynamicObject::HashSection::calculate_gnu_hash(const char* name) const
|
||||
{
|
||||
// GNU ELF hash algorithm
|
||||
u32 hash = 5381;
|
||||
|
||||
for (; *name != '\0'; ++name) {
|
||||
hash = hash * 33 + *name;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
const DynamicObject::Symbol DynamicObject::HashSection::lookup_symbol(const char* name) const
|
||||
{
|
||||
return (this->*(m_lookup_function))(name);
|
||||
}
|
||||
|
||||
const DynamicObject::Symbol DynamicObject::HashSection::lookup_elf_symbol(const char* name) const
|
||||
{
|
||||
u32 hash_value = calculate_elf_hash(name);
|
||||
|
||||
u32* hash_table_begin = (u32*)address().as_ptr();
|
||||
|
||||
size_t num_buckets = hash_table_begin[0];
|
||||
|
||||
// This is here for completeness, but, since we're using the fact that every chain
|
||||
// will end at chain 0 (which means 'not found'), we don't need to check num_chains.
|
||||
// Interestingly, num_chains is required to be num_symbols
|
||||
|
||||
// size_t num_chains = hash_table_begin[1];
|
||||
|
||||
u32* buckets = &hash_table_begin[2];
|
||||
u32* chains = &buckets[num_buckets];
|
||||
|
||||
for (u32 i = buckets[hash_value % num_buckets]; i; i = chains[i]) {
|
||||
auto symbol = m_dynamic.symbol(i);
|
||||
if (strcmp(name, symbol.name()) == 0) {
|
||||
VERBOSE("Returning SYSV dynamic symbol with index %u for %s: %p\n", i, symbol.name(), symbol.address().as_ptr());
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
return Symbol::create_undefined(m_dynamic);
|
||||
}
|
||||
|
||||
const DynamicObject::Symbol DynamicObject::HashSection::lookup_gnu_symbol(const char* name) const
|
||||
{
|
||||
// Algorithm reference: https://ent-voy.blogspot.com/2011/02/
|
||||
// TODO: Handle 64bit bloomwords for ELF_CLASS64
|
||||
using BloomWord = u32;
|
||||
constexpr size_t bloom_word_size = sizeof(BloomWord) * 8;
|
||||
|
||||
const u32* hash_table_begin = (u32*)address().as_ptr();
|
||||
|
||||
const size_t num_buckets = hash_table_begin[0];
|
||||
const size_t num_omitted_symbols = hash_table_begin[1];
|
||||
const u32 num_maskwords = hash_table_begin[2];
|
||||
// This works because num_maskwords is required to be a power of 2
|
||||
const u32 num_maskwords_bitmask = num_maskwords - 1;
|
||||
const u32 shift2 = hash_table_begin[3];
|
||||
|
||||
const BloomWord* bloom_words = &hash_table_begin[4];
|
||||
const u32* const buckets = &bloom_words[num_maskwords];
|
||||
const u32* const chains = &buckets[num_buckets];
|
||||
|
||||
BloomWord hash1 = calculate_gnu_hash(name);
|
||||
BloomWord hash2 = hash1 >> shift2;
|
||||
const BloomWord bitmask = (1 << (hash1 % bloom_word_size)) | (1 << (hash2 % bloom_word_size));
|
||||
|
||||
if ((bloom_words[(hash1 / bloom_word_size) & num_maskwords_bitmask] & bitmask) != bitmask) {
|
||||
return Symbol::create_undefined(m_dynamic);
|
||||
}
|
||||
|
||||
size_t current_sym = buckets[hash1 % num_buckets];
|
||||
if (current_sym == 0) {
|
||||
return Symbol::create_undefined(m_dynamic);
|
||||
}
|
||||
const u32* current_chain = &chains[current_sym - num_omitted_symbols];
|
||||
|
||||
for (hash1 &= ~1;; ++current_sym) {
|
||||
hash2 = *(current_chain++);
|
||||
const auto symbol = m_dynamic.symbol(current_sym);
|
||||
if ((hash1 == (hash2 & ~1)) && strcmp(name, symbol.name()) == 0) {
|
||||
VERBOSE("Returning GNU dynamic symbol with index %zu for %s: %p\n", current_sym, symbol.name(), symbol.address().as_ptr());
|
||||
return symbol;
|
||||
}
|
||||
if (hash2 & 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Symbol::create_undefined(m_dynamic);
|
||||
}
|
||||
|
||||
const char* DynamicObject::symbol_string_table_string(Elf32_Word index) const
|
||||
{
|
||||
return (const char*)base_address().offset(m_string_table_offset + index).as_ptr();
|
||||
}
|
||||
|
||||
DynamicObject::InitializationFunction DynamicObject::init_section_function() const
|
||||
{
|
||||
ASSERT(has_init_section());
|
||||
return (InitializationFunction)init_section().address().as_ptr();
|
||||
}
|
||||
|
||||
static const char* name_for_dtag(Elf32_Sword d_tag)
|
||||
{
|
||||
switch (d_tag) {
|
||||
case DT_NULL:
|
||||
return "NULL"; /* marks end of _DYNAMIC array */
|
||||
case DT_NEEDED:
|
||||
return "NEEDED"; /* string table offset of needed lib */
|
||||
case DT_PLTRELSZ:
|
||||
return "PLTRELSZ"; /* size of relocation entries in PLT */
|
||||
case DT_PLTGOT:
|
||||
return "PLTGOT"; /* address PLT/GOT */
|
||||
case DT_HASH:
|
||||
return "HASH"; /* address of symbol hash table */
|
||||
case DT_STRTAB:
|
||||
return "STRTAB"; /* address of string table */
|
||||
case DT_SYMTAB:
|
||||
return "SYMTAB"; /* address of symbol table */
|
||||
case DT_RELA:
|
||||
return "RELA"; /* address of relocation table */
|
||||
case DT_RELASZ:
|
||||
return "RELASZ"; /* size of relocation table */
|
||||
case DT_RELAENT:
|
||||
return "RELAENT"; /* size of relocation entry */
|
||||
case DT_STRSZ:
|
||||
return "STRSZ"; /* size of string table */
|
||||
case DT_SYMENT:
|
||||
return "SYMENT"; /* size of symbol table entry */
|
||||
case DT_INIT:
|
||||
return "INIT"; /* address of initialization func. */
|
||||
case DT_FINI:
|
||||
return "FINI"; /* address of termination function */
|
||||
case DT_SONAME:
|
||||
return "SONAME"; /* string table offset of shared obj */
|
||||
case DT_RPATH:
|
||||
return "RPATH"; /* string table offset of library search path */
|
||||
case DT_SYMBOLIC:
|
||||
return "SYMBOLIC"; /* start sym search in shared obj. */
|
||||
case DT_REL:
|
||||
return "REL"; /* address of rel. tbl. w addends */
|
||||
case DT_RELSZ:
|
||||
return "RELSZ"; /* size of DT_REL relocation table */
|
||||
case DT_RELENT:
|
||||
return "RELENT"; /* size of DT_REL relocation entry */
|
||||
case DT_PLTREL:
|
||||
return "PLTREL"; /* PLT referenced relocation entry */
|
||||
case DT_DEBUG:
|
||||
return "DEBUG"; /* bugger */
|
||||
case DT_TEXTREL:
|
||||
return "TEXTREL"; /* Allow rel. mod. to unwritable seg */
|
||||
case DT_JMPREL:
|
||||
return "JMPREL"; /* add. of PLT's relocation entries */
|
||||
case DT_BIND_NOW:
|
||||
return "BIND_NOW"; /* Bind now regardless of env setting */
|
||||
case DT_INIT_ARRAY:
|
||||
return "INIT_ARRAY"; /* address of array of init func */
|
||||
case DT_FINI_ARRAY:
|
||||
return "FINI_ARRAY"; /* address of array of term func */
|
||||
case DT_INIT_ARRAYSZ:
|
||||
return "INIT_ARRAYSZ"; /* size of array of init func */
|
||||
case DT_FINI_ARRAYSZ:
|
||||
return "FINI_ARRAYSZ"; /* size of array of term func */
|
||||
case DT_RUNPATH:
|
||||
return "RUNPATH"; /* strtab offset of lib search path */
|
||||
case DT_FLAGS:
|
||||
return "FLAGS"; /* Set of DF_* flags */
|
||||
case DT_ENCODING:
|
||||
return "ENCODING"; /* further DT_* follow encoding rules */
|
||||
case DT_PREINIT_ARRAY:
|
||||
return "PREINIT_ARRAY"; /* address of array of preinit func */
|
||||
case DT_PREINIT_ARRAYSZ:
|
||||
return "PREINIT_ARRAYSZ"; /* size of array of preinit func */
|
||||
case DT_LOOS:
|
||||
return "LOOS"; /* reserved range for OS */
|
||||
case DT_HIOS:
|
||||
return "HIOS"; /* specific dynamic array tags */
|
||||
case DT_LOPROC:
|
||||
return "LOPROC"; /* reserved range for processor */
|
||||
case DT_HIPROC:
|
||||
return "HIPROC"; /* specific dynamic array tags */
|
||||
case DT_GNU_HASH:
|
||||
return "GNU_HASH"; /* address of GNU hash table */
|
||||
case DT_RELACOUNT:
|
||||
return "RELACOUNT"; /* if present, number of RELATIVE */
|
||||
case DT_RELCOUNT:
|
||||
return "RELCOUNT"; /* relocs, which must come first */
|
||||
case DT_FLAGS_1:
|
||||
return "FLAGS_1";
|
||||
default:
|
||||
return "??";
|
||||
}
|
||||
}
|
||||
|
||||
DynamicObject::SymbolLookupResult DynamicObject::lookup_symbol(const char* name) const
|
||||
{
|
||||
auto res = hash_section().lookup_symbol(name);
|
||||
if (res.is_undefined())
|
||||
return {};
|
||||
return SymbolLookupResult { true, res.value(), (FlatPtr)res.address().as_ptr(), res.bind(), this };
|
||||
}
|
||||
|
||||
NonnullRefPtr<DynamicObject> DynamicObject::construct(VirtualAddress base_address, VirtualAddress dynamic_section_address)
|
||||
{
|
||||
return adopt(*new DynamicObject(base_address, dynamic_section_address));
|
||||
}
|
||||
|
||||
// offset is in PLT relocation table
|
||||
Elf32_Addr DynamicObject::patch_plt_entry(u32 relocation_offset)
|
||||
{
|
||||
auto relocation = plt_relocation_section().relocation_at_offset(relocation_offset);
|
||||
|
||||
ASSERT(relocation.type() == R_386_JMP_SLOT);
|
||||
|
||||
auto sym = relocation.symbol();
|
||||
|
||||
u8* relocation_address = relocation.address().as_ptr();
|
||||
auto res = lookup_symbol(sym);
|
||||
|
||||
if (!res.found) {
|
||||
dbgln("did not find symbol: {} ", sym.name());
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
u32 symbol_location = res.address;
|
||||
|
||||
VERBOSE("DynamicLoader: Jump slot relocation: putting %s (%p) into PLT at %p\n", sym.name(), (void*)symbol_location, (void*)relocation_address);
|
||||
|
||||
*(u32*)relocation_address = symbol_location;
|
||||
|
||||
return symbol_location;
|
||||
}
|
||||
|
||||
DynamicObject::SymbolLookupResult DynamicObject::lookup_symbol(const ELF::DynamicObject::Symbol& symbol) const
|
||||
{
|
||||
VERBOSE("looking up symbol: %s\n", symbol.name());
|
||||
if (symbol.is_undefined() || symbol.bind() == STB_WEAK)
|
||||
return DynamicLinker::lookup_global_symbol(symbol.name());
|
||||
|
||||
if (!symbol.is_undefined()) {
|
||||
VERBOSE("symbol is defined in its object\n");
|
||||
return { true, symbol.value(), (FlatPtr)symbol.address().as_ptr(), symbol.bind(), &symbol.object() };
|
||||
}
|
||||
return DynamicLinker::lookup_global_symbol(symbol.name());
|
||||
}
|
||||
|
||||
} // end namespace ELF
|
414
Userland/Libraries/LibELF/DynamicObject.h
Normal file
414
Userland/Libraries/LibELF/DynamicObject.h
Normal file
|
@ -0,0 +1,414 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020, Andrew Kaster <andrewdkaster@gmail.com>
|
||||
* Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <Kernel/VirtualAddress.h>
|
||||
#include <LibELF/exec_elf.h>
|
||||
|
||||
namespace ELF {
|
||||
|
||||
class DynamicObject : public RefCounted<DynamicObject> {
|
||||
public:
|
||||
static NonnullRefPtr<DynamicObject> construct(VirtualAddress base_address, VirtualAddress dynamic_section_address);
|
||||
|
||||
~DynamicObject();
|
||||
void dump() const;
|
||||
|
||||
class DynamicEntry;
|
||||
class Section;
|
||||
class RelocationSection;
|
||||
class Symbol;
|
||||
class Relocation;
|
||||
class HashSection;
|
||||
|
||||
class DynamicEntry {
|
||||
public:
|
||||
DynamicEntry(const Elf32_Dyn& dyn)
|
||||
: m_dyn(dyn)
|
||||
{
|
||||
}
|
||||
|
||||
~DynamicEntry() { }
|
||||
|
||||
Elf32_Sword tag() const { return m_dyn.d_tag; }
|
||||
Elf32_Addr ptr() const { return m_dyn.d_un.d_ptr; }
|
||||
Elf32_Word val() const { return m_dyn.d_un.d_val; }
|
||||
|
||||
private:
|
||||
const Elf32_Dyn& m_dyn;
|
||||
};
|
||||
|
||||
class Symbol {
|
||||
public:
|
||||
Symbol(const DynamicObject& dynamic, unsigned index, const Elf32_Sym& sym)
|
||||
: m_dynamic(dynamic)
|
||||
, m_sym(sym)
|
||||
, m_index(index)
|
||||
{
|
||||
if (section_index() == 0)
|
||||
m_is_undefined = true;
|
||||
}
|
||||
|
||||
Symbol(const Symbol& other)
|
||||
: m_dynamic(other.m_dynamic)
|
||||
, m_sym(other.m_sym)
|
||||
, m_index(other.m_index)
|
||||
, m_is_undefined(other.m_is_undefined)
|
||||
{
|
||||
}
|
||||
|
||||
static Symbol create_undefined(const DynamicObject& dynamic)
|
||||
{
|
||||
auto s = Symbol(dynamic, 0, {});
|
||||
s.m_is_undefined = true;
|
||||
return s;
|
||||
}
|
||||
|
||||
~Symbol() { }
|
||||
|
||||
const char* name() const { return m_dynamic.symbol_string_table_string(m_sym.st_name); }
|
||||
unsigned section_index() const { return m_sym.st_shndx; }
|
||||
unsigned value() const { return m_sym.st_value; }
|
||||
unsigned size() const { return m_sym.st_size; }
|
||||
unsigned index() const { return m_index; }
|
||||
unsigned type() const { return ELF32_ST_TYPE(m_sym.st_info); }
|
||||
unsigned bind() const { return ELF32_ST_BIND(m_sym.st_info); }
|
||||
|
||||
bool is_undefined() const
|
||||
{
|
||||
return m_is_undefined;
|
||||
}
|
||||
VirtualAddress address() const
|
||||
{
|
||||
if (m_dynamic.elf_is_dynamic())
|
||||
return m_dynamic.base_address().offset(value());
|
||||
return VirtualAddress { value() };
|
||||
}
|
||||
const DynamicObject& object() const { return m_dynamic; }
|
||||
|
||||
private:
|
||||
const DynamicObject& m_dynamic;
|
||||
const Elf32_Sym& m_sym;
|
||||
const unsigned m_index;
|
||||
bool m_is_undefined { false };
|
||||
};
|
||||
|
||||
class Section {
|
||||
public:
|
||||
Section(const DynamicObject& dynamic, unsigned section_offset, unsigned section_size_bytes, unsigned entry_size, const char* name)
|
||||
: m_dynamic(dynamic)
|
||||
, m_section_offset(section_offset)
|
||||
, m_section_size_bytes(section_size_bytes)
|
||||
, m_entry_size(entry_size)
|
||||
, m_name(name)
|
||||
{
|
||||
}
|
||||
~Section() { }
|
||||
|
||||
const char* name() const { return m_name; }
|
||||
unsigned offset() const { return m_section_offset; }
|
||||
unsigned size() const { return m_section_size_bytes; }
|
||||
unsigned entry_size() const { return m_entry_size; }
|
||||
unsigned entry_count() const
|
||||
{
|
||||
return !entry_size() ? 0 : size() / entry_size();
|
||||
}
|
||||
VirtualAddress address() const
|
||||
{
|
||||
return m_dynamic.base_address().offset(m_section_offset);
|
||||
}
|
||||
|
||||
protected:
|
||||
friend class RelocationSection;
|
||||
friend class HashSection;
|
||||
const DynamicObject& m_dynamic;
|
||||
unsigned m_section_offset;
|
||||
unsigned m_section_size_bytes;
|
||||
unsigned m_entry_size;
|
||||
const char* m_name { nullptr };
|
||||
};
|
||||
|
||||
class RelocationSection : public Section {
|
||||
public:
|
||||
RelocationSection(const Section& section)
|
||||
: Section(section.m_dynamic, section.m_section_offset, section.m_section_size_bytes, section.m_entry_size, section.m_name)
|
||||
{
|
||||
}
|
||||
unsigned relocation_count() const { return entry_count(); }
|
||||
const Relocation relocation(unsigned index) const;
|
||||
const Relocation relocation_at_offset(unsigned offset) const;
|
||||
template<typename F>
|
||||
void for_each_relocation(F) const;
|
||||
};
|
||||
|
||||
class Relocation {
|
||||
public:
|
||||
Relocation(const DynamicObject& dynamic, const Elf32_Rel& rel, unsigned offset_in_section)
|
||||
: m_dynamic(dynamic)
|
||||
, m_rel(rel)
|
||||
, m_offset_in_section(offset_in_section)
|
||||
{
|
||||
}
|
||||
|
||||
~Relocation() { }
|
||||
|
||||
unsigned offset_in_section() const { return m_offset_in_section; }
|
||||
unsigned offset() const { return m_rel.r_offset; }
|
||||
unsigned type() const { return ELF32_R_TYPE(m_rel.r_info); }
|
||||
unsigned symbol_index() const { return ELF32_R_SYM(m_rel.r_info); }
|
||||
const Symbol symbol() const { return m_dynamic.symbol(symbol_index()); }
|
||||
VirtualAddress address() const
|
||||
{
|
||||
if (m_dynamic.elf_is_dynamic())
|
||||
return m_dynamic.base_address().offset(offset());
|
||||
return VirtualAddress { offset() };
|
||||
}
|
||||
|
||||
private:
|
||||
const DynamicObject& m_dynamic;
|
||||
const Elf32_Rel& m_rel;
|
||||
const unsigned m_offset_in_section;
|
||||
};
|
||||
|
||||
enum class HashType {
|
||||
SYSV,
|
||||
GNU
|
||||
};
|
||||
|
||||
class HashSection : public Section {
|
||||
public:
|
||||
HashSection(const Section& section, HashType hash_type)
|
||||
: Section(section.m_dynamic, section.m_section_offset, section.m_section_size_bytes, section.m_entry_size, section.m_name)
|
||||
{
|
||||
switch (hash_type) {
|
||||
case HashType::SYSV:
|
||||
m_lookup_function = &HashSection::lookup_elf_symbol;
|
||||
break;
|
||||
case HashType::GNU:
|
||||
m_lookup_function = &HashSection::lookup_gnu_symbol;
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const Symbol lookup_symbol(const char*) const;
|
||||
|
||||
private:
|
||||
u32 calculate_elf_hash(const char* name) const;
|
||||
u32 calculate_gnu_hash(const char* name) const;
|
||||
|
||||
const DynamicObject::Symbol lookup_elf_symbol(const char* name) const;
|
||||
const DynamicObject::Symbol lookup_gnu_symbol(const char* name) const;
|
||||
|
||||
typedef const DynamicObject::Symbol (HashSection::*LookupFunction)(const char*) const;
|
||||
LookupFunction m_lookup_function;
|
||||
};
|
||||
|
||||
unsigned symbol_count() const { return m_symbol_count; }
|
||||
|
||||
const Symbol symbol(unsigned) const;
|
||||
|
||||
typedef void (*InitializationFunction)();
|
||||
|
||||
bool has_init_section() const { return m_init_offset != 0; }
|
||||
bool has_init_array_section() const { return m_init_array_offset != 0; }
|
||||
const Section init_section() const;
|
||||
InitializationFunction init_section_function() const;
|
||||
const Section fini_section() const;
|
||||
const Section init_array_section() const;
|
||||
const Section fini_array_section() const;
|
||||
|
||||
const HashSection hash_section() const;
|
||||
|
||||
const RelocationSection relocation_section() const;
|
||||
const RelocationSection plt_relocation_section() const;
|
||||
|
||||
bool should_process_origin() const { return m_dt_flags & DF_ORIGIN; }
|
||||
bool requires_symbolic_symbol_resolution() const { return m_dt_flags & DF_SYMBOLIC; }
|
||||
// Text relocations meaning: we need to edit the .text section which is normally mapped PROT_READ
|
||||
bool has_text_relocations() const { return m_dt_flags & DF_TEXTREL; }
|
||||
bool must_bind_now() const { return m_dt_flags & DF_BIND_NOW; }
|
||||
bool has_static_thread_local_storage() const { return m_dt_flags & DF_STATIC_TLS; }
|
||||
|
||||
VirtualAddress plt_got_base_address() const { return m_base_address.offset(m_procedure_linkage_table_offset); }
|
||||
VirtualAddress base_address() const { return m_base_address; }
|
||||
|
||||
const char* soname() const { return m_has_soname ? symbol_string_table_string(m_soname_index) : nullptr; }
|
||||
|
||||
Optional<FlatPtr> tls_offset() const { return m_tls_offset; }
|
||||
Optional<FlatPtr> tls_size() const { return m_tls_size; }
|
||||
void set_tls_offset(FlatPtr offset) { m_tls_offset = offset; }
|
||||
void set_tls_size(FlatPtr size) { m_tls_size = size; }
|
||||
|
||||
template<typename F>
|
||||
void for_each_needed_library(F) const;
|
||||
|
||||
template<typename F>
|
||||
void for_each_initialization_array_function(F f) const;
|
||||
|
||||
struct SymbolLookupResult {
|
||||
bool found { false };
|
||||
FlatPtr value { 0 };
|
||||
FlatPtr address { 0 };
|
||||
unsigned bind { STB_LOCAL };
|
||||
const ELF::DynamicObject* dynamic_object { nullptr }; // The object in which the symbol is defined
|
||||
};
|
||||
SymbolLookupResult lookup_symbol(const char* name) const;
|
||||
|
||||
// Will be called from _fixup_plt_entry, as part of the PLT trampoline
|
||||
Elf32_Addr patch_plt_entry(u32 relocation_offset);
|
||||
|
||||
SymbolLookupResult lookup_symbol(const ELF::DynamicObject::Symbol& symbol) const;
|
||||
using SymbolLookupFunction = DynamicObject::SymbolLookupResult (*)(const char*);
|
||||
SymbolLookupFunction m_global_symbol_lookup_func { nullptr };
|
||||
|
||||
bool elf_is_dynamic() const { return m_is_elf_dynamic; }
|
||||
|
||||
private:
|
||||
explicit DynamicObject(VirtualAddress base_address, VirtualAddress dynamic_section_address);
|
||||
|
||||
const char* symbol_string_table_string(Elf32_Word) const;
|
||||
void parse();
|
||||
|
||||
template<typename F>
|
||||
void for_each_symbol(F) const;
|
||||
|
||||
template<typename F>
|
||||
void for_each_dynamic_entry(F) const;
|
||||
|
||||
VirtualAddress m_base_address;
|
||||
VirtualAddress m_dynamic_address;
|
||||
VirtualAddress m_elf_base_address;
|
||||
|
||||
unsigned m_symbol_count { 0 };
|
||||
|
||||
// Begin Section information collected from DT_* entries
|
||||
FlatPtr m_init_offset { 0 };
|
||||
FlatPtr m_fini_offset { 0 };
|
||||
|
||||
FlatPtr m_init_array_offset { 0 };
|
||||
size_t m_init_array_size { 0 };
|
||||
FlatPtr m_fini_array_offset { 0 };
|
||||
size_t m_fini_array_size { 0 };
|
||||
|
||||
FlatPtr m_hash_table_offset { 0 };
|
||||
HashType m_hash_type { HashType::SYSV };
|
||||
|
||||
FlatPtr m_string_table_offset { 0 };
|
||||
size_t m_size_of_string_table { 0 };
|
||||
FlatPtr m_symbol_table_offset { 0 };
|
||||
size_t m_size_of_symbol_table_entry { 0 };
|
||||
|
||||
Elf32_Sword m_procedure_linkage_table_relocation_type { -1 };
|
||||
FlatPtr m_plt_relocation_offset_location { 0 }; // offset of PLT relocations, at end of relocations
|
||||
size_t m_size_of_plt_relocation_entry_list { 0 };
|
||||
FlatPtr m_procedure_linkage_table_offset { 0 };
|
||||
|
||||
// NOTE: We'll only ever either RELA or REL entries, not both (thank god)
|
||||
// NOTE: The x86 ABI will only ever genrerate REL entries.
|
||||
size_t m_number_of_relocations { 0 };
|
||||
size_t m_size_of_relocation_entry { 0 };
|
||||
size_t m_size_of_relocation_table { 0 };
|
||||
FlatPtr m_relocation_table_offset { 0 };
|
||||
bool m_is_elf_dynamic { false };
|
||||
|
||||
// DT_FLAGS
|
||||
Elf32_Word m_dt_flags { 0 };
|
||||
|
||||
bool m_has_soname { false };
|
||||
Elf32_Word m_soname_index { 0 }; // Index into dynstr table for SONAME
|
||||
|
||||
Optional<FlatPtr> m_tls_offset;
|
||||
Optional<FlatPtr> m_tls_size;
|
||||
// End Section information from DT_* entries
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
inline void DynamicObject::RelocationSection::for_each_relocation(F func) const
|
||||
{
|
||||
for (unsigned i = 0; i < relocation_count(); ++i) {
|
||||
const auto reloc = relocation(i);
|
||||
if (reloc.type() == 0)
|
||||
continue;
|
||||
if (func(reloc) == IterationDecision::Break)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline void DynamicObject::for_each_symbol(F func) const
|
||||
{
|
||||
for (unsigned i = 0; i < symbol_count(); ++i) {
|
||||
if (func(symbol(i)) == IterationDecision::Break)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline void DynamicObject::for_each_dynamic_entry(F func) const
|
||||
{
|
||||
auto* dyns = reinterpret_cast<const Elf32_Dyn*>(m_dynamic_address.as_ptr());
|
||||
for (unsigned i = 0;; ++i) {
|
||||
auto&& dyn = DynamicEntry(dyns[i]);
|
||||
if (dyn.tag() == DT_NULL)
|
||||
break;
|
||||
if (func(dyn) == IterationDecision::Break)
|
||||
break;
|
||||
}
|
||||
}
|
||||
template<typename F>
|
||||
inline void DynamicObject::for_each_needed_library(F func) const
|
||||
{
|
||||
for_each_dynamic_entry([func, this](auto entry) {
|
||||
if (entry.tag() != DT_NEEDED)
|
||||
return IterationDecision::Continue;
|
||||
Elf32_Word offset = entry.val();
|
||||
const char* name = (const char*)(m_base_address.offset(m_string_table_offset).offset(offset)).as_ptr();
|
||||
if (func(StringView(name)) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void DynamicObject::for_each_initialization_array_function(F f) const
|
||||
{
|
||||
if (!has_init_array_section())
|
||||
return;
|
||||
FlatPtr init_array = (FlatPtr)init_array_section().address().as_ptr();
|
||||
for (size_t i = 0; i < (m_init_array_size / sizeof(void*)); ++i) {
|
||||
InitializationFunction current = ((InitializationFunction*)(init_array))[i];
|
||||
f(current);
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace ELF
|
419
Userland/Libraries/LibELF/Image.cpp
Normal file
419
Userland/Libraries/LibELF/Image.cpp
Normal file
|
@ -0,0 +1,419 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <AK/Demangle.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/QuickSort.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <LibELF/Image.h>
|
||||
#include <LibELF/Validation.h>
|
||||
|
||||
//#define ELF_IMAGE_DEBUG
|
||||
|
||||
namespace ELF {
|
||||
|
||||
Image::Image(ReadonlyBytes bytes, bool verbose_logging)
|
||||
: m_buffer(bytes.data())
|
||||
, m_size(bytes.size())
|
||||
, m_verbose_logging(verbose_logging)
|
||||
{
|
||||
parse();
|
||||
}
|
||||
|
||||
Image::Image(const u8* buffer, size_t size, bool verbose_logging)
|
||||
: Image(ReadonlyBytes { buffer, size }, verbose_logging)
|
||||
{
|
||||
}
|
||||
|
||||
Image::~Image()
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef ELF_IMAGE_DEBUG
|
||||
static const char* object_file_type_to_string(Elf32_Half type)
|
||||
{
|
||||
switch (type) {
|
||||
case ET_NONE:
|
||||
return "None";
|
||||
case ET_REL:
|
||||
return "Relocatable";
|
||||
case ET_EXEC:
|
||||
return "Executable";
|
||||
case ET_DYN:
|
||||
return "Shared object";
|
||||
case ET_CORE:
|
||||
return "Core";
|
||||
default:
|
||||
return "(?)";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
StringView Image::section_index_to_string(unsigned index) const
|
||||
{
|
||||
ASSERT(m_valid);
|
||||
if (index == SHN_UNDEF)
|
||||
return "Undefined";
|
||||
if (index >= SHN_LORESERVE)
|
||||
return "Reserved";
|
||||
return section(index).name();
|
||||
}
|
||||
|
||||
unsigned Image::symbol_count() const
|
||||
{
|
||||
ASSERT(m_valid);
|
||||
if (!section_count())
|
||||
return 0;
|
||||
return section(m_symbol_table_section_index).entry_count();
|
||||
}
|
||||
|
||||
void Image::dump() const
|
||||
{
|
||||
#ifdef ELF_IMAGE_DEBUG
|
||||
dbgln("ELF::Image({:p}) {{", this);
|
||||
dbgln(" is_valid: {}", is_valid());
|
||||
|
||||
if (!is_valid()) {
|
||||
dbgln("}}");
|
||||
return;
|
||||
}
|
||||
|
||||
dbgln(" type: {}", object_file_type_to_string(header().e_type));
|
||||
dbgln(" machine: {}", header().e_machine);
|
||||
dbgln(" entry: {:x}", header().e_entry);
|
||||
dbgln(" shoff: {}", header().e_shoff);
|
||||
dbgln(" shnum: {}", header().e_shnum);
|
||||
dbgln(" phoff: {}", header().e_phoff);
|
||||
dbgln(" phnum: {}", header().e_phnum);
|
||||
dbgln(" shstrndx: {}", header().e_shstrndx);
|
||||
|
||||
for_each_program_header([&](const ProgramHeader& program_header) {
|
||||
dbgln(" Program Header {}: {{", program_header.index());
|
||||
dbgln(" type: {:x}", program_header.type());
|
||||
dbgln(" offset: {:x}", program_header.offset());
|
||||
dbgln(" flags: {:x}", program_header.flags());
|
||||
dbgln(" }}");
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
for (unsigned i = 0; i < header().e_shnum; ++i) {
|
||||
auto& section = this->section(i);
|
||||
dbgln(" Section {}: {{", i);
|
||||
dbgln(" name: {}", section.name());
|
||||
dbgln(" type: {:x}", section.type());
|
||||
dbgln(" offset: {:x}", section.offset());
|
||||
dbgln(" size: {}", section.size());
|
||||
dbgln(" ");
|
||||
dbgln(" }}");
|
||||
}
|
||||
|
||||
dbgln("Symbol count: {} (table is {})", symbol_count(), m_symbol_table_section_index);
|
||||
for (unsigned i = 1; i < symbol_count(); ++i) {
|
||||
auto& sym = symbol(i);
|
||||
dbgln("Symbol @{}:", i);
|
||||
dbgln(" Name: {}", sym.name());
|
||||
dbgln(" In section: {}", section_index_to_string(sym.section_index()));
|
||||
dbgln(" Value: {}", sym.value());
|
||||
dbgln(" Size: {}", sym.size());
|
||||
}
|
||||
|
||||
dbgln("}}");
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned Image::section_count() const
|
||||
{
|
||||
ASSERT(m_valid);
|
||||
return header().e_shnum;
|
||||
}
|
||||
|
||||
unsigned Image::program_header_count() const
|
||||
{
|
||||
ASSERT(m_valid);
|
||||
return header().e_phnum;
|
||||
}
|
||||
|
||||
bool Image::parse()
|
||||
{
|
||||
if (m_size < sizeof(Elf32_Ehdr) || !validate_elf_header(header(), m_size, m_verbose_logging)) {
|
||||
if (m_verbose_logging)
|
||||
dbgln("ELF::Image::parse(): ELF Header not valid");
|
||||
return m_valid = false;
|
||||
}
|
||||
|
||||
if (!validate_program_headers(header(), m_size, m_buffer, m_size, nullptr, m_verbose_logging)) {
|
||||
if (m_verbose_logging)
|
||||
dbgln("ELF::Image::parse(): ELF Program Headers not valid");
|
||||
return m_valid = false;
|
||||
}
|
||||
|
||||
m_valid = true;
|
||||
|
||||
// First locate the string tables.
|
||||
for (unsigned i = 0; i < section_count(); ++i) {
|
||||
auto& sh = section_header(i);
|
||||
if (sh.sh_type == SHT_SYMTAB) {
|
||||
if (m_symbol_table_section_index && m_symbol_table_section_index != i)
|
||||
return m_valid = false;
|
||||
m_symbol_table_section_index = i;
|
||||
}
|
||||
if (sh.sh_type == SHT_STRTAB && i != header().e_shstrndx) {
|
||||
if (section_header_table_string(sh.sh_name) == ELF_STRTAB)
|
||||
m_string_table_section_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Then create a name-to-index map.
|
||||
for (unsigned i = 0; i < section_count(); ++i) {
|
||||
auto& section = this->section(i);
|
||||
m_sections.set(section.name(), move(i));
|
||||
}
|
||||
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
StringView Image::table_string(unsigned table_index, unsigned offset) const
|
||||
{
|
||||
ASSERT(m_valid);
|
||||
auto& sh = section_header(table_index);
|
||||
if (sh.sh_type != SHT_STRTAB)
|
||||
return nullptr;
|
||||
size_t computed_offset = sh.sh_offset + offset;
|
||||
if (computed_offset >= m_size) {
|
||||
if (m_verbose_logging)
|
||||
dbgln("SHENANIGANS! Image::table_string() computed offset outside image.");
|
||||
return {};
|
||||
}
|
||||
size_t max_length = m_size - computed_offset;
|
||||
size_t length = strnlen(raw_data(sh.sh_offset + offset), max_length);
|
||||
return { raw_data(sh.sh_offset + offset), length };
|
||||
}
|
||||
|
||||
StringView Image::section_header_table_string(unsigned offset) const
|
||||
{
|
||||
ASSERT(m_valid);
|
||||
return table_string(header().e_shstrndx, offset);
|
||||
}
|
||||
|
||||
StringView Image::table_string(unsigned offset) const
|
||||
{
|
||||
ASSERT(m_valid);
|
||||
return table_string(m_string_table_section_index, offset);
|
||||
}
|
||||
|
||||
const char* Image::raw_data(unsigned offset) const
|
||||
{
|
||||
ASSERT(offset < m_size); // Callers must check indices into raw_data()'s result are also in bounds.
|
||||
return reinterpret_cast<const char*>(m_buffer) + offset;
|
||||
}
|
||||
|
||||
const Elf32_Ehdr& Image::header() const
|
||||
{
|
||||
ASSERT(m_size >= sizeof(Elf32_Ehdr));
|
||||
return *reinterpret_cast<const Elf32_Ehdr*>(raw_data(0));
|
||||
}
|
||||
|
||||
const Elf32_Phdr& Image::program_header_internal(unsigned index) const
|
||||
{
|
||||
ASSERT(m_valid);
|
||||
ASSERT(index < header().e_phnum);
|
||||
return *reinterpret_cast<const Elf32_Phdr*>(raw_data(header().e_phoff + (index * sizeof(Elf32_Phdr))));
|
||||
}
|
||||
|
||||
const Elf32_Shdr& Image::section_header(unsigned index) const
|
||||
{
|
||||
ASSERT(m_valid);
|
||||
ASSERT(index < header().e_shnum);
|
||||
return *reinterpret_cast<const Elf32_Shdr*>(raw_data(header().e_shoff + (index * header().e_shentsize)));
|
||||
}
|
||||
|
||||
const Image::Symbol Image::symbol(unsigned index) const
|
||||
{
|
||||
ASSERT(m_valid);
|
||||
ASSERT(index < symbol_count());
|
||||
auto* raw_syms = reinterpret_cast<const Elf32_Sym*>(raw_data(section(m_symbol_table_section_index).offset()));
|
||||
return Symbol(*this, index, raw_syms[index]);
|
||||
}
|
||||
|
||||
const Image::Section Image::section(unsigned index) const
|
||||
{
|
||||
ASSERT(m_valid);
|
||||
ASSERT(index < section_count());
|
||||
return Section(*this, index);
|
||||
}
|
||||
|
||||
const Image::ProgramHeader Image::program_header(unsigned index) const
|
||||
{
|
||||
ASSERT(m_valid);
|
||||
ASSERT(index < program_header_count());
|
||||
return ProgramHeader(*this, index);
|
||||
}
|
||||
|
||||
FlatPtr Image::program_header_table_offset() const
|
||||
{
|
||||
return header().e_phoff;
|
||||
}
|
||||
|
||||
const Image::Relocation Image::RelocationSection::relocation(unsigned index) const
|
||||
{
|
||||
ASSERT(index < relocation_count());
|
||||
auto* rels = reinterpret_cast<const Elf32_Rel*>(m_image.raw_data(offset()));
|
||||
return Relocation(m_image, rels[index]);
|
||||
}
|
||||
|
||||
const Image::RelocationSection Image::Section::relocations() const
|
||||
{
|
||||
StringBuilder builder;
|
||||
builder.append(".rel");
|
||||
builder.append(name());
|
||||
|
||||
auto relocation_section = m_image.lookup_section(builder.to_string());
|
||||
if (relocation_section.type() != SHT_REL)
|
||||
return static_cast<const RelocationSection>(m_image.section(0));
|
||||
|
||||
#ifdef ELF_IMAGE_DEBUG
|
||||
dbgln("Found relocations for {} in {}", name(), relocation_section.name());
|
||||
#endif
|
||||
return static_cast<const RelocationSection>(relocation_section);
|
||||
}
|
||||
|
||||
const Image::Section Image::lookup_section(const String& name) const
|
||||
{
|
||||
ASSERT(m_valid);
|
||||
if (auto it = m_sections.find(name); it != m_sections.end())
|
||||
return section((*it).value);
|
||||
return section(0);
|
||||
}
|
||||
|
||||
StringView Image::Symbol::raw_data() const
|
||||
{
|
||||
auto& section = this->section();
|
||||
return { section.raw_data() + (value() - section.address()), size() };
|
||||
}
|
||||
|
||||
Optional<Image::Symbol> Image::find_demangled_function(const String& name) const
|
||||
{
|
||||
Optional<Image::Symbol> found;
|
||||
for_each_symbol([&](const Image::Symbol symbol) {
|
||||
if (symbol.type() != STT_FUNC)
|
||||
return IterationDecision::Continue;
|
||||
if (symbol.is_undefined())
|
||||
return IterationDecision::Continue;
|
||||
auto demangled = demangle(symbol.name());
|
||||
auto index_of_paren = demangled.index_of("(");
|
||||
if (index_of_paren.has_value()) {
|
||||
demangled = demangled.substring(0, index_of_paren.value());
|
||||
}
|
||||
if (demangled != name)
|
||||
return IterationDecision::Continue;
|
||||
found = symbol;
|
||||
return IterationDecision::Break;
|
||||
});
|
||||
return found;
|
||||
}
|
||||
|
||||
Optional<Image::Symbol> Image::find_symbol(u32 address, u32* out_offset) const
|
||||
{
|
||||
auto symbol_count = this->symbol_count();
|
||||
if (!symbol_count)
|
||||
return {};
|
||||
|
||||
SortedSymbol* sorted_symbols = nullptr;
|
||||
if (m_sorted_symbols.is_empty()) {
|
||||
m_sorted_symbols.ensure_capacity(symbol_count);
|
||||
for_each_symbol([this](auto& symbol) {
|
||||
m_sorted_symbols.append({ symbol.value(), symbol.name(), {}, symbol });
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
quick_sort(m_sorted_symbols, [](auto& a, auto& b) {
|
||||
return a.address < b.address;
|
||||
});
|
||||
}
|
||||
sorted_symbols = m_sorted_symbols.data();
|
||||
|
||||
for (size_t i = 0; i < symbol_count; ++i) {
|
||||
if (sorted_symbols[i].address > address) {
|
||||
if (i == 0)
|
||||
return {};
|
||||
auto& symbol = sorted_symbols[i - 1];
|
||||
if (out_offset)
|
||||
*out_offset = address - symbol.address;
|
||||
return symbol.symbol;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
String Image::symbolicate(u32 address, u32* out_offset) const
|
||||
{
|
||||
auto symbol_count = this->symbol_count();
|
||||
if (!symbol_count) {
|
||||
if (out_offset)
|
||||
*out_offset = 0;
|
||||
return "??";
|
||||
}
|
||||
SortedSymbol* sorted_symbols = nullptr;
|
||||
|
||||
if (m_sorted_symbols.is_empty()) {
|
||||
m_sorted_symbols.ensure_capacity(symbol_count);
|
||||
for_each_symbol([this](auto& symbol) {
|
||||
m_sorted_symbols.append({ symbol.value(), symbol.name(), {}, symbol });
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
quick_sort(m_sorted_symbols, [](auto& a, auto& b) {
|
||||
return a.address < b.address;
|
||||
});
|
||||
}
|
||||
sorted_symbols = m_sorted_symbols.data();
|
||||
|
||||
for (size_t i = 0; i < symbol_count; ++i) {
|
||||
if (sorted_symbols[i].address > address) {
|
||||
if (i == 0) {
|
||||
if (out_offset)
|
||||
*out_offset = 0;
|
||||
return "!!";
|
||||
}
|
||||
auto& symbol = sorted_symbols[i - 1];
|
||||
|
||||
auto& demangled_name = symbol.demangled_name;
|
||||
if (demangled_name.is_null()) {
|
||||
demangled_name = demangle(symbol.name);
|
||||
}
|
||||
|
||||
if (out_offset) {
|
||||
*out_offset = address - symbol.address;
|
||||
return demangled_name;
|
||||
}
|
||||
return String::format("%s +0x%x", demangled_name.characters(), address - symbol.address);
|
||||
}
|
||||
}
|
||||
if (out_offset)
|
||||
*out_offset = 0;
|
||||
return "??";
|
||||
}
|
||||
|
||||
} // end namespace ELF
|
299
Userland/Libraries/LibELF/Image.h
Normal file
299
Userland/Libraries/LibELF/Image.h
Normal file
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <Kernel/VirtualAddress.h>
|
||||
#include <LibELF/exec_elf.h>
|
||||
|
||||
namespace ELF {
|
||||
|
||||
class Image {
|
||||
public:
|
||||
explicit Image(ReadonlyBytes, bool verbose_logging = true);
|
||||
explicit Image(const u8*, size_t, bool verbose_logging = true);
|
||||
|
||||
~Image();
|
||||
void dump() const;
|
||||
bool is_valid() const { return m_valid; }
|
||||
bool parse();
|
||||
|
||||
bool is_within_image(const void* address, size_t size) const
|
||||
{
|
||||
if (address < m_buffer)
|
||||
return false;
|
||||
if (((const u8*)address + size) > m_buffer + m_size)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
class Section;
|
||||
class RelocationSection;
|
||||
class Symbol;
|
||||
class Relocation;
|
||||
|
||||
class Symbol {
|
||||
public:
|
||||
Symbol(const Image& image, unsigned index, const Elf32_Sym& sym)
|
||||
: m_image(image)
|
||||
, m_sym(sym)
|
||||
, m_index(index)
|
||||
{
|
||||
}
|
||||
|
||||
~Symbol() { }
|
||||
|
||||
StringView name() const { return m_image.table_string(m_sym.st_name); }
|
||||
unsigned section_index() const { return m_sym.st_shndx; }
|
||||
unsigned value() const { return m_sym.st_value; }
|
||||
unsigned size() const { return m_sym.st_size; }
|
||||
unsigned index() const { return m_index; }
|
||||
unsigned type() const { return ELF32_ST_TYPE(m_sym.st_info); }
|
||||
unsigned bind() const { return ELF32_ST_BIND(m_sym.st_info); }
|
||||
const Section section() const { return m_image.section(section_index()); }
|
||||
bool is_undefined() const { return section_index() == 0; }
|
||||
StringView raw_data() const;
|
||||
|
||||
private:
|
||||
const Image& m_image;
|
||||
const Elf32_Sym& m_sym;
|
||||
const unsigned m_index;
|
||||
};
|
||||
|
||||
class ProgramHeader {
|
||||
public:
|
||||
ProgramHeader(const Image& image, unsigned program_header_index)
|
||||
: m_image(image)
|
||||
, m_program_header(image.program_header_internal(program_header_index))
|
||||
, m_program_header_index(program_header_index)
|
||||
{
|
||||
}
|
||||
~ProgramHeader() { }
|
||||
|
||||
unsigned index() const { return m_program_header_index; }
|
||||
u32 type() const { return m_program_header.p_type; }
|
||||
u32 flags() const { return m_program_header.p_flags; }
|
||||
u32 offset() const { return m_program_header.p_offset; }
|
||||
VirtualAddress vaddr() const { return VirtualAddress(m_program_header.p_vaddr); }
|
||||
u32 size_in_memory() const { return m_program_header.p_memsz; }
|
||||
u32 size_in_image() const { return m_program_header.p_filesz; }
|
||||
u32 alignment() const { return m_program_header.p_align; }
|
||||
bool is_readable() const { return flags() & PF_R; }
|
||||
bool is_writable() const { return flags() & PF_W; }
|
||||
bool is_executable() const { return flags() & PF_X; }
|
||||
const char* raw_data() const { return m_image.raw_data(m_program_header.p_offset); }
|
||||
Elf32_Phdr raw_header() const { return m_program_header; }
|
||||
|
||||
private:
|
||||
const Image& m_image;
|
||||
const Elf32_Phdr& m_program_header;
|
||||
unsigned m_program_header_index { 0 };
|
||||
};
|
||||
|
||||
class Section {
|
||||
public:
|
||||
Section(const Image& image, unsigned sectionIndex)
|
||||
: m_image(image)
|
||||
, m_section_header(image.section_header(sectionIndex))
|
||||
, m_section_index(sectionIndex)
|
||||
{
|
||||
}
|
||||
~Section() { }
|
||||
|
||||
StringView name() const { return m_image.section_header_table_string(m_section_header.sh_name); }
|
||||
unsigned type() const { return m_section_header.sh_type; }
|
||||
unsigned offset() const { return m_section_header.sh_offset; }
|
||||
unsigned size() const { return m_section_header.sh_size; }
|
||||
unsigned entry_size() const { return m_section_header.sh_entsize; }
|
||||
unsigned entry_count() const { return !entry_size() ? 0 : size() / entry_size(); }
|
||||
u32 address() const { return m_section_header.sh_addr; }
|
||||
const char* raw_data() const { return m_image.raw_data(m_section_header.sh_offset); }
|
||||
ReadonlyBytes bytes() const { return { raw_data(), size() }; }
|
||||
bool is_undefined() const { return m_section_index == SHN_UNDEF; }
|
||||
const RelocationSection relocations() const;
|
||||
u32 flags() const { return m_section_header.sh_flags; }
|
||||
bool is_writable() const { return flags() & SHF_WRITE; }
|
||||
bool is_executable() const { return flags() & PF_X; }
|
||||
|
||||
protected:
|
||||
friend class RelocationSection;
|
||||
const Image& m_image;
|
||||
const Elf32_Shdr& m_section_header;
|
||||
unsigned m_section_index;
|
||||
};
|
||||
|
||||
class RelocationSection : public Section {
|
||||
public:
|
||||
RelocationSection(const Section& section)
|
||||
: Section(section.m_image, section.m_section_index)
|
||||
{
|
||||
}
|
||||
unsigned relocation_count() const { return entry_count(); }
|
||||
const Relocation relocation(unsigned index) const;
|
||||
template<typename F>
|
||||
void for_each_relocation(F) const;
|
||||
};
|
||||
|
||||
class Relocation {
|
||||
public:
|
||||
Relocation(const Image& image, const Elf32_Rel& rel)
|
||||
: m_image(image)
|
||||
, m_rel(rel)
|
||||
{
|
||||
}
|
||||
|
||||
~Relocation() { }
|
||||
|
||||
unsigned offset() const { return m_rel.r_offset; }
|
||||
unsigned type() const { return ELF32_R_TYPE(m_rel.r_info); }
|
||||
unsigned symbol_index() const { return ELF32_R_SYM(m_rel.r_info); }
|
||||
const Symbol symbol() const { return m_image.symbol(symbol_index()); }
|
||||
|
||||
private:
|
||||
const Image& m_image;
|
||||
const Elf32_Rel& m_rel;
|
||||
};
|
||||
|
||||
unsigned symbol_count() const;
|
||||
unsigned section_count() const;
|
||||
unsigned program_header_count() const;
|
||||
|
||||
const Symbol symbol(unsigned) const;
|
||||
const Section section(unsigned) const;
|
||||
const ProgramHeader program_header(unsigned const) const;
|
||||
FlatPtr program_header_table_offset() const;
|
||||
|
||||
template<typename F>
|
||||
void for_each_section(F) const;
|
||||
template<typename F>
|
||||
void for_each_section_of_type(unsigned, F) const;
|
||||
template<typename F>
|
||||
void for_each_symbol(F) const;
|
||||
template<typename F>
|
||||
void for_each_program_header(F) const;
|
||||
|
||||
// NOTE: Returns section(0) if section with name is not found.
|
||||
// FIXME: I don't love this API.
|
||||
const Section lookup_section(const String& name) const;
|
||||
|
||||
bool is_executable() const { return header().e_type == ET_EXEC; }
|
||||
bool is_relocatable() const { return header().e_type == ET_REL; }
|
||||
bool is_dynamic() const { return header().e_type == ET_DYN; }
|
||||
|
||||
VirtualAddress entry() const { return VirtualAddress(header().e_entry); }
|
||||
FlatPtr base_address() const { return (FlatPtr)m_buffer; }
|
||||
size_t size() const { return m_size; }
|
||||
|
||||
Optional<Symbol> find_demangled_function(const String& name) const;
|
||||
|
||||
bool has_symbols() const { return symbol_count(); }
|
||||
String symbolicate(u32 address, u32* offset = nullptr) const;
|
||||
Optional<Image::Symbol> find_symbol(u32 address, u32* offset = nullptr) const;
|
||||
|
||||
private:
|
||||
const char* raw_data(unsigned offset) const;
|
||||
const Elf32_Ehdr& header() const;
|
||||
const Elf32_Shdr& section_header(unsigned) const;
|
||||
const Elf32_Phdr& program_header_internal(unsigned) const;
|
||||
StringView table_string(unsigned offset) const;
|
||||
StringView section_header_table_string(unsigned offset) const;
|
||||
StringView section_index_to_string(unsigned index) const;
|
||||
StringView table_string(unsigned table_index, unsigned offset) const;
|
||||
|
||||
const u8* m_buffer { nullptr };
|
||||
size_t m_size { 0 };
|
||||
bool m_verbose_logging { true };
|
||||
HashMap<String, unsigned> m_sections;
|
||||
bool m_valid { false };
|
||||
unsigned m_symbol_table_section_index { 0 };
|
||||
unsigned m_string_table_section_index { 0 };
|
||||
|
||||
struct SortedSymbol {
|
||||
u32 address;
|
||||
StringView name;
|
||||
String demangled_name;
|
||||
Optional<Image::Symbol> symbol;
|
||||
};
|
||||
|
||||
mutable Vector<SortedSymbol> m_sorted_symbols;
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
inline void Image::for_each_section(F func) const
|
||||
{
|
||||
auto section_count = this->section_count();
|
||||
for (unsigned i = 0; i < section_count; ++i)
|
||||
func(section(i));
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline void Image::for_each_section_of_type(unsigned type, F func) const
|
||||
{
|
||||
auto section_count = this->section_count();
|
||||
for (unsigned i = 0; i < section_count; ++i) {
|
||||
auto& section = this->section(i);
|
||||
if (section.type() == type) {
|
||||
if (func(section) == IterationDecision::Break)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline void Image::RelocationSection::for_each_relocation(F func) const
|
||||
{
|
||||
auto relocation_count = this->relocation_count();
|
||||
for (unsigned i = 0; i < relocation_count; ++i) {
|
||||
if (func(relocation(i)) == IterationDecision::Break)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline void Image::for_each_symbol(F func) const
|
||||
{
|
||||
auto symbol_count = this->symbol_count();
|
||||
for (unsigned i = 0; i < symbol_count; ++i) {
|
||||
if (func(symbol(i)) == IterationDecision::Break)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline void Image::for_each_program_header(F func) const
|
||||
{
|
||||
auto program_header_count = this->program_header_count();
|
||||
for (unsigned i = 0; i < program_header_count; ++i) {
|
||||
if (func(program_header(i)) == IterationDecision::Break)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace ELF
|
267
Userland/Libraries/LibELF/Validation.cpp
Normal file
267
Userland/Libraries/LibELF/Validation.cpp
Normal file
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andrew Kaster <andrewdkaster@gmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibELF/Validation.h>
|
||||
#include <LibELF/exec_elf.h>
|
||||
|
||||
namespace ELF {
|
||||
|
||||
bool validate_elf_header(const Elf32_Ehdr& elf_header, size_t file_size, bool verbose)
|
||||
{
|
||||
if (!IS_ELF(elf_header)) {
|
||||
if (verbose)
|
||||
dbgln("File is not an ELF file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ELFCLASS32 != elf_header.e_ident[EI_CLASS]) {
|
||||
if (verbose)
|
||||
dbgln("File is not a 32 bit ELF file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ELFDATA2LSB != elf_header.e_ident[EI_DATA]) {
|
||||
if (verbose)
|
||||
dbgln("File is not a little endian ELF file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (EV_CURRENT != elf_header.e_ident[EI_VERSION]) {
|
||||
if (verbose)
|
||||
dbgln("File has unrecognized ELF version ({}), expected ({})!", elf_header.e_ident[EI_VERSION], EV_CURRENT);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ELFOSABI_SYSV != elf_header.e_ident[EI_OSABI]) {
|
||||
if (verbose)
|
||||
dbgln("File has unknown OS ABI ({}), expected SYSV(0)!", elf_header.e_ident[EI_OSABI]);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (0 != elf_header.e_ident[EI_ABIVERSION]) {
|
||||
if (verbose)
|
||||
dbgln("File has unknown SYSV ABI version ({})!", elf_header.e_ident[EI_ABIVERSION]);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (EM_386 != elf_header.e_machine) {
|
||||
if (verbose)
|
||||
dbgln("File has unknown machine ({}), expected i386 (3)!", elf_header.e_machine);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ET_EXEC != elf_header.e_type && ET_DYN != elf_header.e_type && ET_REL != elf_header.e_type && ET_CORE != elf_header.e_type) {
|
||||
if (verbose)
|
||||
dbgln("File has unloadable ELF type ({}), expected REL (1), EXEC (2), DYN (3) or CORE(4)!", elf_header.e_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (EV_CURRENT != elf_header.e_version) {
|
||||
if (verbose)
|
||||
dbgln("File has unrecognized ELF version ({}), expected ({})!", elf_header.e_version, EV_CURRENT);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sizeof(Elf32_Ehdr) != elf_header.e_ehsize) {
|
||||
if (verbose)
|
||||
dbgln("File has incorrect ELF header size..? ({}), expected ({})!", elf_header.e_ehsize, sizeof(Elf32_Ehdr));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elf_header.e_phoff < elf_header.e_ehsize || (elf_header.e_shnum != SHN_UNDEF && elf_header.e_shoff < elf_header.e_ehsize)) {
|
||||
if (verbose) {
|
||||
dbgln("SHENANIGANS! program header offset ({}) or section header offset ({}) overlap with ELF header!",
|
||||
elf_header.e_phoff, elf_header.e_shoff);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elf_header.e_phoff > file_size || elf_header.e_shoff > file_size) {
|
||||
if (verbose) {
|
||||
dbgln("SHENANIGANS! program header offset ({}) or section header offset ({}) are past the end of the file!",
|
||||
elf_header.e_phoff, elf_header.e_shoff);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elf_header.e_phnum == 0 && elf_header.e_phoff != 0) {
|
||||
if (verbose)
|
||||
dbgln("SHENANIGANS! File has no program headers, but it does have a program header offset ({})!", elf_header.e_phoff);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elf_header.e_phnum != 0 && elf_header.e_phoff != elf_header.e_ehsize) {
|
||||
if (verbose) {
|
||||
dbgln("File does not have program headers directly after the ELF header? program header offset ({}), expected ({}).",
|
||||
elf_header.e_phoff, elf_header.e_ehsize);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (0 != elf_header.e_flags) {
|
||||
if (verbose)
|
||||
dbgln("File has incorrect ELF header flags...? ({}), expected ({}).", elf_header.e_flags, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (0 != elf_header.e_phnum && sizeof(Elf32_Phdr) != elf_header.e_phentsize) {
|
||||
if (verbose)
|
||||
dbgln("File has incorrect program header size..? ({}), expected ({}).", elf_header.e_phentsize, sizeof(Elf32_Phdr));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sizeof(Elf32_Shdr) != elf_header.e_shentsize) {
|
||||
if (verbose)
|
||||
dbgln("File has incorrect section header size..? ({}), expected ({}).", elf_header.e_shentsize, sizeof(Elf32_Shdr));
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t end_of_last_program_header = elf_header.e_phoff + (elf_header.e_phnum * elf_header.e_phentsize);
|
||||
if (end_of_last_program_header > file_size) {
|
||||
if (verbose)
|
||||
dbgln("SHENANIGANS! End of last program header ({}) is past the end of the file!", end_of_last_program_header);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elf_header.e_shoff != SHN_UNDEF && elf_header.e_shoff < end_of_last_program_header) {
|
||||
if (verbose) {
|
||||
dbgln("SHENANIGANS! Section header table begins at file offset {}, which is within program headers [ {} - {} ]!",
|
||||
elf_header.e_shoff, elf_header.e_phoff, end_of_last_program_header);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t end_of_last_section_header = elf_header.e_shoff + (elf_header.e_shnum * elf_header.e_shentsize);
|
||||
if (end_of_last_section_header > file_size) {
|
||||
if (verbose)
|
||||
dbgln("SHENANIGANS! End of last section header ({}) is past the end of the file!", end_of_last_section_header);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elf_header.e_shstrndx != SHN_UNDEF && elf_header.e_shstrndx >= elf_header.e_shnum) {
|
||||
if (verbose)
|
||||
dbgln("SHENANIGANS! Section header string table index ({}) is not a valid index given we have {} section headers!", elf_header.e_shstrndx, elf_header.e_shnum);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validate_program_headers(const Elf32_Ehdr& elf_header, size_t file_size, const u8* buffer, size_t buffer_size, String* interpreter_path, bool verbose)
|
||||
{
|
||||
// Can we actually parse all the program headers in the given buffer?
|
||||
size_t end_of_last_program_header = elf_header.e_phoff + (elf_header.e_phnum * elf_header.e_phentsize);
|
||||
if (end_of_last_program_header > buffer_size) {
|
||||
if (verbose)
|
||||
dbgln("Unable to parse program headers from buffer, buffer too small! Buffer size: {}, End of program headers {}",
|
||||
buffer_size, end_of_last_program_header);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file_size < buffer_size) {
|
||||
dbgln("We somehow read more from a file than was in the file in the first place!");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
size_t num_program_headers = elf_header.e_phnum;
|
||||
auto program_header_begin = (const Elf32_Phdr*)&(buffer[elf_header.e_phoff]);
|
||||
|
||||
for (size_t header_index = 0; header_index < num_program_headers; ++header_index) {
|
||||
auto& program_header = program_header_begin[header_index];
|
||||
|
||||
if (program_header.p_filesz > program_header.p_memsz) {
|
||||
if (verbose)
|
||||
dbgln("Program header ({}) has p_filesz ({}) larger than p_memsz ({})", header_index, program_header.p_filesz, program_header.p_memsz);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (program_header.p_memsz <= 0 && (program_header.p_type == PT_TLS || program_header.p_type == PT_LOAD)) {
|
||||
if (verbose)
|
||||
dbgln("Program header ({}) has invalid size in memory ({})", header_index, program_header.p_memsz);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (program_header.p_type == PT_LOAD && program_header.p_align != PAGE_SIZE) {
|
||||
if (elf_header.e_type != ET_CORE) {
|
||||
if (verbose)
|
||||
dbgln("Program header ({}) with p_type PT_LOAD has p_align ({}) not equal to page size ({})", header_index, program_header.p_align, PAGE_SIZE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
switch (program_header.p_type) {
|
||||
case PT_INTERP:
|
||||
// We checked above that file_size was >= buffer size. We only care about buffer size anyway, we're trying to read this!
|
||||
if (program_header.p_offset + program_header.p_filesz > buffer_size) {
|
||||
if (verbose)
|
||||
dbgln("Found PT_INTERP header ({}), but the .interp section was not within the buffer :(", header_index);
|
||||
return false;
|
||||
}
|
||||
if (interpreter_path)
|
||||
*interpreter_path = String((const char*)&buffer[program_header.p_offset], program_header.p_filesz - 1);
|
||||
break;
|
||||
case PT_LOAD:
|
||||
case PT_DYNAMIC:
|
||||
case PT_NOTE:
|
||||
case PT_PHDR:
|
||||
case PT_TLS:
|
||||
if (program_header.p_offset + program_header.p_filesz > file_size) {
|
||||
if (verbose)
|
||||
dbgln("SHENANIGANS! Program header {} segment leaks beyond end of file!", header_index);
|
||||
return false;
|
||||
}
|
||||
if ((program_header.p_flags & PF_X) && (program_header.p_flags & PF_W)) {
|
||||
if (verbose)
|
||||
dbgln("SHENANIGANS! Program header {} segment is marked write and execute", header_index);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case PT_GNU_STACK:
|
||||
if (program_header.p_flags & PF_X) {
|
||||
if (verbose)
|
||||
dbgln("Possible shenanigans! Validating an ELF with executable stack.");
|
||||
}
|
||||
break;
|
||||
case PT_GNU_RELRO:
|
||||
if ((program_header.p_flags & PF_X) && (program_header.p_flags & PF_W)) {
|
||||
if (verbose)
|
||||
dbgln("SHENANIGANS! Program header {} segment is marked write and execute", header_index);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Not handling other program header types in other code so... let's not surprise them
|
||||
if (verbose)
|
||||
dbgln("Found program header ({}) of unrecognized type {}!", header_index, program_header.p_type);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // end namespace ELF
|
36
Userland/Libraries/LibELF/Validation.h
Normal file
36
Userland/Libraries/LibELF/Validation.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andrew Kaster <andrewdkaster@gmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibELF/exec_elf.h>
|
||||
|
||||
namespace ELF {
|
||||
|
||||
bool validate_elf_header(const Elf32_Ehdr& elf_header, size_t file_size, bool verbose = true);
|
||||
bool validate_program_headers(const Elf32_Ehdr& elf_header, size_t file_size, const u8* buffer, size_t buffer_size, String* interpreter_path, bool verbose = true);
|
||||
|
||||
} // end namespace ELF
|
792
Userland/Libraries/LibELF/exec_elf.h
Normal file
792
Userland/Libraries/LibELF/exec_elf.h
Normal file
|
@ -0,0 +1,792 @@
|
|||
/* $OpenBSD: exec_elf.h,v 1.83 2019/01/22 23:23:18 jsg Exp $ */
|
||||
/*
|
||||
* Copyright (c) 1995, 1996 Erik Theisen. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is the ELF ABI header file
|
||||
* formerly known as "elf_abi.h".
|
||||
*/
|
||||
|
||||
#ifndef _SYS_EXEC_ELF_H_
|
||||
#define _SYS_EXEC_ELF_H_
|
||||
|
||||
#include <AK/Types.h>
|
||||
|
||||
typedef uint8_t Elf_Byte;
|
||||
|
||||
typedef uint32_t Elf32_Addr; /* Unsigned program address */
|
||||
typedef uint32_t Elf32_Off; /* Unsigned file offset */
|
||||
typedef int32_t Elf32_Sword; /* Signed large integer */
|
||||
typedef uint32_t Elf32_Word; /* Unsigned large integer */
|
||||
typedef uint16_t Elf32_Half; /* Unsigned medium integer */
|
||||
typedef uint64_t Elf32_Lword;
|
||||
|
||||
typedef uint64_t Elf64_Addr;
|
||||
typedef uint64_t Elf64_Off;
|
||||
typedef int32_t Elf64_Shalf;
|
||||
|
||||
#ifdef __alpha__
|
||||
typedef int64_t Elf64_Sword;
|
||||
typedef uint64_t Elf64_Word;
|
||||
#else
|
||||
typedef int32_t Elf64_Sword;
|
||||
typedef uint32_t Elf64_Word;
|
||||
#endif
|
||||
|
||||
typedef int64_t Elf64_Sxword;
|
||||
typedef uint64_t Elf64_Xword;
|
||||
typedef uint64_t Elf64_Lword;
|
||||
|
||||
typedef uint32_t Elf64_Half;
|
||||
typedef uint16_t Elf64_Quarter;
|
||||
|
||||
/*
|
||||
* e_ident[] identification indexes
|
||||
* See http://www.sco.com/developers/gabi/latest/ch4.eheader.html
|
||||
*/
|
||||
#define EI_MAG0 0 /* file ID */
|
||||
#define EI_MAG1 1 /* file ID */
|
||||
#define EI_MAG2 2 /* file ID */
|
||||
#define EI_MAG3 3 /* file ID */
|
||||
#define EI_CLASS 4 /* file class */
|
||||
#define EI_DATA 5 /* data encoding */
|
||||
#define EI_VERSION 6 /* ELF header version */
|
||||
#define EI_OSABI 7 /* OS/ABI ID */
|
||||
#define EI_ABIVERSION 8 /* ABI version */
|
||||
#define EI_PAD 9 /* start of pad bytes */
|
||||
#define EI_NIDENT 16 /* Gfx::Size of e_ident[] */
|
||||
|
||||
/* e_ident[] magic number */
|
||||
#define ELFMAG0 0x7f /* e_ident[EI_MAG0] */
|
||||
#define ELFMAG1 'E' /* e_ident[EI_MAG1] */
|
||||
#define ELFMAG2 'L' /* e_ident[EI_MAG2] */
|
||||
#define ELFMAG3 'F' /* e_ident[EI_MAG3] */
|
||||
#define ELFMAG "\177ELF" /* magic */
|
||||
#define SELFMAG 4 /* size of magic */
|
||||
|
||||
/* e_ident[] file class */
|
||||
#define ELFCLASSNONE 0 /* invalid */
|
||||
#define ELFCLASS32 1 /* 32-bit objs */
|
||||
#define ELFCLASS64 2 /* 64-bit objs */
|
||||
#define ELFCLASSNUM 3 /* number of classes */
|
||||
|
||||
/* e_ident[] data encoding */
|
||||
#define ELFDATANONE 0 /* invalid */
|
||||
#define ELFDATA2LSB 1 /* Little-Endian */
|
||||
#define ELFDATA2MSB 2 /* Big-Endian */
|
||||
#define ELFDATANUM 3 /* number of data encode defines */
|
||||
|
||||
/* e_ident[] Operating System/ABI */
|
||||
#define ELFOSABI_SYSV 0 /* UNIX System V ABI */
|
||||
#define ELFOSABI_HPUX 1 /* HP-UX operating system */
|
||||
#define ELFOSABI_NETBSD 2 /* NetBSD */
|
||||
#define ELFOSABI_LINUX 3 /* GNU/Linux */
|
||||
#define ELFOSABI_HURD 4 /* GNU/Hurd */
|
||||
#define ELFOSABI_86OPEN 5 /* 86Open common IA32 ABI */
|
||||
#define ELFOSABI_SOLARIS 6 /* Solaris */
|
||||
#define ELFOSABI_MONTEREY 7 /* Monterey */
|
||||
#define ELFOSABI_IRIX 8 /* IRIX */
|
||||
#define ELFOSABI_FREEBSD 9 /* FreeBSD */
|
||||
#define ELFOSABI_TRU64 10 /* TRU64 UNIX */
|
||||
#define ELFOSABI_MODESTO 11 /* Novell Modesto */
|
||||
#define ELFOSABI_OPENBSD 12 /* OpenBSD */
|
||||
#define ELFOSABI_ARM 97 /* ARM */
|
||||
#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */
|
||||
|
||||
/* e_ident */
|
||||
#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && (ehdr).e_ident[EI_MAG1] == ELFMAG1 && (ehdr).e_ident[EI_MAG2] == ELFMAG2 && (ehdr).e_ident[EI_MAG3] == ELFMAG3)
|
||||
|
||||
/* ELF Header */
|
||||
typedef struct elfhdr {
|
||||
unsigned char e_ident[EI_NIDENT]; /* ELF Identification */
|
||||
Elf32_Half e_type; /* object file type */
|
||||
Elf32_Half e_machine; /* machine */
|
||||
Elf32_Word e_version; /* object file version */
|
||||
Elf32_Addr e_entry; /* virtual entry point */
|
||||
Elf32_Off e_phoff; /* program header table offset */
|
||||
Elf32_Off e_shoff; /* section header table offset */
|
||||
Elf32_Word e_flags; /* processor-specific flags */
|
||||
Elf32_Half e_ehsize; /* ELF header size */
|
||||
Elf32_Half e_phentsize; /* program header entry size */
|
||||
Elf32_Half e_phnum; /* number of program header entries */
|
||||
Elf32_Half e_shentsize; /* section header entry size */
|
||||
Elf32_Half e_shnum; /* number of section header entries */
|
||||
Elf32_Half e_shstrndx; /* section header table's "section
|
||||
header string table" entry offset */
|
||||
} Elf32_Ehdr;
|
||||
|
||||
typedef struct {
|
||||
unsigned char e_ident[EI_NIDENT]; /* Id bytes */
|
||||
Elf64_Quarter e_type; /* file type */
|
||||
Elf64_Quarter e_machine; /* machine type */
|
||||
Elf64_Half e_version; /* version number */
|
||||
Elf64_Addr e_entry; /* entry point */
|
||||
Elf64_Off e_phoff; /* Program hdr offset */
|
||||
Elf64_Off e_shoff; /* Section hdr offset */
|
||||
Elf64_Half e_flags; /* Processor flags */
|
||||
Elf64_Quarter e_ehsize; /* sizeof ehdr */
|
||||
Elf64_Quarter e_phentsize; /* Program header entry size */
|
||||
Elf64_Quarter e_phnum; /* Number of program headers */
|
||||
Elf64_Quarter e_shentsize; /* Section header entry size */
|
||||
Elf64_Quarter e_shnum; /* Number of section headers */
|
||||
Elf64_Quarter e_shstrndx; /* String table index */
|
||||
} Elf64_Ehdr;
|
||||
|
||||
/* e_type */
|
||||
#define ET_NONE 0 /* No file type */
|
||||
#define ET_REL 1 /* relocatable file */
|
||||
#define ET_EXEC 2 /* executable file */
|
||||
#define ET_DYN 3 /* shared object file */
|
||||
#define ET_CORE 4 /* core file */
|
||||
#define ET_NUM 5 /* number of types */
|
||||
#define ET_LOPROC 0xff00 /* reserved range for processor */
|
||||
#define ET_HIPROC 0xffff /* specific e_type */
|
||||
|
||||
/* e_machine */
|
||||
#define EM_NONE 0 /* No Machine */
|
||||
#define EM_M32 1 /* AT&T WE 32100 */
|
||||
#define EM_SPARC 2 /* SPARC */
|
||||
#define EM_386 3 /* Intel 80386 */
|
||||
#define EM_68K 4 /* Motorola 68000 */
|
||||
#define EM_88K 5 /* Motorola 88000 */
|
||||
#define EM_486 6 /* Intel 80486 - unused? */
|
||||
#define EM_860 7 /* Intel 80860 */
|
||||
#define EM_MIPS 8 /* MIPS R3000 Big-Endian only */
|
||||
/*
|
||||
* Don't know if EM_MIPS_RS4_BE,
|
||||
* EM_SPARC64, EM_PARISC,
|
||||
* or EM_PPC are ABI compliant
|
||||
*/
|
||||
#define EM_MIPS_RS4_BE 10 /* MIPS R4000 Big-Endian */
|
||||
#define EM_SPARC64 11 /* SPARC v9 64-bit unofficial */
|
||||
#define EM_PARISC 15 /* HPPA */
|
||||
#define EM_SPARC32PLUS 18 /* Enhanced instruction set SPARC */
|
||||
#define EM_PPC 20 /* PowerPC */
|
||||
#define EM_PPC64 21 /* PowerPC 64 */
|
||||
#define EM_ARM 40 /* Advanced RISC Machines ARM */
|
||||
#define EM_ALPHA 41 /* DEC ALPHA */
|
||||
#define EM_SH 42 /* Hitachi/Renesas Super-H */
|
||||
#define EM_SPARCV9 43 /* SPARC version 9 */
|
||||
#define EM_IA_64 50 /* Intel IA-64 Processor */
|
||||
#define EM_AMD64 62 /* AMD64 architecture */
|
||||
#define EM_X86_64 EM_AMD64
|
||||
#define EM_VAX 75 /* DEC VAX */
|
||||
#define EM_AARCH64 183 /* ARM 64-bit architecture (AArch64) */
|
||||
|
||||
/* Non-standard */
|
||||
#define EM_ALPHA_EXP 0x9026 /* DEC ALPHA */
|
||||
#define EM__LAST__ (EM_ALPHA_EXP + 1)
|
||||
|
||||
#define EM_NUM 22 /* number of machine types */
|
||||
|
||||
/* Version */
|
||||
#define EV_NONE 0 /* Invalid */
|
||||
#define EV_CURRENT 1 /* Current */
|
||||
#define EV_NUM 2 /* number of versions */
|
||||
|
||||
/* Magic for e_phnum: get real value from sh_info of first section header */
|
||||
#define PN_XNUM 0xffff
|
||||
|
||||
/* Section Header */
|
||||
typedef struct {
|
||||
Elf32_Word sh_name; /* name - index into section header
|
||||
string table section */
|
||||
Elf32_Word sh_type; /* type */
|
||||
Elf32_Word sh_flags; /* flags */
|
||||
Elf32_Addr sh_addr; /* address */
|
||||
Elf32_Off sh_offset; /* file offset */
|
||||
Elf32_Word sh_size; /* section size */
|
||||
Elf32_Word sh_link; /* section header table index link */
|
||||
Elf32_Word sh_info; /* extra information */
|
||||
Elf32_Word sh_addralign; /* address alignment */
|
||||
Elf32_Word sh_entsize; /* section entry size */
|
||||
} Elf32_Shdr;
|
||||
|
||||
typedef struct {
|
||||
Elf64_Half sh_name; /* section name */
|
||||
Elf64_Half sh_type; /* section type */
|
||||
Elf64_Xword sh_flags; /* section flags */
|
||||
Elf64_Addr sh_addr; /* virtual address */
|
||||
Elf64_Off sh_offset; /* file offset */
|
||||
Elf64_Xword sh_size; /* section size */
|
||||
Elf64_Half sh_link; /* link to another */
|
||||
Elf64_Half sh_info; /* misc info */
|
||||
Elf64_Xword sh_addralign; /* memory alignment */
|
||||
Elf64_Xword sh_entsize; /* table entry size */
|
||||
} Elf64_Shdr;
|
||||
|
||||
/* Special Section Indexes */
|
||||
#define SHN_UNDEF 0 /* undefined */
|
||||
#define SHN_LORESERVE 0xff00 /* lower bounds of reserved indexes */
|
||||
#define SHN_LOPROC 0xff00 /* reserved range for processor */
|
||||
#define SHN_HIPROC 0xff1f /* specific section indexes */
|
||||
#define SHN_ABS 0xfff1 /* absolute value */
|
||||
#define SHN_COMMON 0xfff2 /* common symbol */
|
||||
#define SHN_XINDEX 0xffff /* Escape -- index stored elsewhere. */
|
||||
#define SHN_HIRESERVE 0xffff /* upper bounds of reserved indexes */
|
||||
|
||||
/* sh_type */
|
||||
#define SHT_NULL 0 /* inactive */
|
||||
#define SHT_PROGBITS 1 /* program defined information */
|
||||
#define SHT_SYMTAB 2 /* symbol table section */
|
||||
#define SHT_STRTAB 3 /* string table section */
|
||||
#define SHT_RELA 4 /* relocation section with addends*/
|
||||
#define SHT_HASH 5 /* symbol hash table section */
|
||||
#define SHT_DYNAMIC 6 /* dynamic section */
|
||||
#define SHT_NOTE 7 /* note section */
|
||||
#define SHT_NOBITS 8 /* no space section */
|
||||
#define SHT_REL 9 /* relation section without addends */
|
||||
#define SHT_SHLIB 10 /* reserved - purpose unknown */
|
||||
#define SHT_DYNSYM 11 /* dynamic symbol table section */
|
||||
#define SHT_NUM 12 /* number of section types */
|
||||
#define SHT_INIT_ARRAY 14 /* pointers to init functions */
|
||||
#define SHT_FINI_ARRAY 15 /* pointers to termination functions */
|
||||
#define SHT_PREINIT_ARRAY 16 /* ptrs to funcs called before init */
|
||||
#define SHT_GROUP 17 /* defines a section group */
|
||||
#define SHT_SYMTAB_SHNDX 18 /* Section indexes (see SHN_XINDEX). */
|
||||
#define SHT_LOOS 0x60000000 /* reserved range for OS specific */
|
||||
#define SHT_SUNW_dof 0x6ffffff4 /* used by dtrace */
|
||||
#define SHT_GNU_LIBLIST 0x6ffffff7 /* libraries to be prelinked */
|
||||
#define SHT_SUNW_move 0x6ffffffa /* inf for partially init'ed symbols */
|
||||
#define SHT_SUNW_syminfo 0x6ffffffc /* ad symbol information */
|
||||
#define SHT_SUNW_verdef 0x6ffffffd /* symbol versioning inf */
|
||||
#define SHT_SUNW_verneed 0x6ffffffe /* symbol versioning req */
|
||||
#define SHT_SUNW_versym 0x6fffffff /* symbol versioning table */
|
||||
#define SHT_HIOS 0x6fffffff /* section header types */
|
||||
#define SHT_LOPROC 0x70000000 /* reserved range for processor */
|
||||
#define SHT_HIPROC 0x7fffffff /* specific section header types */
|
||||
#define SHT_LOUSER 0x80000000 /* reserved range for application */
|
||||
#define SHT_HIUSER 0xffffffff /* specific indexes */
|
||||
|
||||
#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table section */
|
||||
|
||||
/* Section names */
|
||||
#define ELF_BSS ".bss" /* uninitialized data */
|
||||
#define ELF_DATA ".data" /* initialized data */
|
||||
#define ELF_CTF ".SUNW_ctf" /* CTF data */
|
||||
#define ELF_DEBUG ".debug" /* debug */
|
||||
#define ELF_DYNAMIC ".dynamic" /* dynamic linking information */
|
||||
#define ELF_DYNSTR ".dynstr" /* dynamic string table */
|
||||
#define ELF_DYNSYM ".dynsym" /* dynamic symbol table */
|
||||
#define ELF_FINI ".fini" /* termination code */
|
||||
#define ELF_GOT ".got" /* global offset table */
|
||||
#define ELF_HASH ".hash" /* symbol hash table */
|
||||
#define ELF_INIT ".init" /* initialization code */
|
||||
#define ELF_REL_DATA ".rel.data" /* relocation data */
|
||||
#define ELF_REL_FINI ".rel.fini" /* relocation termination code */
|
||||
#define ELF_REL_INIT ".rel.init" /* relocation initialization code */
|
||||
#define ELF_REL_DYN ".rel.dyn" /* relocation dynamic link info */
|
||||
#define ELF_REL_RODATA ".rel.rodata" /* relocation read-only data */
|
||||
#define ELF_REL_TEXT ".rel.text" /* relocation code */
|
||||
#define ELF_RODATA ".rodata" /* read-only data */
|
||||
#define ELF_SHSTRTAB ".shstrtab" /* section header string table */
|
||||
#define ELF_STRTAB ".strtab" /* string table */
|
||||
#define ELF_SYMTAB ".symtab" /* symbol table */
|
||||
#define ELF_TEXT ".text" /* code */
|
||||
#define ELF_OPENBSDRANDOMDATA ".openbsd.randomdata" /* constant randomdata */
|
||||
|
||||
/* Section Attribute Flags - sh_flags */
|
||||
#define SHF_WRITE 0x1 /* Writable */
|
||||
#define SHF_ALLOC 0x2 /* occupies memory */
|
||||
#define SHF_EXECINSTR 0x4 /* executable */
|
||||
#define SHF_MERGE 0x10 /* may be merged */
|
||||
#define SHF_STRINGS 0x20 /* contains strings */
|
||||
#define SHF_INFO_LINK 0x40 /* sh_info holds section index */
|
||||
#define SHF_LINK_ORDER 0x80 /* ordering requirements */
|
||||
#define SHF_OS_NONCONFORMING 0x100 /* OS-specific processing required */
|
||||
#define SHF_GROUP 0x200 /* member of section group */
|
||||
#define SHF_TLS 0x400 /* thread local storage */
|
||||
#define SHF_COMPRESSED 0x800 /* contains compressed data */
|
||||
#define SHF_MASKOS 0x0ff00000 /* OS-specific semantics */
|
||||
#define SHF_MASKPROC 0xf0000000 /* reserved bits for processor */
|
||||
/* specific section attributes */
|
||||
|
||||
/* Symbol Table Entry */
|
||||
typedef struct elf32_sym {
|
||||
Elf32_Word st_name; /* name - index into string table */
|
||||
Elf32_Addr st_value; /* symbol value */
|
||||
Elf32_Word st_size; /* symbol size */
|
||||
unsigned char st_info; /* type and binding */
|
||||
unsigned char st_other; /* 0 - no defined meaning */
|
||||
Elf32_Half st_shndx; /* section header index */
|
||||
} Elf32_Sym;
|
||||
|
||||
typedef struct {
|
||||
Elf64_Half st_name; /* Symbol name index in str table */
|
||||
Elf_Byte st_info; /* type / binding attrs */
|
||||
Elf_Byte st_other; /* unused */
|
||||
Elf64_Quarter st_shndx; /* section index of symbol */
|
||||
Elf64_Xword st_value; /* value of symbol */
|
||||
Elf64_Xword st_size; /* size of symbol */
|
||||
} Elf64_Sym;
|
||||
|
||||
/* Symbol table index */
|
||||
#define STN_UNDEF 0 /* undefined */
|
||||
|
||||
/* Extract symbol info - st_info */
|
||||
#define ELF32_ST_BIND(x) ((x) >> 4)
|
||||
#define ELF32_ST_TYPE(x) (((unsigned int)x) & 0xf)
|
||||
#define ELF32_ST_INFO(b, t) (((b) << 4) + ((t)&0xf))
|
||||
|
||||
#define ELF64_ST_BIND(x) ((x) >> 4)
|
||||
#define ELF64_ST_TYPE(x) (((unsigned int)x) & 0xf)
|
||||
#define ELF64_ST_INFO(b, t) (((b) << 4) + ((t)&0xf))
|
||||
|
||||
/* Symbol Binding - ELF32_ST_BIND - st_info */
|
||||
#define STB_LOCAL 0 /* Local symbol */
|
||||
#define STB_GLOBAL 1 /* Global symbol */
|
||||
#define STB_WEAK 2 /* like global - lower precedence */
|
||||
#define STB_NUM 3 /* number of symbol bindings */
|
||||
#define STB_LOPROC 13 /* reserved range for processor */
|
||||
#define STB_HIPROC 15 /* specific symbol bindings */
|
||||
|
||||
/* Symbol type - ELF32_ST_TYPE - st_info */
|
||||
#define STT_NOTYPE 0 /* not specified */
|
||||
#define STT_OBJECT 1 /* data object */
|
||||
#define STT_FUNC 2 /* function */
|
||||
#define STT_SECTION 3 /* section */
|
||||
#define STT_FILE 4 /* file */
|
||||
#define STT_TLS 6 /* thread local storage */
|
||||
#define STT_LOPROC 13 /* reserved range for processor */
|
||||
#define STT_HIPROC 15 /* specific symbol types */
|
||||
|
||||
/* Extract symbol visibility - st_other */
|
||||
#define ELF_ST_VISIBILITY(v) ((v)&0x3)
|
||||
#define ELF32_ST_VISIBILITY ELF_ST_VISIBILITY
|
||||
#define ELF64_ST_VISIBILITY ELF_ST_VISIBILITY
|
||||
|
||||
#define STV_DEFAULT 0 /* Visibility set by binding type */
|
||||
#define STV_INTERNAL 1 /* OS specific version of STV_HIDDEN */
|
||||
#define STV_HIDDEN 2 /* can only be seen inside own .so */
|
||||
#define STV_PROTECTED 3 /* HIDDEN inside, DEFAULT outside */
|
||||
|
||||
/* Relocation entry with implicit addend */
|
||||
typedef struct {
|
||||
Elf32_Addr r_offset; /* offset of relocation */
|
||||
Elf32_Word r_info; /* symbol table index and type */
|
||||
} Elf32_Rel;
|
||||
|
||||
/* Relocation entry with explicit addend */
|
||||
typedef struct {
|
||||
Elf32_Addr r_offset; /* offset of relocation */
|
||||
Elf32_Word r_info; /* symbol table index and type */
|
||||
Elf32_Sword r_addend;
|
||||
} Elf32_Rela;
|
||||
|
||||
/* Extract relocation info - r_info */
|
||||
#define ELF32_R_SYM(i) ((i) >> 8)
|
||||
#define ELF32_R_TYPE(i) ((unsigned char)(i))
|
||||
#define ELF32_R_INFO(s, t) (((s) << 8) + (unsigned char)(t))
|
||||
|
||||
typedef struct {
|
||||
Elf64_Xword r_offset; /* where to do it */
|
||||
Elf64_Xword r_info; /* index & type of relocation */
|
||||
} Elf64_Rel;
|
||||
|
||||
typedef struct {
|
||||
Elf64_Xword r_offset; /* where to do it */
|
||||
Elf64_Xword r_info; /* index & type of relocation */
|
||||
Elf64_Sxword r_addend; /* adjustment value */
|
||||
} Elf64_Rela;
|
||||
|
||||
#define ELF64_R_SYM(info) ((info) >> 32)
|
||||
#define ELF64_R_TYPE(info) ((info)&0xFFFFFFFF)
|
||||
#define ELF64_R_INFO(s, t) (((s) << 32) + (uint32_t)(t))
|
||||
|
||||
#if defined(__mips64__) && defined(__MIPSEL__)
|
||||
/*
|
||||
* The 64-bit MIPS ELF ABI uses a slightly different relocation format
|
||||
* than the regular ELF ABI: the r_info field is split into several
|
||||
* pieces (see gnu/usr.bin/binutils-2.17/include/elf/mips.h for details).
|
||||
*/
|
||||
# undef ELF64_R_SYM
|
||||
# undef ELF64_R_TYPE
|
||||
# undef ELF64_R_INFO
|
||||
# define ELF64_R_TYPE(info) ((uint64_t)swap32((info) >> 32))
|
||||
# define ELF64_R_SYM(info) ((info)&0xFFFFFFFF)
|
||||
# define ELF64_R_INFO(s, t) (((uint64_t)swap32(t) << 32) + (uint32_t)(s))
|
||||
#endif /* __mips64__ && __MIPSEL__ */
|
||||
|
||||
/* Program Header */
|
||||
typedef struct {
|
||||
Elf32_Word p_type; /* segment type */
|
||||
Elf32_Off p_offset; /* segment offset */
|
||||
Elf32_Addr p_vaddr; /* virtual address of segment */
|
||||
Elf32_Addr p_paddr; /* physical address - ignored? */
|
||||
Elf32_Word p_filesz; /* number of bytes in file for seg. */
|
||||
Elf32_Word p_memsz; /* number of bytes in mem. for seg. */
|
||||
Elf32_Word p_flags; /* flags */
|
||||
Elf32_Word p_align; /* memory alignment */
|
||||
} Elf32_Phdr;
|
||||
|
||||
typedef struct {
|
||||
Elf64_Half p_type; /* entry type */
|
||||
Elf64_Half p_flags; /* flags */
|
||||
Elf64_Off p_offset; /* offset */
|
||||
Elf64_Addr p_vaddr; /* virtual address */
|
||||
Elf64_Addr p_paddr; /* physical address */
|
||||
Elf64_Xword p_filesz; /* file size */
|
||||
Elf64_Xword p_memsz; /* memory size */
|
||||
Elf64_Xword p_align; /* memory & file alignment */
|
||||
} Elf64_Phdr;
|
||||
|
||||
/* Segment types - p_type */
|
||||
#define PT_NULL 0 /* unused */
|
||||
#define PT_LOAD 1 /* loadable segment */
|
||||
#define PT_DYNAMIC 2 /* dynamic linking section */
|
||||
#define PT_INTERP 3 /* the RTLD */
|
||||
#define PT_NOTE 4 /* auxiliary information */
|
||||
#define PT_SHLIB 5 /* reserved - purpose undefined */
|
||||
#define PT_PHDR 6 /* program header */
|
||||
#define PT_TLS 7 /* thread local storage */
|
||||
#define PT_LOOS 0x60000000 /* reserved range for OS */
|
||||
#define PT_HIOS 0x6fffffff /* specific segment types */
|
||||
#define PT_LOPROC 0x70000000 /* reserved range for processor */
|
||||
#define PT_HIPROC 0x7fffffff /* specific segment types */
|
||||
|
||||
#define PT_GNU_EH_FRAME 0x6474e550 /* Exception handling info */
|
||||
#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */
|
||||
#define PT_GNU_STACK 0x6474e551 /* Stack permissions info */
|
||||
|
||||
#define PT_OPENBSD_RANDOMIZE 0x65a3dbe6 /* fill with random data */
|
||||
#define PT_OPENBSD_WXNEEDED 0x65a3dbe7 /* program performs W^X violations */
|
||||
#define PT_OPENBSD_BOOTDATA 0x65a41be6 /* section for boot arguments */
|
||||
|
||||
/* Segment flags - p_flags */
|
||||
#define PF_X 0x1 /* Executable */
|
||||
#define PF_W 0x2 /* Writable */
|
||||
#define PF_R 0x4 /* Readable */
|
||||
#define PF_MASKPROC 0xf0000000 /* reserved bits for processor */
|
||||
/* specific segment flags */
|
||||
|
||||
/* Dynamic structure */
|
||||
typedef struct {
|
||||
Elf32_Sword d_tag; /* controls meaning of d_val */
|
||||
union {
|
||||
Elf32_Word d_val; /* Multiple meanings - see d_tag */
|
||||
Elf32_Addr d_ptr; /* program virtual address */
|
||||
} d_un;
|
||||
} Elf32_Dyn;
|
||||
|
||||
typedef struct {
|
||||
Elf64_Xword d_tag; /* controls meaning of d_val */
|
||||
union {
|
||||
Elf64_Addr d_ptr;
|
||||
Elf64_Xword d_val;
|
||||
} d_un;
|
||||
} Elf64_Dyn;
|
||||
|
||||
/* Dynamic Array Tags - d_tag */
|
||||
#define DT_NULL 0 /* marks end of _DYNAMIC array */
|
||||
#define DT_NEEDED 1 /* string table offset of needed lib */
|
||||
#define DT_PLTRELSZ 2 /* size of relocation entries in PLT */
|
||||
#define DT_PLTGOT 3 /* address PLT/GOT */
|
||||
#define DT_HASH 4 /* address of symbol hash table */
|
||||
#define DT_STRTAB 5 /* address of string table */
|
||||
#define DT_SYMTAB 6 /* address of symbol table */
|
||||
#define DT_RELA 7 /* address of relocation table */
|
||||
#define DT_RELASZ 8 /* size of relocation table */
|
||||
#define DT_RELAENT 9 /* size of relocation entry */
|
||||
#define DT_STRSZ 10 /* size of string table */
|
||||
#define DT_SYMENT 11 /* size of symbol table entry */
|
||||
#define DT_INIT 12 /* address of initialization func. */
|
||||
#define DT_FINI 13 /* address of termination function */
|
||||
#define DT_SONAME 14 /* string table offset of shared obj */
|
||||
#define DT_RPATH 15 /* string table offset of library \
|
||||
search path */
|
||||
#define DT_SYMBOLIC 16 /* start sym search in shared obj. */
|
||||
#define DT_REL 17 /* address of rel. tbl. w addends */
|
||||
#define DT_RELSZ 18 /* size of DT_REL relocation table */
|
||||
#define DT_RELENT 19 /* size of DT_REL relocation entry */
|
||||
#define DT_PLTREL 20 /* PLT referenced relocation entry */
|
||||
#define DT_DEBUG 21 /* bugger */
|
||||
#define DT_TEXTREL 22 /* Allow rel. mod. to unwritable seg */
|
||||
#define DT_JMPREL 23 /* add. of PLT's relocation entries */
|
||||
#define DT_BIND_NOW 24 /* Bind now regardless of env setting */
|
||||
#define DT_INIT_ARRAY 25 /* address of array of init func */
|
||||
#define DT_FINI_ARRAY 26 /* address of array of term func */
|
||||
#define DT_INIT_ARRAYSZ 27 /* size of array of init func */
|
||||
#define DT_FINI_ARRAYSZ 28 /* size of array of term func */
|
||||
#define DT_RUNPATH 29 /* strtab offset of lib search path */
|
||||
#define DT_FLAGS 30 /* Set of DF_* flags */
|
||||
#define DT_ENCODING 31 /* further DT_* follow encoding rules */
|
||||
#define DT_PREINIT_ARRAY 32 /* address of array of preinit func */
|
||||
#define DT_PREINIT_ARRAYSZ 33 /* size of array of preinit func */
|
||||
#define DT_LOOS 0x6000000d /* reserved range for OS */
|
||||
#define DT_HIOS 0x6ffff000 /* specific dynamic array tags */
|
||||
#define DT_LOPROC 0x70000000 /* reserved range for processor */
|
||||
#define DT_HIPROC 0x7fffffff /* specific dynamic array tags */
|
||||
|
||||
/* some other useful tags */
|
||||
#define DT_GNU_HASH 0x6ffffef5 /* address of GNU hash table */
|
||||
#define DT_RELACOUNT 0x6ffffff9 /* if present, number of RELATIVE */
|
||||
#define DT_RELCOUNT 0x6ffffffa /* relocs, which must come first */
|
||||
#define DT_FLAGS_1 0x6ffffffb
|
||||
|
||||
/* Dynamic Flags - DT_FLAGS .dynamic entry */
|
||||
#define DF_ORIGIN 0x00000001
|
||||
#define DF_SYMBOLIC 0x00000002
|
||||
#define DF_TEXTREL 0x00000004
|
||||
#define DF_BIND_NOW 0x00000008
|
||||
#define DF_STATIC_TLS 0x00000010
|
||||
|
||||
/* Dynamic Flags - DT_FLAGS_1 .dynamic entry */
|
||||
#define DF_1_NOW 0x00000001
|
||||
#define DF_1_GLOBAL 0x00000002
|
||||
#define DF_1_GROUP 0x00000004
|
||||
#define DF_1_NODELETE 0x00000008
|
||||
#define DF_1_LOADFLTR 0x00000010
|
||||
#define DF_1_INITFIRST 0x00000020
|
||||
#define DF_1_NOOPEN 0x00000040
|
||||
#define DF_1_ORIGIN 0x00000080
|
||||
#define DF_1_DIRECT 0x00000100
|
||||
#define DF_1_TRANS 0x00000200
|
||||
#define DF_1_INTERPOSE 0x00000400
|
||||
#define DF_1_NODEFLIB 0x00000800
|
||||
#define DF_1_NODUMP 0x00001000
|
||||
#define DF_1_CONLFAT 0x00002000
|
||||
|
||||
/*
|
||||
* Note header
|
||||
*/
|
||||
typedef struct {
|
||||
Elf32_Word n_namesz;
|
||||
Elf32_Word n_descsz;
|
||||
Elf32_Word n_type;
|
||||
} Elf32_Nhdr;
|
||||
|
||||
typedef struct {
|
||||
Elf64_Half n_namesz;
|
||||
Elf64_Half n_descsz;
|
||||
Elf64_Half n_type;
|
||||
} Elf64_Nhdr;
|
||||
|
||||
/*
|
||||
* Note Definitions
|
||||
*/
|
||||
typedef struct {
|
||||
Elf32_Word namesz;
|
||||
Elf32_Word descsz;
|
||||
Elf32_Word type;
|
||||
} Elf32_Note;
|
||||
|
||||
typedef struct {
|
||||
Elf64_Half namesz;
|
||||
Elf64_Half descsz;
|
||||
Elf64_Half type;
|
||||
} Elf64_Note;
|
||||
|
||||
/* Values for n_type. */
|
||||
#define NT_PRSTATUS 1 /* Process status. */
|
||||
#define NT_FPREGSET 2 /* Floating point registers. */
|
||||
#define NT_PRPSINFO 3 /* Process state info. */
|
||||
|
||||
/*
|
||||
* OpenBSD-specific core file information.
|
||||
*
|
||||
* OpenBSD ELF core files use notes to provide information about
|
||||
* the process's state. The note name is "OpenBSD" for information
|
||||
* that is global to the process, and "OpenBSD@nn", where "nn" is the
|
||||
* thread ID of the thread that the information belongs to (such as
|
||||
* register state).
|
||||
*
|
||||
* We use the following note identifiers:
|
||||
*
|
||||
* NT_OPENBSD_PROCINFO
|
||||
* Note is a "elfcore_procinfo" structure.
|
||||
* NT_OPENBSD_AUXV
|
||||
* Note is a a bunch of Auxiliary Vectors, terminated by
|
||||
* an AT_NULL entry.
|
||||
* NT_OPENBSD_REGS
|
||||
* Note is a "reg" structure.
|
||||
* NT_OPENBSD_FPREGS
|
||||
* Note is a "fpreg" structure.
|
||||
*
|
||||
* Please try to keep the members of the "elfcore_procinfo" structure
|
||||
* nicely aligned, and if you add elements, add them to the end and
|
||||
* bump the version.
|
||||
*/
|
||||
|
||||
#define NT_OPENBSD_PROCINFO 10
|
||||
#define NT_OPENBSD_AUXV 11
|
||||
|
||||
#define NT_OPENBSD_REGS 20
|
||||
#define NT_OPENBSD_FPREGS 21
|
||||
#define NT_OPENBSD_XFPREGS 22
|
||||
#define NT_OPENBSD_WCOOKIE 23
|
||||
|
||||
struct elfcore_procinfo {
|
||||
/* Version 1 fields start here. */
|
||||
uint32_t cpi_version; /* netbsd_elfcore_procinfo version */
|
||||
#define ELFCORE_PROCINFO_VERSION 1
|
||||
uint32_t cpi_cpisize; /* sizeof(netbsd_elfcore_procinfo) */
|
||||
uint32_t cpi_signo; /* killing signal */
|
||||
uint32_t cpi_sigcode; /* signal code */
|
||||
uint32_t cpi_sigpend; /* pending signals */
|
||||
uint32_t cpi_sigmask; /* blocked signals */
|
||||
uint32_t cpi_sigignore; /* ignored signals */
|
||||
uint32_t cpi_sigcatch; /* signals being caught by user */
|
||||
int32_t cpi_pid; /* process ID */
|
||||
int32_t cpi_ppid; /* parent process ID */
|
||||
int32_t cpi_pgrp; /* process group ID */
|
||||
int32_t cpi_sid; /* session ID */
|
||||
uint32_t cpi_ruid; /* real user ID */
|
||||
uint32_t cpi_euid; /* effective user ID */
|
||||
uint32_t cpi_svuid; /* saved user ID */
|
||||
uint32_t cpi_rgid; /* real group ID */
|
||||
uint32_t cpi_egid; /* effective group ID */
|
||||
uint32_t cpi_svgid; /* saved group ID */
|
||||
int8_t cpi_name[32]; /* copy of pr->ps_comm */
|
||||
};
|
||||
|
||||
/*
|
||||
* XXX - these _KERNEL items aren't part of the ABI!
|
||||
*/
|
||||
#if defined(_KERNEL) || defined(_DYN_LOADER)
|
||||
|
||||
# define ELF32_NO_ADDR ((uint32_t)~0) /* Indicates addr. not yet filled in */
|
||||
|
||||
typedef struct {
|
||||
Elf32_Sword au_id; /* 32-bit id */
|
||||
Elf32_Word au_v; /* 32-bit value */
|
||||
} Aux32Info;
|
||||
|
||||
# define ELF64_NO_ADDR ((uint64_t)~0) /* Indicates addr. not yet filled in */
|
||||
|
||||
typedef struct {
|
||||
Elf64_Shalf au_id; /* 32-bit id */
|
||||
Elf64_Xword au_v; /* 64-bit value */
|
||||
} Aux64Info;
|
||||
|
||||
enum AuxID {
|
||||
AUX_null = 0,
|
||||
AUX_ignore = 1,
|
||||
AUX_execfd = 2,
|
||||
AUX_phdr = 3, /* &phdr[0] */
|
||||
AUX_phent = 4, /* sizeof(phdr[0]) */
|
||||
AUX_phnum = 5, /* # phdr entries */
|
||||
AUX_pagesz = 6, /* PAGESIZE */
|
||||
AUX_base = 7, /* ld.so base addr */
|
||||
AUX_flags = 8, /* processor flags */
|
||||
AUX_entry = 9, /* a.out entry */
|
||||
AUX_sun_uid = 2000, /* euid */
|
||||
AUX_sun_ruid = 2001, /* ruid */
|
||||
AUX_sun_gid = 2002, /* egid */
|
||||
AUX_sun_rgid = 2003 /* rgid */
|
||||
};
|
||||
|
||||
struct elf_args {
|
||||
u_long arg_entry; /* program entry point */
|
||||
u_long arg_interp; /* Interpreter load address */
|
||||
u_long arg_phaddr; /* program header address */
|
||||
u_long arg_phentsize; /* Gfx::Size of program header */
|
||||
u_long arg_phnum; /* Number of program headers */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(ELFSIZE) && defined(ARCH_ELFSIZE)
|
||||
# define ELFSIZE ARCH_ELFSIZE
|
||||
#endif
|
||||
|
||||
#if defined(ELFSIZE)
|
||||
# define CONCAT(x, y) __CONCAT(x, y)
|
||||
# define ELFNAME(x) CONCAT(elf, CONCAT(ELFSIZE, CONCAT(_, x)))
|
||||
# define ELFDEFNNAME(x) CONCAT(ELF, CONCAT(ELFSIZE, CONCAT(_, x)))
|
||||
#endif
|
||||
|
||||
#if defined(ELFSIZE) && (ELFSIZE == 32)
|
||||
# define Elf_Ehdr Elf32_Ehdr
|
||||
# define Elf_Phdr Elf32_Phdr
|
||||
# define Elf_Shdr Elf32_Shdr
|
||||
# define Elf_Sym Elf32_Sym
|
||||
# define Elf_Rel Elf32_Rel
|
||||
# define Elf_RelA Elf32_Rela
|
||||
# define Elf_Dyn Elf32_Dyn
|
||||
# define Elf_Half Elf32_Half
|
||||
# define Elf_Word Elf32_Word
|
||||
# define Elf_Sword Elf32_Sword
|
||||
# define Elf_Addr Elf32_Addr
|
||||
# define Elf_Off Elf32_Off
|
||||
# define Elf_Nhdr Elf32_Nhdr
|
||||
# define Elf_Note Elf32_Note
|
||||
|
||||
# define ELF_R_SYM ELF32_R_SYM
|
||||
# define ELF_R_TYPE ELF32_R_TYPE
|
||||
# define ELF_R_INFO ELF32_R_INFO
|
||||
# define ELFCLASS ELFCLASS32
|
||||
|
||||
# define ELF_ST_BIND ELF32_ST_BIND
|
||||
# define ELF_ST_TYPE ELF32_ST_TYPE
|
||||
# define ELF_ST_INFO ELF32_ST_INFO
|
||||
|
||||
# define ELF_NO_ADDR ELF32_NO_ADDR
|
||||
# define AuxInfo Aux32Info
|
||||
#elif defined(ELFSIZE) && (ELFSIZE == 64)
|
||||
# define Elf_Ehdr Elf64_Ehdr
|
||||
# define Elf_Phdr Elf64_Phdr
|
||||
# define Elf_Shdr Elf64_Shdr
|
||||
# define Elf_Sym Elf64_Sym
|
||||
# define Elf_Rel Elf64_Rel
|
||||
# define Elf_RelA Elf64_Rela
|
||||
# define Elf_Dyn Elf64_Dyn
|
||||
# define Elf_Half Elf64_Half
|
||||
# define Elf_Word Elf64_Word
|
||||
# define Elf_Sword Elf64_Sword
|
||||
# define Elf_Addr Elf64_Addr
|
||||
# define Elf_Off Elf64_Off
|
||||
# define Elf_Nhdr Elf64_Nhdr
|
||||
# define Elf_Note Elf64_Note
|
||||
|
||||
# define ELF_R_SYM ELF64_R_SYM
|
||||
# define ELF_R_TYPE ELF64_R_TYPE
|
||||
# define ELF_R_INFO ELF64_R_INFO
|
||||
# define ELFCLASS ELFCLASS64
|
||||
|
||||
# define ELF_ST_BIND ELF64_ST_BIND
|
||||
# define ELF_ST_TYPE ELF64_ST_TYPE
|
||||
# define ELF_ST_INFO ELF64_ST_INFO
|
||||
|
||||
# define ELF_NO_ADDR ELF64_NO_ADDR
|
||||
# define AuxInfo Aux64Info
|
||||
#endif
|
||||
|
||||
#define ELF_TARG_VER 1 /* The ver for which this code is intended */
|
||||
|
||||
/* Relocation types */
|
||||
#define R_386_NONE 0
|
||||
#define R_386_32 1 /* Symbol + Addend */
|
||||
#define R_386_PC32 2 /* Symbol + Addend - Section offset */
|
||||
#define R_386_GOT32 3 /* Used by build-time linker to create GOT entry */
|
||||
#define R_386_PLT32 4 /* Used by build-time linker to create PLT entry */
|
||||
#define R_386_COPY 5 /* https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter4-10454.html#chapter4-84604 */
|
||||
#define R_386_GLOB_DAT 6 /* Relation b/w GOT entry and symbol */
|
||||
#define R_386_JMP_SLOT 7 /* Fixed up by dynamic loader */
|
||||
#define R_386_RELATIVE 8 /* Base address + Addned */
|
||||
#define R_386_TLS_TPOFF 14 /* Negative offset into the static TLS storage */
|
||||
#define R_386_TLS_TPOFF32 37
|
||||
|
||||
#endif /* _SYS_EXEC_ELF_H_ */
|
Loading…
Add table
Add a link
Reference in a new issue