diff options
Diffstat (limited to 'lib/binary-elf.cpp')
-rw-r--r-- | lib/binary-elf.cpp | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/lib/binary-elf.cpp b/lib/binary-elf.cpp new file mode 100644 index 0000000..a1ea831 --- /dev/null +++ b/lib/binary-elf.cpp @@ -0,0 +1,436 @@ +/* + * PSX-Tools Bundle Pack + * Copyright (C) 2002-2005 Nicolas "Pixel" Noble + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* $Id: binary-elf.cpp,v 1.1 2005-12-02 11:28:27 pixel Exp $ */ + +#include <vector> +#include "binary.h" +#include "binary-elf.h" + +#ifdef WORDS_BIGENDIAN +static void sw32(u32 & x) { + x = (x >> 24) | ((x >> 8) & 0x0000ff00) | ((x << 8) & 0x00ff0000) | (x << 24); +} +static void sw16(u16 & x) { + x = (x >> 8) | ((x << 8); +} +#else +#define sw32(x) while(0) +#define sw16(x) while(0) +#endif + +static String elf_classes[] = { + "Invalid class", + "32-bit objects", + "64-bit objects", +}; + +static String elf_encodings[] = { + "Invalid encoding", + "Little endian", + "Big endian", +}; + +static String elf_types[] = { + "No file type", + "Relocatable file", + "Executable file", + "Shared object file", + "Core file", +}; + +static String elf_machines[] = { + "No machine", + "AT&T WE 32100", + "SPARC", + "Intel Architecture", + "Motorola 68000", + "Motorola 88000", + "Intel 80860", + "MIPS RS3000 Big-Endian", + "MIPS RS4000 Big-Endian", +}; + +static String section_types[] = { + "Null", + "Progbits", + "Symtab", + "Strtab", + "Rela", + "Hash", + "Dynamic", + "Note", + "Nobits", + "Rel", + "Shlib", + "Dynsym", +}; + +static String section_flags[] = { + "---", + "--W", + "-R-", + "-RW", + "X--", + "X-W", + "XR-", + "XRW", +}; + +static String symbol_types[] = { + "NoType", + "Object", + "Func", + "Section", + "File", + "?? (5)", + "?? (6)", + "?? (7)", + "?? (8)", + "?? (9)", + "?? (10)", + "?? (11)", + "?? (12)", + "LoProc", + "?? (14)", + "HiProc", +}; + +static String binding_types[] = { + "Local", + "Global", + "Weak", + "?? (3)", + "?? (4)", + "?? (5)", + "?? (6)", + "?? (7)", + "?? (8)", + "?? (9)", + "?? (10)", + "?? (11)", + "?? (12)", + "LoProc", + "?? (14)", + "HiProc", +}; + +static String reloc_types[] = { + "R_MIPS_NONE", + "R_MIPS_16", + "R_MIPS_32", + "R_MIPS_REL32", + "R_MIPS_26", + "R_MIPS_HI16", + "R_MIPS_LO16", + "R_MIPS_GPREL16", + "R_MIPS_LITERAL", + "R_MIPS_GOT16", + "R_MIPS_PC16", + "R_MIPS_CALL16", + "R_MIPS_GPREL32" +}; + +struct elf_header_t read_elf_header(Handle * elf) throw (GeneralException) { + struct elf_header_t eh; + u8 * magic; + + elf->read(&eh, sizeof(eh)); + + magic = eh.e_ident.cook.ei_magic; + + if ((magic[0] != 0x7f) || (magic[1] != 'E') || (magic[2] != 'L') || (magic[3] != 'F')) + throw GeneralException("Not an ELF file."); + sw16(eh.e_type); + sw16(eh.e_machine); + sw32(eh.e_version); + sw32(eh.e_entry); + sw32(eh.e_phoff); + sw32(eh.e_shoff); + sw32(eh.e_flags); + sw16(eh.e_ehsize); + sw16(eh.e_phentsize); + sw16(eh.e_phnum); + sw16(eh.e_shentsize); + sw16(eh.e_shnum); + sw16(eh.e_shstrndx); + + return eh; +} + +struct elf_section_t read_elf_section(Handle * elf) { + struct elf_section_t es; + + elf->read(&es, sizeof(es)); + + sw32(es.sh_name); + sw32(es.sh_type); + sw32(es.sh_flags); + sw32(es.sh_addr); + sw32(es.sh_offset); + sw32(es.sh_size); + sw32(es.sh_link); + sw32(es.sh_info); + sw32(es.sh_addralign); + sw32(es.sh_entsize); + + return es; +} + +struct elf_symbol_t read_elf_symbol(Handle * elf) { + struct elf_symbol_t es; + + elf->read(&es, sizeof(es)); + + sw32(es.st_name); + sw32(es.st_value); + sw32(es.st_size); + sw16(es.st_shndx); + + return es; +} + +struct elf_reloc_t read_elf_reloc(Handle * elf) { + struct elf_reloc_t er; + + elf->read(&er, sizeof(er)); + + sw32(er.r_offset); + sw32(er.r_info); + + return er; +} + +Binary_elf::Binary_elf(Handle * elf) throw (GeneralException) : Binary(elf->GetName()) { + int i, j; + + struct elf_header_t head; + struct elf_section_t * sec = 0; + char * names = 0, * strtab_names = 0; + int symtab = 0, strtab = 0, linked_strtab = 0; + struct elf_symbol_t * sym = 0; + struct elf_reloc_t reloc; + + verbosity = M_INFO; + + printm(M_INFO, "Loading elf file '" + elf->GetName() + "'\n"); + + head = read_elf_header(elf); + + printm(M_INFO, "ELF Class : " + elf_classes[head.e_ident.cook.ei_class] + "\n"); + printm(M_INFO, "Data encoding: " + elf_encodings[head.e_ident.cook.ei_data] + "\n"); + printm(M_INFO, "Elf version : %i\n", head.e_ident.cook.ei_version); + if (head.e_type == 0xffff) { + printm(M_INFO, "Object type : Processor specific (hi)\n"); + } else if (head.e_type == 0xff00) { + printm(M_INFO, "Object type : Processor specific (lo)\n"); + } else { + printm(M_INFO, "Object type : " + elf_types[head.e_type] + "\n"); + } + printm(M_INFO, "Machine type : " + elf_machines[head.e_machine] + "\n"); + printm(M_INFO, "Object ver. : %i\n", head.e_version); + printm(M_INFO, "Elf entry : %08X\n", head.e_entry); + printm(M_INFO, "PH offset : %08X\n", head.e_phoff); + printm(M_INFO, "SH offset : %08X\n", head.e_shoff); + printm(M_INFO, "Flags : %08X\n", head.e_flags); + printm(M_INFO, "Header size : %04X\n", head.e_ehsize); + printm(M_INFO, "PH ent. size : %04X\n", head.e_phentsize); + printm(M_INFO, "PH number : %04X\n", head.e_phnum); + printm(M_INFO, "SH ent. size : %04X\n", head.e_shentsize); + printm(M_INFO, "SH number : %04X\n", head.e_shnum); + printm(M_INFO, "SH str index : %04X\n", head.e_shstrndx); + + setEntryPoint(head.e_entry); + + if (sizeof(struct elf_section_t) != head.e_shentsize) + throw GeneralException("Inconsistancy in section table entries."); + + sec = (struct elf_section_t *) malloc(sizeof(struct elf_section_t) * head.e_shnum); + elf->seek(head.e_shoff); + for (i = 0; i < head.e_shnum; i++) + sec[i] = read_elf_section(elf); + + if (sec[head.e_shstrndx].sh_size != 0) { + names = (char *) malloc(sec[head.e_shstrndx].sh_size); + elf->seek(sec[head.e_shstrndx].sh_offset); + elf->read(names, sec[head.e_shstrndx].sh_size); + } else { + // no section table names... huh... ? Most probably a quite bugged .elf file. + throw GeneralException("No section table name."); + } + + std::vector<Section *> section_ptrs; + section_ptrs.push_back(0); + + printm(M_INFO, "##: type flags address offset size link info align entsize name\n"); + for (i = 1; i < head.e_shnum; i++) { + if (!strcmp(names + sec[i].sh_name, ".symtab")) { + symtab = i; + linked_strtab = sec[i].sh_link; + } else if (!strcmp(names + sec[i].sh_name, ".strtab")) { + strtab = i; + } + + printm(M_INFO, "%2i: ", i); + if (sec[i].sh_type <= 0xff) { + printm(-M_INFO, "%-8s ", section_types[sec[i].sh_type].to_charp()); + } else if (sec[i].sh_type == 0x70000006) { + printm(-M_INFO, "Reginfo "); + } else { + printm(-M_INFO, "UNKNOW "); + } + + printm(-M_INFO, "%3s ", section_flags[sec[i].sh_flags & 7].to_charp()); + printm(-M_INFO, "%08X ", sec[i].sh_addr); + printm(-M_INFO, "%08X ", sec[i].sh_offset); + printm(-M_INFO, "%08X ", sec[i].sh_size); + printm(-M_INFO, "%5i ", sec[i].sh_link); + printm(-M_INFO, "%5i ", sec[i].sh_info); + printm(-M_INFO, "%5i ", sec[i].sh_addralign); + printm(-M_INFO, "%5i ", sec[i].sh_entsize); + printm(-M_INFO, "%s\n", names + sec[i].sh_name); + + section_ptrs.push_back(0); + + if (sec[i].sh_size == 0) + continue; + + if (!strcmp(names + sec[i].sh_name, ".comment")) // crude hack, but, well... + continue; + + switch (sec[i].sh_type) { + case PROGBITS: + printm(M_INFO, "Adding %i bytes from offset %i at address %i.\n", sec[i].sh_size, sec[i].sh_offset, sec[i].sh_addr); + elf->seek(sec[i].sh_offset); + elf->read((section_ptrs[i] = add_section(sec[i].sh_size, sec[i].sh_addr))->get_bytes(), sec[i].sh_size); + break; + case NOBITS: + printm(M_INFO, "Adding %i bytes at address %i.\n", sec[i].sh_size, sec[i].sh_addr); + section_ptrs[i] = add_bss(sec[i].sh_size, sec[i].sh_addr); + break; + } + } + + if (symtab) { + printm(M_INFO, "Discovered symtab = %i\n", symtab); + } else { + printm(M_INFO, "No symbol table.\n"); + } + + if (strtab) { + printm(M_INFO, "Discovered strtab = %i\n", strtab); + } else { + printm(M_INFO, "No string table.\n"); + } + + if (strtab != linked_strtab) { + throw GeneralException(String("Warning, inconsistancy: strtab != symtab.sh_link ") + strtab + " != " + linked_strtab + ")\n"); + } + + if (symtab) + if (sizeof(struct elf_symbol_t) != sec[symtab].sh_entsize) + throw GeneralException("Symbol entries not consistant.\n"); + + if ((symtab ? 1 : 0) ^ (strtab ? 1 : 0)) { + throw GeneralException(String("Inconsistancy: symtab is") + (symtab ? "" : "n't") + " available, while strtab is" + (strtab ? "" : "n't") + "."); + } + + if (!strtab) { + if (sec) + free(sec); + + if (names) + free(names); + + return; + } + + strtab_names = (char *) malloc(sec[strtab].sh_size); + elf->seek(sec[strtab].sh_offset); + elf->read(strtab_names, sec[strtab].sh_size); + + sym = (struct elf_symbol_t *) malloc(sec[symtab].sh_size); + elf->seek(sec[symtab].sh_offset); + for (i = 0; i < sec[symtab].sh_size / sec[symtab].sh_entsize; i++) + sym[i] = read_elf_symbol(elf); + + printm(M_INFO, " Num: Value Size Type Bind Ndx Name\n"); + for (i = 0; i < sec[symtab].sh_size / sec[symtab].sh_entsize; i++) { + if (((sym[i].st_info >> 4) == GLOBAL) || ((sym[i].st_info >> 4) == WEAK)) { + if ((sym[i].st_info & 15) != NOTYPE) { + printm(M_INFO, "Export symbol (section %i):\n", sym[i].st_shndx); + section_ptrs[sym[i].st_shndx]->add_export(strtab_names + sym[i].st_name, sym[i].st_value - sec[sym[i].st_shndx].sh_addr, sym[i].st_info >> 4); + } + } + + printm(M_INFO, "%6i: %08X %08X %-7s %-6s %6i %-10s : %s\n", i, + sym[i].st_value, sym[i].st_size, symbol_types[sym[i].st_info & 15].to_charp(), + binding_types[sym[i].st_info >> 4].to_charp(), sym[i].st_shndx, + sym[i].st_name ? strtab_names + sym[i].st_name : "(null)", + sym[i].st_shndx ? names + sec[sym[i].st_shndx].sh_name : "(null)"); + } + + for (i = 0; i < head.e_shnum; i++) { + if (sec[i].sh_type != REL) + continue; + printm(M_INFO, "Section %i (%s) contains relocations for section %i (%s):\n", + i, names + sec[i].sh_name, sec[i].sh_info, names + sec[sec[i].sh_info].sh_name); + + if (sec[i].sh_entsize != sizeof(struct elf_reloc_t)) + throw GeneralException("Warning: inconsistancy in relocation table.\n"); + + elf->seek(sec[i].sh_offset); + + printm(M_INFO, " Num: Offset Type Symbol\n"); + + for (j = 0; j < (sec[i].sh_size / sec[i].sh_entsize); j++) { + int sym_n; + + reloc = read_elf_reloc(elf); + + sym_n = reloc.r_info >> 8; + printm(M_INFO, "%6i: %08X %-14s %3i: ", j, reloc.r_offset, reloc_types[reloc.r_info & 255].to_charp(), sym_n); + + switch(sym[sym_n].st_info & 15) { + case NOTYPE: + printm(-M_INFO, "external symbol reloc to symbol %s\n", strtab_names + sym[sym_n].st_name); + section_ptrs[sec[i].sh_info]->add_import(strtab_names + sym[sym_n].st_name, reloc.r_offset, reloc.r_info & 255); + break; + case SECTION: + printm(-M_INFO, "internal section reloc to section %i (%s)\n", sym[sym_n].st_shndx, names + sec[sym[sym_n].st_shndx].sh_name); + section_ptrs[sec[i].sh_info]->add_internal(section_ptrs[sym[sym_n].st_shndx], reloc.r_offset, reloc.r_info & 255); + break; + default: + throw GeneralException("Internal relocation symbol. Bug inside."); + } + } + } + + if (sec) + free(sec); + + if (names) + free(names); + + if (strtab_names) + free(strtab_names); + + if (sym) + free(sym); +} |