diff --git a/tools/common/utils.h b/tools/common/utils.h index a20e0e6..971d286 100644 --- a/tools/common/utils.h +++ b/tools/common/utils.h @@ -115,6 +115,13 @@ align_down(Type value, size_t alignment) return static_cast(static_cast(value) & ~(alignment - 1)); } +template +constexpr bool +align_check(Type value, size_t alignment) +{ + return (static_cast(value) & (alignment - 1)) == 0; +} + #define CHECK_SIZE(Type, Size) \ static_assert(sizeof(Type) == Size, \ #Type " must be " #Size " bytes") diff --git a/tools/readrpl/main.cpp b/tools/readrpl/main.cpp index 5d61a5d..3b67871 100644 --- a/tools/readrpl/main.cpp +++ b/tools/readrpl/main.cpp @@ -1,555 +1,44 @@ +#include "elf.h" +#include "print.h" +#include "verify.h" + #include #include #include #include #include #include -#include "elf.h" -struct Section +uint32_t +getSectionIndex(const Rpl &rpl, + const Section §ion) { - 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 ""; - } + return static_cast(§ion - &rpl.sections[0]); } -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) +bool +readSection(std::ifstream &fh, + Section §ion, + size_t i) { // Read section header - fh.read(reinterpret_cast(&header), sizeof(elf::SectionHeader)); + fh.read(reinterpret_cast(§ion.header), sizeof(elf::SectionHeader)); - if (header.type == elf::SHT_NOBITS || !header.size) { + if (section.header.type == elf::SHT_NOBITS || !section.header.size) { return true; } // Read section data - if (header.flags & elf::SHF_DEFLATED) { + if (section.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.seekg(section.header.offset.value()); fh.read(reinterpret_cast(&size), sizeof(uint32_t)); size = byte_swap(size); - data.resize(size); + section.data.resize(size); // Inflate memset(&stream, 0, sizeof(stream)); @@ -561,32 +50,32 @@ bool readSection(std::ifstream &fh, if (ret != Z_OK) { fmt::print("Couldn't decompress .rpx section because inflateInit returned {}\n", ret); - data.clear(); + section.data.clear(); return false; } else { std::vector temp; - temp.resize(header.size-sizeof(uint32_t)); + temp.resize(section.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()); + stream.avail_in = section.header.size; + stream.next_in = reinterpret_cast(temp.data()); + stream.avail_out = static_cast(section.data.size()); + stream.next_out = reinterpret_cast(section.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(); + section.data.clear(); return false; } inflateEnd(&stream); } } else { - data.resize(header.size); - fh.seekg(header.offset.value()); - fh.read(data.data(), header.size); + section.data.resize(section.header.size); + fh.seekg(section.header.offset.value()); + fh.read(section.data.data(), section.header.size); } return true; @@ -660,57 +149,62 @@ int main(int argc, char **argv) // 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)); + Rpl rpl; + fh.read(reinterpret_cast(&rpl.header), sizeof(elf::Header)); - if (header.magic != elf::HeaderMagic) { + if (rpl.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) { + for (auto i = 0u; i < rpl.header.shnum; ++i) { Section section; - fh.seekg(header.shoff + header.shentsize * i); + fh.seekg(rpl.header.shoff + rpl.header.shentsize * i); - if (!readSection(fh, section.header, section.data)) { + if (!readSection(fh, section, i)) { fmt::print("Error reading section {}", i); return -1; } - sections.push_back(section); + rpl.sections.push_back(section); } - // Find strtab - auto shStrTab = reinterpret_cast(sections[header.shstrndx].data.data()); + // Set section names + auto shStrTab = reinterpret_cast(rpl.sections[rpl.header.shstrndx].data.data()); + for (auto §ion : rpl.sections) { + section.name = shStrTab + section.header.name; + } + + // Verify rpl format + verifyFile(rpl); + verifyCrcs(rpl); + verifyFileBounds(rpl); + verifyRelocationTypes(rpl); + verifySectionAlignment(rpl); + verifySectionOrder(rpl); // Format shit if (dumpElfHeader) { - printHeader(header); + printHeader(rpl); } if (dumpSectionSummary) { - printSectionSummary(sections, shStrTab); + printSectionSummary(rpl); } - checkRplCrcs(sections); - // Print section data - for (auto i = 0u; i < sections.size(); ++i) { - auto §ion = sections[i]; - auto name = shStrTab + section.header.name; + for (auto i = 0u; i < rpl.sections.size(); ++i) { + auto §ion = rpl.sections[i]; auto printSectionHeader = [&](){ fmt::print( "Section {}: {}, {}, {} bytes\n", - i, formatSHT(section.header.type), name, section.data.size()); + i, formatSHT(section.header.type), section.name, section.data.size()); }; switch (section.header.type) { @@ -724,7 +218,7 @@ int main(int argc, char **argv) } printSectionHeader(); - printRela(sections, shStrTab, section); + printRela(rpl, section); break; case elf::SHT_SYMTAB: if (!dumpSectionSymtab) { @@ -732,7 +226,7 @@ int main(int argc, char **argv) } printSectionHeader(); - printSymTab(sections, section); + printSymTab(rpl, section); break; case elf::SHT_STRTAB: break; @@ -744,7 +238,7 @@ int main(int argc, char **argv) } printSectionHeader(); - printRplExports(section); + printRplExports(rpl, section); break; case elf::SHT_RPL_IMPORTS: if (!dumpSectionRplImports) { @@ -752,7 +246,7 @@ int main(int argc, char **argv) } printSectionHeader(); - printRplImports(sections, i, section); + printRplImports(rpl, section); break; case elf::SHT_RPL_CRCS: if (!dumpSectionRplCrcs) { @@ -760,7 +254,7 @@ int main(int argc, char **argv) } printSectionHeader(); - printRplCrcs(sections, shStrTab, section); + printRplCrcs(rpl, section); break; case elf::SHT_RPL_FILEINFO: if (!dumpSectionRplFileinfo) { @@ -768,7 +262,7 @@ int main(int argc, char **argv) } printSectionHeader(); - printFileInfo(section); + printFileInfo(rpl, section); break; } } diff --git a/tools/readrpl/print.cpp b/tools/readrpl/print.cpp new file mode 100644 index 0000000..cb8f972 --- /dev/null +++ b/tools/readrpl/print.cpp @@ -0,0 +1,477 @@ +#include "print.h" +#include +#include + +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 fmt::format("{}", type); + } +} + +static std::string +formatEM(uint32_t machine) +{ + switch (machine) { + case elf::EM_PPC: + return "EM_PPC"; + default: + return fmt::format("{}", machine); + } +} + +static std::string +formatEABI(uint32_t eabi) +{ + switch (eabi) { + case elf::EABI_CAFE: + return "EABI_CAFE"; + default: + return fmt::format("{}", eabi); + } +} + +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; +} + +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 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); + } +} + +void +printHeader(const Rpl &rpl) +{ + const auto &header = rpl.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); +} + +void +printSectionSummary(const Rpl &rpl) +{ + 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 < rpl.sections.size(); ++i) { + auto §ion = rpl.sections[i]; + 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, + section.name, + type, + section.header.addr, + section.header.offset, + section.header.size, + section.header.entsize, + flags, + section.header.link, + section.header.info, + section.header.addralign); + } +} + +void +printFileInfo(const Rpl &rpl, + const Section §ion) +{ + auto &info = *reinterpret_cast(section.data.data()); + 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) { + auto filename = section.data.data() + 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); + } + } +} + +void +printRela(const Rpl &rpl, + const Section §ion) +{ + fmt::print( + " {:<8} {:<8} {:<16} {:<8} {}\n", "Offset", "Info", "Type", "Value", "Name + Addend"); + + auto &symSec = rpl.sections[section.header.link]; + auto symbols = reinterpret_cast(symSec.data.data()); + auto &symStrTab = rpl.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); + } +} + +void +printSymTab(const Rpl &rpl, + const Section §ion) +{ + auto strTab = reinterpret_cast(rpl.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; + } +} + +void +printRplImports(const Rpl &rpl, + const Section §ion) +{ + auto sectionIndex = getSectionIndex(rpl, section); + 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 : rpl.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(rpl.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 == sectionIndex && + (type == elf::STT_FUNC || type == elf::STT_OBJECT)) { + fmt::print(" {}\n", strTab + symbol.name); + } + } + } + } +} + +void +printRplCrcs(const Rpl &rpl, + const Section §ion) +{ + auto crcs = reinterpret_cast(section.data.data()); + auto count = section.data.size() / sizeof(elf::RplCrc); + + for (auto i = 0u; i < count; ++i) { + fmt::print(" [{:>2}] 0x{:08X} {}\n", i, crcs[i].crc, section.name); + } +} + +void +printRplExports(const Rpl &rpl, + const 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", value, name); + } +} diff --git a/tools/readrpl/print.h b/tools/readrpl/print.h new file mode 100644 index 0000000..ff1e8b9 --- /dev/null +++ b/tools/readrpl/print.h @@ -0,0 +1,35 @@ +#pragma once +#include "readrpl.h" + +std::string +formatSHT(uint32_t type); + +void +printHeader(const Rpl &rpl); + +void +printSectionSummary(const Rpl &rpl); + +void +printFileInfo(const Rpl &rpl, + const Section §ion); + +void +printRela(const Rpl &rpl, + const Section §ion); + +void +printSymTab(const Rpl &rpl, + const Section §ion); + +void +printRplImports(const Rpl &rpl, + const Section §ion); + +void +printRplCrcs(const Rpl &rpl, + const Section §ion); + +void +printRplExports(const Rpl &rpl, + const Section §ion); diff --git a/tools/readrpl/readrpl.h b/tools/readrpl/readrpl.h new file mode 100644 index 0000000..683fe7b --- /dev/null +++ b/tools/readrpl/readrpl.h @@ -0,0 +1,22 @@ +#pragma once +#include "elf.h" +#include +#include + +struct Section +{ + elf::SectionHeader header; + std::string name; + std::vector data; +}; + +struct Rpl +{ + elf::Header header; + uint32_t fileSize; + std::vector
sections; +}; + +uint32_t +getSectionIndex(const Rpl &rpl, + const Section §ion); diff --git a/tools/readrpl/verify.cpp b/tools/readrpl/verify.cpp new file mode 100644 index 0000000..a87ca40 --- /dev/null +++ b/tools/readrpl/verify.cpp @@ -0,0 +1,585 @@ +#include "verify.h" +#include +#include +#include +#include + +static bool +sValidateRelocsAddTable(const Rpl &rpl, + const Section §ion) +{ + const auto &header = section.header; + if (!header.size) { + return true; + } + + auto entsize = static_cast(header.entsize); + if (!entsize) { + entsize = static_cast(sizeof(elf::Rela)); + } + + if (entsize < sizeof(elf::Rela)) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0002E); + return false; + } + + auto numRelas = (header.size / entsize); + if (!numRelas) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0000A); + return false; + } + + if (!header.link || header.link >= rpl.header.shnum) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0000B); + return false; + } + + const auto &symbolSection = rpl.sections[header.link]; + if (symbolSection.header.type != elf::SHT_SYMTAB) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0000C); + return false; + } + + auto symEntsize = symbolSection.header.entsize ? + static_cast(symbolSection.header.entsize) : + static_cast(sizeof(elf::Symbol)); + if (symEntsize < sizeof(elf::Symbol)) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0002F); + return false; + } + + if (header.info >= rpl.header.shnum) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0000D); + return false; + } + + const auto &targetSection = rpl.sections[header.info]; + if (targetSection.header.type != elf::SHT_NULL) { + auto numSymbols = symbolSection.data.size() / symEntsize; + for (auto i = 0u; i < numRelas; ++i) { + auto rela = reinterpret_cast(section.data.data() + i * entsize); + if (rela->info && (rela->info >> 8) >= numSymbols) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0000F); + return false; + } + } + } + + return true; +} + +static bool +sValidateSymbolTable(const Rpl &rpl, + const Section §ion) +{ + auto result = true; + const auto &header = section.header; + if (!header.size) { + return true; + } + + const Section *symStrTabSection = nullptr; + if (header.link) { + if (header.link >= rpl.header.shnum) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00001); + return false; + } + + symStrTabSection = &rpl.sections[header.link]; + if (symStrTabSection->header.type != elf::SHT_STRTAB) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00002); + return false; + } + } + + auto entsize = header.entsize ? + static_cast(header.entsize) : + static_cast(sizeof(elf::Symbol)); + if (entsize < sizeof(elf::Symbol)) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0002D); + return false; + } + + auto numSymbols = header.size / entsize; + if (!numSymbols) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00003); + result = false; + } + + for (auto i = 0u; i < numSymbols; ++i) { + auto symbol = reinterpret_cast(section.data.data() + i * entsize); + + if (symStrTabSection && + symbol->name > symStrTabSection->data.size()) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00004); + } + + auto type = symbol->info & 0xF; + if (symbol->shndx && + symbol->shndx < elf::SHN_LORESERVE && + type != elf::STT_SECTION && + type != elf::STT_FILE) { + if (symbol->shndx >= rpl.header.shnum) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00005); + result = false; + } else if (type == elf::STT_OBJECT) { + const auto &targetSection = rpl.sections[symbol->shndx]; + auto targetSectionSize = targetSection.data.size() ? + static_cast(targetSection.data.size()) : + static_cast(targetSection.header.size); + + if (targetSectionSize && + targetSection.header.flags & elf::SHF_ALLOC) { + if (targetSection.header.type == elf::SHT_NULL) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00006); + result = false; + } + + auto position = symbol->value - targetSection.header.addr; + if (position > targetSectionSize || position + symbol->size > targetSectionSize) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00007); + result = false; + } + } + } else if (type == elf::STT_FUNC) { + const auto &targetSection = rpl.sections[symbol->shndx]; + auto targetSectionSize = targetSection.data.size() ? + static_cast(targetSection.data.size()) : + static_cast(targetSection.header.size); + + if (targetSectionSize && + targetSection.header.flags & elf::SHF_ALLOC) { + if (targetSection.header.type == elf::SHT_NULL) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00008); + result = false; + } + + auto position = symbol->value - targetSection.header.addr; + if (position > targetSectionSize || position + symbol->size > targetSectionSize) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00009); + result = false; + } + } + } + } + } + + return result; +} + +/** + * Equivalent to loader.elf ELFFILE_ValidateAndPrepare + */ +bool +verifyFile(const Rpl &rpl) +{ + const auto &header = rpl.header; + auto result = true; + + if (rpl.fileSize < 0x104) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00018); + return false; + } + + if (header.magic != elf::HeaderMagic) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00019); + result = false; + } + + if (header.fileClass != elf::ELFCLASS32) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001A); + result = false; + } + + if (header.elfVersion > elf::EV_CURRENT) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001B); + result = false; + } + + if (!header.machine) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001C); + result = false; + } + + if (!header.version != 1) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001D); + result = false; + } + + auto ehsize = static_cast(header.ehsize); + if (ehsize) { + if (header.ehsize < sizeof(elf::Header)) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001E); + result = false; + } + } else { + ehsize = static_cast(sizeof(elf::Header)); + } + + auto phoff = header.phoff; + if (phoff && (phoff < ehsize || phoff >= rpl.fileSize)) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001F); + result = false; + } + + auto shoff = header.shoff; + if (shoff && (shoff < ehsize || shoff >= rpl.fileSize)) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00020); + result = false; + } + + if (header.shstrndx && header.shstrndx >= header.shnum) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00021); + result = false; + } + + auto phentsize = header.phentsize ? + static_cast(header.phentsize) : + static_cast(32); + if (header.phoff && + (header.phoff + phentsize * header.phnum) > rpl.fileSize) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00022); + result = false; + } + + auto shentsize = header.shentsize ? + static_cast(header.shentsize) : + static_cast(sizeof(elf::SectionHeader)); + if (header.shoff && + (header.shoff + shentsize * header.shnum) > rpl.fileSize) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00023); + result = false; + } + + for (auto §ion : rpl.sections) { + if (section.header.size && + section.header.type != elf::SHT_NOBITS) { + if (section.header.offset < ehsize) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00024); + result = false; + } + + if (section.header.offset >= shoff && + section.header.offset < (shoff + header.shnum * shentsize)) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00027); + result = false; + } + } + } + + if (header.shstrndx) { + const auto &shStrTabSection = rpl.sections[header.shstrndx]; + if (shStrTabSection.header.type != elf::SHT_STRTAB) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0002A); + result = false; + } else { + for (auto §ion : rpl.sections) { + if (section.header.name >= shStrTabSection.data.size()) { + fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0002B); + result = false; + } + } + } + } + + for (const auto §ion : rpl.sections) { + if (section.header.type == elf::SHT_RELA) { + result = sValidateRelocsAddTable(rpl, section) && result; + } else if (section.header.type == elf::SHT_SYMTAB) { + result = sValidateSymbolTable(rpl, section) && result; + } + } + + return result; +} + + +/** + * Verify values in SHT_RPL_CRCS + */ +bool +verifyCrcs(const Rpl &rpl) +{ + const elf::RplCrc *crcs = NULL; + auto result = true; + + for (const auto §ion : rpl.sections) { + if (section.header.type == elf::SHT_RPL_CRCS) { + crcs = reinterpret_cast(section.data.data()); + break; + } + } + + if (!crcs) { + return false; + } + + auto sectionIndex = 0u; + for (const auto §ion : rpl.sections) { + auto crc = uint32_t { 0u }; + if (section.header.type != elf::SHT_RPL_CRCS && + section.data.size()) { + crc = crc32(0, Z_NULL, 0); + crc = crc32(crc, reinterpret_cast(section.data.data()), section.data.size()); + } + + if (crc != crcs[sectionIndex].crc) { + fmt::print("Unexpected crc for section {}, read 0x{:08X} but calculated 0x{:08X}", + sectionIndex, crcs[sectionIndex].crc, crc); + result = false; + } + + sectionIndex++; + } + + return result; +} + + +/** + * Equivalent to loader.elf LiCheckFileBounds + */ +bool +verifyFileBounds(const Rpl &rpl) +{ + auto result = true; + auto dataMin = -1; + auto dataMax = 0; + + auto readMin = -1; + auto readMax = 0; + + auto textMin = -1; + auto textMax = 0; + + auto tempMin = -1; + auto tempMax = 0; + + for (const auto §ion : rpl.sections) { + if (section.header.size == 0 || + section.header.type == elf::SHT_RPL_FILEINFO || + section.header.type == elf::SHT_RPL_CRCS || + section.header.type == elf::SHT_NOBITS || + section.header.type == elf::SHT_RPL_IMPORTS) { + continue; + } + + if ((section.header.flags & elf::SHF_EXECINSTR) && + section.header.type != elf::SHT_RPL_EXPORTS) { + textMin = std::min(textMin, static_cast(section.header.offset)); + textMax = std::min(textMax, static_cast(section.header.offset + section.header.size)); + } else { + if (section.header.flags & elf::SHF_ALLOC) { + if (section.header.flags & elf::SHF_WRITE) { + dataMin = std::min(dataMin, static_cast(section.header.offset)); + dataMax = std::min(dataMax, static_cast(section.header.offset + section.header.size)); + } else { + readMin = std::min(readMin, static_cast(section.header.offset)); + readMax = std::min(readMax, static_cast(section.header.offset + section.header.size)); + } + } else { + tempMin = std::min(tempMin, static_cast(section.header.offset)); + tempMax = std::min(tempMax, static_cast(section.header.offset + section.header.size)); + } + } + } + + if (dataMin == -1) { + dataMax = (rpl.header.shnum * rpl.header.shentsize) + rpl.header.shoff; + } + + if (readMin == -1) { + readMin = dataMax; + readMax = dataMax; + } + + if (textMin == -1) { + textMin = readMax; + textMax = readMax; + } + + if (tempMin == -1) { + tempMin = textMax; + tempMax = textMax; + } + + if (dataMin < rpl.header.shoff) { + fmt::print("*** SecHrs, FileInfo, or CRCs in bad spot in file. Return %d.\n", -470026); + result = false; + } + + // Data + if (dataMin > dataMax) { + fmt::print("*** DataMin > DataMax. break.\n"); + result = false; + } + + if (dataMin > readMin) { + fmt::print("*** DataMin > ReadMin. break.\n"); + result = false; + } + + if (dataMax > readMin) { + fmt::print("*** DataMax > ReadMin, break.\n"); + result = false; + } + + // Read + if (readMin > readMax) { + fmt::print("*** ReadMin > ReadMax. break.\n"); + result = false; + } + + if (readMin > textMin) { + fmt::print("*** ReadMin > TextMin. break.\n"); + result = false; + } + + if (readMax > textMin) { + fmt::print("*** ReadMax > TextMin. break.\n"); + result = false; + } + + // Text + if (textMin > textMax) { + fmt::print("*** TextMin > TextMax. break.\n"); + result = false; + } + + if (textMin > tempMin) { + fmt::print("*** TextMin > TempMin. break.\n"); + result = false; + } + + if (textMax > tempMin) { + fmt::print("*** TextMax > TempMin. break.\n"); + result = false; + } + + // Temp + if (tempMin > tempMax) { + fmt::print("*** TempMin > TempMax. break.\n"); + result = false; + } + + if (!result) { + fmt::print("dataMin = 0x{:08X}\n", dataMin); + fmt::print("dataMax = 0x{:08X}\n", dataMax); + fmt::print("readMin = 0x{:08X}\n", readMin); + fmt::print("readMax = 0x{:08X}\n", readMax); + fmt::print("textMin = 0x{:08X}\n", textMin); + fmt::print("textMax = 0x{:08X}\n", textMax); + fmt::print("tempMin = 0x{:08X}\n", tempMin); + fmt::print("tempMax = 0x{:08X}\n", tempMax); + } + + return result; +} + + +/** + * Check that the rpl only uses relocation types which are supported by + * loader.elf + */ +bool +verifyRelocationTypes(const Rpl &rpl) +{ + std::set unsupportedTypes; + + for (auto §ion : rpl.sections) { + if (section.header.type != elf::SHT_RELA) { + continue; + } + + auto &symbolSection = rpl.sections[section.header.link]; + auto &targetSection = rpl.sections[section.header.info]; + auto rels = reinterpret_cast(section.data.data()); + auto numRels = section.data.size() / sizeof(elf::Rela); + + for (auto i = 0u; i < numRels; ++i) { + auto info = rels[i].info; + auto addend = rels[i].addend; + auto offset = rels[i].offset; + auto index = info >> 8; + auto type = info & 0xFF; + + switch (type) { + case elf::R_PPC_NONE: + case elf::R_PPC_ADDR32: + case elf::R_PPC_ADDR16_LO: + case elf::R_PPC_ADDR16_HI: + case elf::R_PPC_ADDR16_HA: + case elf::R_PPC_REL24: + case elf::R_PPC_REL14: + case elf::R_PPC_DTPMOD32: + case elf::R_PPC_DTPREL32: + case elf::R_PPC_EMB_SDA21: + case elf::R_PPC_EMB_RELSDA: + case elf::R_PPC_DIAB_SDA21_LO: + case elf::R_PPC_DIAB_SDA21_HI: + case elf::R_PPC_DIAB_SDA21_HA: + case elf::R_PPC_DIAB_RELSDA_LO: + case elf::R_PPC_DIAB_RELSDA_HI: + case elf::R_PPC_DIAB_RELSDA_HA: + case elf::R_PPC_GHS_REL16_HA: + case elf::R_PPC_GHS_REL16_HI: + case elf::R_PPC_GHS_REL16_LO: + // All valid relocations on Wii U, do nothing + break; + default: + // Only print error once per type + if (!unsupportedTypes.count(type)) { + fmt::print("Unsupported relocation type {}\n", type); + unsupportedTypes.insert(type); + } + } + } + } + + return unsupportedTypes.empty(); +} + + +/** + * Verify that section.addr is aligned by section.addralign + */ +bool +verifySectionAlignment(const Rpl &rpl) +{ + auto result = true; + for (auto §ion : rpl.sections) { + if (!align_check(section.header.addr, section.header.addralign)) { + fmt::print("Unaligned section {}, addr {}, addralign {}", + getSectionIndex(rpl, section), + section.header.addr, + section.header.addralign); + result = false; + } + } + return result; +} + + +bool +verifySectionOrder(const Rpl &rpl) +{ + auto lastSection = rpl.sections[rpl.header.shnum - 1]; + auto penultimateSection = rpl.sections[rpl.header.shnum - 2]; + + + if (lastSection.header.type != elf::SHT_RPL_FILEINFO || + (lastSection.header.flags & elf::SHF_DEFLATED)) { + fmt::print("***shnum-1 section type = 0x{:08X}, flags=0x{:08X}\n", + lastSection.header.type, + lastSection.header.flags); + } + + if (penultimateSection.header.type != elf::SHT_RPL_CRCS || + (penultimateSection.header.flags & elf::SHF_DEFLATED)) { + fmt::print("***shnum-2 section type = 0x{:08X}, flags=0x{:08X}\n", + penultimateSection.header.type, + penultimateSection.header.flags); + } + + return true; +} diff --git a/tools/readrpl/verify.h b/tools/readrpl/verify.h new file mode 100644 index 0000000..8636f5f --- /dev/null +++ b/tools/readrpl/verify.h @@ -0,0 +1,20 @@ +#pragma once +#include "readrpl.h" + +bool +verifyFile(const Rpl &rpl); + +bool +verifyCrcs(const Rpl &rpl); + +bool +verifyFileBounds(const Rpl &rpl); + +bool +verifyRelocationTypes(const Rpl &rpl); + +bool +verifySectionAlignment(const Rpl &rpl); + +bool +verifySectionOrder(const Rpl &rpl);