From c245949ea4e574eaa5a1ef0efe324a4cff18dd99 Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sun, 18 Feb 2024 17:39:42 -0800 Subject: [PATCH] [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. --- assets/build/target.user.shared.yaml | 7 + src/libraries/j6/include/j6/init.h | 8 +- src/libraries/j6/include/j6/protocols/vfs.hh | 3 +- src/libraries/j6/init.cpp | 2 +- src/libraries/j6/protocols/vfs.cpp | 12 +- src/libraries/libc/__j6libc/increase_core.cpp | 3 +- src/libraries/util/util.module | 1 + src/user/ld.so/image.cpp | 369 ++++++++++++++++++ src/user/ld.so/image.h | 67 ++++ src/user/ld.so/ld.so.module | 2 +- src/user/ld.so/main.cpp | 63 +-- src/user/ld.so/relocate.cpp | 155 -------- src/user/ld.so/relocate.h | 27 +- src/user/ld.so/start.s | 33 +- src/user/ld.so/symbols.h | 36 ++ src/user/srv.init/initfs.cpp | 2 +- src/user/srv.init/loader.cpp | 20 +- 17 files changed, 607 insertions(+), 203 deletions(-) create mode 100644 assets/build/target.user.shared.yaml create mode 100644 src/user/ld.so/image.cpp create mode 100644 src/user/ld.so/image.h delete mode 100644 src/user/ld.so/relocate.cpp create mode 100644 src/user/ld.so/symbols.h diff --git a/assets/build/target.user.shared.yaml b/assets/build/target.user.shared.yaml new file mode 100644 index 0000000..666830b --- /dev/null +++ b/assets/build/target.user.shared.yaml @@ -0,0 +1,7 @@ +--- +ccflags: [ +] + +ldflags: [ + "-shared", +] diff --git a/src/libraries/j6/include/j6/init.h b/src/libraries/j6/include/j6/init.h index a257263..b5dcef1 100644 --- a/src/libraries/j6/include/j6/init.h +++ b/src/libraries/j6/include/j6/init.h @@ -2,7 +2,9 @@ /// \file init.h /// Process initialization utility functions +#include #include + #include #include @@ -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 \ No newline at end of file +#undef add_header diff --git a/src/libraries/j6/include/j6/protocols/vfs.hh b/src/libraries/j6/include/j6/protocols/vfs.hh index e02e799..4699234 100644 --- a/src/libraries/j6/include/j6/protocols/vfs.hh +++ b/src/libraries/j6/include/j6/protocols/vfs.hh @@ -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; diff --git a/src/libraries/j6/init.cpp b/src/libraries/j6/init.cpp index c89f557..1befef1 100644 --- a/src/libraries/j6/init.cpp +++ b/src/libraries/j6/init.cpp @@ -10,7 +10,7 @@ #include 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 diff --git a/src/libraries/j6/protocols/vfs.cpp b/src/libraries/j6/protocols/vfs.cpp index 6b17ec3..fd87a3c 100644 --- a/src/libraries/j6/protocols/vfs.cpp +++ b/src/libraries/j6/protocols/vfs.cpp @@ -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(data); // contains a status diff --git a/src/libraries/libc/__j6libc/increase_core.cpp b/src/libraries/libc/__j6libc/increase_core.cpp index f23cb8c..991c548 100644 --- a/src/libraries/libc/__j6libc/increase_core.cpp +++ b/src/libraries/libc/__j6libc/increase_core.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include 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; diff --git a/src/libraries/util/util.module b/src/libraries/util/util.module index a1049a9..9a002ab 100644 --- a/src/libraries/util/util.module +++ b/src/libraries/util/util.module @@ -2,6 +2,7 @@ module("util", kind = "lib", + static = True, sources = [ "cdb.cpp", "bip_buffer.cpp", diff --git a/src/user/ld.so/image.cpp b/src/user/ld.so/image.cpp new file mode 100644 index 0000000..7e598f3 --- /dev/null +++ b/src/user/ld.so/image.cpp @@ -0,0 +1,369 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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(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(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(start); + uint8_t *dest = reinterpret_cast(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(dyn.value + base); + break; + + case dyn_type::strtab: + strtab.pointer = reinterpret_cast(dyn.value + base); + break; + + case dyn_type::symtab: + dynsym = reinterpret_cast(dyn.value + base); + break; + + case dyn_type::rela: + dynrel.pointer = reinterpret_cast(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(dyn.value + base); + break; + + case dyn_type::gnu_hash: + gnu_hash = reinterpret_cast(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( + &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 &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(rel.address + base) = sym_addr; + break; + + case reloc::relative: + *reinterpret_cast(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(this); + got[2] = reinterpret_cast(&_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; +} diff --git a/src/user/ld.so/image.h b/src/user/ld.so/image.h new file mode 100644 index 0000000..e1d04fb --- /dev/null +++ b/src/user/ld.so/image.h @@ -0,0 +1,67 @@ +#pragma once +/// \file image.h +/// Definition of a class representing a loaded ELF image + +#include +#include +#include +#include + +#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 jmprel; + util::counted 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(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 &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 +{ + /// 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); +}; diff --git a/src/user/ld.so/ld.so.module b/src/user/ld.so/ld.so.module index fe79419..5942274 100644 --- a/src/user/ld.so/ld.so.module +++ b/src/user/ld.so/ld.so.module @@ -8,8 +8,8 @@ ldso = module("ld.so", deps = [ "libc", "util", "elf" ], description = "Dynamic Linker", sources = [ + "image.cpp", "main.cpp", - "relocate.cpp", "start.s", ]) diff --git a/src/user/ld.so/main.cpp b/src/user/ld.so/main.cpp index 01c3b8e..3dda4ec 100644 --- a/src/user/ld.so/main.cpp +++ b/src/user/ld.so/main.cpp @@ -3,32 +3,16 @@ #include #include +#include #include #include -#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(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(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(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; -} \ No newline at end of file +} diff --git a/src/user/ld.so/relocate.cpp b/src/user/ld.so/relocate.cpp deleted file mode 100644 index 8bf1c03..0000000 --- a/src/user/ld.so/relocate.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include -#include -#include -#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(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 -{ -public: - string_table(const char *start, size_t size) : - util::counted {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 T off(uintptr_t p, uintptr_t base) { return reinterpret_cast(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(values.strtab + base), - values.strsz, - }; - - symbol *symtab = off(values.symtab, base); - gnu_hash_table *hashtab = off(values.hash, base); - - uintptr_t *jmprel = off(values.jmprel, base); - uintptr_t *plt = off(values.pltgot, base); - - size_t nrela = values.relacount; - for (size_t i = 0; i < values.relacount; ++i) { - rela *rel = off(values.rela + i * values.relaent, base); - switch (rel->type) - { - case reloc::relative: - *reinterpret_cast(rel->address + base) = base + rel->offset; - break; - - default: - j6::syslog("Unknown relocation type %d", rel->type); - exit(126); - break; - } - } -} \ No newline at end of file diff --git a/src/user/ld.so/relocate.h b/src/user/ld.so/relocate.h index 30d9003..d169523 100644 --- a/src/user/ld.so/relocate.h +++ b/src/user/ld.so/relocate.h @@ -2,6 +2,31 @@ /// \file relocate.h /// Image relocation services +#include #include -void relocate_image(uintptr_t base, uintptr_t dyn_section); \ No newline at end of file +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; +}; diff --git a/src/user/ld.so/start.s b/src/user/ld.so/start.s index bbc2c77..73fdbd5 100644 --- a/src/user/ld.so/start.s +++ b/src/user/ld.so/start.s @@ -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: \ No newline at end of file +.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: diff --git a/src/user/ld.so/symbols.h b/src/user/ld.so/symbols.h new file mode 100644 index 0000000..f4b90e9 --- /dev/null +++ b/src/user/ld.so/symbols.h @@ -0,0 +1,36 @@ +#pragma once +/// \file symbols.h +/// Symbol lookup routines and related data structures + +#include +#include + +class string_table : + public util::counted +{ +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]; +}; diff --git a/src/user/srv.init/initfs.cpp b/src/user/srv.init/initfs.cpp index 4bd3a28..9f6ca50 100644 --- a/src/user/srv.init/initfs.cpp +++ b/src/user/srv.init/initfs.cpp @@ -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 diff --git a/src/user/srv.init/loader.cpp b/src/user/srv.init/loader.cpp index 0cb0770..e0c06b6 100644 --- a/src/user/srv.init/loader.cpp +++ b/src/user/srv.init/loader.cpp @@ -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(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(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(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;