391 lines
8 KiB
C
391 lines
8 KiB
C
#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 = sizeof(Elf32_Shdr) *
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Create the phdr
|
|
*/
|
|
static int create_phdr(struct object *object)
|
|
{
|
|
uint32_t entries = 0;
|
|
for (uint32_t i = 0; i < object->shdr_len; i++) {
|
|
Elf32_Shdr *hdr = &object->shdr[i];
|
|
uint32_t type = B32(hdr->sh_type);
|
|
|
|
if (type != SHT_PROGBITS && type != SHT_NOBITS)
|
|
continue;
|
|
|
|
entries += 1;
|
|
}
|
|
|
|
Elf32_Phdr *phdr = malloc(entries * sizeof(Elf32_Phdr));
|
|
if (phdr == NULL) {
|
|
PERROR("cannot alloc");
|
|
return M_ERROR;
|
|
}
|
|
|
|
object->phdr = phdr;
|
|
object->phdr_len = entries;
|
|
|
|
uint32_t *mapping = malloc(entries * sizeof(uint32_t));
|
|
if (mapping == NULL) {
|
|
PERROR("cannot alloc");
|
|
return M_ERROR;
|
|
}
|
|
|
|
object->phdr_to_shdr_mapping = mapping;
|
|
|
|
uint32_t index = 0;
|
|
for (uint32_t i = 0; i < object->shdr_len; i++) {
|
|
Elf32_Shdr *hdr = &object->shdr[i];
|
|
uint32_t type = B32(hdr->sh_type);
|
|
|
|
if (type != SHT_PROGBITS && type != SHT_NOBITS)
|
|
continue;
|
|
mapping[index] = i;
|
|
phdr[index++] = (Elf32_Phdr) {
|
|
.p_type = B32(PT_LOAD),
|
|
.p_flags = B32(
|
|
// execute
|
|
((B32(hdr->sh_flags) & SHF_EXECINSTR)
|
|
? PF_X : 0) |
|
|
// write
|
|
((B32(hdr->sh_flags) & SHF_WRITE)
|
|
? PF_W : 0) |
|
|
// read
|
|
((B32(hdr->sh_flags) & SHF_ALLOC)
|
|
? PF_R : 0)
|
|
),
|
|
.p_offset = hdr->sh_offset,
|
|
.p_vaddr = hdr->sh_addr,
|
|
.p_paddr = hdr->sh_addr,
|
|
.p_filesz = hdr->sh_size,
|
|
.p_memsz = hdr->sh_size,
|
|
.p_align = B32(SEC_ALIGN),
|
|
};
|
|
}
|
|
|
|
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;
|
|
symtab->size = 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 / sizeof(Elf32_Sym);
|
|
symtab->size = len / sizeof(Elf32_Sym);
|
|
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 = object->phdr_len;
|
|
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 | PROT_WRITE,
|
|
MAP_PRIVATE, 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, uint32_t index)
|
|
{
|
|
current_file = path;
|
|
|
|
object->fd = 0;
|
|
object->segments = NULL;
|
|
object->symtabs = NULL;
|
|
object->strtabs = NULL;
|
|
object->mapped = NULL;
|
|
object->name = path;
|
|
object->index = index;
|
|
object->phdr = NULL;
|
|
object->phdr_to_shdr_mapping = NULL;
|
|
|
|
/** 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 (create_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->phdr != NULL)
|
|
free(obj->phdr);
|
|
if (obj->phdr_to_shdr_mapping)
|
|
free(obj->phdr_to_shdr_mapping);
|
|
if (obj->fd > 0)
|
|
close(obj->fd);
|
|
if (obj->mapped != NULL && obj->mapped != MAP_FAILED)
|
|
munmap(obj->mapped, obj->mapped_size);
|
|
}
|