/**************************** elf2asm.cpp ********************************* * Author: Agner Fog * Date created: 2007-04-22 * Last modified: 2016-11-06 * Project: objconv * Module: elf2asm.cpp * Description: * Module for disassembling ELF * * Copyright 2007-2016 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. // Constructor template CELF2ASM::CELF2ASM() { } // FindImageBase() template void CELF2ASM::FindImageBase() { // Find image base if executable file // Check if executable switch (this->FileHeader.e_type) { case ET_REL: default: // Not an executable file ExeType = 0; ImageBase = 0; return; case ET_DYN: // Shared object ExeType = 1; break; case ET_EXEC: // Executable file ExeType = 2; break; } // Loop through sections to find the first allocated section for (uint32 sc = 0; sc < this->NSections; sc++) { if (this->SectionHeaders[sc].sh_type == SHT_PROGBITS // Must be code or data section && (this->SectionHeaders[sc].sh_flags & SHF_ALLOC) // Must be allocated && this->SectionHeaders[sc].sh_offset <= this->SectionHeaders[sc].sh_addr) { // Avoid negative // Image base can be calculated from this section ImageBase = this->SectionHeaders[sc].sh_addr - this->SectionHeaders[sc].sh_offset; // Make sure ImageBase is divisible by page size ImageBase = ImageBase & - 0x1000; // Stop searching return; } } // Failure. Cannot compute image base from any of the sections ImageBase = 0; return; } // Convert template void CELF2ASM::Convert() { // Do the conversion // Find image base and executable type FindImageBase(); // Tell disassembler Disasm.Init(ExeType, ImageBase); // Set image base // Make Sections list in Disasm MakeSectionList(); // Make Symbols list in Disasm MakeSymbolList(); // Make relocations for object and executable files MakeRelocations(); if (ImageBase) { // Executable file MakeImportList(); // Make imported symbols for executable files MakeExportList(); // Make exported symbols for executable files MakeListLabels(); // Put labels on all image directory tables } Disasm.Go(); // Disassemble *this << Disasm.OutFile; // Take over output file from Disasm } // MakeSectionList template void CELF2ASM::MakeSectionList() { // Make Sections list and Relocations list in Disasm // Allocate array for translating oroginal section numbers to new index SectionNumberTranslate.SetNum(this->NSections + 1); uint32 NewSectionIndex = 0; for (uint32 sc = 0; sc < this->NSections; sc++) { // Get copy of 32-bit header or converted 64-bit header TELF_SectionHeader sheader = this->SectionHeaders[sc]; //int entrysize = (uint32)(sheader.sh_entsize); uint32 namei = sheader.sh_name; if (namei >= this->SecStringTableLen) {err.submit(2112); break;} // if (sheader.sh_type == SHT_PROGBITS || sheader.sh_type == SHT_NOBITS) { // // This is a code, data or bss section if (sheader.sh_flags & SHF_ALLOC) { // This is an allocated section // Give it a new index SectionNumberTranslate[sc] = ++NewSectionIndex; // Get section parameters uint8 * Buffer = (uint8*)(this->Buf()) + (uint32)sheader.sh_offset; uint32 InitSize = (sheader.sh_type == SHT_NOBITS) ? 0 : (uint32)sheader.sh_size; uint32 TotalSize = (uint32)sheader.sh_size; uint32 SectionAddress = (uint32)sheader.sh_addr - (uint32)ImageBase; uint32 Align = FloorLog2((uint32)sheader.sh_addralign); const char * Name = this->SecStringTableLen ? this->SecStringTable + namei : "???"; // Detect segment type uint32 Type = 0; if (sheader.sh_flags & SHF_ALLOC) { // Allocate if (sheader.sh_type == SHT_NOBITS) { // Uninitialized data Type = 3; } else if (sheader.sh_flags & SHF_EXECINSTR) { // Executable Type = 1; } else if (!(sheader.sh_flags & SHF_WRITE)) { // Not writeable Type = 4; } else { // Initialized writeable data Type = 2; } } // Save section record Disasm.AddSection(Buffer, InitSize, TotalSize, SectionAddress, Type, Align, this->WordSize, Name); } } } // MakeSymbolList template void CELF2ASM::MakeSymbolList() { // Make Symbols list in Disasm // Allocate array for translate symbol indices for multiple symbol tables in // source file to a single symbol table in disassembler SymbolTableOffset.SetNum(this->NSections + 1); NumSymbols = 0; for (uint32 sc = 0; sc < this->NSections; sc++) { // Get copy of 32-bit header or converted 64-bit header TELF_SectionHeader sheader = this->SectionHeaders[sc]; int entrysize = (uint32)(sheader.sh_entsize); if (sheader.sh_type==SHT_SYMTAB || sheader.sh_type==SHT_DYNSYM) { // This is a symbol table // Offset for symbols in this symbol table = number of preceding symbols from other symbol tables SymbolTableOffset[sc] = NumSymbols; // Find associated string table if (sheader.sh_link >= this->NSections) {err.submit(2035); sheader.sh_link = 0;} int8 * strtab = this->Buf() + uint32(this->SectionHeaders[sheader.sh_link].sh_offset); // Find symbol table uint32 symtabsize = (uint32)(sheader.sh_size); int8 * symtab = this->Buf() + uint32(sheader.sh_offset); int8 * symtabend = symtab + symtabsize; if (entrysize < (int)sizeof(TELF_Symbol)) {err.submit(2033); entrysize = (int)sizeof(TELF_Symbol);} // Loop through symbol table uint32 symi1; // Symbol number in this table uint32 symi2; // Symbol number in joined table symtab += entrysize; // Skip symbol number 0 for (symi1 = 1; symtab < symtabend; symtab += entrysize, symi1++) { // Symbol number in joined table = symi1 + number of symbols in preceding tables symi2 = SymbolTableOffset[sc] + symi1; // Copy 32 bit symbol table entry or convert 64 bit entry TELF_Symbol sym = *(TELF_Symbol*)symtab; // Parameters uint32 Offset = uint32(sym.st_value); uint32 Size = (uint32)sym.st_size; // Get section int32 Section = int16(sym.st_shndx); if (Section >= (int32)(this->NSections)) { // Error. wrong section Section = 0; } if (Section > 0) { // Translate to new section index Section = SectionNumberTranslate[Section]; } else if ((int16)Section < 0) { // Special section values if ((int16)Section == SHN_ABS) { // Absolute symbol Section = ASM_SEGMENT_ABSOLUTE; } else { // Other special values Section = ASM_SEGMENT_ERROR; } } // Get name const char * Name = 0; if (*(strtab + sym.st_name)) { Name = strtab + sym.st_name; } // Get import .so name const char * DLLName = 0; if (sheader.sh_type==SHT_DYNSYM && sym.st_value == 0 && sym.st_shndx == 0 && sym.st_size > 0) { // I don't know how to find out which .so the symbol is imported from // It must be something in the .dynamic section. DLLName = "?.so"; } // Get scope uint32 Scope = 0; switch (sym.st_bind) { case STB_LOCAL: Scope = 2; break; case STB_WEAK: Scope = 8; if (Section > 0) break; // Section == 0: continue as global case STB_GLOBAL: // Public or external Scope = (sym.st_shndx > 0) ? 4 : 0x20; break; } // Get type uint32 Type = 0; if (sym.st_type == STT_FUNC) { // Function Type = 0x83; } else if (sym.st_type == STT_GNU_IFUNC) { // Gnu indirect function Type = 0x40000083; } else if (sym.st_type == STT_OBJECT) { // Probably a data object switch (Size) { case 1: Type = 1; break; case 2: Type = 2; break; case 4: Type = 3; break; case 8: Type = 4; break; default: Type = 1; break; } } else if (sym.st_type == STT_COMMON) { // Communal? Type = 0; Scope = 0x10; } else if (sym.st_type == STT_SECTION) { // This is a section Type = 0x80000082; Scope = 0; } else if (sym.st_type == STT_NOTYPE) { Type = 0; } else if (sym.st_type == STT_FILE) { // file name. ignore continue; } else { // unknown type. warning err.submit(1062, Name); Type = 0; //continue; } if (Scope != 0x20) { // Not external // Check if offset is absolute or section relative if (ExeType && Offset >= (uint32)ImageBase) { // Offset is absolute address if (Section >= 0 && (uint32)Section < this->NSections && Offset >= (uint32)this->SectionHeaders[Section].sh_addr && Offset - (uint32)this->SectionHeaders[Section].sh_addr < (uint32)(this->SectionHeaders[Section].sh_size)) { // Change to section relative offset Offset -= (uint32)(this->SectionHeaders[Section].sh_addr); } else { // Address is outside specified section or otherwise inconsistent. // Let Disasm try to find the address Section = ASM_SEGMENT_IMGREL; Offset -= (uint32)ImageBase; } } } // Store new symbol record Disasm.AddSymbol(Section, Offset, Size, Type, Scope, symi2, Name, DLLName); // Count symbols NumSymbols++; } } } } // MakeRelocations template void CELF2ASM::MakeRelocations() { // Make relocations for object and executable files int32 Section; // Source section new index // Loop through sections for (uint32 sc = 0; sc < this->NSections; sc++) { // Get copy of 32-bit header or converted 64-bit header TELF_SectionHeader sheader = this->SectionHeaders[sc]; int entrysize = (uint32)(sheader.sh_entsize); if (sheader.sh_type == SHT_REL || sheader.sh_type == SHT_RELA) { // Relocations section int8 * reltab = this->Buf() + uint32(sheader.sh_offset); int8 * reltabend = reltab + uint32(sheader.sh_size); int expectedentrysize = sheader.sh_type == SHT_RELA ? sizeof(TELF_Relocation) : // Elf32_Rela, Elf64_Rela sizeof(TELF_Relocation) - this->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); // Get section-relative or absolute address uint32 Offset = (uint32)rel.r_offset; // Get addend, if any int32 Addend = (uint32)rel.r_addend; // Find target symbol uint32 TargetIndex = rel.r_sym; if (sheader.sh_link < this->NSections) { // sh_link indicates which symbol table r_sym refers to TargetIndex += SymbolTableOffset[sheader.sh_link]; } // Find section if (sheader.sh_info < this->NSections) { Section = SectionNumberTranslate[sheader.sh_info]; } else { // Not found. Try to let disasm find by absolute address Section = ASM_SEGMENT_IMGREL; if (Offset < (uint32)ImageBase) Offset += (uint32)ImageBase; } // Get relocation type and size uint32 Type = 0; uint32 Size = 0; if (this->WordSize == 32) { switch (rel.r_type) { case R_386_RELATIVE: // Adjust by program base Type = 0x21; Size = 4; break; case R_386_JMP_SLOT: // Create PLT entry Type = 0x41; Size = 4; break; case R_386_PLT32: // Self-relative to PLT Type = 0x2002; Size = 4; break; case R_386_32: // Direct 32 bit Type = 1; Size = 4; break; case R_386_PC32: // Self-relative 32 bit Type = 2; Size = 4; break; case R_386_GOTPC: // Self-relative offset to GOT Type = 0x1002; Size = 4; break; case R_386_IRELATIVE: // Reference to Gnu indirect function Type = 0x81; Size = 4; break; case R_386_GLOB_DAT: case R_386_GOT32: case R_386_GOTOFF: // Create GOT entry Type = 0x1001; Size = 4; break; } } else { // 64 bit switch (rel.r_type) { case R_X86_64_RELATIVE: // Adjust by program base Type = 0x21; Size = 8; break; case R_X86_64_JUMP_SLOT: // Create PLT entry Type = 0x41; Size = 8; break; case R_X86_64_64: // Direct 64 bit Type = 1; Size = 8; break; case R_X86_64_PC32: // Self relative 32 bit signed Type = 2; Size = 4; break; case R_X86_64_32: case R_X86_64_32S: // Direct 32 bit zero extended or sign extend Type = 1; Size = 4; break; case R_X86_64_16: // Direct 16 bit zero extended Type = 1; Size = 2; break; case R_X86_64_PC16: // 16 bit sign extended pc relative Type = 2; Size = 2; break; case R_X86_64_8: // Direct 8 bit sign extended Type = 1; Size = 1; break; case R_X86_64_PC8: // 8 bit sign extended pc relative Type = 2; Size = 1; break; case R_X86_64_GOTPCREL: // Self relative 32 bit signed offset to GOT entry Type = 0x1002; Size = 4; break; case R_X86_64_IRELATIVE: // Reference to Gnu indirect function Type = 0x81; Size = 4; break; case R_X86_64_PLT32: // Self-relative to PLT Type = 0x2002; Size = 4; break; case R_X86_64_GLOB_DAT: // Create GOT entry case R_X86_64_GOT32: Type = 0x1001; Size = 4; break; } } // Check if offset is absolute or section relative if (ImageBase && Offset > (uint32)ImageBase) { // Offset is absolute address if (Section > 0 && (uint32)Section < this->NSections && Offset >= (uint32)(this->SectionHeaders[Section].sh_addr) && Offset - (uint32)(this->SectionHeaders[Section].sh_addr) < (uint32)(this->SectionHeaders[Section].sh_size)) { // Change to section relative offset Offset -= (uint32)(this->SectionHeaders[Section].sh_addr); } else { // Inconsistent. Let Disasm try to find the address Section = ASM_SEGMENT_IMGREL; Offset -= (uint32)ImageBase; } } // Save relocation record Disasm.AddRelocation(Section, Offset, Addend, Type, Size, TargetIndex); } } } } // MakeImportList template void CELF2ASM::MakeImportList() { // Make imported symbols for executable files } // MakeExportList template void CELF2ASM::MakeExportList() { // Make exported symbols for executable files } // MakeListLabels template void CELF2ASM::MakeListLabels() { // Attach names to all image directories } // Make template instances for 32 and 64 bits template class CELF2ASM; template class CELF2ASM;