1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 13:17:44 +00:00

Libraries: Move to Userland/Libraries/

This commit is contained in:
Andreas Kling 2021-01-12 12:17:30 +01:00
parent dc28c07fa5
commit 13d7c09125
1857 changed files with 266 additions and 274 deletions

View 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

View 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;
};
}

View file

@ -0,0 +1 @@
serenity_install_sources(Libraries/LibELF)

View 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
};
}

View 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();
}
}

View 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;
};
}

View 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 = &region;
else if (region.is_load()) {
if (region.is_executable())
text_region_ptr = &region;
else
data_region_ptr = &region;
} 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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_ */