summaryrefslogtreecommitdiff
path: root/lib/binary-elf.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/binary-elf.cpp')
-rw-r--r--lib/binary-elf.cpp436
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);
+}