aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjohannst <johannes.stoelp@gmail.com>2021-04-25 23:38:36 +0200
committerjohannst <johannes.stoelp@gmail.com>2021-04-25 23:38:36 +0200
commit05f740db6fe966d32256d4ed3b897f7b3e051fff (patch)
treeeedf0ef4e68a3a2ecfd06f714cc77bcbd0e8e2b1
parent6f7efd7075fcea29922536d8fb1ad810182437cd (diff)
downloaddynld-05f740db6fe966d32256d4ed3b897f7b3e051fff.tar.gz
dynld-05f740db6fe966d32256d4ed3b897f7b3e051fff.zip
added support for R_X86_64_64/R_X86_64_RELATIVE relocations + added init/fini
-rw-r--r--04_dynld_nostd/dynld.c78
-rw-r--r--04_dynld_nostd/libgreet.c10
-rw-r--r--lib/include/elf.h64
3 files changed, 110 insertions, 42 deletions
diff --git a/04_dynld_nostd/dynld.c b/04_dynld_nostd/dynld.c
index 56d48b7..a7b0fe1 100644
--- a/04_dynld_nostd/dynld.c
+++ b/04_dynld_nostd/dynld.c
@@ -205,6 +205,41 @@ const Elf64Rela* get_reloca(const Dso* dso, const uint64_t idx) {
}
+/// -----------
+/// Init & Fini
+/// -----------
+
+typedef void (*initfptr)();
+
+static void init(const Dso* dso) {
+ if (dso->dynamic[DT_INIT]) {
+ initfptr* fn = (initfptr*)(dso->base + dso->dynamic[DT_INIT]);
+ (*fn)();
+ }
+
+ size_t nfns = dso->dynamic[DT_INIT_ARRAYSZ] / sizeof(initfptr);
+ initfptr* fns = (initfptr*)(dso->base + dso->dynamic[DT_INIT_ARRAY]);
+ while (nfns--) {
+ (*fns++)();
+ }
+}
+
+typedef void (*finifptr)();
+
+static void fini(const Dso* dso) {
+ size_t nfns = dso->dynamic[DT_FINI_ARRAYSZ] / sizeof(finifptr);
+ finifptr* fns = (finifptr*)(dso->base + dso->dynamic[DT_FINI_ARRAY]) + nfns /* reverse destruction order */;
+ while (nfns--) {
+ (*--fns)();
+ }
+
+ if (dso->dynamic[DT_FINI]) {
+ finifptr* fn = (finifptr*)(dso->base + dso->dynamic[DT_FINI]);
+ (*fn)();
+ }
+}
+
+
/// -------------
/// Symbol lookup
/// -------------
@@ -226,12 +261,12 @@ int strcmp(const char* s1, const char* s2) {
//
// `dso` A handle to the dso which dynamic symbol table should be searched.
// `sym_name` Name of the symbol to look up.
-// `sym_type` Type of the symbol to look up (STT_OBJECT | STT_FUNC).
-void* lookup_sym(const Dso* dso, const char* sym_name, unsigned sym_type) {
+void* lookup_sym(const Dso* dso, const char* sym_name) {
for (unsigned i = 0; i < get_num_dynsyms(dso); ++i) {
const Elf64Sym* sym = get_sym(dso, i);
- if (ELF64_ST_TYPE(sym->info) == sym_type && ELF64_ST_BIND(sym->info) == STB_GLOBAL && sym->shndx != SHN_UNDEF) {
+ if ((ELF64_ST_TYPE(sym->info) == STT_OBJECT || ELF64_ST_TYPE(sym->info) == STT_FUNC) && ELF64_ST_BIND(sym->info) == STB_GLOBAL &&
+ sym->shndx != SHN_UNDEF) {
if (strcmp(sym_name, get_str(dso, sym->name)) == 0) {
return dso->base + sym->value;
}
@@ -367,7 +402,7 @@ typedef struct LinkMap LinkMap;
// Resolve the relocation `reloc` by looking up the address of the symbol
// referenced by the relocation. If the address of the symbol was found the
// relocation is patched, if the address was not found the process exits.
-static void resolve_reloc(const Dso* dso, const LinkMap* map, const Elf64Rela* reloc, unsigned symtype) {
+static void resolve_reloc(const Dso* dso, const LinkMap* map, const Elf64Rela* reloc) {
// Get symbol referenced by relocation.
const int symidx = ELF64_R_SYM(reloc->info);
const Elf64Sym* sym = get_sym(dso, symidx);
@@ -376,20 +411,28 @@ static void resolve_reloc(const Dso* dso, const LinkMap* map, const Elf64Rela* r
// Get relocation typy.
unsigned reloctype = ELF64_R_TYPE(reloc->info);
- // Lookup symbol address.
+ // Find symbol address.
void* symaddr = 0;
- // TODO: Explain special handling of R_X86_64_COPY.
- for (const LinkMap* lmap = (reloctype == R_X86_64_COPY ? map->next : map); lmap && symaddr == 0; lmap = lmap->next) {
- symaddr = lookup_sym(lmap->dso, symname, symtype);
+ // FIXME: Should relocations of type `R_X86_64_64` only be looked up in `dso` directly?
+ if (reloctype == R_X86_64_RELATIVE) {
+ // Symbols address is computed by re-basing the relative address based on the DSOs base address.
+ symaddr = (void*)(dso->base + reloc->addend);
+ } else {
+ // TODO: Explain special handling of R_X86_64_COPY.
+ for (const LinkMap* lmap = (reloctype == R_X86_64_COPY ? map->next : map); lmap && symaddr == 0; lmap = lmap->next) {
+ symaddr = lookup_sym(lmap->dso, symname);
+ }
}
ERROR_ON(symaddr == 0, "Failed lookup symbol %s while resolving relocations!", symname);
- pfmt("Resolved reloc %s to %p (base %p)\n", symname, symaddr, dso->base);
+ pfmt("Resolved reloc %s to %p (base %p)\n", reloctype == R_X86_64_RELATIVE ? "<relative>" : symname, symaddr, dso->base);
// Perform relocation according to relocation type.
switch (reloctype) {
case R_X86_64_GLOB_DAT: /* GOT entry for data objects. */
case R_X86_64_JUMP_SLOT: /* PLT entry. */
+ case R_X86_64_64: /* 64bit relocation (non-lazy). */
+ case R_X86_64_RELATIVE: /* DSO base relative relocation. */
// Patch storage unit of relocation with absolute address of the symbol.
*(uint64_t*)(dso->base + reloc->offset) = (uint64_t)symaddr;
break;
@@ -412,14 +455,14 @@ static void resolve_relocs(const Dso* dso, const LinkMap* map) {
// variables).
for (unsigned long relocidx = 0; relocidx < (dso->dynamic[DT_RELASZ] / sizeof(Elf64Rela)); ++relocidx) {
const Elf64Rela* reloc = get_reloca(dso, relocidx);
- resolve_reloc(dso, map, reloc, STT_OBJECT);
+ resolve_reloc(dso, map, reloc);
}
// Resolve all relocation from the PLT jump table found in `dso`. There is
// typically one relocation per undefined dynamic function symbol.
for (unsigned long relocidx = 0; relocidx < (dso->dynamic[DT_PLTRELSZ] / sizeof(Elf64Rela)); ++relocidx) {
const Elf64Rela* reloc = get_pltreloca(dso, relocidx);
- resolve_reloc(dso, map, reloc, STT_FUNC);
+ resolve_reloc(dso, map, reloc);
}
}
@@ -486,11 +529,15 @@ void dl_entry(const uint64_t* prctx) {
// Resolve relocations of the library (dependency).
resolve_relocs(&dso_lib, &map_prog);
-
// Resolve relocations of the main program.
resolve_relocs(&dso_prog, &map_prog);
- // Install dynamic resolve handler.
+ // Initialize library.
+ init(&dso_lib);
+ // Initialize main program.
+ init(&dso_prog);
+
+ // Install dynamic resolve handler (lazy resolve).
//
// The dynamic resolve handler is used when binding symbols lazily. Hence
// it should not be called in this example as we resolve all relocations
@@ -517,5 +564,10 @@ void dl_entry(const uint64_t* prctx) {
// Transfer control to user program.
dso_prog.entry();
+ // Finalize main program.
+ fini(&dso_prog);
+ // Finalize library.
+ fini(&dso_lib);
+
_exit(0);
}
diff --git a/04_dynld_nostd/libgreet.c b/04_dynld_nostd/libgreet.c
index 439e236..e697690 100644
--- a/04_dynld_nostd/libgreet.c
+++ b/04_dynld_nostd/libgreet.c
@@ -1,5 +1,7 @@
// Copyright (c) 2020 Johannes Stoelp
+#include <io.h>
+
int gCalled = 0;
const char* get_greet() {
@@ -11,3 +13,11 @@ const char* get_greet2() {
++gCalled;
return "Hello 2 from libgreet.so!";
}
+
+__attribute__((constructor)) static void libinit() { /* static -> generates R_X86_64_RELATIVE relocation */
+ pfmt("libgreet.so: libinit\n");
+}
+
+__attribute__((destructor)) void libfini() { /* non static -> generates R_X86_64_64 relocation */
+ pfmt("libgreet.so: libfini\n");
+}
diff --git a/lib/include/elf.h b/lib/include/elf.h
index f0a0940..7a2e597 100644
--- a/lib/include/elf.h
+++ b/lib/include/elf.h
@@ -84,32 +84,36 @@ typedef struct {
/// Dynamic Section
/// ---------------
-#define DT_NULL 0 /* [ignored] Marks end of dynamic section */
-#define DT_NEEDED 1 /* [val] Name of needed library */
-#define DT_PLTRELSZ 2 /* [val] Size in bytes of PLT relocs */
-#define DT_PLTGOT 3 /* [ptr] Processor defined value */
-#define DT_HASH 4 /* [ptr] Address of symbol hash table */
-#define DT_STRTAB 5 /* [ptr] Address of string table */
-#define DT_SYMTAB 6 /* [ptr] Address of symbol table */
-#define DT_RELA 7 /* [ptr] Address of Rela relocs */
-#define DT_RELASZ 8 /* [val] Total size of Rela relocs */
-#define DT_RELAENT 9 /* [val] Size of one Rela reloc */
-#define DT_STRSZ 10 /* [val] Size of string table */
-#define DT_SYMENT 11 /* [val] Size of one symbol table entry */
-#define DT_INIT 12 /* [ptr] Address of init function */
-#define DT_FINI 13 /* [ptr] Address of termination function */
-#define DT_SONAME 14 /* [val] Name of shared object */
-#define DT_RPATH 15 /* [val] Library search path (deprecated) */
-#define DT_SYMBOLIC 16 /* [ignored] Start symbol search here */
-#define DT_REL 17 /* [ptr] Address of Rel relocs */
-#define DT_RELSZ 18 /* [val] Total size of Rel relocs */
-#define DT_RELENT 19 /* [val] Size of one Rel reloc */
-#define DT_PLTREL 20 /* [val] Type of reloc in PLT */
-#define DT_DEBUG 21 /* [ptr] For debugging; unspecified */
-#define DT_TEXTREL 22 /* [ignored] Reloc might modify .text */
-#define DT_JMPREL 23 /* [ptr] Address of PLT relocs */
-#define DT_BIND_NOW 24 /* [ignored] Process relocations of object */
-#define DT_MAX_CNT 25
+#define DT_NULL 0 /* [ignored] Marks end of dynamic section */
+#define DT_NEEDED 1 /* [val] Name of needed library */
+#define DT_PLTRELSZ 2 /* [val] Size in bytes of PLT relocs */
+#define DT_PLTGOT 3 /* [ptr] Processor defined value */
+#define DT_HASH 4 /* [ptr] Address of symbol hash table */
+#define DT_STRTAB 5 /* [ptr] Address of string table */
+#define DT_SYMTAB 6 /* [ptr] Address of symbol table */
+#define DT_RELA 7 /* [ptr] Address of Rela relocs */
+#define DT_RELASZ 8 /* [val] Total size of Rela relocs */
+#define DT_RELAENT 9 /* [val] Size of one Rela reloc */
+#define DT_STRSZ 10 /* [val] Size of string table */
+#define DT_SYMENT 11 /* [val] Size of one symbol table entry */
+#define DT_INIT 12 /* [ptr] Address of init function */
+#define DT_FINI 13 /* [ptr] Address of termination function */
+#define DT_SONAME 14 /* [val] Name of shared object */
+#define DT_RPATH 15 /* [val] Library search path (deprecated) */
+#define DT_SYMBOLIC 16 /* [ignored] Start symbol search here */
+#define DT_REL 17 /* [ptr] Address of Rel relocs */
+#define DT_RELSZ 18 /* [val] Total size of Rel relocs */
+#define DT_RELENT 19 /* [val] Size of one Rel reloc */
+#define DT_PLTREL 20 /* [val] Type of reloc in PLT */
+#define DT_DEBUG 21 /* [ptr] For debugging; unspecified */
+#define DT_TEXTREL 22 /* [ignored] Reloc might modify .text */
+#define DT_JMPREL 23 /* [ptr] Address of PLT relocs */
+#define DT_BIND_NOW 24 /* [ignored] Process relocations of object */
+#define DT_INIT_ARRAY 25 /* [ptr] Address of array of initialization functions */
+#define DT_FINI_ARRAY 26 /* [ptr] Address of array of termination functions */
+#define DT_INIT_ARRAYSZ 27 /* [val] Size in bytes of the initialization array */
+#define DT_FINI_ARRAYSZ 28 /* [val] Size in bytes of the termination array */
+#define DT_MAX_CNT 29
typedef struct {
uint64_t tag;
@@ -167,6 +171,8 @@ typedef struct {
#define ELF64_R_TYPE(i) ((i)&0xffffffffL)
// x86_64 relocation types.
-#define R_X86_64_COPY 5 /* Copy content from sym addr to relocation address */
-#define R_X86_64_GLOB_DAT 6 /* Address affected by relocation: `offset` (+ base) */
-#define R_X86_64_JUMP_SLOT 7 /* Address affected by relocation: `offset` (+ base) */
+#define R_X86_64_64 1 /* Absolute 64bit address, address affected by relocation: `base + offset` */
+#define R_X86_64_COPY 5 /* Copy content from sym addr to relocation address: `base + offset` */
+#define R_X86_64_GLOB_DAT 6 /* Address affected by relocation: `base + offset` */
+#define R_X86_64_JUMP_SLOT 7 /* Address affected by relocation: `base + offset` */
+#define R_X86_64_RELATIVE 8 /* Relative address *`base + offset` = `base + addend` */