#include #include #include #include #include #include #include "elf.h" struct Section { elf::SectionHeader header; std::vector data; }; static std::string formatET(uint32_t type) { switch (type) { case elf::ET_NONE: return "ET_NONE"; case elf::ET_REL: return "ET_REL"; case elf::ET_EXEC: return "ET_EXEC"; case elf::ET_DYN: return "ET_DYN"; case elf::ET_CORE: return "ET_CORE"; case elf::ET_CAFE_RPL: return "ET_CAFE_RPL"; default: return ""; } } static std::string formatEM(uint32_t machine) { switch (machine) { case elf::EM_PPC: return "EM_PPC"; default: return ""; } } static std::string formatEABI(uint32_t machine) { switch (machine) { case elf::EABI_CAFE: return "EABI_CAFE"; default: return ""; } } static std::string formatSHF(uint32_t flags) { std::string result = ""; if (flags & elf::SHF_WRITE) { result += "W"; } if (flags & elf::SHF_ALLOC) { result += "A"; } if (flags & elf::SHF_EXECINSTR) { result += "X"; } if (flags & elf::SHF_DEFLATED) { result += "Z"; } return result; } static std::string formatSHT(uint32_t type) { switch (type) { case elf::SHT_NULL: return "SHT_NULL"; case elf::SHT_PROGBITS: return "SHT_PROGBITS"; case elf::SHT_SYMTAB: return "SHT_SYMTAB"; case elf::SHT_STRTAB: return "SHT_STRTAB"; case elf::SHT_RELA: return "SHT_RELA"; case elf::SHT_HASH: return "SHT_HASH"; case elf::SHT_DYNAMIC: return "SHT_DYNAMIC"; case elf::SHT_NOTE: return "SHT_NOTE"; case elf::SHT_NOBITS: return "SHT_NOBITS"; case elf::SHT_REL: return "SHT_REL"; case elf::SHT_SHLIB: return "SHT_SHLIB"; case elf::SHT_DYNSYM: return "SHT_DYNSYM"; case elf::SHT_INIT_ARRAY: return "SHT_INIT_ARRAY"; case elf::SHT_FINI_ARRAY: return "SHT_FINI_ARRAY"; case elf::SHT_PREINIT_ARRAY: return "SHT_PREINIT_ARRAY"; case elf::SHT_GROUP: return "SHT_GROUP"; case elf::SHT_SYMTAB_SHNDX: return "SHT_SYMTAB_SHNDX"; case elf::SHT_LOPROC: return "SHT_LOPROC"; case elf::SHT_HIPROC: return "SHT_HIPROC"; case elf::SHT_LOUSER: return "SHT_LOUSER"; case elf::SHT_RPL_EXPORTS: return "SHT_RPL_EXPORTS"; case elf::SHT_RPL_IMPORTS: return "SHT_RPL_IMPORTS"; case elf::SHT_RPL_CRCS: return "SHT_RPL_CRCS"; case elf::SHT_RPL_FILEINFO: return "SHT_RPL_FILEINFO"; case elf::SHT_HIUSER: return "SHT_HIUSER"; default: return fmt::format("{}", type); } } static void printHeader(elf::Header &header) { fmt::print("ElfHeader\n"); fmt::print(" {:<20} = 0x{:08X}\n", "magic", header.magic); fmt::print(" {:<20} = {}\n", "fileClass", header.fileClass); fmt::print(" {:<20} = {}\n", "encoding", header.encoding); fmt::print(" {:<20} = {}\n", "elfVersion", header.elfVersion); fmt::print(" {:<20} = {} 0x{:04x}\n", "abi", formatEABI(header.abi), header.abi); fmt::print(" {:<20} = {} 0x{:04X}\n", "type", formatET(header.type), header.type); fmt::print(" {:<20} = {} {}\n", "machine", formatEM(header.machine), header.machine); fmt::print(" {:<20} = 0x{:X}\n", "version", header.version); fmt::print(" {:<20} = 0x{:08X}\n", "entry", header.entry); fmt::print(" {:<20} = 0x{:X}\n", "phoff", header.phoff); fmt::print(" {:<20} = 0x{:X}\n", "shoff", header.shoff); fmt::print(" {:<20} = 0x{:X}\n", "flags", header.flags); fmt::print(" {:<20} = {}\n", "ehsize", header.ehsize); fmt::print(" {:<20} = {}\n", "phentsize", header.phentsize); fmt::print(" {:<20} = {}\n", "phnum", header.phnum); fmt::print(" {:<20} = {}\n", "shentsize", header.shentsize); fmt::print(" {:<20} = {}\n", "shnum", header.shnum); fmt::print(" {:<20} = {}\n", "shstrndx", header.shstrndx); } static void printSectionSummary(std::vector
§ions, const char *shStrTab) { fmt::print("Sections:\n"); fmt::print( " {:<4} {:<20} {:<16} {:<8} {:<6} {:<6} {:<2} {:<4} {:<2} {:<4} {:<5}\n", "[Nr]", "Name", "Type", "Addr", "Off", "Size", "ES", "Flag", "Lk", "Info", "Align"); for (auto i = 0u; i < sections.size(); ++i) { auto §ion = sections[i]; auto name = shStrTab + section.header.name; auto type = formatSHT(section.header.type); auto flags = formatSHF(section.header.flags); fmt::print( " [{:>2}] {:<20} {:<16} {:08X} {:06X} {:06X} {:02X} {:>4} {:>2} {:>4} {:>5}\n", i, name, type, static_cast(section.header.addr), section.header.offset, section.header.size, section.header.entsize, flags, section.header.link, section.header.info, section.header.addralign); } } static void printFileInfo(Section §ion) { auto &info = *reinterpret_cast(section.data.data()); auto filename = section.data.data() + info.filename; fmt::print(" {:<20} = 0x{:08X}\n", "version", info.version); fmt::print(" {:<20} = 0x{:08X}\n", "textSize", info.textSize); fmt::print(" {:<20} = 0x{:X}\n", "textAlign", info.textAlign); fmt::print(" {:<20} = 0x{:08X}\n", "dataSize", info.dataSize); fmt::print(" {:<20} = 0x{:X}\n", "dataAlign", info.dataAlign); fmt::print(" {:<20} = 0x{:08X}\n", "loadSize", info.loadSize); fmt::print(" {:<20} = 0x{:X}\n", "loadAlign", info.loadAlign); fmt::print(" {:<20} = 0x{:X}\n", "tempSize", info.tempSize); fmt::print(" {:<20} = 0x{:X}\n", "trampAdjust", info.trampAdjust); fmt::print(" {:<20} = 0x{:X}\n", "trampAddition", info.trampAddition); fmt::print(" {:<20} = 0x{:08X}\n", "sdaBase", info.sdaBase); fmt::print(" {:<20} = 0x{:08X}\n", "sda2Base", info.sda2Base); fmt::print(" {:<20} = 0x{:08X}\n", "stackSize", info.stackSize); fmt::print(" {:<20} = 0x{:08X}\n", "heapSize", info.heapSize); if (info.filename) { fmt::print(" {:<20} = {}\n", "filename", filename); } else { fmt::print(" {:<20} = 0\n", "filename"); } fmt::print(" {:<20} = 0x{:X}\n", "flags", info.flags); fmt::print(" {:<20} = 0x{:08X}\n", "minSdkVersion", info.minVersion); fmt::print(" {:<20} = {}\n", "compressionLevel", info.compressionLevel); fmt::print(" {:<20} = 0x{:X}\n", "fileInfoPad", info.fileInfoPad); fmt::print(" {:<20} = 0x{:X}\n", "sdkVersion", info.cafeSdkVersion); fmt::print(" {:<20} = 0x{:X}\n", "sdkRevision", info.cafeSdkRevision); fmt::print(" {:<20} = 0x{:X}\n", "tlsModuleIndex", info.tlsModuleIndex); fmt::print(" {:<20} = 0x{:X}\n", "tlsAlignShift", info.tlsAlignShift); fmt::print(" {:<20} = 0x{:X}\n", "runtimeFileInfoSize", info.runtimeFileInfoSize); if (info.tagOffset) { const char *tags = section.data.data() + info.tagOffset; fmt::print(" Tags:\n"); while (*tags) { auto key = tags; tags += strlen(tags) + 1; auto value = tags; tags += strlen(tags) + 1; fmt::print(" \"{}\" = \"{}\"\n", key, value); } } } static std::string formatRelType(uint32_t type) { switch (type) { case elf::R_PPC_NONE: return "NONE"; case elf::R_PPC_ADDR32: return "ADDR32"; case elf::R_PPC_ADDR16_LO: return "ADDR16_LO"; case elf::R_PPC_ADDR16_HI: return "ADDR16_HI"; case elf::R_PPC_ADDR16_HA: return "ADDR16_HA"; case elf::R_PPC_REL24: return "REL24"; case elf::R_PPC_REL14: return "REL14"; case elf::R_PPC_DTPMOD32: return "DTPMOD32"; case elf::R_PPC_DTPREL32: return "DTPREL32"; case elf::R_PPC_EMB_SDA21: return "EMB_SDA21"; case elf::R_PPC_EMB_RELSDA: return "EMB_RELSDA"; case elf::R_PPC_DIAB_SDA21_LO: return "DIAB_SDA21_LO"; case elf::R_PPC_DIAB_SDA21_HI: return "DIAB_SDA21_HI"; case elf::R_PPC_DIAB_SDA21_HA: return "DIAB_SDA21_HA"; case elf::R_PPC_DIAB_RELSDA_LO: return "DIAB_RELSDA_LO"; case elf::R_PPC_DIAB_RELSDA_HI: return "DIAB_RELSDA_HI"; case elf::R_PPC_DIAB_RELSDA_HA: return "DIAB_RELSDA_HA"; case elf::R_PPC_GHS_REL16_HA: return "GHS_REL16_HA"; case elf::R_PPC_GHS_REL16_HI: return "GHS_REL16_HI"; case elf::R_PPC_GHS_REL16_LO: return "GHS_REL16_LO"; default: return fmt::format("{}", type); } } static std::string formatSymType(uint32_t type) { switch (type) { case elf::STT_NOTYPE: return "NOTYPE"; case elf::STT_OBJECT: return "OBJECT"; case elf::STT_FUNC: return "FUNC"; case elf::STT_SECTION: return "SECTION"; case elf::STT_FILE: return "FILE"; case elf::STT_COMMON: return "COMMON"; case elf::STT_TLS: return "TLS"; case elf::STT_LOOS: return "LOOS"; case elf::STT_HIOS: return "HIOS"; case elf::STT_GNU_IFUNC: return "GNU_IFUNC"; default: return fmt::format("{}", type); } } static std::string formatSymBinding(uint32_t type) { switch (type) { case elf::STB_LOCAL: return "LOCAL"; case elf::STB_GLOBAL: return "GLOBAL"; case elf::STB_WEAK: return "WEAK"; case elf::STB_GNU_UNIQUE: return "UNIQUE"; default: return fmt::format("{}", type); } } static std::string formatSymShndx(uint32_t type) { switch (type) { case elf::SHN_UNDEF: return "UND"; case elf::SHN_ABS: return "ABS"; case elf::SHN_COMMON: return "CMN"; case elf::SHN_XINDEX: return "UND"; default: return fmt::format("{}", type); } } static void printRela(std::vector
§ions, const char *shStrTab, Section §ion) { fmt::print( " {:<8} {:<8} {:<16} {:<8} {}\n", "Offset", "Info", "Type", "Value", "Name + Addend"); auto &symSec = sections[section.header.link]; auto symbols = reinterpret_cast(symSec.data.data()); auto &symStrTab = sections[symSec.header.link]; auto relas = reinterpret_cast(section.data.data()); auto count = section.data.size() / sizeof(elf::Rela); for (auto i = 0u; i < count; ++i) { auto &rela = relas[i]; auto index = rela.info >> 8; auto type = rela.info & 0xff; auto typeName = formatRelType(type); auto symbol = symbols[index]; auto name = reinterpret_cast(symStrTab.data.data()) + symbol.name; fmt::print( " {:08X} {:08X} {:<16} {:08X} {} + {:X}\n", rela.offset, rela.info, typeName, symbol.value, name, rela.addend); } } static void printSymTab(std::vector
§ions, Section §ion) { auto strTab = reinterpret_cast(sections[section.header.link].data.data()); fmt::print( " {:<4} {:<8} {:<6} {:<8} {:<8} {:<3} {}\n", "Num", "Value", "Size", "Type", "Bind", "Ndx", "Name"); auto id = 0u; auto symbols = reinterpret_cast(section.data.data()); auto count = section.data.size() / sizeof(elf::Symbol); for (auto i = 0u; i < count; ++i) { auto &symbol = symbols[i]; auto name = strTab + symbol.name; auto binding = symbol.info >> 4; auto type = symbol.info & 0xf; auto typeName = formatSymType(type); auto bindingName = formatSymBinding(binding); auto ndx = formatSymShndx(symbol.shndx); fmt::print( " {:>4} {:08X} {:>6} {:<8} {:<8} {:>3} {}\n", id, symbol.value, symbol.size, typeName, bindingName, ndx, name); ++id; } } static void printRplImports(std::vector
§ions, uint32_t index, Section §ion) { auto import = reinterpret_cast(section.data.data()); fmt::print(" {:<20} = {}\n", "name", import->name); fmt::print(" {:<20} = 0x{:08X}\n", "signature", import->signature); fmt::print(" {:<20} = {}\n", "count", import->count); if (import->count) { for (auto &symSection : sections) { if (symSection.header.type != elf::SHT_SYMTAB) { continue; } auto symbols = reinterpret_cast(symSection.data.data()); auto count = symSection.data.size() / sizeof(elf::Symbol); auto strTab = reinterpret_cast(sections[symSection.header.link].data.data()); for (auto i = 0u; i < count; ++i) { auto &symbol = symbols[i]; auto type = symbol.info & 0xf; if (symbol.shndx == index && (type == elf::STT_FUNC || type == elf::STT_OBJECT)) { fmt::print(" {}\n", strTab + symbol.name); } } } } } static void checkRplCrcs(std::vector
§ions) { elf::RplCrc *crcs = NULL; auto crcSectionIndex = 0u; for (auto i = 0u; i < sections.size(); ++i) { auto §ion = sections[i]; if (section.header.type == elf::SHT_RPL_CRCS) { crcs = reinterpret_cast(section.data.data()); crcSectionIndex = i; break; } } if (!crcs) { return; } for (auto i = 0u; i < sections.size(); ++i) { auto §ion = sections[i]; auto crc = uint32_t { 0u }; if (i == crcSectionIndex) { continue; } if (section.data.size()) { crc = crc32(0, Z_NULL, 0); crc = crc32(crc, reinterpret_cast(section.data.data()), section.data.size()); } if (crc != crcs[i].crc) { fmt::print("Unexpected crc for section {}, read 0x{:08X} but calculated 0x{:08X}", i, crcs[i].crc, crc); } } } static void printRplCrcs(std::vector
§ions, const char *shStrTab, Section §ion) { auto crcs = reinterpret_cast(section.data.data()); auto count = section.data.size() / sizeof(elf::RplCrc); for (auto i = 0u; i < count; ++i) { auto name = shStrTab + sections[i].header.name; fmt::print(" [{:>2}] 0x{:08X} {}\n", i, static_cast(crcs[i].crc), name); } } static void printRplExports(Section §ion) { auto exports = reinterpret_cast(section.data.data()); auto strTab = section.data.data(); fmt::print(" {:<20} = 0x{:08X}\n", "signature", exports->signature); fmt::print(" {:<20} = {}\n", "count", exports->count); for (auto i = 0u; i < exports->count; ++i) { // TLS exports have the high bit set in name for some unknown reason... auto name = strTab + (exports->exports[i].name & 0x7FFFFFFF); auto value = exports->exports[i].value; fmt::print(" 0x{:08X} {}\n", static_cast(value), name); } } bool readSection(std::ifstream &fh, elf::SectionHeader &header, std::vector &data) { // Read section header fh.read(reinterpret_cast(&header), sizeof(elf::SectionHeader)); if (header.type == elf::SHT_NOBITS || !header.size) { return true; } // Read section data if (header.flags & elf::SHF_DEFLATED) { auto stream = z_stream {}; auto ret = Z_OK; // Read the original size uint32_t size = 0; fh.seekg(header.offset.value()); fh.read(reinterpret_cast(&size), sizeof(uint32_t)); size = byte_swap(size); data.resize(size); // Inflate memset(&stream, 0, sizeof(stream)); stream.zalloc = Z_NULL; stream.zfree = Z_NULL; stream.opaque = Z_NULL; ret = inflateInit(&stream); if (ret != Z_OK) { fmt::print("Couldn't decompress .rpx section because inflateInit returned {}\n", ret); data.clear(); return false; } else { std::vector temp; temp.resize(header.size-sizeof(uint32_t)); fh.read(temp.data(), temp.size()); stream.avail_in = header.size; stream.next_in = reinterpret_cast(temp.data()); stream.avail_out = static_cast(data.size()); stream.next_out = reinterpret_cast(data.data()); ret = inflate(&stream, Z_FINISH); if (ret != Z_OK && ret != Z_STREAM_END) { fmt::print("Couldn't decompress .rpx section because inflate returned {}\n", ret); data.clear(); return false; } inflateEnd(&stream); } } else { data.resize(header.size); fh.seekg(header.offset.value()); fh.read(data.data(), header.size); } return true; } int main(int argc, char **argv) { excmd::parser parser; excmd::option_state options; using excmd::description; using excmd::value; try { parser.global_options() .add_option("H,help", description { "Show help." }) .add_option("a,all", description { "Equivalent to: -h -S -s -r -i -x -c -f" }) .add_option("h,file-header", description { "Display the ELF file header" }) .add_option("S,sections", description { "Display the sections' header" }) .add_option("s,symbols", description { "Display the symbol table" }) .add_option("r,relocs", description { "Display the relocations" }) .add_option("i,imports", description { "Display the RPL imports" }) .add_option("x,exports", description { "Display the RPL exports" }) .add_option("c,crc", description { "Display the RPL crc" }) .add_option("f,file-info", description { "Display the RPL file info" }); parser.default_command() .add_argument("path", description { "Path to RPL file" }, value {}); options = parser.parse(argc, argv); } catch (excmd::exception ex) { std::cout << "Error parsing options: " << ex.what() << std::endl; return -1; } if (options.empty() || options.has("help") || !options.has("path")) { fmt::print("{} path\n", argv[0]); fmt::print("{}\n", parser.format_help(argv[0])); return 0; } auto all = options.has("all"); auto dumpElfHeader = all || options.has("file-header"); auto dumpSectionSummary = all || options.has("sections"); auto dumpSectionRela = all || options.has("relocs"); auto dumpSectionSymtab = all || options.has("symbols"); auto dumpSectionRplExports = all || options.has("exports"); auto dumpSectionRplImports = all || options.has("imports"); auto dumpSectionRplCrcs = all || options.has("crc"); auto dumpSectionRplFileinfo = all || options.has("file-info"); auto path = options.get("path"); // If no options are set (other than "path"), let's default to a summary if (options.set_options.size() == 1) { dumpElfHeader = true; dumpSectionSummary = true; dumpSectionRplFileinfo = true; } // Read file std::ifstream fh { path, std::ifstream::binary }; if (!fh.is_open()) { fmt::print("Could not open {} for reading\n", path); return -1; } elf::Header header; fh.read(reinterpret_cast(&header), sizeof(elf::Header)); if (header.magic != elf::HeaderMagic) { fmt::print("Invalid ELF magic header\n"); return -1; } // Read sections auto sections = std::vector
{}; for (auto i = 0u; i < header.shnum; ++i) { Section section; fh.seekg(header.shoff + header.shentsize * i); if (!readSection(fh, section.header, section.data)) { fmt::print("Error reading section {}", i); return -1; } sections.push_back(section); } // Find strtab auto shStrTab = reinterpret_cast(sections[header.shstrndx].data.data()); // Format shit if (dumpElfHeader) { printHeader(header); } if (dumpSectionSummary) { printSectionSummary(sections, shStrTab); } checkRplCrcs(sections); // Print section data for (auto i = 0u; i < sections.size(); ++i) { auto §ion = sections[i]; auto name = shStrTab + section.header.name; auto printSectionHeader = [&](){ fmt::print( "Section {}: {}, {}, {} bytes\n", i, formatSHT(section.header.type), name, section.data.size()); }; switch (section.header.type) { case elf::SHT_NULL: case elf::SHT_NOBITS: // Print nothing break; case elf::SHT_RELA: if (!dumpSectionRela) { continue; } printSectionHeader(); printRela(sections, shStrTab, section); break; case elf::SHT_SYMTAB: if (!dumpSectionSymtab) { continue; } printSectionHeader(); printSymTab(sections, section); break; case elf::SHT_STRTAB: break; case elf::SHT_PROGBITS: break; case elf::SHT_RPL_EXPORTS: if (!dumpSectionRplExports) { continue; } printSectionHeader(); printRplExports(section); break; case elf::SHT_RPL_IMPORTS: if (!dumpSectionRplImports) { continue; } printSectionHeader(); printRplImports(sections, i, section); break; case elf::SHT_RPL_CRCS: if (!dumpSectionRplCrcs) { continue; } printSectionHeader(); printRplCrcs(sections, shStrTab, section); break; case elf::SHT_RPL_FILEINFO: if (!dumpSectionRplFileinfo) { continue; } printSectionHeader(); printFileInfo(section); break; } } return 0; }