140 lines
3 KiB
C
140 lines
3 KiB
C
#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)
|
|
{
|
|
uint32_t shndx = obj->phdr_to_shdr_mapping[index];
|
|
Elf32_Shdr *hdr = &obj->shdr[shndx];
|
|
|
|
// 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;
|
|
}
|
|
|
|
seg->shdr = hdr;
|
|
seg->shdr_idx = shndx;
|
|
|
|
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 ((type == SHT_REL && B32(hdr->sh_entsize) !=
|
|
sizeof(Elf32_Rel)) || (type == SHT_RELA &&
|
|
B32(hdr->sh_entsize) != sizeof(Elf32_Rela))) {
|
|
ERROR("reltab [%d] has invalid entry size", i);
|
|
return M_ERROR;
|
|
}
|
|
|
|
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 / B32(hdr->sh_entsize);
|
|
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;
|
|
|
|
seg->off = B32(seg->phdr->p_offset);
|
|
seg->vaddr = B32(seg->phdr->p_vaddr);
|
|
seg->size = B32(seg->phdr->p_filesz);
|
|
seg->new_off = 0;
|
|
seg->new_vaddr = 0;
|
|
|
|
seg->read = B32(seg->phdr->p_flags) & PF_R;
|
|
seg->write = B32(seg->phdr->p_flags) & PF_W;
|
|
seg->execute = B32(seg->phdr->p_flags) & PF_X;
|
|
seg->align = B32(seg->phdr->p_align);
|
|
|
|
seg->obj = obj;
|
|
seg->ent = NULL;
|
|
|
|
return M_SUCCESS;
|
|
}
|