mirror of
https://github.com/justinian/jsix.git
synced 2025-12-09 16:04:32 -08:00
[ld.so] Add dynamic library linking
ld.so will now go through all DT_NEEDED entries in the dynamic table and load and relocate those shared libraries as well. Lazy linking of functions via the PLT is not yet supported, all PLT entries are looked up ahead of time by ld.so.
This commit is contained in:
@@ -2,7 +2,9 @@
|
||||
/// \file init.h
|
||||
/// Process initialization utility functions
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <j6/types.h>
|
||||
#include <util/api.h>
|
||||
|
||||
@@ -38,9 +40,7 @@ struct j6_arg_loader
|
||||
add_header(loader);
|
||||
uintptr_t loader_base;
|
||||
uintptr_t image_base;
|
||||
uintptr_t phdr;
|
||||
size_t phdr_size;
|
||||
size_t phdr_count;
|
||||
uintptr_t *got;
|
||||
uintptr_t entrypoint;
|
||||
};
|
||||
|
||||
@@ -84,4 +84,4 @@ int driver_main(unsigned, const char **, const char **, const j6_init_args *);
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#undef add_header
|
||||
#undef add_header
|
||||
|
||||
@@ -18,7 +18,8 @@ public:
|
||||
/// Load a file into a VMA
|
||||
/// \arg path Path of the file to load
|
||||
/// \arg vma [out] Handle to the loaded VMA, or invalid if not found
|
||||
j6_status_t load_file(char *path, j6_handle_t &vma);
|
||||
/// \arg size [out] Size of the file
|
||||
j6_status_t load_file(char *path, j6_handle_t &vma, size_t &size);
|
||||
|
||||
private:
|
||||
j6_handle_t m_service;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <j6/types.h>
|
||||
|
||||
namespace {
|
||||
constexpr size_t static_arr_count = 8;
|
||||
constexpr size_t static_arr_count = 32;
|
||||
j6_handle_descriptor handle_array[static_arr_count];
|
||||
j6_init_args init_args;
|
||||
} // namespace
|
||||
|
||||
@@ -15,7 +15,7 @@ client::client(j6_handle_t vfs_mb) :
|
||||
inline size_t simple_strlen(const char *s) { size_t n = 0; while (s && *s) s++, n++; return n; }
|
||||
|
||||
j6_status_t
|
||||
client::load_file(char *path, j6_handle_t &vma)
|
||||
client::load_file(char *path, j6_handle_t &vma, size_t &size)
|
||||
{
|
||||
if (!path)
|
||||
return j6_err_invalid_arg;
|
||||
@@ -43,8 +43,16 @@ client::load_file(char *path, j6_handle_t &vma)
|
||||
if (s != j6_status_ok)
|
||||
return s;
|
||||
|
||||
if (tag == j6_proto_vfs_file)
|
||||
if (tag == j6_proto_vfs_file) {
|
||||
size = 0;
|
||||
|
||||
// Get the size into `size`
|
||||
s = j6_vma_resize(vma, &size);
|
||||
if (s != j6_status_ok)
|
||||
return s;
|
||||
|
||||
return j6_status_ok; // handle is already in `vma`
|
||||
}
|
||||
|
||||
else if (tag == j6_proto_base_status)
|
||||
return *reinterpret_cast<j6_status_t*>(data); // contains a status
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <j6/errors.h>
|
||||
#include <j6/flags.h>
|
||||
#include <j6/syscalls.h>
|
||||
|
||||
namespace __j6libc {
|
||||
@@ -21,7 +22,7 @@ void * increase_core(intptr_t i)
|
||||
if (i < 0)
|
||||
return (void*)-1;
|
||||
|
||||
j6_status_t result = j6_vma_create_map(&core_handle, i, &core_base, 1);
|
||||
j6_status_t result = j6_vma_create_map(&core_handle, i, &core_base, j6_vm_flag_write);
|
||||
if (result != j6_status_ok)
|
||||
return (void*)-1;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
module("util",
|
||||
kind = "lib",
|
||||
static = True,
|
||||
sources = [
|
||||
"cdb.cpp",
|
||||
"bip_buffer.cpp",
|
||||
|
||||
369
src/user/ld.so/image.cpp
Normal file
369
src/user/ld.so/image.cpp
Normal file
@@ -0,0 +1,369 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <elf/file.h>
|
||||
#include <j6/errors.h>
|
||||
#include <j6/flags.h>
|
||||
#include <j6/memutils.h>
|
||||
#include <j6/protocols/vfs.hh>
|
||||
#include <j6/syscalls.h>
|
||||
#include <j6/syslog.hh>
|
||||
#include <util/format.h>
|
||||
|
||||
#include "image.h"
|
||||
#include "j6/types.h"
|
||||
#include "relocate.h"
|
||||
#include "symbols.h"
|
||||
|
||||
extern "C" void _ldso_plt_lookup();
|
||||
extern image_list all_images;
|
||||
|
||||
|
||||
// Can't use strcmp because it's from another library, and
|
||||
// this needs to be used as part of relocation or symbol lookup
|
||||
static inline bool
|
||||
str_equal(const char *a, const char *b)
|
||||
{
|
||||
if (!a || !b)
|
||||
return a == b;
|
||||
|
||||
size_t i = 0;
|
||||
while(a[i] && b[i] && a[i] == b[i]) ++i;
|
||||
return a[i] == b[i];
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
gnu_hash_func(const char *s)
|
||||
{
|
||||
uint32_t h = 5381;
|
||||
while (s && *s)
|
||||
h = (h<<5) + h + *s++;
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
inline image_list::item_type *
|
||||
new_image(const char *name)
|
||||
{
|
||||
// Use malloc() instead of new to simplify linkage
|
||||
image_list::item_type *i = reinterpret_cast<image_list::item_type*>(malloc(sizeof(*i)));
|
||||
i->base = 0;
|
||||
i->name = name;
|
||||
i->got = nullptr;
|
||||
return i;
|
||||
}
|
||||
|
||||
static uintptr_t
|
||||
load_image(image_list::item_type &img, j6::proto::vfs::client &vfs)
|
||||
{
|
||||
uintptr_t eop = 0; // end of program
|
||||
|
||||
char path [1024];
|
||||
util::format({path, sizeof(path)}, "/jsix/lib/%s", img.name);
|
||||
|
||||
size_t file_size = 0;
|
||||
j6_handle_t vma = j6_handle_invalid;
|
||||
j6_status_t r = vfs.load_file(path, vma, file_size);
|
||||
if (r != j6_status_ok) {
|
||||
j6::syslog("Error %d opening %s", r, path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uintptr_t file_addr = 0;
|
||||
r = j6_vma_map(vma, 0, &file_addr, 0);
|
||||
if (r != j6_status_ok) {
|
||||
j6::syslog("Error %d opening %s", r, path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
elf::file file { util::const_buffer::from(file_addr, file_size) };
|
||||
if (!file.valid(elf::filetype::shared)) {
|
||||
j6::syslog("Error opening %s: Not an ELF shared object", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (auto &seg : file.segments()) {
|
||||
if (seg.type == elf::segment_type::dynamic) {
|
||||
const dyn_entry *table =
|
||||
reinterpret_cast<const dyn_entry*>(img.base + seg.vaddr);
|
||||
img.read_dyn_table(table);
|
||||
}
|
||||
|
||||
if (seg.type != elf::segment_type::load)
|
||||
continue;
|
||||
|
||||
// TODO: way to remap VMA as read-only if there's no write flag on
|
||||
// the segment
|
||||
unsigned long flags = j6_vm_flag_exact | j6_vm_flag_write;
|
||||
if (seg.flags && elf::segment_flags::exec)
|
||||
flags |= j6_vm_flag_exec;
|
||||
|
||||
uintptr_t start = file.base() + seg.offset;
|
||||
size_t prologue = seg.vaddr & 0xfff;
|
||||
size_t epilogue = seg.mem_size - seg.file_size;
|
||||
|
||||
uintptr_t addr = (img.base + seg.vaddr) & ~0xfffull;
|
||||
j6_handle_t sub_vma = j6_handle_invalid;
|
||||
j6_status_t res = j6_vma_create_map(&sub_vma, seg.mem_size+prologue, &addr, flags);
|
||||
if (res != j6_status_ok) {
|
||||
j6::syslog(" ** error loading ELF '%s': creating sub vma: %lx", path, res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t *src = reinterpret_cast<uint8_t *>(start);
|
||||
uint8_t *dest = reinterpret_cast<uint8_t *>(addr);
|
||||
memset(dest, 0, prologue);
|
||||
memcpy(dest+prologue, src, seg.file_size);
|
||||
memset(dest+prologue+seg.file_size, 0, epilogue);
|
||||
|
||||
// end of segment
|
||||
uintptr_t eos = addr + seg.vaddr + seg.mem_size + prologue;
|
||||
if (eos > eop)
|
||||
eop = eos;
|
||||
}
|
||||
|
||||
j6_vma_unmap(vma, 0);
|
||||
|
||||
return eop;
|
||||
}
|
||||
|
||||
void
|
||||
image::read_dyn_table(dyn_entry const *table)
|
||||
{
|
||||
size_t dynrel_size = 0;
|
||||
size_t sizeof_rela = sizeof(rela);
|
||||
size_t jmprel_size = 0;
|
||||
size_t soname_index = 0;
|
||||
|
||||
bool parsing = true;
|
||||
while (parsing) {
|
||||
const dyn_entry &dyn = *table++;
|
||||
|
||||
switch (dyn.tag) {
|
||||
case dyn_type::null:
|
||||
parsing = false;
|
||||
break;
|
||||
|
||||
case dyn_type::pltrelsz:
|
||||
jmprel_size = dyn.value;
|
||||
break;
|
||||
|
||||
case dyn_type::pltgot:
|
||||
got = reinterpret_cast<uintptr_t*>(dyn.value + base);
|
||||
break;
|
||||
|
||||
case dyn_type::strtab:
|
||||
strtab.pointer = reinterpret_cast<char const*>(dyn.value + base);
|
||||
break;
|
||||
|
||||
case dyn_type::symtab:
|
||||
dynsym = reinterpret_cast<const symbol*>(dyn.value + base);
|
||||
break;
|
||||
|
||||
case dyn_type::rela:
|
||||
dynrel.pointer = reinterpret_cast<rela const*>(dyn.value + base);
|
||||
break;
|
||||
|
||||
case dyn_type::relasz:
|
||||
dynrel_size = dyn.value;
|
||||
break;
|
||||
|
||||
case dyn_type::relaent:
|
||||
sizeof_rela = dyn.value;
|
||||
break;
|
||||
|
||||
case dyn_type::strsz:
|
||||
strtab.count = dyn.value;
|
||||
break;
|
||||
|
||||
case dyn_type::jmprel:
|
||||
jmprel.pointer = reinterpret_cast<rela const*>(dyn.value + base);
|
||||
break;
|
||||
|
||||
case dyn_type::gnu_hash:
|
||||
gnu_hash = reinterpret_cast<const gnu_hash_table*>(dyn.value + base);
|
||||
break;
|
||||
|
||||
case dyn_type::soname:
|
||||
soname_index = dyn.value;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dynrel_size && sizeof_rela)
|
||||
dynrel.count = dynrel_size / sizeof_rela;
|
||||
|
||||
if (jmprel_size && sizeof_rela)
|
||||
jmprel.count = jmprel_size / sizeof_rela;
|
||||
|
||||
if (soname_index && strtab)
|
||||
name = string(soname_index);
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
image::lookup(const char *name) const
|
||||
{
|
||||
if (!gnu_hash || !dynsym || !strtab.pointer)
|
||||
return 0;
|
||||
|
||||
// Convenience references
|
||||
const gnu_hash_table &gh = *gnu_hash;
|
||||
|
||||
uint32_t h = gnu_hash_func(name);
|
||||
|
||||
// Check bloom filter
|
||||
static constexpr uint64_t bloom_bits = 6;
|
||||
static constexpr uint64_t mask = (1ull << bloom_bits) - 1;
|
||||
uint64_t bloom_index = (h >> bloom_bits) % gh.bloom_count;
|
||||
uint64_t bloom = gh.bloom[bloom_index];
|
||||
uint64_t test = (1ull << (h & mask)) | (1ull << ((h >> gh.bloom_shift) & mask));
|
||||
if ((bloom & test) != test)
|
||||
return 0;
|
||||
|
||||
const uint32_t *buckets = reinterpret_cast<const uint32_t*>(
|
||||
&gh.bloom[gh.bloom_count]);
|
||||
const uint32_t *chains = &buckets[gh.bucket_count];
|
||||
|
||||
uint32_t i = buckets[h % gh.bucket_count];
|
||||
if (i < gh.start_symbol)
|
||||
return 0;
|
||||
|
||||
while (true) {
|
||||
const symbol &sym = dynsym[i];
|
||||
const char *sym_name = strtab.lookup(sym.name);
|
||||
uint32_t sym_hash = chains[i - gh.start_symbol];
|
||||
|
||||
// Low bit is used to mark end-of-chain
|
||||
if ((h|1) == (sym_hash|1) && str_equal(name, sym_name))
|
||||
return base + sym.address;
|
||||
|
||||
if (sym_hash & 1)
|
||||
break;
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
add_needed_entries(image &img, image_list &open, image_list &closed)
|
||||
{
|
||||
dyn_entry const *dyn = img.dyn_table();
|
||||
|
||||
while (dyn->tag != dyn_type::null) {
|
||||
if (dyn->tag == dyn_type::needed) {
|
||||
const char *name = img.string(dyn->value);
|
||||
if (!open.find_image(name) && !closed.find_image(name))
|
||||
open.push_back(new_image(name));
|
||||
}
|
||||
++dyn;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
image_list::load(j6_handle_t vfs_mb, uintptr_t addr)
|
||||
{
|
||||
image_list open;
|
||||
j6::proto::vfs::client vfs {vfs_mb};
|
||||
|
||||
for (auto *img : *this)
|
||||
add_needed_entries(*img, open, *this);
|
||||
|
||||
while (!open.empty()) {
|
||||
image_list::item_type *img = open.pop_front();
|
||||
img->base = addr;
|
||||
|
||||
// Load the file
|
||||
addr = load_image(*img, vfs);
|
||||
if (!img->got) {
|
||||
j6::syslog("Error opening %s: Could not find GOT", img->name);
|
||||
return;
|
||||
}
|
||||
|
||||
j6::syslog("Loaded %s at base address 0x%x", img->name, img->base);
|
||||
addr = (addr & ~0xffffull) + 0x10000;
|
||||
|
||||
// Find the DT_NEEDED entries
|
||||
add_needed_entries(*img, open, *this);
|
||||
push_back(img);
|
||||
}
|
||||
|
||||
for (auto *img : *this)
|
||||
img->relocate(*this);
|
||||
}
|
||||
|
||||
void
|
||||
image::parse_rela_table(const util::counted<const rela> &table, image_list &ctx)
|
||||
{
|
||||
for (size_t i = 0; i < table.count; ++i) {
|
||||
const rela &rel = table[i];
|
||||
|
||||
const symbol *sym_obj = dynsym ? &dynsym[rel.symbol] : nullptr;
|
||||
const char *sym_name = sym_obj ? string(sym_obj->name) : nullptr;
|
||||
uintptr_t sym_addr = sym_name && *sym_name ? ctx.resolve(sym_name) : 0;
|
||||
|
||||
switch (rel.type)
|
||||
{
|
||||
case reloc::glob_dat:
|
||||
case reloc::jump_slot:
|
||||
*reinterpret_cast<uint64_t*>(rel.address + base) = sym_addr;
|
||||
break;
|
||||
|
||||
case reloc::relative:
|
||||
*reinterpret_cast<uint64_t*>(rel.address + base) = base + rel.offset;
|
||||
break;
|
||||
|
||||
default:
|
||||
j6::syslog("Unknown rela relocation type %d in %s", rel.type, name);
|
||||
exit(126);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
image::relocate(image_list &ctx)
|
||||
{
|
||||
if (relocated)
|
||||
return;
|
||||
|
||||
parse_rela_table(dynrel, ctx);
|
||||
parse_rela_table(jmprel, ctx);
|
||||
|
||||
got[1] = reinterpret_cast<uintptr_t>(this);
|
||||
got[2] = reinterpret_cast<uintptr_t>(&_ldso_plt_lookup);
|
||||
relocated = true;
|
||||
}
|
||||
|
||||
image_list::item_type *
|
||||
image_list::find_image(const char *name)
|
||||
{
|
||||
for (auto *i : *this) {
|
||||
if (str_equal(i->name, name))
|
||||
return i;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
image_list::resolve(const char *name)
|
||||
{
|
||||
for (auto *img : *this) {
|
||||
uintptr_t addr = img->lookup(name);
|
||||
if (addr) return addr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" uintptr_t
|
||||
ldso_plt_lookup(const image *img, unsigned jmprel_index)
|
||||
{
|
||||
const rela &rel = img->jmprel[jmprel_index];
|
||||
const symbol &sym = img->dynsym[rel.symbol];
|
||||
const char *name = img->string(sym.name);
|
||||
uintptr_t addr = all_images.resolve(name);
|
||||
return addr;
|
||||
}
|
||||
67
src/user/ld.so/image.h
Normal file
67
src/user/ld.so/image.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
/// \file image.h
|
||||
/// Definition of a class representing a loaded ELF image
|
||||
|
||||
#include <stdint.h>
|
||||
#include <j6/types.h>
|
||||
#include <util/counted.h>
|
||||
#include <util/linked_list.h>
|
||||
|
||||
#include "symbols.h"
|
||||
|
||||
struct dyn_entry;
|
||||
struct string_table;
|
||||
struct rela;
|
||||
struct image_list;
|
||||
|
||||
struct image
|
||||
{
|
||||
uintptr_t base;
|
||||
const char *name;
|
||||
uintptr_t *got;
|
||||
|
||||
string_table strtab;
|
||||
util::counted<rela const> jmprel;
|
||||
util::counted<rela const> dynrel;
|
||||
|
||||
symbol const *dynsym = nullptr;
|
||||
gnu_hash_table const *gnu_hash = nullptr;
|
||||
|
||||
bool relocated = false;
|
||||
|
||||
/// Look up a string table entry in this image's string table.
|
||||
const char * string(unsigned index) const {
|
||||
if (index > strtab.count) return nullptr;
|
||||
return strtab.pointer + index;
|
||||
}
|
||||
|
||||
/// Get the address of the DYNAMIC table
|
||||
inline const dyn_entry *dyn_table() const {
|
||||
return reinterpret_cast<const dyn_entry*>(got[0] + base);
|
||||
}
|
||||
|
||||
void read_dyn_table(dyn_entry const *table = nullptr);
|
||||
|
||||
/// Do all relocation on this image
|
||||
void relocate(image_list &ctx);
|
||||
|
||||
/// Do the relocations from a single table
|
||||
void parse_rela_table(const util::counted<const rela> &table, image_list &ctx);
|
||||
|
||||
/// Look up a symbol in this image's symbol table, and return an address
|
||||
/// if it is defined, or otherwise 0.
|
||||
uintptr_t lookup(const char *name) const;
|
||||
};
|
||||
|
||||
struct image_list :
|
||||
public util::linked_list<image>
|
||||
{
|
||||
/// Resolve a symbol name to an address, respecting library load order
|
||||
uintptr_t resolve(const char *symbol);
|
||||
|
||||
/// Recursively load images and return an image_list
|
||||
void load(j6_handle_t vfs_mb, uintptr_t addr);
|
||||
|
||||
/// Find an image with the given name in the list, or return null.
|
||||
item_type * find_image(const char *name);
|
||||
};
|
||||
@@ -8,8 +8,8 @@ ldso = module("ld.so",
|
||||
deps = [ "libc", "util", "elf" ],
|
||||
description = "Dynamic Linker",
|
||||
sources = [
|
||||
"image.cpp",
|
||||
"main.cpp",
|
||||
"relocate.cpp",
|
||||
"start.s",
|
||||
])
|
||||
|
||||
|
||||
@@ -3,32 +3,16 @@
|
||||
|
||||
#include <elf/headers.h>
|
||||
#include <j6/init.h>
|
||||
#include <j6/protocols/vfs.hh>
|
||||
#include <j6/syslog.hh>
|
||||
#include <util/pointers.h>
|
||||
#include "relocate.h"
|
||||
|
||||
uintptr_t
|
||||
locate_dyn_section(uintptr_t base, uintptr_t phdr, size_t phdr_size, size_t phdr_count)
|
||||
{
|
||||
if (!phdr || !phdr_count)
|
||||
return 0;
|
||||
#include "image.h"
|
||||
|
||||
const elf::segment_header *ph_base =
|
||||
reinterpret_cast<const elf::segment_header*>(phdr + base);
|
||||
|
||||
for (size_t i = 0; i < phdr_count; ++i) {
|
||||
const elf::segment_header *ph =
|
||||
util::offset_pointer(ph_base, i*phdr_size);
|
||||
|
||||
if (ph->type == elf::segment_type::dynamic)
|
||||
return ph->vaddr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
image_list all_images;
|
||||
|
||||
extern "C" uintptr_t
|
||||
ldso_init(j6_arg_header *stack_args, uintptr_t const *got)
|
||||
ldso_init(j6_arg_header *stack_args, uintptr_t *got)
|
||||
{
|
||||
j6_arg_loader *arg_loader = nullptr;
|
||||
j6_arg_handles *arg_handles = nullptr;
|
||||
@@ -57,14 +41,37 @@ ldso_init(j6_arg_header *stack_args, uintptr_t const *got)
|
||||
exit(127);
|
||||
}
|
||||
|
||||
relocate_image(arg_loader->loader_base, got[0]);
|
||||
j6_handle_t vfs = j6_handle_invalid;
|
||||
if (arg_handles) {
|
||||
for (size_t i = 0; i < arg_handles->nhandles; ++i) {
|
||||
j6_arg_handle_entry &ent = arg_handles->handles[i];
|
||||
if (ent.proto == j6::proto::vfs::id) {
|
||||
vfs = ent.handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t dyn_section = locate_dyn_section(
|
||||
arg_loader->image_base,
|
||||
arg_loader->phdr,
|
||||
arg_loader->phdr_size,
|
||||
arg_loader->phdr_count);
|
||||
relocate_image(arg_loader->image_base, dyn_section);
|
||||
|
||||
// First relocate ld.so itself. It cannot have any dependencies
|
||||
image_list::item_type ldso_image;
|
||||
ldso_image.base = arg_loader->loader_base;
|
||||
ldso_image.got = got;
|
||||
ldso_image.read_dyn_table(
|
||||
reinterpret_cast<const dyn_entry*>(got[0] + arg_loader->loader_base));
|
||||
|
||||
image_list just_ldso;
|
||||
just_ldso.push_back(&ldso_image);
|
||||
ldso_image.relocate(just_ldso);
|
||||
|
||||
image_list::item_type target_image;
|
||||
target_image.base = arg_loader->image_base;
|
||||
target_image.got = arg_loader->got;
|
||||
target_image.read_dyn_table(
|
||||
reinterpret_cast<const dyn_entry*>(arg_loader->got[0] + arg_loader->image_base));
|
||||
|
||||
all_images.push_back(&target_image);
|
||||
all_images.load(vfs, 0xb00'0000);
|
||||
|
||||
return arg_loader->entrypoint + arg_loader->image_base;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <j6/syslog.hh>
|
||||
#include <util/counted.h>
|
||||
#include "relocate.h"
|
||||
|
||||
enum class dyn_type : uint64_t {
|
||||
null, needed, pltrelsz, pltgot, hash, strtab, symtab, rela, relasz, relaent,
|
||||
strsz, syment, init, fini, soname, rpath, symbolic, rel, relsz, relent, pltrel,
|
||||
debug, textrel, jmprel, bind_now, init_array, fini_array, init_arraysz, fini_arraysz,
|
||||
gnu_hash = 0x6ffffef5, relacount = 0x6ffffff9,
|
||||
};
|
||||
|
||||
struct dyn_entry {
|
||||
dyn_type tag;
|
||||
uintptr_t value;
|
||||
};
|
||||
|
||||
struct dyn_values {
|
||||
uintptr_t pltrelsz;
|
||||
uintptr_t pltgot;
|
||||
uintptr_t hash;
|
||||
uintptr_t pltrel;
|
||||
uintptr_t strtab;
|
||||
uintptr_t symtab;
|
||||
uintptr_t rela;
|
||||
uintptr_t relasz;
|
||||
uintptr_t relaent;
|
||||
uintptr_t strsz;
|
||||
uintptr_t syment;
|
||||
uintptr_t jmprel;
|
||||
uintptr_t relacount;
|
||||
};
|
||||
|
||||
#define read_dyn_value(name) \
|
||||
case dyn_type::name: \
|
||||
values.name = dyn.value; \
|
||||
break;
|
||||
|
||||
void
|
||||
read_dynamic_values(uintptr_t base, uintptr_t dyn_section, dyn_values &values)
|
||||
{
|
||||
const dyn_entry *dyns = reinterpret_cast<const dyn_entry*>(dyn_section + base);
|
||||
|
||||
unsigned i = 0;
|
||||
while (true) {
|
||||
const dyn_entry &dyn = dyns[i++];
|
||||
switch (dyn.tag) {
|
||||
case dyn_type::null:
|
||||
return;
|
||||
|
||||
read_dyn_value(pltrelsz);
|
||||
read_dyn_value(pltgot);
|
||||
read_dyn_value(pltrel);
|
||||
read_dyn_value(strtab);
|
||||
read_dyn_value(symtab);
|
||||
read_dyn_value(rela);
|
||||
read_dyn_value(relasz);
|
||||
read_dyn_value(relaent);
|
||||
read_dyn_value(strsz);
|
||||
read_dyn_value(syment);
|
||||
read_dyn_value(jmprel);
|
||||
read_dyn_value(relacount);
|
||||
|
||||
case dyn_type::gnu_hash:
|
||||
values.hash = dyn.value;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef read_dyn_value(name)
|
||||
|
||||
class string_table :
|
||||
private util::counted<const char>
|
||||
{
|
||||
public:
|
||||
string_table(const char *start, size_t size) :
|
||||
util::counted<const char> {start, size} {}
|
||||
|
||||
const char *lookup(size_t offset) {
|
||||
if (offset > count) return nullptr;
|
||||
return pointer + offset;
|
||||
}
|
||||
};
|
||||
|
||||
struct symbol
|
||||
{
|
||||
uint32_t name;
|
||||
uint8_t type : 4;
|
||||
uint8_t binding : 4;
|
||||
uint8_t _reserved0;
|
||||
uint16_t section;
|
||||
uintptr_t address;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct gnu_hash_table
|
||||
{
|
||||
uint32_t bucket_count;
|
||||
uint32_t start_symbol;
|
||||
uint32_t bloom_count;
|
||||
uint32_t bloom_shift;
|
||||
uint64_t bloom [0];
|
||||
};
|
||||
|
||||
enum class reloc : uint32_t {
|
||||
relative = 8,
|
||||
};
|
||||
|
||||
struct rela
|
||||
{
|
||||
uintptr_t address;
|
||||
reloc type;
|
||||
uint32_t symbol;
|
||||
ptrdiff_t offset;
|
||||
};
|
||||
|
||||
template <typename T> T off(uintptr_t p, uintptr_t base) { return reinterpret_cast<T>(p ? p + base : 0); }
|
||||
|
||||
void
|
||||
relocate_image(uintptr_t base, uintptr_t dyn_section)
|
||||
{
|
||||
dyn_values values = {0};
|
||||
read_dynamic_values(base, dyn_section, values);
|
||||
|
||||
string_table strtab {
|
||||
reinterpret_cast<const char *>(values.strtab + base),
|
||||
values.strsz,
|
||||
};
|
||||
|
||||
symbol *symtab = off<symbol *>(values.symtab, base);
|
||||
gnu_hash_table *hashtab = off<gnu_hash_table *>(values.hash, base);
|
||||
|
||||
uintptr_t *jmprel = off<uintptr_t *>(values.jmprel, base);
|
||||
uintptr_t *plt = off<uintptr_t *>(values.pltgot, base);
|
||||
|
||||
size_t nrela = values.relacount;
|
||||
for (size_t i = 0; i < values.relacount; ++i) {
|
||||
rela *rel = off<rela*>(values.rela + i * values.relaent, base);
|
||||
switch (rel->type)
|
||||
{
|
||||
case reloc::relative:
|
||||
*reinterpret_cast<uint64_t*>(rel->address + base) = base + rel->offset;
|
||||
break;
|
||||
|
||||
default:
|
||||
j6::syslog("Unknown relocation type %d", rel->type);
|
||||
exit(126);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,31 @@
|
||||
/// \file relocate.h
|
||||
/// Image relocation services
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void relocate_image(uintptr_t base, uintptr_t dyn_section);
|
||||
enum class dyn_type : uint64_t {
|
||||
null, needed, pltrelsz, pltgot, hash, strtab, symtab, rela, relasz, relaent,
|
||||
strsz, syment, init, fini, soname, rpath, symbolic, rel, relsz, relent, pltrel,
|
||||
debug, textrel, jmprel, bind_now, init_array, fini_array, init_arraysz, fini_arraysz,
|
||||
gnu_hash = 0x6ffffef5, relacount = 0x6ffffff9,
|
||||
};
|
||||
|
||||
struct dyn_entry {
|
||||
dyn_type tag;
|
||||
uintptr_t value;
|
||||
};
|
||||
|
||||
enum class reloc : uint32_t {
|
||||
glob_dat = 6,
|
||||
jump_slot = 7,
|
||||
relative = 8,
|
||||
};
|
||||
|
||||
struct rela
|
||||
{
|
||||
uintptr_t address;
|
||||
reloc type;
|
||||
uint32_t symbol;
|
||||
ptrdiff_t offset;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
extern ldso_init
|
||||
extern ldso_plt_lookup
|
||||
extern _GLOBAL_OFFSET_TABLE_
|
||||
|
||||
global _ldso_start:function hidden (_ldso_start.end - _ldso_start)
|
||||
@@ -30,4 +31,34 @@ _ldso_start:
|
||||
pop rdi
|
||||
|
||||
jmp rax
|
||||
.end:
|
||||
.end:
|
||||
|
||||
|
||||
global _ldso_plt_lookup:function hidden (_ldso_plt_lookup.end - _ldso_plt_lookup)
|
||||
_ldso_plt_lookup:
|
||||
pop rax ; image struct address
|
||||
pop r11 ; jmprel entry index
|
||||
|
||||
; Save off anything that might be a function arg
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
push rcx
|
||||
push r8
|
||||
push r9
|
||||
|
||||
mov rdi, rax
|
||||
mov rsi, r11
|
||||
call ldso_plt_lookup
|
||||
; The function's address is now in rax
|
||||
|
||||
; Put the function call params back
|
||||
pop r9
|
||||
pop r8
|
||||
pop rcx
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
|
||||
jmp rax
|
||||
.end:
|
||||
|
||||
36
src/user/ld.so/symbols.h
Normal file
36
src/user/ld.so/symbols.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
/// \file symbols.h
|
||||
/// Symbol lookup routines and related data structures
|
||||
|
||||
#include <stdint.h>
|
||||
#include <util/counted.h>
|
||||
|
||||
class string_table :
|
||||
public util::counted<char const>
|
||||
{
|
||||
public:
|
||||
const char *lookup(size_t offset) const {
|
||||
if (offset > count) return nullptr;
|
||||
return pointer + offset;
|
||||
}
|
||||
};
|
||||
|
||||
struct symbol
|
||||
{
|
||||
uint32_t name;
|
||||
uint8_t type : 4;
|
||||
uint8_t binding : 4;
|
||||
uint8_t _reserved0;
|
||||
uint16_t section;
|
||||
uintptr_t address;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct gnu_hash_table
|
||||
{
|
||||
uint32_t bucket_count;
|
||||
uint32_t start_symbol;
|
||||
uint32_t bloom_count;
|
||||
uint32_t bloom_shift;
|
||||
uint64_t bloom [0];
|
||||
};
|
||||
@@ -26,7 +26,7 @@ handle_load_request(j6romfs::fs &fs, const char *path, j6_handle_t &vma)
|
||||
fs.load_inode_data(in, dest);
|
||||
j6_vma_unmap(vma, 0);
|
||||
|
||||
return j6_err_nyi;
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -65,7 +65,7 @@ stack_push(uint8_t *&stack, size_t extra)
|
||||
uintptr_t
|
||||
load_program_into(j6_handle_t proc, elf::file &file, uintptr_t image_base, const char *path)
|
||||
{
|
||||
uintptr_t eop = 0;
|
||||
uintptr_t eop = 0; // end of program
|
||||
|
||||
for (auto &seg : file.segments()) {
|
||||
if (seg.type != elf::segment_type::load)
|
||||
@@ -96,6 +96,7 @@ load_program_into(j6_handle_t proc, elf::file &file, uintptr_t image_base, const
|
||||
memcpy(dest+prologue, src, seg.file_size);
|
||||
memset(dest+prologue+seg.file_size, 0, epilogue);
|
||||
|
||||
// end of segment
|
||||
uintptr_t eos = image_base + seg.vaddr + seg.mem_size + prologue;
|
||||
if (eos > eop)
|
||||
eop = eos;
|
||||
@@ -219,12 +220,16 @@ load_program(
|
||||
j6_arg_loader *loader_arg = stack_push<j6_arg_loader>(stack, 0);
|
||||
const elf::file_header *h = program_elf.header();
|
||||
loader_arg->image_base = program_image_base;
|
||||
loader_arg->phdr = h->ph_offset;
|
||||
loader_arg->phdr_count = h->ph_num;
|
||||
loader_arg->entrypoint = entrypoint;
|
||||
|
||||
const elf::section_header *got_section = program_elf.get_section_by_name(".got.plt");
|
||||
if (got_section)
|
||||
loader_arg->got = reinterpret_cast<uintptr_t*>(program_image_base + got_section->addr);
|
||||
|
||||
// The dynamic linker will offset the entrypoint, don't do it here.
|
||||
loader_arg->entrypoint = program_elf.entrypoint();
|
||||
|
||||
j6_arg_handles *handles_arg = stack_push<j6_arg_handles>(stack, 2 * sizeof(j6_arg_handle_entry));
|
||||
handles_arg->nhandles = 1;
|
||||
handles_arg->nhandles = 2;
|
||||
handles_arg->handles[0].handle = slp;
|
||||
handles_arg->handles[0].proto = j6::proto::sl::id;
|
||||
handles_arg->handles[1].handle = vfs;
|
||||
@@ -268,8 +273,9 @@ load_program(
|
||||
}
|
||||
|
||||
j6_handle_t thread = j6_handle_invalid;
|
||||
ptrdiff_t stack_consumed = stack_orig - stack;
|
||||
res = j6_thread_create(&thread, proc, stack_top - stack_consumed, entrypoint, program_image_base, 0);
|
||||
uintptr_t target_stack = stack_top - (stack_orig - stack);
|
||||
target_stack &= ~0xfull; // Align to 16-byte stack
|
||||
res = j6_thread_create(&thread, proc, target_stack, entrypoint, program_image_base, 0);
|
||||
if (res != j6_status_ok) {
|
||||
j6::syslog(" ** error loading program '%s': creating thread: %lx", path, res);
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user