300 lines
9.7 KiB
HolyC
300 lines
9.7 KiB
HolyC
#define EI_NIDENT 16
|
|
#define EM_X86_64 0x3E
|
|
#define ET_EXEC 2
|
|
#define ET_DYN 3
|
|
|
|
U0 @elf64_debug_print(U8 fmt, ...)
|
|
{
|
|
// FIXME: Remove unnecessary debug_print statements and PrintErr for errors.
|
|
no_warn fmt, argc, argv;
|
|
}
|
|
|
|
class Elf64_Ehdr {
|
|
U8 e_ident[EI_NIDENT]; /* Magic number and other info */
|
|
U16 e_type; /* Object file type */
|
|
U16 e_machine; /* Architecture */
|
|
U32 e_version; /* Object file version */
|
|
U64 e_entry; /* Entry point virtual address */
|
|
U64 e_phoff; /* Program header table file offset */
|
|
U64 e_shoff; /* Section header table file offset */
|
|
U32 e_flags; /* Processor-specific flags */
|
|
U16 e_ehsize; /* ELF header size in bytes */
|
|
U16 e_phentsize; /* Program header table entry size */
|
|
U16 e_phnum; /* Program header table entry count */
|
|
U16 e_shentsize; /* Section header table entry size */
|
|
U16 e_shnum; /* Section header table entry count */
|
|
U16 e_shstrndx; /* Section header string table index */
|
|
};
|
|
|
|
class Elf64_Shdr {
|
|
U32 sh_name; /* Section name (string tbl index) */
|
|
U32 sh_type; /* Section type */
|
|
U64 sh_flags; /* Section flags */
|
|
U64 sh_addr; /* Section virtual addr at execution */
|
|
U64 sh_offset; /* Section file offset */
|
|
U64 sh_size; /* Section size in bytes */
|
|
U32 sh_link; /* Link to another section */
|
|
U32 sh_info; /* Additional section information */
|
|
U64 sh_addralign; /* Section alignment */
|
|
U64 sh_entsize; /* Entry size if section holds table */
|
|
};
|
|
|
|
class Elf64_Sym {
|
|
U32 st_name; /* Symbol name (string tbl index) */
|
|
U8 st_info; /* Symbol type and binding */
|
|
U8 st_other; /* Symbol visibility */
|
|
U16 st_shndx; /* Section index */
|
|
U64 st_value; /* Symbol value */
|
|
U64 st_size; /* Symbol size */
|
|
};
|
|
|
|
class PLT_entry {
|
|
U8 pad[0x10];
|
|
};
|
|
|
|
class RELA_entry {
|
|
U64 r_offset;
|
|
U64 r_info;
|
|
I64 r_addend;
|
|
};
|
|
|
|
class Elf {
|
|
union {
|
|
U8* u8;
|
|
Elf64_Ehdr* ehdr;
|
|
} I64 size;
|
|
U8* dynstr;
|
|
Elf64_Sym* dynsym;
|
|
PLT_entry* plt;
|
|
RELA_entry* rela_dyn;
|
|
RELA_entry* rela_plt;
|
|
Elf64_Sym* strtab;
|
|
Elf64_Sym* symtab;
|
|
I64 rela_dyn_size;
|
|
I64 rela_plt_size;
|
|
I64 strtab_size;
|
|
I64 symtab_size;
|
|
};
|
|
|
|
U0 (*_start)();
|
|
|
|
U0 unimplemented_symbol()
|
|
{
|
|
I32 s = 0xDEADF00D;
|
|
PrintWarn("Unimplemented symbol: %s\n", s);
|
|
Dbg;
|
|
while (1)
|
|
Sleep(1);
|
|
}
|
|
|
|
Bool is_valid_elf(Elf* elf)
|
|
{
|
|
Bool res = TRUE;
|
|
if (MemCmp(elf->u8 + 1, "ELF", 3)) {
|
|
@elf64_debug_print("Invalid signature (not ELF).\n");
|
|
res = FALSE;
|
|
}
|
|
if (elf->ehdr->e_type != ET_EXEC && elf->ehdr->e_type != ET_DYN) {
|
|
@elf64_debug_print("Invalid object file type.\n");
|
|
res = FALSE;
|
|
}
|
|
if (elf->ehdr->e_machine != EM_X86_64) {
|
|
@elf64_debug_print("Invalid architecture.\n");
|
|
res = FALSE;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
U0 process_elf_section_header_table(Elf* elf)
|
|
{
|
|
Elf64_Shdr* shdr = elf->u8 + elf->ehdr->e_shoff;
|
|
Elf64_Shdr* shdr_shstrtab = shdr + elf->ehdr->e_shstrndx;
|
|
U8* shstrtab = elf->u8 + shdr_shstrtab->sh_offset;
|
|
I64 i = 0;
|
|
while (i < elf->ehdr->e_shnum) {
|
|
if (!StrCmp(shstrtab + shdr->sh_name, ".symtab")) {
|
|
@elf64_debug_print("found symtab at 0x%08x, size = %d\n", shdr->sh_offset,
|
|
shdr->sh_size);
|
|
elf->symtab = elf->u8 + shdr->sh_offset;
|
|
elf->symtab_size = shdr->sh_size;
|
|
}
|
|
if (!StrCmp(shstrtab + shdr->sh_name, ".strtab")) {
|
|
@elf64_debug_print("found strtab at 0x%08x, size = %d\n", shdr->sh_offset,
|
|
shdr->sh_size);
|
|
elf->strtab = elf->u8 + shdr->sh_offset;
|
|
elf->strtab_size = shdr->sh_size;
|
|
}
|
|
if (shdr->sh_addr) {
|
|
MemCpy(shdr->sh_addr, elf->u8 + shdr->sh_offset, shdr->sh_size);
|
|
if (!StrCmp(shstrtab + shdr->sh_name, ".dynstr"))
|
|
elf->dynstr = shdr->sh_addr;
|
|
if (!StrCmp(shstrtab + shdr->sh_name, ".dynsym"))
|
|
elf->dynsym = shdr->sh_addr;
|
|
if (!StrCmp(shstrtab + shdr->sh_name, ".plt"))
|
|
elf->plt = shdr->sh_addr;
|
|
if (!StrCmp(shstrtab + shdr->sh_name, ".rela.dyn")) {
|
|
elf->rela_dyn = shdr->sh_addr;
|
|
elf->rela_dyn_size = shdr->sh_size / shdr->sh_entsize;
|
|
}
|
|
if (!StrCmp(shstrtab + shdr->sh_name, ".rela.plt")) {
|
|
elf->rela_plt = shdr->sh_addr;
|
|
elf->rela_plt_size = shdr->sh_size / shdr->sh_entsize;
|
|
}
|
|
if (!StrCmp(shstrtab + shdr->sh_name, ".bss") || !StrCmp(shstrtab + shdr->sh_name, ".tbss")) {
|
|
MemSet(shdr->sh_addr, NULL, shdr->sh_size);
|
|
@elf64_debug_print(
|
|
"Zeroed out section '%s' at physical address 0x%06x, size = %d bytes\n",
|
|
shstrtab + shdr->sh_name, shdr->sh_addr, shdr->sh_size);
|
|
} else
|
|
@elf64_debug_print(
|
|
"MemCpy section '%s' to physical address 0x%06x, size = %d bytes\n",
|
|
shstrtab + shdr->sh_name, shdr->sh_addr, shdr->sh_size);
|
|
if (!StrCmp(shstrtab + shdr->sh_name, ".bss")) {
|
|
MemSet(shdr->sh_addr, NULL, shdr->sh_size);
|
|
@elf64_debug_print("MemSet section '%s' at physical address 0x%06x to NULL, "
|
|
"size = %d bytes\n",
|
|
shstrtab + shdr->sh_name, shdr->sh_addr, shdr->sh_size);
|
|
}
|
|
}
|
|
shdr++;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
U0 process_elf_rela_dyn_entries(Elf* elf)
|
|
{
|
|
I64 i;
|
|
U8* entry_name;
|
|
RELA_entry* rela_dyn = elf->rela_dyn;
|
|
for (i = 0; i < elf->rela_dyn_size; i++) {
|
|
entry_name = elf->dynstr + elf->dynsym[(rela_dyn->r_info >> 32)].st_name;
|
|
@elf64_debug_print("rela_dyn->r_offset = %08x\n", rela_dyn->r_offset);
|
|
@elf64_debug_print("entry name = '%s'\n", entry_name);
|
|
if (!StrCmp(entry_name, "__libc_start_main")) {
|
|
*(rela_dyn->r_offset)(U64*) = &_main;
|
|
@elf64_debug_print("Set value for .rela.dyn entry '%s' to: &_main\n",
|
|
entry_name);
|
|
}
|
|
if (!StrCmp(entry_name, "stdin")) {
|
|
*(rela_dyn->r_offset)(U64*) = 0;
|
|
@elf64_debug_print("Set value for .rela.dyn entry '%s' to: %d\n", entry_name, 0);
|
|
}
|
|
if (!StrCmp(entry_name, "stdout")) {
|
|
*(rela_dyn->r_offset)(U64*) = 1;
|
|
@elf64_debug_print("Set value for .rela.dyn entry '%s' to: %d\n", entry_name, 1);
|
|
}
|
|
if (!StrCmp(entry_name, "stderr")) {
|
|
*(rela_dyn->r_offset)(U64*) = 2;
|
|
@elf64_debug_print("Set value for .rela.dyn entry '%s' to: %d\n", entry_name, 2);
|
|
}
|
|
rela_dyn++;
|
|
}
|
|
}
|
|
|
|
CHashClass* get_symbol_hash_entry(U8* entry_name)
|
|
{
|
|
I64 i;
|
|
CHashSrcSym* sym;
|
|
CHashTable* tbl = Fs->hash_table;
|
|
while (tbl) {
|
|
for (i = 0; i < tbl->mask; i++) {
|
|
sym = tbl->body[i];
|
|
while (sym) {
|
|
if (sym->type == HTT_CLASS)
|
|
if (!StrCmp(sym->str, entry_name))
|
|
return sym;
|
|
sym = sym->next;
|
|
}
|
|
}
|
|
tbl = tbl->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
U64 get_symbol_address(U8* entry_name)
|
|
{
|
|
CHash* h = HashFind(entry_name, Fs->hash_table, Fs->hash_table->mask);
|
|
if (!h)
|
|
return NULL;
|
|
switch (h->type) {
|
|
case HTT_GLBL_VAR:
|
|
return h(CHashGlblVar*)->data_addr;
|
|
break;
|
|
case HTT_FUN:
|
|
return h(CHashFun*)->exe_addr;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
break;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
U0 process_elf_rela_plt_entries(Elf* elf)
|
|
{
|
|
I64 i;
|
|
U32 handler;
|
|
U32* patch;
|
|
U8* entry_name;
|
|
Bool symbol_exists;
|
|
PLT_entry* plt = elf->plt;
|
|
RELA_entry* rela_plt = elf->rela_plt;
|
|
plt++;
|
|
for (i = 0; i < elf->rela_plt_size; i++) {
|
|
symbol_exists = FALSE;
|
|
entry_name = elf->dynstr + elf->dynsym[(rela_plt->r_info >> 32)].st_name;
|
|
handler = MAlloc(sizeof(unimplemented_symbol), adam_task->code_heap);
|
|
MemCpy(handler, &unimplemented_symbol, sizeof(unimplemented_symbol));
|
|
patch = handler + 0x0A;
|
|
*patch = entry_name;
|
|
@patch_jmp_rel32(plt, handler);
|
|
@patch_call_rel32(handler + 0x16, &PrintErr);
|
|
//@patch_call_rel32(handler + 0x21, &_exit);
|
|
if (!StrCmp(entry_name, "__libc_start_main")) {
|
|
symbol_exists = TRUE;
|
|
@patch_jmp_rel32(plt, &_main);
|
|
@elf64_debug_print("Set value for .rela.plt entry '%s' to &_main\n", entry_name);
|
|
}
|
|
if (get_symbol_address(entry_name)) {
|
|
symbol_exists = TRUE;
|
|
@patch_jmp_rel32(plt, get_symbol_address(entry_name));
|
|
@elf64_debug_print("Set value for .rela.plt entry '%s' to &%s\n", entry_name,
|
|
entry_name);
|
|
}
|
|
if (!symbol_exists)
|
|
@elf64_debug_print(
|
|
"Set value for .rela.plt entry '%s' to &unimplemented_symbol\n",
|
|
entry_name);
|
|
rela_plt++;
|
|
plt++;
|
|
}
|
|
}
|
|
|
|
U0 load_elf(...)
|
|
{
|
|
if (argc < 1) {
|
|
PrintErr("Not enough arguments.\n");
|
|
return;
|
|
}
|
|
if (!FileFind(argv[0])) {
|
|
PrintErr("File not found: %s\n", argv[0]);
|
|
return;
|
|
}
|
|
|
|
Elf elf;
|
|
elf.u8 = FileRead(argv[0], &elf.size);
|
|
@elf64_debug_print("Load file '%s', size = %d bytes\n", argv[0], elf.size);
|
|
|
|
if (!is_valid_elf(&elf)) {
|
|
PrintErr("File is not a valid ELF x86-64 executable.\n");
|
|
return;
|
|
}
|
|
|
|
process_elf_section_header_table(&elf);
|
|
process_elf_rela_dyn_entries(&elf);
|
|
process_elf_rela_plt_entries(&elf);
|
|
|
|
_start = elf.ehdr->e_entry;
|
|
elf_argc = argc;
|
|
elf_argv = argv;
|
|
}
|