mirror of
https://git.checksum.fail/alec/TOL.git
synced 2026-05-26 18:50:18 +00:00
355 lines
10 KiB
HolyC
355 lines
10 KiB
HolyC
#define EI_NIDENT 16
|
|
#define EM_X86_64 0x3E
|
|
#define ET_EXEC 2
|
|
#define ET_DYN 3
|
|
|
|
#define STT_OBJECT 1
|
|
#define STT_FUNC 2
|
|
|
|
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;
|
|
"Unimplemented symbol: %s\n", s;
|
|
while (1)
|
|
Sleep(1);
|
|
}
|
|
|
|
Bool is_valid_elf(Elf *elf) {
|
|
Bool res = TRUE;
|
|
if (MemCmp(elf->u8 + 1, "ELF", 3)) {
|
|
debug_print("Invalid signature (not ELF).\n");
|
|
res = FALSE;
|
|
}
|
|
if (elf->ehdr->e_type != ET_EXEC && elf->ehdr->e_type != ET_DYN) {
|
|
debug_print("Invalid object file type.\n");
|
|
res = FALSE;
|
|
}
|
|
if (elf->ehdr->e_machine != EM_X86_64) {
|
|
debug_print("Invalid architecture.\n");
|
|
res = FALSE;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
U0 find_value(U8 *value) {
|
|
U64 addr = 0x0;
|
|
while (addr < 0x2000000) {
|
|
if (!MemCmp(addr, value, StrLen(value))) {
|
|
"found at 0x%08x\n", addr;
|
|
return;
|
|
}
|
|
addr++;
|
|
}
|
|
"not found\n";
|
|
}
|
|
|
|
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")) {
|
|
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")) {
|
|
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;
|
|
}
|
|
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);
|
|
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;
|
|
debug_print("rela_dyn->r_offset = %08x\n", rela_dyn->r_offset);
|
|
debug_print("entry name = '%s'\n", entry_name);
|
|
if (!StrCmp(entry_name, "__libc_start_main")) {
|
|
*(rela_dyn->r_offset)(U64 *) = &_main;
|
|
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;
|
|
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;
|
|
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;
|
|
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;
|
|
}
|
|
|
|
U0 process_elf_debug_symbols(Elf *elf) {
|
|
I64 i = 0;
|
|
I64 entry_bind;
|
|
U64 entry_name;
|
|
I64 entry_type;
|
|
CHashFun *hf;
|
|
CHashGlblVar *hgv;
|
|
Elf64_Sym *symtab = elf->symtab;
|
|
entry_name = elf->strtab;
|
|
entry_name += symtab->st_name;
|
|
while (i < 476) {
|
|
entry_bind = symtab->st_info >> 4;
|
|
entry_type = symtab->st_info & 0xf;
|
|
switch (entry_type) {
|
|
case STT_OBJECT:
|
|
hgv = CAlloc(sizeof(CHashGlblVar));
|
|
hgv->str = entry_name;
|
|
hgv->type = HTT_GLBL_VAR;
|
|
hgv->data_addr = symtab->st_value;
|
|
hgv->size = symtab->st_size;
|
|
// TempleOS reboots if I nest a switch table here, for some reason, so we
|
|
// have to do this dumb shit instead...
|
|
hgv->var_class = get_symbol_hash_entry("U32");
|
|
if (hgv->size == 1)
|
|
hgv->var_class = get_symbol_hash_entry("U8");
|
|
if (hgv->size == 2)
|
|
hgv->var_class = get_symbol_hash_entry("U16");
|
|
HashAdd(hgv, Fs->hash_table);
|
|
debug_print(
|
|
"Add global variable '%s' to hash table, addr = 0x%08x, size = %d\n",
|
|
entry_name, symtab->st_value, symtab->st_size);
|
|
break;
|
|
case STT_FUNC:
|
|
hf = CAlloc(sizeof(CHashFun));
|
|
hf->str = entry_name;
|
|
hf->type = HTT_FUN;
|
|
hf->exe_addr = symtab->st_value;
|
|
hf->size = symtab->st_size;
|
|
HashAdd(hf, Fs->hash_table);
|
|
debug_print("Add function '%s' to hash table, addr = 0x%08x, size = %d\n",
|
|
entry_name, symtab->st_value, symtab->st_size);
|
|
break;
|
|
}
|
|
symtab++;
|
|
i++;
|
|
entry_name = elf->strtab;
|
|
entry_name += symtab->st_name;
|
|
}
|
|
}
|
|
|
|
U64 get_symbol_address(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_GLBL_VAR)
|
|
if (!StrCmp(sym->str, entry_name))
|
|
return sym(CHashGlblVar *)->data_addr;
|
|
if (sym->type == HTT_FUN)
|
|
if (!StrCmp(sym->str, entry_name))
|
|
return sym(CHashFun *)->exe_addr;
|
|
sym = sym->next;
|
|
}
|
|
}
|
|
tbl = tbl->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
U0 process_debug_patches(Elf *elf) { no_warn elf; }
|
|
|
|
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), Fs->code_heap);
|
|
MemCpy(handler, &unimplemented_symbol, sizeof(unimplemented_symbol));
|
|
patch = handler + 0x0A;
|
|
*patch = entry_name;
|
|
PatchJmpRel32(plt, handler);
|
|
PatchCallRel32(handler + 0x16, &PrintErr);
|
|
PatchCallRel32(handler + 0x21, &_exit);
|
|
if (!StrCmp(entry_name, "__libc_start_main")) {
|
|
symbol_exists = TRUE;
|
|
PatchJmpRel32(plt, &_main);
|
|
debug_print("Set value for .rela.plt entry '%s' to &_main\n", entry_name);
|
|
}
|
|
if (get_symbol_address(entry_name)) {
|
|
symbol_exists = TRUE;
|
|
PatchJmpRel32(plt, get_symbol_address(entry_name));
|
|
debug_print("Set value for .rela.plt entry '%s' to &%s\n", entry_name,
|
|
entry_name);
|
|
}
|
|
if (!symbol_exists)
|
|
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);
|
|
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);
|
|
process_elf_debug_symbols(&elf);
|
|
|
|
_start = elf.ehdr->e_entry;
|
|
elf_argc = argc;
|
|
elf_argv = argv;
|
|
|
|
process_debug_patches(&elf);
|
|
|
|
//_start();
|
|
} |