diff options
Diffstat (limited to '')
-rw-r--r-- | mld/seg.c | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/mld/seg.c b/mld/seg.c new file mode 100644 index 0000000..a720720 --- /dev/null +++ b/mld/seg.c @@ -0,0 +1,135 @@ +#include <merror.h> +#include <melf.h> +#include <elf.h> +#include <sys/mman.h> + +#include "link.h" + +/** + * Read the phdr for this segment + */ +static int load_phdr(struct object *obj, struct segment *seg, size_t index) +{ + if (index >= obj->phdr_len) { + ERROR("phdr index [%d] out of bounds", index); + return M_ERROR; + } + + seg->phdr = &obj->phdr[index]; + seg->phdr_idx = index; + return M_SUCCESS; +} + +/** + * Read the shdr for this segment + */ +static int load_shdr(struct object *obj, struct segment *seg, size_t index) +{ + bool found = false; + + for (size_t i = 0; i < obj->shdr_len; i++) { + Elf32_Shdr *hdr = &obj->shdr[i]; + + // find shdr that matches the offset in phdr + if (seg->phdr->p_offset != hdr->sh_offset) + continue; + + + // get name + uint32_t name = B32(hdr->sh_name); + if (name >= obj->shstrtab->len) { + ERROR("section name index [%d] out of bounds", name); + return M_ERROR; + } + seg->name = &obj->shstrtab->data[name]; + + // map bytes + uint32_t len = B32(hdr->sh_size); + uint32_t off = B32(hdr->sh_offset); + seg->bytes = (unsigned char *) (obj->mapped + off); + + if (BOUND_CHK(obj, len, off)) { + ERROR("cannot map seg %s:%d", seg->name, index); + return M_ERROR; + } + + found = true; + seg->shdr = hdr; + seg->shdr_idx = i; + break; + } + + if (!found) { + ERROR("cannot find shdr for segment [%d]", index); + return M_ERROR; + } + + return M_SUCCESS; +} + +/** + * Read the relocation table for this segment (if one exists) + */ +static int load_reltab(struct object *obj, struct segment *seg) +{ + // default to none + seg->reltab.len = 0; + + for (size_t i = 0; i < obj->shdr_len; i++) { + Elf32_Shdr *hdr = &obj->shdr[i]; + uint32_t type = B32(hdr->sh_type); + + if (type != SHT_REL && type != SHT_RELA) + continue; + + if (B32(hdr->sh_info) != seg->shdr_idx) + continue; + + uint32_t symtab_idx = B32(hdr->sh_link); + if (symtab_idx >= obj->shdr_len) { + ERROR("reltab [%d] symtab index [%d] out of bounds", + i, symtab_idx); + return M_ERROR; + } + + struct symbol_table *symtab = &obj->symtabs[symtab_idx]; + if (symtab->len < 1) { + ERROR("reltab [%d] symtab is empty or invalid", i); + return M_ERROR; + } + + uint32_t len = B32(hdr->sh_size); + uint32_t off = B32(hdr->sh_offset); + + + seg->reltab.symtab = symtab; + seg->reltab.len = len; + seg->reltab.raw = obj->mapped + off; + seg->reltab.type = type; + + if (BOUND_CHK(obj, len, off)) { + ERROR("cannot map reltab [%d] for %s", + i, seg->name); + return M_ERROR; + } + + break; + } + + return M_SUCCESS; +} + +int segment_load(struct object *obj, struct segment *seg, size_t index) +{ + + if (load_phdr(obj, seg, index)) + return M_ERROR; + + if (load_shdr(obj, seg, index)) + return M_ERROR; + + if (load_reltab(obj, seg)) + return M_ERROR; + + return M_SUCCESS; +} |