From 90314052fabbb97045ebfe0832aae2cfdb225763 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 31 Jul 2015 02:38:20 +0300 Subject: [PATCH] Implement a dynamic linker. --- software/include/dyld/dyld.h | 32 +++++++ software/include/dyld/elf.h | 38 ++++++++ software/include/dyld/link.h | 6 +- software/libdyld/Makefile | 19 ++++ software/libdyld/dyld.c | 166 +++++++++++++++++++++++++++++++++++ 5 files changed, 258 insertions(+), 3 deletions(-) create mode 100644 software/include/dyld/dyld.h create mode 100644 software/libdyld/Makefile create mode 100644 software/libdyld/dyld.c diff --git a/software/include/dyld/dyld.h b/software/include/dyld/dyld.h new file mode 100644 index 00000000..eeb20628 --- /dev/null +++ b/software/include/dyld/dyld.h @@ -0,0 +1,32 @@ +#ifndef __DYLD_H +#define __DYLD_H + +#include + +struct dyld_info { + Elf32_Addr base; + void *init; + const char *strtab; + Elf32_Sym *symtab; + struct { + Elf32_Word nbucket; + Elf32_Word nchain; + Elf32_Word *bucket; + Elf32_Word *chain; + } hash; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +int dyld_load(void *shlib, Elf32_Addr base, + Elf32_Addr (*resolve_import)(const char *), + struct dyld_info *info, const char **error_out); +void *dyld_lookup(const char *symbol, struct dyld_info *info); + +#ifdef __cplusplus +} +#endif + +#endif /* __DYLD_H */ diff --git a/software/include/dyld/elf.h b/software/include/dyld/elf.h index 8ffb0672..c84c28a1 100644 --- a/software/include/dyld/elf.h +++ b/software/include/dyld/elf.h @@ -3297,6 +3297,44 @@ typedef Elf32_Addr Elf32_Conflict; #define R_TILEGX_NUM 130 +/* OR1K relocations */ +#define R_OR1K_NONE 0 +#define R_OR1K_32 1 +#define R_OR1K_16 2 +#define R_OR1K_8 3 +#define R_OR1K_LO_16_IN_INSN 4 +#define R_OR1K_HI_16_IN_INSN 5 +#define R_OR1K_INSN_REL_26 6 +#define R_OR1K_GNU_VTENTRY 7 +#define R_OR1K_GNU_VTINHERIT 8 +#define R_OR1K_32_PCREL 9 +#define R_OR1K_16_PCREL 10 +#define R_OR1K_8_PCREL 11 +#define R_OR1K_GOTPC_HI16 12 +#define R_OR1K_GOTPC_LO16 13 +#define R_OR1K_GOT16 14 +#define R_OR1K_PLT26 15 +#define R_OR1K_GOTOFF_HI16 16 +#define R_OR1K_GOTOFF_LO16 17 +#define R_OR1K_COPY 18 +#define R_OR1K_GLOB_DAT 19 +#define R_OR1K_JMP_SLOT 20 +#define R_OR1K_RELATIVE 21 +#define R_OR1K_TLS_GD_HI16 22 +#define R_OR1K_TLS_GD_LO16 23 +#define R_OR1K_TLS_LDM_HI16 24 +#define R_OR1K_TLS_LDM_LO16 25 +#define R_OR1K_TLS_LDO_HI16 26 +#define R_OR1K_TLS_LDO_LO16 27 +#define R_OR1K_TLS_IE_HI16 28 +#define R_OR1K_TLS_IE_LO16 29 +#define R_OR1K_TLS_LE_HI16 30 +#define R_OR1K_TLS_LE_LO16 31 +#define R_OR1K_TLS_TPOFF 32 +#define R_OR1K_TLS_DTPOFF 33 +#define R_OR1K_TLS_DTPMOD 34 + +#define R_OR1K_NUM 35 #ifdef __cplusplus } diff --git a/software/include/dyld/link.h b/software/include/dyld/link.h index 24bac433..effa32b8 100644 --- a/software/include/dyld/link.h +++ b/software/include/dyld/link.h @@ -1,5 +1,5 @@ -#ifndef __LIMITS_H -#define __LIMITS_H +#ifndef __LINK_H +#define __LINK_H #include #include @@ -25,4 +25,4 @@ extern int dl_iterate_phdr (int (*__callback) (struct dl_phdr_info *, } #endif -#endif /* __LIMITS_H */ +#endif /* __LINK_H */ diff --git a/software/libdyld/Makefile b/software/libdyld/Makefile new file mode 100644 index 00000000..27a29db8 --- /dev/null +++ b/software/libdyld/Makefile @@ -0,0 +1,19 @@ +MSCDIR=../.. +include $(MSCDIR)/software/common.mak + +COMMONFLAGS += -I$(MSCDIR)/software/include/dyld + +OBJECTS=dyld.o + +all: libdyld.a + +# pull in dependency info for *existing* .o files +-include $(OBJECTS:.o=.d) + +libdyld.a: $(OBJECTS) + $(AR) crs libdyld.a $(OBJECTS) + +.PHONY: clean + +clean: + $(RM) $(OBJECTS) $(OBJECTS:.o=.d) libdyld.a .*~ *~ diff --git a/software/libdyld/dyld.c b/software/libdyld/dyld.c new file mode 100644 index 00000000..04cdbc62 --- /dev/null +++ b/software/libdyld/dyld.c @@ -0,0 +1,166 @@ +#include +#include +#include +#include + +static int fixup_rela(Elf32_Addr base, Elf32_Rela *rela, + const char *strtab, Elf32_Sym *symtab, + Elf32_Addr (*resolve_import)(const char *), + const char **error_out) { + Elf32_Sym *sym = NULL; + if(ELF32_R_SYM(rela->r_info) != 0) + sym = &symtab[ELF32_R_SYM(rela->r_info)]; + Elf32_Addr value; + + switch(ELF32_R_TYPE(rela->r_info)) { + case R_OR1K_RELATIVE: + value = base + (sym ? sym->st_value : 0) + rela->r_addend; + break; + + case R_OR1K_JMP_SLOT: + value = resolve_import(&strtab[sym->st_name]); + if(value == 0) { + static char error[256]; + error[scnprintf(error, sizeof(error), "ELF object has an unresolved symbol: %s", + &strtab[sym->st_name])] = 0; + *error_out = error; + return 0; + } + break; + + default: + *error_out = "ELF object uses an unsupported relocation type"; + return 0; + } + + *(Elf32_Addr*)(base + rela->r_offset) = value; + + return 1; +} + +int dyld_load(void *shlib, Elf32_Addr base, + Elf32_Addr (*resolve_import)(const char *), + struct dyld_info *info, const char **error_out) { + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)shlib; + + const unsigned char expected_ident[EI_NIDENT] = { + ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, + ELFCLASS32, ELFDATA2MSB, EV_CURRENT, + ELFOSABI_NONE, /* ABI version */ 0 + }; + if(memcmp(ehdr->e_ident, expected_ident, EI_NIDENT) || + ehdr->e_type != ET_DYN) { + *error_out = "ELF object is not a shared library"; + return 0; + } + +#ifdef __or1k__ + if(ehdr->e_machine != EM_OPENRISC) { + *error_out = "ELF object does not contain OpenRISC machine code"; + return 0; + } +#else +#error Unsupported architecture +#endif + + Elf32_Phdr *phdr = (Elf32_Phdr *)((intptr_t)shlib + ehdr->e_phoff); + Elf32_Dyn *dyn = NULL; + for(int i = 0; i < ehdr->e_phnum; i++) { + if(phdr[i].p_type == PT_DYNAMIC) + dyn = (Elf32_Dyn *)((intptr_t)shlib + phdr[i].p_offset); + + memcpy((void*)(base + phdr[i].p_vaddr), + (void*)((intptr_t)shlib + phdr[i].p_offset), + phdr[i].p_filesz); + } + + if(dyn == NULL) { + *error_out = "ELF object does not have a PT_DYNAMIC header"; + return 0; + } + + char *strtab = NULL; + Elf32_Sym *symtab = NULL; + Elf32_Rela *rela = NULL, *pltrel = NULL; + Elf32_Word *hash = NULL, init = 0; + size_t syment = sizeof(Elf32_Sym), relaent = sizeof(Elf32_Rela), + relanum = 0, pltrelnum = 0; + while(dyn->d_tag != DT_NULL) { + switch(dyn->d_tag) { + case DT_STRTAB: strtab = (char *)(base + dyn->d_un.d_ptr); break; + case DT_SYMTAB: symtab = (Elf32_Sym *)(base + dyn->d_un.d_ptr); break; + case DT_SYMENT: syment = dyn->d_un.d_val; break; + case DT_RELA: rela = (Elf32_Rela *)(base + dyn->d_un.d_ptr); break; + case DT_RELAENT: relaent = dyn->d_un.d_val; break; + case DT_RELASZ: relanum = dyn->d_un.d_val / sizeof(Elf32_Rela); break; + case DT_JMPREL: pltrel = (Elf32_Rela *)(base + dyn->d_un.d_ptr); break; + case DT_PLTRELSZ: pltrelnum = dyn->d_un.d_val / sizeof(Elf32_Rela); break; + case DT_HASH: hash = (Elf32_Word *)dyn->d_un.d_val; break; + case DT_INIT: init = dyn->d_un.d_val; break; + + case DT_REL: + *error_out = "ELF object uses Rel relocations, which are not supported"; + return 0; + } + + ++dyn; + } + + if(symtab == NULL || syment == 0 || strtab == NULL) { + *error_out = "ELF object must contain a symbol table"; + return 0; + } + + if(syment != sizeof(Elf32_Sym) || relaent != sizeof(Elf32_Rela)) { + *error_out = "ELF object uses an unknown format for symbols and relocations"; + return 0; + } + + for(int i = 0; i < relanum; i++) { + if(!fixup_rela(base, &rela[i], strtab, symtab, resolve_import, error_out)) + return 0; + } + + for(int i = 0; i < pltrelnum; i++) { + if(!fixup_rela(base, &pltrel[i], strtab, symtab, resolve_import, error_out)) + return 0; + } + + info->base = base; + info->init = (void*)(base + init); + info->strtab = strtab; + info->symtab = symtab; + info->hash.nbucket = hash[0]; + info->hash.nchain = hash[1]; + info->hash.bucket = &hash[2]; + info->hash.chain = &hash[2 + info->hash.nbucket]; + + return 1; +} + +static unsigned long elf_hash(const unsigned char *name) +{ + unsigned long h = 0, g; + while(*name) { + h = (h << 4) + *name++; + if((g = h & 0xf0000000)) { + h ^= g >> 24; + h &= ~g; + } + } + return h; +} + +void *dyld_lookup(const char *symbol, struct dyld_info *info) { + unsigned hash = elf_hash((const unsigned char*) symbol); + unsigned index = info->hash.bucket[hash % info->hash.nbucket]; + while(strcmp(&info->strtab[info->symtab[index].st_name], symbol) && + info->hash.chain[index] != STN_UNDEF) + index = info->hash.chain[index]; + + if(info->hash.chain[index] != STN_UNDEF) { + return (void*)(info->base + info->symtab[index].st_value); + } else { + return NULL; + } +} -- 2.30.2