diff options
Diffstat (limited to 'mld/obj.c')
-rw-r--r-- | mld/obj.c | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/mld/obj.c b/mld/obj.c new file mode 100644 index 0000000..9a83d31 --- /dev/null +++ b/mld/obj.c @@ -0,0 +1,335 @@ +#include <elf.h> +#include <merror.h> +#include <melf.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +#include "link.h" + +extern char *current_file; + +static int obj_assert_ehdr(const void *given, const void *assert, size_t size) +{ + if (memcmp(given, assert, size) == 0) + return M_SUCCESS; + ERROR("invalid elf file"); + return M_ERROR; +} + +/** + * Map the ehdr + */ +static int load_ehdr(struct object *object) +{ + uint32_t off = 0; + object->ehdr = (Elf32_Ehdr *) (object->mapped + off); + + if (BOUND_CHK(object, sizeof(Elf32_Ehdr), off)) { + ERROR("cannot read ehdr"); + return M_ERROR; + } + + /** + * Compare each "static" value in the ehdr and make sure it + * is what it should be. If not throw and error and eventually + * return. + */ + + #define EHDR_ASSERT(name, size) \ + if (res == M_SUCCESS) \ + res |= obj_assert_ehdr(&MIPS_ELF_EHDR.e_##name, \ + &object->ehdr->e_##name, size) \ + + int res = 0; + EHDR_ASSERT(ident, EI_NIDENT); + EHDR_ASSERT(type, sizeof(Elf32_Half)); + EHDR_ASSERT(machine, sizeof(Elf32_Half)); + EHDR_ASSERT(version, sizeof(Elf32_Word)); + EHDR_ASSERT(flags, sizeof(Elf32_Word)); + EHDR_ASSERT(ehsize, sizeof(Elf32_Half)); + EHDR_ASSERT(phentsize, sizeof(Elf32_Half)); + EHDR_ASSERT(shentsize, sizeof(Elf32_Half)); + + #undef EHDR_ASSERT + + if (res) + return M_ERROR; + + return M_SUCCESS; +} + +/** + * Map the shdr + */ +static int load_shdr(struct object *object) +{ + size_t shdr_len = B16(object->ehdr->e_shentsize) * + B16(object->ehdr->e_shnum); + size_t shdr_off = B32(object->ehdr->e_shoff); + object->shdr = (Elf32_Shdr *) (object->mapped + shdr_off); + object->shdr_len = B16(object->ehdr->e_shnum); + + if (BOUND_CHK(object, shdr_len, shdr_off)) { + ERROR("cannot read shdr"); + return M_ERROR; + } + + return M_SUCCESS; +} + +/** + * Map the phdr + */ +static int load_phdr(struct object *object) +{ + size_t phdr_len = B16(object->ehdr->e_phentsize) * + B16(object->ehdr->e_phnum); + size_t phdr_off = B32(object->ehdr->e_phoff); + object->phdr = (Elf32_Phdr *) (object->mapped + phdr_off); + object->phdr_len = B16(object->ehdr->e_phnum); + + if (BOUND_CHK(object, phdr_len, phdr_off)) { + ERROR("cannot read phdr"); + return M_ERROR; + } + + return M_SUCCESS; +} + +/** + * Load the strtabs + */ +static int load_strtabs(struct object *object) +{ + uint32_t max_entries = object->shdr_len; + struct string_table *strtabs = malloc(max_entries * + sizeof(struct string_table)); + + if (strtabs == NULL) { + PERROR("cannot alloc"); + return M_ERROR; + } + + for (size_t i = 0; i < object->shdr_len; i++) { + Elf32_Shdr *hdr = &object->shdr[i]; + struct string_table *strtab = &strtabs[i]; + + if (B32(hdr->sh_type) != SHT_STRTAB) { + strtab->len = 0; + continue; + } + + uint32_t off = B32(hdr->sh_offset); + uint32_t len = B32(hdr->sh_size); + + strtab->len = len; + strtab->data = object->mapped + off; + + if (len < 1) { + ERROR("invalid or empty strtab [%d]", i); + return M_ERROR; + } + + if (BOUND_CHK(object, len, off)) { + ERROR("cannot map strtab [%d]", i); + return M_ERROR; + } + + if (strtab->data[0] != '\0' || + strtab->data[len - 1] != '\0') { + ERROR("strtab [%d] doesn't not have bounding null " + "values", i); + return M_ERROR; + } + } + + object->strtabs = strtabs; + return M_SUCCESS; +} + +/** + * Load the symtabs + */ +static int load_symtabs(struct object *object) +{ + uint32_t max_entries = object->shdr_len; + struct symbol_table *symtabs = malloc(max_entries * + sizeof(struct symbol_table)); + + if (symtabs == NULL) { + PERROR("cannot alloc"); + return M_ERROR; + } + + for (size_t i = 0; i < object->shdr_len; i++) { + Elf32_Shdr *hdr = &object->shdr[i]; + struct symbol_table *symtab = &symtabs[i]; + + if (B32(hdr->sh_type) != SHT_SYMTAB) { + symtab->len = 0; + continue; + } + + uint32_t off = B32(hdr->sh_offset); + uint32_t len = B32(hdr->sh_size); + uint32_t stridx = B32(hdr->sh_link); + + if (stridx >= max_entries) { + ERROR("strtab index [%d] out of bounds", stridx); + return M_ERROR; + } + + struct string_table *strtab = &object->strtabs[stridx]; + if (strtab->len < 1) { + ERROR("strtab index [%d] empty or invalid", stridx); + return M_ERROR; + } + + symtab->strtab = strtab; + symtab->len = len; + symtab->syms = (Elf32_Sym *) (object->mapped + off); + + if (BOUND_CHK(object, len, off)) { + ERROR("cannot map symtab index [%d]", i); + return M_ERROR; + } + } + + object->symtabs = symtabs; + return M_SUCCESS; +} + +/** + * Load the shstrtab + */ +static int load_shstrtab(struct object *object) +{ + uint16_t idx = B16(object->ehdr->e_shstrndx); + if (idx >= object->shdr_len) { + ERROR("shstrndx [%d] out of bounds", idx); + return M_ERROR; + } + + struct string_table *strtab = &object->strtabs[idx]; + if (strtab->len < 1) { + ERROR("shstrndx is invalid or empty"); + return M_ERROR; + } + + object->shstrtab = strtab; + return M_SUCCESS; +} + +/** + * Load the program segments + */ +static int load_segments(struct object *object) +{ + object->segment_len = B16(object->ehdr->e_phnum); + object->segments = malloc(sizeof(struct segment) * + object->segment_len); + + if (object->segments == NULL) { + PERROR("cannot alloc"); + return M_ERROR; + } + + for (size_t i = 0; i < object->segment_len; i++) { + struct segment *seg = &object->segments[i]; + if (segment_load(object, seg, i)) { + return M_ERROR; + } + } + + return M_SUCCESS; +} + +static int map_file(struct object *obj, char *path) +{ + obj->fd = open(path, O_RDONLY); + if (obj->fd == -1) { + PERROR("cannot read"); + return M_ERROR; + } + + struct stat st; + if (stat(path, &st)) { + PERROR("cannot stat"); + return M_ERROR; + } + + obj->mapped_size = st.st_size; + obj->mapped = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, + obj->fd, 0); + + if (obj->mapped == MAP_FAILED) { + PERROR("cannot map"); + return M_ERROR; + } + + return M_SUCCESS; +} + +int object_load(struct object *object, char *path) +{ + current_file = path; + + object->fd = 0; + object->segments = NULL; + object->symtabs = NULL; + object->strtabs = NULL; + object->mapped = NULL; + object->name = path; + + /** load the file */ + if (map_file(object, path)) + return M_ERROR; + + /* ehdr */ + if (load_ehdr(object)) + return M_ERROR; + + /* shdr */ + if (load_shdr(object)) + return M_ERROR; + + /* phdr */ + if (load_phdr(object)) + return M_ERROR; + + /* strtabs */ + if (load_strtabs(object)) + return M_ERROR; + + /* symtabs */ + if (load_symtabs(object)) + return M_ERROR; + + /* shstrtab */ + if (load_shstrtab(object)) + return M_ERROR; + + /* segments */ + if (load_segments(object)) + return M_ERROR; + + return M_SUCCESS; +} + +void object_free(struct object *obj) +{ + if (obj->symtabs != NULL) + free(obj->symtabs); + if (obj->strtabs != NULL) + free(obj->strtabs); + if (obj->segments != NULL) + free(obj->segments); + if (obj->fd > 0) + close(obj->fd); + if (obj->mapped != NULL && obj->mapped != MAP_FAILED) + munmap(obj->mapped, obj->mapped_size); +} |