/* * 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 #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_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); }