/**************************** elf.cpp ********************************* * Author: Agner Fog * Date created: 2006-07-18 * Last modified: 2017-10-18 * Project: objconv * Module: elf.cpp * Description: * Module for reading ELF files * * Class CELF is used for reading, interpreting and dumping ELF files. * * Copyright 2006-2017 GNU General Public License http://www.gnu.org/licenses *****************************************************************************/ #include "stdafx.h" // All functions in this module are templated to make two versions: 32 and 64 bits. // See instantiations at the end of this file. // File class names SIntTxt ELFFileClassNames[] = { {ELFCLASSNONE, "None"}, {ELFCLASS32, "32-bit object"}, {ELFCLASS64, "64-bit object"} }; // Data encoding names SIntTxt ELFDataEncodeNames[] = { {ELFDATANONE, "None"}, {ELFDATA2LSB, "Little Endian"}, {ELFDATA2MSB, "Big Endian"} }; // ABI names SIntTxt ELFABINames[] = { {ELFOSABI_SYSV, "System V"}, {ELFOSABI_HPUX, "HP-UX"}, {ELFOSABI_ARM, "ARM"}, {ELFOSABI_STANDALONE,"Embedded"}, }; // File type names SIntTxt ELFFileTypeNames[] = { {ET_NONE, "None"}, {ET_REL, "Relocatable"}, {ET_EXEC, "Executable"}, {ET_DYN, "Shared object"}, {ET_CORE, "Core file"} }; // Section type names SIntTxt ELFSectionTypeNames[] = { {SHT_NULL, "None"}, {SHT_PROGBITS, "Program data"}, {SHT_SYMTAB, "Symbol table"}, {SHT_STRTAB, "String table"}, {SHT_RELA, "Relocation w addends"}, {SHT_HASH, "Symbol hash table"}, {SHT_DYNAMIC, "Dynamic linking info"}, {SHT_NOTE, "Notes"}, {SHT_NOBITS, "bss"}, {SHT_REL, "Relocation entries"}, {SHT_SHLIB, "Reserved"}, {SHT_DYNSYM, "Dynamic linker symbol table"}, {SHT_INIT_ARRAY, "Array of constructors"}, {SHT_FINI_ARRAY, "Array of destructors"}, {SHT_PREINIT_ARRAY, "Array of pre-constructors"}, {SHT_GROUP, "Section group"}, {SHT_SYMTAB_SHNDX, "Extended section indices"} }; // Section flag names SIntTxt ELFSectionFlagNames[] = { {SHF_WRITE, "Writeable"}, {SHF_ALLOC, "Allocate"}, {SHF_EXECINSTR, "Executable"}, {SHF_MERGE, "Merge"}, {SHF_STRINGS, "Strings"}, {SHF_INFO_LINK, "sh_info"}, {SHF_LINK_ORDER, "Preserve order"}, {SHF_OS_NONCONFORMING,"OS specific"} }; // Symbol binding names SIntTxt ELFSymbolBindingNames[] = { {STB_LOCAL, "Local"}, {STB_GLOBAL, "Global"}, {STB_WEAK, "Weak"} }; // Symbol Type names SIntTxt ELFSymbolTypeNames[] = { {STT_NOTYPE, "None"}, {STT_OBJECT, "Object"}, {STT_FUNC, "Function"}, {STT_SECTION, "Section"}, {STT_FILE, "File"}, {STT_COMMON, "Common"}, {STT_GNU_IFUNC, "Indirect function/dispatcher"} }; // Relocation type names x86 32 bit SIntTxt ELF32RelocationNames[] = { {R_386_NONE, "None"}, {R_386_32, "Absolute 32 bit"}, {R_386_PC32, "Self-relative 32 bit"}, {R_386_GOT32, "32 bit GOT entry"}, {R_386_PLT32, "32 bit PLT address"}, {R_386_COPY, "Copy symbol at runtime"}, {R_386_GLOB_DAT, "Create GOT entry"}, {R_386_JMP_SLOT, "Create PLT entry"}, {R_386_RELATIVE, "Adjust by image base"}, {R_386_GOTOFF, "32 bit offset to GOT"}, {R_386_GOTPC, "32 bit PC relative offset to GOT"}, {R_386_IRELATIVE, "32 bit ref. to indirect function PLT"} }; // Relocation type names x86 64 bit SIntTxt ELF64RelocationNames[] = { {R_X86_64_NONE, "None"}, {R_X86_64_64, "Direct 64 bit"}, {R_X86_64_PC32, "Self relative 32 bit signed"}, {R_X86_64_GOT32, "32 bit GOT entry"}, {R_X86_64_PLT32, "32 bit PLT address"}, {R_X86_64_COPY, "Copy symbol at runtime"}, {R_X86_64_GLOB_DAT, "Create GOT entry"}, {R_X86_64_JUMP_SLOT, "Create PLT entry"}, {R_X86_64_RELATIVE, "Adjust by program base"}, {R_X86_64_GOTPCREL, "32 bit signed pc relative offset to GOT"}, {R_X86_64_32, "Direct 32 bit zero extended"}, {R_X86_64_32S, "Direct 32 bit sign extended"}, {R_X86_64_16, "Direct 16 bit zero extended"}, {R_X86_64_PC16, "16 bit sign extended pc relative"}, {R_X86_64_8, "Direct 8 bit sign extended"}, {R_X86_64_PC8, "8 bit sign extended pc relative"}, {R_X86_64_IRELATIVE, "32 bit ref. to indirect function PLT"} }; // Machine names SIntTxt ELFMachineNames[] = { {EM_NONE, "None"}, // No machine {EM_M32, "AT&T WE 32100"}, {EM_SPARC, "SPARC"}, {EM_386, "Intel x86"}, {EM_68K, "Motorola m68k"}, {EM_88K, "Motorola m88k"}, {EM_860, "MIPS R3000 big-endian"}, {EM_MIPS, "MIPS R3000 big-endian"}, {EM_S370, "IBM System/370"}, {EM_MIPS_RS3_LE, "NMIPS R3000 little-endianone"}, {EM_PARISC, "HPPA"}, {EM_VPP500, "Fujitsu VPP500"}, {EM_SPARC32PLUS, "Sun v8plus"}, {EM_960, "Intel 80960"}, {EM_PPC, "PowerPC"}, {EM_PPC64, "PowerPC 64-bit"}, {EM_S390, "IBM S390"}, {EM_V800, "NEC V800"}, {EM_FR20, "Fujitsu FR20"}, {EM_RH32, "TRW RH-32"}, {EM_RCE, "Motorola RCE"}, {EM_ARM, "ARM"}, {EM_FAKE_ALPHA, "Digital Alpha"}, {EM_SH, "Hitachi SH"}, {EM_SPARCV9, "SPARC v9 64-bit"}, {EM_TRICORE, "Siemens Tricore"}, {EM_ARC, "Argonaut RISC"}, {EM_H8_300, "Hitachi H8/300"}, {EM_H8_300H, "Hitachi H8/300H"}, {EM_H8S, "Hitachi H8S"}, {EM_H8_500, "EM_H8_500"}, {EM_IA_64, "Intel IA64"}, {EM_MIPS_X, "Stanford MIPS-X"}, {EM_COLDFIRE, "Motorola Coldfire"}, {EM_68HC12, "Motorola M68HC12"}, {EM_MMA, "Fujitsu MMA"}, {EM_PCP, "Siemens PCP"}, {EM_NCPU, "Sony nCPU"}, {EM_NDR1, "Denso NDR1"}, {EM_STARCORE, "Motorola Start*Core"}, {EM_ME16, "Toyota ME16"}, {EM_ST100, "ST100"}, {EM_TINYJ, "Tinyj"}, {EM_X86_64, "x86-64"}, {EM_PDSP, "Sony DSP"}, {EM_FX66, "Siemens FX66"}, {EM_ST9PLUS, "ST9+ 8/16"}, {EM_ST7, "ST7 8"}, {EM_68HC16, "MC68HC16"}, {EM_68HC11, "MC68HC11"}, {EM_68HC08, "MC68HC08"}, {EM_68HC05, "MC68HC05"}, {EM_SVX, "SVx"}, {EM_AT19, "ST19"}, {EM_VAX, "VAX"}, {EM_CRIS, "Axis"}, {EM_JAVELIN, "Infineon"}, {EM_FIREPATH, "Element 14"}, {EM_ZSP, "LSI Logic"}, {EM_HUANY, "Harvard"}, {EM_PRISM, "SiTera Prism"}, {EM_AVR, "Atmel AVR"}, {EM_FR30, "FR30"}, {EM_D10V, "D10V"}, {EM_D30V, "D30V"}, {EM_V850, "NEC v850"}, {EM_M32R, "M32R"}, {EM_MN10300, "MN10300"}, {EM_MN10200, "MN10200"}, {EM_PJ, "picoJava"}, {EM_ALPHA, "Alpha"} }; // Program header type names SIntTxt ELFPTypeNames[] = { {PT_NULL, "Unused"}, {PT_LOAD, "Loadable program segment"}, {PT_DYNAMIC, "Dynamic linking information"}, {PT_INTERP, "Program interpreter"}, {PT_NOTE, "Auxiliary information"}, {PT_SHLIB, "Reserved"}, {PT_PHDR, "Entry for header table itself"} }; // Class CELF members: // Constructor template CELF::CELF() { memset(this, 0, sizeof(*this)); } // ParseFile template void CELF::ParseFile(){ // Load and parse file buffer uint32 i; FileHeader = *(TELF_Header*)Buf(); // Copy file header NSections = FileHeader.e_shnum; SectionHeaders.SetNum(NSections); // Allocate space for section headers SectionHeaders.SetZero(); uint32 Symtabi = 0; // Index to symbol table // check header integrity if (FileHeader.e_phoff > GetDataSize() || FileHeader.e_phoff + FileHeader.e_phentsize > GetDataSize()) err.submit(2035); if (FileHeader.e_shoff > GetDataSize() || FileHeader.e_shoff + FileHeader.e_shentsize > GetDataSize()) err.submit(2035); // Find section headers SectionHeaderSize = FileHeader.e_shentsize; if (SectionHeaderSize <= 0) err.submit(2033); uint32 SectionOffset = uint32(FileHeader.e_shoff); for (i = 0; i < NSections; i++) { SectionHeaders[i] = Get(SectionOffset); // check section header integrity if (SectionHeaders[i].sh_type != SHT_NOBITS && (SectionHeaders[i].sh_offset > GetDataSize() || SectionHeaders[i].sh_offset + SectionHeaders[i].sh_size > GetDataSize() || SectionHeaders[i].sh_offset + SectionHeaders[i].sh_entsize > GetDataSize())) { err.submit(2035); } SectionOffset += SectionHeaderSize; if (SectionHeaders[i].sh_type == SHT_SYMTAB) { // Symbol table found Symtabi = i; } } // if (Buf() && GetNumEntries()) { if (Buf() && GetDataSize()) { SecStringTable = Buf() + uint32(SectionHeaders[FileHeader.e_shstrndx].sh_offset); SecStringTableLen = uint32(SectionHeaders[FileHeader.e_shstrndx].sh_size); } if (SectionOffset > GetDataSize()) { err.submit(2110); // Section table points to outside file } if (Symtabi) { // Save offset to symbol table SymbolTableOffset = (uint32)(SectionHeaders[Symtabi].sh_offset); SymbolTableEntrySize = (uint32)(SectionHeaders[Symtabi].sh_entsize); // Entry size of symbol table if (SymbolTableEntrySize == 0) {err.submit(2034); return;} // Avoid division by zero SymbolTableEntries = uint32(SectionHeaders[Symtabi].sh_size) / SymbolTableEntrySize; // Find associated string table uint32 Stringtabi = SectionHeaders[Symtabi].sh_link; if (Stringtabi < NSections) { SymbolStringTableOffset = (uint32)(SectionHeaders[Stringtabi].sh_offset); SymbolStringTableSize = (uint32)(SectionHeaders[Stringtabi].sh_size); } else { Symtabi = 0; // Error } } } // Dump template void CELF::Dump(int options) { uint32 i; if (options & DUMP_FILEHDR) { // File header printf("\nDump of ELF file %s", FileName); printf("\n-----------------------------------------------"); printf("\nFile size: %i", GetDataSize()); printf("\nFile header:"); printf("\nFile class: %s, Data encoding: %s, ELF version %i, ABI: %s, ABI version %i", Lookup(ELFFileClassNames, FileHeader.e_ident[EI_CLASS]), Lookup(ELFDataEncodeNames, FileHeader.e_ident[EI_DATA]), FileHeader.e_ident[EI_VERSION], Lookup(ELFABINames, FileHeader.e_ident[EI_OSABI]), FileHeader.e_ident[EI_ABIVERSION]); printf("\nFile type: %s, Machine: %s, version: %i", Lookup(ELFFileTypeNames, FileHeader.e_type), Lookup(ELFMachineNames, FileHeader.e_machine), FileHeader.e_version); printf("\nNumber of sections: %2i, Processor flags: 0x%X", NSections, FileHeader.e_flags); } if ((options & DUMP_SECTHDR) && FileHeader.e_phnum) { // Dump program headers uint32 nProgramHeaders = FileHeader.e_phnum; uint32 programHeaderSize = FileHeader.e_phentsize; if (programHeaderSize <= 0) err.submit(2033); uint32 programHeaderOffset = (uint32)FileHeader.e_phoff; Elf64_Phdr pHeader; for (i = 0; i < nProgramHeaders; i++) { if (WordSize == 32) { Elf32_Phdr pHeader32 = Get(programHeaderOffset); pHeader.p_type = pHeader32.p_type; pHeader.p_offset = pHeader32.p_offset; pHeader.p_vaddr = pHeader32.p_vaddr; pHeader.p_paddr = pHeader32.p_paddr; pHeader.p_filesz = pHeader32.p_filesz; pHeader.p_memsz = pHeader32.p_memsz; pHeader.p_flags = pHeader32.p_flags; pHeader.p_align = pHeader32.p_align; } else { pHeader = Get(programHeaderOffset); } printf("\nProgram header Type: %s, flags 0x%X", Lookup(ELFPTypeNames, (uint32)pHeader.p_type), (uint32)pHeader.p_flags); printf("\noffset = 0x%X, vaddr = 0x%X, paddr = 0x%X, filesize = 0x%X, memsize = 0x%X, align = 0x%X", (uint32)pHeader.p_offset, (uint32)pHeader.p_vaddr, (uint32)pHeader.p_paddr, (uint32)pHeader.p_filesz, (uint32)pHeader.p_memsz, (uint32)pHeader.p_align); programHeaderOffset += programHeaderSize; if (pHeader.p_filesz < 0x100 && (int32)pHeader.p_offset < GetDataSize() && memchr(Buf()+pHeader.p_offset, 0, (uint32)pHeader.p_filesz)) { printf("\nContents: %s", Buf()+(int32)pHeader.p_offset); } } } if (options & DUMP_SECTHDR) { // Dump section headers printf("\n\nSection headers:"); for (uint32 sc = 0; sc < NSections; sc++) { // Get copy of 32-bit header or converted 64-bit header TELF_SectionHeader sheader = SectionHeaders[sc]; uint32 entrysize = (uint32)(sheader.sh_entsize); uint32 namei = sheader.sh_name; if (namei >= SecStringTableLen) {err.submit(2112); break;} printf("\n%2i Name: %-18s Type: %s", sc, SecStringTable + namei, Lookup(ELFSectionTypeNames, sheader.sh_type)); if (sheader.sh_flags) { printf("\n Flags: 0x%X:", uint32(sheader.sh_flags)); for (int fi = 1; fi < (1 << 30); fi <<= 1) { if (uint32(sheader.sh_flags) & fi) { printf(" %s", Lookup(ELFSectionFlagNames,fi)); } } } if (sheader.sh_addr) { printf("\n Address: 0x%X", uint32(sheader.sh_addr)); } if (sheader.sh_offset || sheader.sh_size) { printf("\n FileOffset: 0x%X, Size: 0x%X", uint32(sheader.sh_offset), uint32(sheader.sh_size)); } if (sheader.sh_addralign) { printf("\n Alignment: 0x%X", uint32(sheader.sh_addralign)); } if (sheader.sh_entsize) { printf("\n Entry size: 0x%X", uint32(sheader.sh_entsize)); switch (sheader.sh_type) { case SHT_DYNAMIC: printf("\n String table: %i", sheader.sh_link); break; case SHT_HASH: printf("\n Symbol table: %i", sheader.sh_link); break; case SHT_REL: case SHT_RELA: printf("\n Symbol table: %i, Reloc. section: %i", sheader.sh_link, sheader.sh_info); break; case SHT_SYMTAB: case SHT_DYNSYM: printf("\n Symbol string table: %i, First global symbol: %i", sheader.sh_link, sheader.sh_info); break; default: if (sheader.sh_link) { printf("\n Link: %i", sheader.sh_link); } if (sheader.sh_info) { printf("\n Info: %i", sheader.sh_info); } } } if (sheader.sh_type == SHT_STRTAB && (options & DUMP_STRINGTB)) { // Print string table printf("\n String table:"); char * p = Buf() + uint32(sheader.sh_offset) + 1; uint32 nread = 1, len; while (nread < uint32(sheader.sh_size)) { len = (uint32)strlen(p); printf(" >>%s<<", p); nread += len + 1; p += len + 1; } } if ((sheader.sh_type==SHT_SYMTAB || sheader.sh_type==SHT_DYNSYM) && (options & DUMP_SYMTAB)) { // Dump symbol table // Find associated string table if (sheader.sh_link >= (uint32)NSections) {err.submit(2035); sheader.sh_link = 0;} int8 * strtab = Buf() + uint32(SectionHeaders[sheader.sh_link].sh_offset); // Find symbol table uint32 symtabsize = (uint32)(sheader.sh_size); int8 * symtab = Buf() + uint32(sheader.sh_offset); int8 * symtabend = symtab + symtabsize; if (entrysize < sizeof(TELF_Symbol)) {err.submit(2033); entrysize = sizeof(TELF_Symbol);} printf("\n Symbols:"); // Loop through symbol table int symi; // Symbol number for (symi = 0; symtab < symtabend; symtab += entrysize, symi++) { // Copy 32 bit symbol table entry or convert 64 bit entry TELF_Symbol sym = *(TELF_Symbol*)symtab; int type = sym.st_type; int binding = sym.st_bind; if (*(strtab + sym.st_name)) { printf("\n %2i Name: %s,", symi, strtab + sym.st_name);} else { printf("\n %2i Unnamed,", symi);} if (sym.st_value || type == STT_OBJECT || type == STT_FUNC || type == STT_GNU_IFUNC || int16(sym.st_shndx) < 0) printf(" Value: 0x%X", uint32(sym.st_value)); if (sym.st_size) printf(" Size: %i", uint32(sym.st_size)); if (sym.st_other) printf(" Other: 0x%X", sym.st_other); if (int16(sym.st_shndx) >= 0) printf(" Section: %i", sym.st_shndx); else { // Special segment values switch (int16(sym.st_shndx)) { case SHN_ABS: printf(" Absolute,"); break; case SHN_COMMON: printf(" Common,"); break; case SHN_XINDEX: printf(" Index in extra table,"); break; default: printf(" Section: 0x%X", sym.st_shndx); } } if (sym.st_type || sym.st_bind) { printf(" Type: %s, Binding: %s", Lookup(ELFSymbolTypeNames, type), Lookup(ELFSymbolBindingNames, binding)); } } } if ((sheader.sh_type==SHT_REL || sheader.sh_type==SHT_RELA ) && (options & DUMP_RELTAB)) { printf("\n Relocations:"); int8 * reltab = Buf() + uint32(sheader.sh_offset); int8 * reltabend = reltab + uint32(sheader.sh_size); uint32 expectedentrysize = sheader.sh_type == SHT_RELA ? sizeof(TELF_Relocation) : // Elf32_Rela, Elf64_Rela sizeof(TELF_Relocation) - WordSize/8; // Elf32_Rel, Elf64_Rel if (entrysize < expectedentrysize) {err.submit(2033); entrysize = expectedentrysize;} // Loop through entries for (; reltab < reltabend; reltab += entrysize) { // Copy relocation table entry with or without addend TELF_Relocation rel; rel.r_addend = 0; memcpy(&rel, reltab, entrysize); printf ("\n Offset: 0x%X, Symbol: %i, Name: %s\n Type: %s", uint32(rel.r_offset), rel.r_sym, SymbolName(rel.r_sym), (WordSize == 32) ? Lookup (ELF32RelocationNames, rel.r_type) : Lookup (ELF64RelocationNames, rel.r_type)); if (rel.r_addend) printf (", Addend: 0x%X", uint32(rel.r_addend)); // Find inline addend TELF_SectionHeader relsheader = SectionHeaders[sheader.sh_info]; uint32 relsoffset = uint32(relsheader.sh_offset); if (relsoffset+rel.r_offset < GetDataSize()) { int32 * piaddend = (int32*)(Buf()+relsoffset+rel.r_offset); if (* piaddend) printf (", Inline addend: 0x%X", * piaddend); } } } } } } // PublicNames template void CELF::PublicNames(CMemoryBuffer * Strings, CSList * Index, int m) { // Make list of public names // Interpret header: ParseFile(); // Loop through section headers for (uint32 sc = 0; sc < NSections; sc++) { // Get copy of 32-bit header or converted 64-bit header TELF_SectionHeader sheader = SectionHeaders[sc]; uint32 entrysize = uint32(sheader.sh_entsize); if (sheader.sh_type==SHT_SYMTAB || sheader.sh_type==SHT_DYNSYM) { // Dump symbol table // Find associated string table if (sheader.sh_link >= (uint32)NSections) {err.submit(2035); sheader.sh_link = 0;} int8 * strtab = Buf() + uint32(SectionHeaders[sheader.sh_link].sh_offset); // Find symbol table uint32 symtabsize = uint32(sheader.sh_size); int8 * symtab = Buf() + uint32(sheader.sh_offset); int8 * symtabend = symtab + symtabsize; if (entrysize < sizeof(TELF_Symbol)) {err.submit(2033); entrysize = sizeof(TELF_Symbol);} // Loop through symbol table for (int symi = 0; symtab < symtabend; symtab += entrysize, symi++) { // Copy 32 bit symbol table entry or convert 64 bit entry TELF_Symbol sym = *(TELF_Symbol*)symtab; int type = sym.st_type; int binding = sym.st_bind; if (int16(sym.st_shndx) > 0 && type != STT_SECTION && type != STT_FILE && (binding == STB_GLOBAL || binding == STB_WEAK)) { // Public symbol found SStringEntry se; se.Member = m; // Store name se.String = Strings->PushString(strtab + sym.st_name); // Store name index Index->Push(se); } } } } } // SymbolName template const char * CELF::SymbolName(uint32 index) { // Get name of symbol. (ParseFile() must be called first) const char * symname = "?"; // Symbol name uint32 symi; // Symbol index uint32 stri; // String index if (SymbolTableOffset) { symi = SymbolTableOffset + index * SymbolTableEntrySize; if (symi < GetDataSize()) { stri = Get(symi).st_name; if (stri < SymbolStringTableSize) { symname = Buf() + SymbolStringTableOffset + stri; } } } return symname; } // Make template instances for 32 and 64 bits template class CELF; template class CELF;