aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjohannst <johannes.stoelp@gmail.com>2021-04-21 23:41:59 +0200
committerjohannst <johannes.stoelp@gmail.com>2021-04-21 23:41:59 +0200
commit85230524414b6d27664bf77c8584bfeced6c71cb (patch)
treea407d634cbd379dbdf4451fee4abfd64aeaa5f67
parent1df4dfdcdbbaef7e5b32c5c0bfadec02b3ccd6f0 (diff)
downloaddynld-85230524414b6d27664bf77c8584bfeced6c71cb.tar.gz
dynld-85230524414b6d27664bf77c8584bfeced6c71cb.zip
add support to resolve all relocations in PLT & RELA tables; add global variable as example to libgreet.so
-rw-r--r--04_dynld_nostd/Makefile6
-rw-r--r--04_dynld_nostd/dynld.c115
-rw-r--r--04_dynld_nostd/libgreet.c4
-rw-r--r--04_dynld_nostd/main.c6
-rw-r--r--lib/include/elf.h7
5 files changed, 100 insertions, 38 deletions
diff --git a/04_dynld_nostd/Makefile b/04_dynld_nostd/Makefile
index 8cd9d35..c1a472a 100644
--- a/04_dynld_nostd/Makefile
+++ b/04_dynld_nostd/Makefile
@@ -16,12 +16,6 @@ main: dynld.so main.c ../lib/libcommon.a
-Wl,--hash-style=sysv \
libgreet.c
- @if readelf -W -S libgreet.so | grep plt >& /dev/null; then \
- echo "ERROR: libgreet.so contains PLT while we don't support relocation calls in libgreet.so!"; \
- echo " All function calls in libgreet.so must be statically resolved!"; \
- exit 1; \
- fi
-
@# For now ew only add support for ELF hash tables (DT_HASH).
@# Therefore we specify the `hash-style` below.
gcc -o $@ \
diff --git a/04_dynld_nostd/dynld.c b/04_dynld_nostd/dynld.c
index ad600f5..56d48b7 100644
--- a/04_dynld_nostd/dynld.c
+++ b/04_dynld_nostd/dynld.c
@@ -105,7 +105,7 @@ void decode_dynamic(Dso* dso, uint64_t dynoff) {
ERROR_ON(dso->dynamic[DT_HASH] == 0, "DT_HASH missing in dynamic section!");
}
-Dso get_prog_info(const ExecInfo* info) {
+Dso get_prog_dso(const ExecInfo* info) {
Dso prog = {0};
// Determine the base address of the user program.
@@ -194,11 +194,16 @@ const Elf64Sym* get_sym(const Dso* dso, const uint64_t idx) {
return (const Elf64Sym*)(dso->base + dso->dynamic[DT_SYMTAB]) + idx;
}
-const Elf64Rela* get_pltreloc(const Dso* dso, const uint64_t idx) {
+const Elf64Rela* get_pltreloca(const Dso* dso, const uint64_t idx) {
ERROR_ON(dso->dynamic[DT_PLTRELSZ] < sizeof(Elf64Rela) * idx, "PLT relocation table indexed out-of-bounds!");
return (const Elf64Rela*)(dso->base + dso->dynamic[DT_JMPREL]) + idx;
}
+const Elf64Rela* get_reloca(const Dso* dso, const uint64_t idx) {
+ ERROR_ON(dso->dynamic[DT_RELASZ] < sizeof(Elf64Rela) * idx, "RELA relocation table indexed out-of-bounds!");
+ return (const Elf64Rela*)(dso->base + dso->dynamic[DT_RELA]) + idx;
+}
+
/// -------------
/// Symbol lookup
@@ -212,17 +217,26 @@ int strcmp(const char* s1, const char* s2) {
return *(unsigned char*)s1 - *(unsigned char*)s2;
}
-void* lookup_sym(const Dso* dso, const char* sym_name) {
+// Perform naive lookup for global symbol and return address if symbol was found.
+//
+// For simplicity this lookup doesn't use the hash table (`DT_HASH` |
+// `DT_GNU_HASH`) but rather iterates of the dynamic symbol table. Using the
+// hash table doesn't change the lookup result, however it yields better
+// performance for large symbol tables.
+//
+// `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) {
for (unsigned i = 0; i < get_num_dynsyms(dso); ++i) {
const Elf64Sym* sym = get_sym(dso, i);
- if (ELF64_ST_TYPE(sym->info) == STT_FUNC && ELF64_ST_BIND(sym->info) == STB_GLOBAL && sym->shndx != SHN_UNDEF) {
+ if (ELF64_ST_TYPE(sym->info) == sym_type && 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;
}
}
}
-
return 0;
}
@@ -324,7 +338,7 @@ Dso map_dependency(const char* dependency) {
//
// This is typically used by the `.bss` section.
if (p->memsz > p->filesz) {
- memset(base + addr_start + p->filesz, 0 /* byte */, p->memsz - p->filesz /*len*/);
+ memset(base + p->vaddr + p->filesz, 0 /* byte */, p->memsz - p->filesz /*len*/);
}
}
@@ -338,34 +352,74 @@ Dso map_dependency(const char* dependency) {
}
-/// ------------------------------
-/// Dynamic Linking (lazy resolve)
-/// ------------------------------
+/// -------------------
+/// Resolve relocations
+/// -------------------
struct LinkMap {
const Dso* dso; // Pointer to Dso list object.
- const struct LinkMap* next; // Pointer to next LinkMap entry.
+ const struct LinkMap* next; // Pointer to next LinkMap entry ('0' terminates the list).
};
typedef struct LinkMap LinkMap;
-void resolve_relocs(const Dso* dso, const LinkMap* map) {
- for (unsigned long relocidx = 0; relocidx < (dso->dynamic[DT_PLTRELSZ] / sizeof(Elf64Rela)); ++relocidx) {
- const Elf64Rela* reloc = get_pltreloc(dso, relocidx);
- ERROR_ON(ELF64_R_TYPE(reloc->info) != R_X86_64_JUMP_SLOT, "Expected relocation entry of type X86_64_JUMP_SLOT!");
-
- const int symidx = ELF64_R_SYM(reloc->info);
- const char* symname = get_str(dso, get_sym(dso, symidx)->name);
-
- void* symaddr = 0;
- for (const LinkMap* lmap = 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);
+// Resolve a single relocation of `dso`.
+//
+// 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) {
+ // Get symbol referenced by relocation.
+ const int symidx = ELF64_R_SYM(reloc->info);
+ const Elf64Sym* sym = get_sym(dso, symidx);
+ const char* symname = get_str(dso, sym->name);
+
+ // Get relocation typy.
+ unsigned reloctype = ELF64_R_TYPE(reloc->info);
+
+ // Lookup 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);
+ }
+ 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);
+
+ // 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. */
+ // Patch storage unit of relocation with absolute address of the symbol.
+ *(uint64_t*)(dso->base + reloc->offset) = (uint64_t)symaddr;
+ break;
+ case R_X86_64_COPY: /* Reference to global variable in shared ELF file. */
+ // Copy initial value of variable into relocation address.
+ memcpy(dso->base + reloc->offset, (void*)symaddr, sym->size);
+ break;
+ default:
+ ERROR_ON(true, "Unsupported relocation type %d!\n", reloctype);
+ }
+}
- pfmt("Resolved reloc %s to %p\n", symname, symaddr);
+// Resolve all relocations of `dso`.
+//
+// Resolve relocations from the PLT & RELA tables. Use `map` as link map which
+// defines the order of the symbol lookup.
+static void resolve_relocs(const Dso* dso, const LinkMap* map) {
+ // Resolve all relocation from the RELA table found in `dso`. There is
+ // typically one relocation per undefined dynamic object symbol (eg global
+ // 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);
+ }
- // Patch storage unit of relocation with absolute address of the symbol.
- *(uint64_t*)(dso->base + reloc->offset) = (uint64_t)symaddr;
+ // 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);
}
}
@@ -408,7 +462,7 @@ void dl_entry(const uint64_t* prctx) {
// Initialize dso handle for user program but extracting necesarry
// information from `AUXV` and the `PHDR`.
- const Dso dso_prog = get_prog_info(&exec_info);
+ const Dso dso_prog = get_prog_dso(&exec_info);
// Map dependency.
//
@@ -430,9 +484,11 @@ void dl_entry(const uint64_t* prctx) {
const LinkMap map_lib = {.dso = &dso_lib, .next = 0};
const LinkMap map_prog = {.dso = &dso_prog, .next = &map_lib};
- // Resolve relocations for the main program.
- resolve_relocs(&dso_prog, &map_prog);
+ // 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.
//
@@ -458,6 +514,7 @@ void dl_entry(const uint64_t* prctx) {
// GOT[1]; // Pushed by PLT0 pad on stack before jumping to got[2] -> Word the dynamic linker can use to identify the caller.
// GOT[2]; // Jump target for PLT0 pad (when doing lazy resolving).
+ // Transfer control to user program.
dso_prog.entry();
_exit(0);
diff --git a/04_dynld_nostd/libgreet.c b/04_dynld_nostd/libgreet.c
index f2a96a3..439e236 100644
--- a/04_dynld_nostd/libgreet.c
+++ b/04_dynld_nostd/libgreet.c
@@ -1,9 +1,13 @@
// Copyright (c) 2020 Johannes Stoelp
+int gCalled = 0;
+
const char* get_greet() {
+ ++gCalled;
return "Hello from libgreet.so!";
}
const char* get_greet2() {
+ ++gCalled;
return "Hello 2 from libgreet.so!";
}
diff --git a/04_dynld_nostd/main.c b/04_dynld_nostd/main.c
index ca250ef..a787158 100644
--- a/04_dynld_nostd/main.c
+++ b/04_dynld_nostd/main.c
@@ -6,13 +6,17 @@
// API of `libgreet.so`.
extern const char* get_greet();
extern const char* get_greet2();
+extern int gCalled;
void _start() {
pfmt("Running _start() @ %s\n", __FILE__);
- // Call function from libgreet.so -> generates PLT entry.
+ // Call function from libgreet.so -> generates PLT relocations.
pfmt("get_greet() -> %s\n", get_greet());
pfmt("get_greet2() -> %s\n", get_greet2());
+ // Reference global variable from libgreet.so -> generates RELA relocation.
+ pfmt("libgreet.so called %d times\n", gCalled);
+
_exit(0);
}
diff --git a/lib/include/elf.h b/lib/include/elf.h
index 4856c79..f0a0940 100644
--- a/lib/include/elf.h
+++ b/lib/include/elf.h
@@ -141,11 +141,12 @@ typedef struct {
// Symbol Types.
#define STT_NOTYPE 0 /* No type. */
+#define STT_OBJECT 1 /* Data Object. */
#define STT_FUNC 2 /* Function entry point. */
// Special Section Indicies.
-#define SHN_UNDEF 0 /* Undefined section. */
-#define SHN_ABS 0xff1 /* Indicates an absolute value. */
+#define SHN_UNDEF 0 /* Undefined section. */
+#define SHN_ABS 0xff1 /* Indicates an absolute value. */
/// -----------------
/// Relocations Entry
@@ -166,4 +167,6 @@ 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) */