const char *elf_get_shared_object_name(const struct elf64_header *header, const size_t size) { const uint8_t *start = (const uint8_t *)header; if (!elf_verify(header, size) || elf_verify_shared_object(header)) { return NULL; } size_t dynamic_size; const struct elf_dyn *dynamic = elf_get_section(header, size, ".dynamic", &dynamic_size); if (dynamic == NULL || (uint8_t *)dynamic + dynamic_size > start + size) { return NULL; } size_t dynstrtab_size; const char *dynstrtab = elf_get_section(header, size, ".dynstr", &dynstrtab_size); if (dynstrtab == NULL || (uint8_t *)dynstrtab + dynstrtab_size > start + size) { return NULL; } for (size_t i = 0; i < dynamic_size / sizeof(struct elf_dyn); ++i) { if (dynamic[i].d_tag == DT_SONAME) { const char *soname = dynstrtab + dynamic[i].d_un.d_val; if (soname > dynstrtab + dynstrtab_size) { return NULL; } return soname; } } return NULL; }
void *elf_get_symbol_address(void *elf, size_t size, const char *name) { const struct elf_section_header *section; void *base; void *location; size_t strtab_size; const char *strtab = elf_get_section(elf, size, ".strtab", &strtab_size); if (strtab == NULL) { log(Log_Error, "Could not find strtab"); return NULL; } size_t symtab_size; const struct elf_symbol *symtab = elf_get_section(elf, size, ".symtab", &symtab_size); if (symtab == NULL) { log(Log_Error, "Could not find symtab"); return NULL; } base = elf_get_base_address(elf, size); if (base == NULL) { log(Log_Error, "Bad base"); return NULL; } for (size_t i = 0; i < symtab_size / sizeof(struct elf_symbol); ++i) { if (ELF64_R_TYPE(symtab[i].st_info) != STT_FILE) { const char *symbol_name = &strtab[symtab[i].st_name]; if ((void *)symbol_name > elf + size) { log(Log_Error, "Symbol name out of bounds"); return NULL; } section = elf_get_section_index(elf, size, symtab[i].st_index); if (section == NULL) { log(Log_Error, "Bad symbol index"); return NULL; } if (strncmp(symbol_name, name, strlen(name)) == 0) { location = base + symtab[i].st_value; if (location > elf + size) { logf(Log_Error, "Location out of bound %p %p\n", location, elf + size); return NULL; } return location; } } } return NULL; }
range elf_get_section_r(void* elf, const char* name) { range r; r.start = elf_get_section(elf, name, &r.elem_size); r.end = r.start + r.elem_size; return r; }
static enum status elf_run_init_fini_helper(void *module_start, size_t module_size, int dt_array, int dt_array_size) { void *base; size_t funcs_size = 0; bool funcs_size_found = false; enum status (**funcs)(void) = NULL; size_t dynamic_size; const struct elf_dyn *dynamic = elf_get_section(module_start, module_size, ".dynamic", &dynamic_size); if (dynamic == NULL) { log(Log_Error, "Couldn't find section .dynamic"); return Error_Invalid; } base = elf_get_base_address(module_start, module_size); if (base == NULL) { log(Log_Error, "Bad base"); return Error_Invalid; } for (size_t i = 0; i < dynamic_size / sizeof(struct elf_dyn); ++i) { if (dynamic[i].d_tag == dt_array) { funcs = base + dynamic[i].d_un.d_ptr; } else if (dynamic[i].d_tag == dt_array_size) { funcs_size = dynamic[i].d_un.d_val; funcs_size_found = true; } if (funcs != NULL && funcs_size_found) { break; } } if (funcs == NULL || !funcs_size_found) { log(Log_Info, "Module has no init/fini"); return Ok; } else if ((void *)funcs + funcs_size > module_start + module_size) { log(Log_Info, "Module init/fini out of bounds"); return Error_Invalid; } for (size_t i = 0; i < funcs_size / sizeof(enum status (*)(void)); ++i) { enum status status = funcs[i](); if (status != Ok) { return status; } } return Ok; }
enum status elf_relocate(void *module_start, size_t module_size) { int64_t *pointer; int64_t value; const struct elf_symbol *symbol; size_t rela_size; const struct elf_rela *rela; size_t dynsym_size; const struct elf_symbol *dynsym; size_t dynstr_size; void *base; const char *dynstr = elf_get_section(module_start, module_size, ".dynstr", &dynstr_size); if (dynstr == NULL) { log(Log_Error, "Couldn't find section .dynstr"); return Error_Invalid; } rela = elf_get_section(module_start, module_size, ".rela.dyn", &rela_size); if (rela == NULL) { log(Log_Error, "Couldn't find section .rela.dyn"); return Error_Invalid; } dynsym = elf_get_section(module_start, module_size, ".dynsym", &dynsym_size); if (dynsym == NULL) { log(Log_Error, "Couldn't find section .dynsym"); return Error_Invalid; } rela = elf_get_section(module_start, module_size, ".rela.dyn", &rela_size); if (rela == NULL) { log(Log_Error, "Couldn't find section .rela.dyn"); return Error_Invalid; } base = elf_get_base_address(module_start, module_size); if (base == NULL) { log(Log_Error, "Bad base"); return Error_Invalid; } for (size_t i = 0; i < rela_size / sizeof(struct elf_rela); ++i) { int r_type = ELF64_R_TYPE(rela[i].r_info); switch (r_type) { case R_X86_64_RELATIVE: pointer = module_start + rela[i].r_offset; value = (uintptr_t)base + rela[i].r_addend; *pointer = value; break; case R_X86_64_JUMP_SLOT: case R_X86_64_GLOB_DAT: case R_X86_64_64: symbol = &dynsym[ELF64_R_SYM(rela[i].r_info)]; if ((void *)(symbol + 1) > module_start + module_size) { logf(Log_Error, "Symbol out of bounds\n"); return Error_Invalid; } pointer = base + rela[i].r_offset; if (symbol->st_index == SHN_UNDEF) { if ((void *)&dynsym[symbol->st_name] > module_start + module_size) { log(Log_Error, "String out of bounds"); return Error_Invalid; } value = (uintptr_t)symbol_get(&dynstr[symbol->st_name]); if (value == 0) { logf(Log_Error, "External symbol %s not loaded\n", &dynstr[symbol->st_name]); return Error_Invalid; } } else if (r_type == R_X86_64_64) { value = (uintptr_t)base + symbol->st_value + rela[i].r_addend; } else { value = (uintptr_t)base + symbol->st_value; } *pointer = value; break; default: logf(Log_Error, "Unable to resolve rela symbol of type %lx\n", rela[i].r_info); return Error_Invalid; } } return Ok; }