#include "ps2recomp/elf_parser.h" #include "ps2recomp/recompiler_reporter.h" #include "ps2recomp/types.h" #include #include #include #define NOMINMAX #include #if defined(_WIN32) #include #include #include #else #include #endif #include "libdwarf_private.h" #include #include #include #include #include #include namespace { bool IsAutoGeneratedName(const std::string &name) { return name.rfind("sub_", 0) == 0 || name.rfind("FUN_", 0) == 0 || name.rfind("LAB_", 0) == 0 || name.rfind("DAT_", 0) == 0; } void AppendLoadSegmentsAsSections(const ELFIO::elfio &elf, std::vector §ions) { const ELFIO::Elf_Half segCount = elf.segments.size(); if (segCount == 0) { return; } for (ELFIO::Elf_Half i = 0; i < segCount; ++i) { ELFIO::segment *segment = elf.segments[i]; if (!segment || segment->get_type() != ELFIO::PT_LOAD) { continue; } const ELFIO::Elf64_Addr vaddr = segment->get_virtual_address(); const ELFIO::Elf_Xword fileSize = segment->get_file_size(); const ELFIO::Elf_Xword memSize = segment->get_memory_size(); const ELFIO::Elf_Word flags = segment->get_flags(); if (vaddr > 0xFFFFFFFFu || fileSize > 0xFFFFFFFFu || memSize > 0xFFFFFFFFu) { continue; } if (fileSize > 0) { ps2recomp::Section load{}; load.name = "LOAD" + std::to_string(i); load.address = static_cast(vaddr); load.size = static_cast(fileSize); load.offset = static_cast(segment->get_offset()); load.isCode = (flags & ELFIO::PF_X) != 0; load.isData = (flags & ELFIO::PF_W) != 0 || (flags & ELFIO::PF_R) != 0; load.isBSS = false; load.isReadOnly = (flags & ELFIO::PF_W) == 0; load.data = const_cast( reinterpret_cast(segment->get_data())); sections.push_back(load); } if (memSize > fileSize) { ps2recomp::Section bss{}; bss.name = "LOAD" + std::to_string(i) + ".bss"; bss.address = static_cast(vaddr + fileSize); bss.size = static_cast(memSize - fileSize); bss.offset = static_cast(segment->get_offset() + fileSize); bss.isCode = false; bss.isData = true; bss.isBSS = true; bss.isReadOnly = false; bss.data = nullptr; sections.push_back(bss); } } if (!sections.empty()) { std::sort(sections.begin(), sections.end(), [](const ps2recomp::Section &a, const ps2recomp::Section &b) { return a.address < b.address; }); } } const ps2recomp::Section *FindSectionByAddress(const std::vector §ions, uint32_t address) { for (const auto §ion : sections) { if (address >= section.address && address < (section.address + section.size)) { return §ion; } } return nullptr; } } namespace { bool HasDwarfSections(const ELFIO::elfio &elf) { for (ELFIO::Elf_Half i = 0; i < elf.sections.size(); ++i) { const ELFIO::section *section = elf.sections[i]; const std::string &name = section->get_name(); if (name.rfind(".debug_", 0) == 0 || name.rfind(".zdebug_", 0) == 0) { return true; } } return false; } const ps2recomp::Section *FindCodeSectionByAddress(const std::vector §ions, uint32_t address) { for (const auto §ion : sections) { if (!section.isCode) { continue; } if (address >= section.address && address < (section.address + section.size)) { return §ion; } } return nullptr; } bool HasAnyExecutableSection(const std::vector §ions) { for (const auto §ion : sections) { if (section.isCode) { return true; } } return false; } const ps2recomp::Section *FindFunctionSectionByAddress(const std::vector §ions, uint32_t address) { const ps2recomp::Section *codeSection = FindCodeSectionByAddress(sections, address); if (codeSection) { return codeSection; } // Some malformed/stripped ELFs may not carry executable section flags. if (!HasAnyExecutableSection(sections)) { return FindSectionByAddress(sections, address); } return nullptr; } uint32_t ClampFunctionEndToSection(const ps2recomp::Section *section, uint32_t start, uint32_t requestedEnd) { if (!section) { return requestedEnd; } const uint64_t sectionEnd64 = static_cast(section->address) + static_cast(section->size); const uint32_t sectionEnd = (sectionEnd64 > 0xFFFFFFFFull) ? 0xFFFFFFFFu : static_cast(sectionEnd64); uint32_t end = requestedEnd; if (end == 0 || end > sectionEnd) { end = sectionEnd; } if (end <= start) { const uint64_t minimumEnd64 = static_cast(start) + 4ull; if (minimumEnd64 <= sectionEnd64) { end = static_cast(minimumEnd64); } else { end = sectionEnd; } } return end; } std::string MakeAutoFunctionName(uint32_t address) { char buffer[32]{}; std::snprintf(buffer, sizeof(buffer), "sub_%08X", address); return std::string(buffer); } std::string ReadDieName(Dwarf_Debug dbg, Dwarf_Die die, Dwarf_Error *error) { const int kAttrsToTry[] = { #ifdef DW_AT_linkage_name DW_AT_linkage_name, #endif #ifdef DW_AT_MIPS_linkage_name DW_AT_MIPS_linkage_name, #endif }; for (int attrNum : kAttrsToTry) { Dwarf_Attribute attr = nullptr; if (dwarf_attr(die, attrNum, &attr, error) == DW_DLV_OK) { char *attrString = nullptr; if (dwarf_formstring(attr, &attrString, error) == DW_DLV_OK && attrString) { std::string result(attrString); dwarf_dealloc(dbg, attrString, DW_DLA_STRING); dwarf_dealloc(dbg, attr, DW_DLA_ATTR); return result; } dwarf_dealloc(dbg, attr, DW_DLA_ATTR); } } // Fallback: DW_AT_name char *dieName = nullptr; if (dwarf_diename(die, &dieName, error) == DW_DLV_OK && dieName) { std::string result(dieName); dwarf_dealloc(dbg, dieName, DW_DLA_STRING); return result; } return {}; } bool TryReadDieRange( Dwarf_Debug dbg, Dwarf_Die die, uint32_t &outLowPc, uint32_t &outHighPc, Dwarf_Error *error) { outLowPc = 0; outHighPc = 0; Dwarf_Addr lowPc = 0; if (dwarf_lowpc(die, &lowPc, error) != DW_DLV_OK) { return false; } // high_pc can be absolute address (DWARF2/3) or offset from low_pc (DWARF4+) Dwarf_Addr highPc = 0; Dwarf_Half highPcForm = 0; Dwarf_Form_Class highPcClass = DW_FORM_CLASS_UNKNOWN; if (dwarf_highpc_b(die, &highPc, &highPcForm, &highPcClass, error) == DW_DLV_OK) { if (highPcClass == DW_FORM_CLASS_CONSTANT) { highPc = lowPc + highPc; } if (lowPc <= 0xFFFFFFFFu && highPc <= 0xFFFFFFFFu && highPc > lowPc) { outLowPc = static_cast(lowPc); outHighPc = static_cast(highPc); return true; } return false; } // If no high_pc, try DW_AT_ranges Dwarf_Attribute rangesAttr = nullptr; if (dwarf_attr(die, DW_AT_ranges, &rangesAttr, error) != DW_DLV_OK) { if (lowPc <= 0xFFFFFFFFu) { outLowPc = static_cast(lowPc); outHighPc = static_cast(lowPc + 4); return true; } return false; } Dwarf_Off rangesOffset = 0; if (dwarf_global_formref(rangesAttr, &rangesOffset, error) != DW_DLV_OK) { dwarf_dealloc(dbg, rangesAttr, DW_DLA_ATTR); return false; } Dwarf_Ranges *ranges = nullptr; Dwarf_Signed rangesCount = 0; Dwarf_Unsigned byteCount = 0; Dwarf_Off realOffset = 0; if (dwarf_get_ranges_b(dbg, rangesOffset, die, &realOffset, &ranges, &rangesCount, &byteCount, error) != DW_DLV_OK) { dwarf_dealloc(dbg, rangesAttr, DW_DLA_ATTR); return false; } Dwarf_Addr baseAddr = lowPc; Dwarf_Addr minPc = 0; Dwarf_Addr maxPc = 0; bool hasAny = false; for (Dwarf_Signed i = 0; i < rangesCount; ++i) { const Dwarf_Ranges &entry = ranges[i]; if (entry.dwr_type == DW_RANGES_END) { break; } if (entry.dwr_type == DW_RANGES_ADDRESS_SELECTION) { baseAddr = entry.dwr_addr2; continue; } if (entry.dwr_type != DW_RANGES_ENTRY) { continue; } const Dwarf_Addr start = baseAddr + entry.dwr_addr1; const Dwarf_Addr end = baseAddr + entry.dwr_addr2; if (end <= start) { continue; } if (!hasAny) { minPc = start; maxPc = end; hasAny = true; } else { minPc = std::min(minPc, start); maxPc = std::max(maxPc, end); } } dwarf_dealloc_ranges(dbg, ranges, rangesCount); dwarf_dealloc(dbg, rangesAttr, DW_DLA_ATTR); if (!hasAny) { return false; } if (minPc <= 0xFFFFFFFFu && maxPc <= 0xFFFFFFFFu && maxPc > minPc) { outLowPc = static_cast(minPc); outHighPc = static_cast(maxPc); return true; } return false; } void VisitDieTreeAndCollectFunctions( Dwarf_Debug dbg, Dwarf_Die rootDie, ps2recomp::ElfParser *parser, std::vector &outFunctions) { Dwarf_Error error = nullptr; Dwarf_Die current = rootDie; while (current) { Dwarf_Half tag = 0; if (dwarf_tag(current, &tag, &error) == DW_DLV_OK) { if (tag == DW_TAG_subprogram) { uint32_t lowPc = 0; uint32_t highPc = 0; if (TryReadDieRange(dbg, current, lowPc, highPc, &error)) { if (FindCodeSectionByAddress(parser->getSections(), lowPc)) { ps2recomp::Function func{}; func.name = ReadDieName(dbg, current, &error); func.start = lowPc; func.end = highPc; func.isRecompiled = false; func.isStub = false; func.isSkipped = false; if (func.name.empty()) { func.name = MakeAutoFunctionName(func.start); } outFunctions.push_back(std::move(func)); } } } } // Depth-first: child first Dwarf_Die child = nullptr; if (dwarf_child(current, &child, &error) == DW_DLV_OK) { VisitDieTreeAndCollectFunctions(dbg, child, parser, outFunctions); } // Next sibling Dwarf_Die sibling = nullptr; const int siblingResult = dwarf_siblingof_b(dbg, current, TRUE, &sibling, &error); dwarf_dealloc(dbg, current, DW_DLA_DIE); if (siblingResult != DW_DLV_OK) { break; } current = sibling; } } void ScanJalTargetsFallback(ps2recomp::ElfParser *parser, std::vector &outFunctions) { std::unordered_set starts; starts.reserve(4096); const uint32_t entry = parser->getEntryPoint(); if (FindCodeSectionByAddress(parser->getSections(), entry)) { starts.insert(entry); } const auto §ions = parser->getSections(); for (const auto §ion : sections) { if (!section.isCode || !section.data || section.size < 4) { continue; } for (uint32_t offset = 0; offset + 4 <= section.size; offset += 4) { const uint32_t pc = section.address + offset; uint32_t raw = 0; std::memcpy(&raw, section.data + offset, sizeof(uint32_t)); const uint32_t op = (raw >> 26) & 0x3F; if (op != 0x03) // JAL { continue; } const uint32_t index = raw & 0x03FFFFFF; const uint32_t target = ((pc + 4) & 0xF0000000u) | (index << 2); if (FindCodeSectionByAddress(sections, target)) { starts.insert(target); } } } std::vector sortedStarts(starts.begin(), starts.end()); std::sort(sortedStarts.begin(), sortedStarts.end()); for (size_t i = 0; i < sortedStarts.size(); ++i) { const uint32_t start = sortedStarts[i]; const ps2recomp::Section *sec = FindCodeSectionByAddress(sections, start); if (!sec) { continue; } const uint32_t secEnd = sec->address + sec->size; uint32_t end = secEnd; if (i + 1 < sortedStarts.size()) { const uint32_t next = sortedStarts[i + 1]; if (next > start && next < secEnd) { end = next; } } ps2recomp::Function func{}; func.name = MakeAutoFunctionName(start); func.start = start; func.end = (end > start) ? end : (start + 4); func.isRecompiled = false; func.isStub = false; func.isSkipped = false; outFunctions.push_back(std::move(func)); } } } namespace ps2recomp { ElfParser::ElfParser(const std::string &filePath) : m_filePath(filePath), m_elf(new ELFIO::elfio()) { } bool ElfParser::isExecutableSection(const ELFIO::section *section) const { return (section->get_flags() & ELFIO::SHF_EXECINSTR) != 0; } bool ElfParser::isDataSection(const ELFIO::section *section) const { return (section->get_flags() & ELFIO::SHF_ALLOC) != 0 && !(section->get_flags() & ELFIO::SHF_EXECINSTR); } std::vector ElfParser::extractFunctions() const { std::vector functions; functions.reserve(m_symbols.size() + m_extraFunctions.size()); std::unordered_map indexByStart; indexByStart.reserve(functions.capacity()); // Symbol table sizes are authoritative wwhen exist std::unordered_map authoritativeEndByStart; authoritativeEndByStart.reserve(m_symbols.size()); for (const auto &symbol : m_symbols) { if (!symbol.isFunction || symbol.isImported || symbol.size == 0) { continue; } if (m_hasLoadedGhidraMap && IsAutoGeneratedName(symbol.name) && !m_ghidraMapStarts.contains(symbol.address)) { continue; } const Section *functionSection = FindFunctionSectionByAddress(m_sections, symbol.address); if (!functionSection) { continue; } const uint64_t symbolEnd64 = static_cast(symbol.address) + static_cast(symbol.size); uint32_t symbolEnd = (symbolEnd64 > 0xFFFFFFFFull) ? 0xFFFFFFFFu : static_cast(symbolEnd64); symbolEnd = ClampFunctionEndToSection(functionSection, symbol.address, symbolEnd); if (symbolEnd <= symbol.address) { continue; } auto inserted = authoritativeEndByStart.emplace(symbol.address, symbolEnd); if (!inserted.second && symbolEnd > inserted.first->second) { inserted.first->second = symbolEnd; } } // Named debug/map functions with explicit bounds are authoritative too. for (const auto &extra : m_extraFunctions) { if (extra.start == 0 || extra.end <= extra.start || extra.name.empty() || IsAutoGeneratedName(extra.name)) { continue; } const Section *functionSection = FindFunctionSectionByAddress(m_sections, extra.start); if (!functionSection) { continue; } const uint32_t clampedEnd = ClampFunctionEndToSection(functionSection, extra.start, extra.end); if (clampedEnd <= extra.start) { continue; } auto inserted = authoritativeEndByStart.emplace(extra.start, clampedEnd); if (!inserted.second && clampedEnd > inserted.first->second) { inserted.first->second = clampedEnd; } } std::vector> authoritativeRanges; authoritativeRanges.reserve(authoritativeEndByStart.size()); for (const auto &entry : authoritativeEndByStart) { authoritativeRanges.emplace_back(entry.first, entry.second); } std::sort(authoritativeRanges.begin(), authoritativeRanges.end(), [](const std::pair &a, const std::pair &b) { return a.first < b.first; }); auto isInsideAuthoritativeRange = [&](uint32_t startAddress) { if (authoritativeRanges.empty()) { return false; } auto it = std::upper_bound( authoritativeRanges.begin(), authoritativeRanges.end(), startAddress, [](uint32_t value, const std::pair &range) { return value < range.first; }); if (it == authoritativeRanges.begin()) { return false; } --it; return startAddress > it->first && startAddress < it->second; }; auto addOrMerge = [&](const Function &newFunction) { if (newFunction.start == 0) { return; } if (!FindFunctionSectionByAddress(m_sections, newFunction.start)) { return; } const bool insideAuthoritativeRange = isInsideAuthoritativeRange(newFunction.start); const bool hasOwnAuthoritativeRange = authoritativeEndByStart.contains(newFunction.start); const bool hasAutoName = newFunction.name.empty() || IsAutoGeneratedName(newFunction.name); if (insideAuthoritativeRange && (!hasOwnAuthoritativeRange || hasAutoName)) { return; } auto it = indexByStart.find(newFunction.start); if (it == indexByStart.end()) { indexByStart.emplace(newFunction.start, functions.size()); functions.push_back(newFunction); Function &insertedFunction = functions.back(); auto authoritativeIt = authoritativeEndByStart.find(insertedFunction.start); if (authoritativeIt != authoritativeEndByStart.end()) { insertedFunction.end = ClampFunctionEndToSection( FindFunctionSectionByAddress(m_sections, insertedFunction.start), insertedFunction.start, authoritativeIt->second); } return; } Function &existing = functions[it->second]; if (!newFunction.name.empty()) { if (existing.name.empty() || (IsAutoGeneratedName(existing.name) && !IsAutoGeneratedName(newFunction.name))) { existing.name = newFunction.name; } } auto authoritativeIt = authoritativeEndByStart.find(existing.start); if (authoritativeIt != authoritativeEndByStart.end()) { existing.end = ClampFunctionEndToSection( FindFunctionSectionByAddress(m_sections, existing.start), existing.start, authoritativeIt->second); } else if (newFunction.end > existing.end) { existing.end = newFunction.end; } existing.isStub = existing.isStub || newFunction.isStub; existing.isSkipped = existing.isSkipped || newFunction.isSkipped; }; for (const auto &symbol : m_symbols) { if (!symbol.isFunction || symbol.isImported) { continue; } if (!FindFunctionSectionByAddress(m_sections, symbol.address)) { continue; } Function func; func.name = symbol.name; func.start = symbol.address; if (symbol.size > 0) { const uint64_t end64 = static_cast(symbol.address) + static_cast(symbol.size); func.end = (end64 > 0xFFFFFFFFull) ? 0xFFFFFFFFu : static_cast(end64); } else { func.end = 0; } func.isRecompiled = false; func.isStub = false; func.isSkipped = false; addOrMerge(func); } for (const auto &func : m_extraFunctions) { addOrMerge(func); } std::sort(functions.begin(), functions.end(), [](const Function &a, const Function &b) { return a.start < b.start; }); for (size_t index = 0; index < functions.size(); ++index) { Function &func = functions[index]; auto authoritativeIt = authoritativeEndByStart.find(func.start); if (authoritativeIt != authoritativeEndByStart.end()) { func.end = authoritativeIt->second; continue; } if (func.end > func.start) { continue; } const Section *section = FindFunctionSectionByAddress(m_sections, func.start); uint32_t sectionEnd = section ? (section->address + section->size) : (func.start + 4); uint32_t nextStart = sectionEnd; if (index + 1 < functions.size()) { const uint32_t candidate = functions[index + 1].start; if (candidate > func.start && section && candidate < sectionEnd) { nextStart = candidate; } } func.end = (nextStart > func.start) ? nextStart : (func.start + 4); } return functions; } std::vector ElfParser::extractSymbols() { return m_symbols; } std::vector
ElfParser::getSections() { return m_sections; } std::vector ElfParser::getRelocations() { return m_relocations; } std::vector ElfParser::extractExtraFunctions() const { return m_extraFunctions; } bool ElfParser::isValidAddress(uint32_t address) const { for (const auto §ion : m_sections) { if (address >= section.address && address < (section.address + section.size)) { return true; } } return false; } uint32_t ElfParser::readWord(uint32_t address) const { for (const auto §ion : m_sections) { if (address < section.address || section.size < sizeof(uint32_t)) { continue; } const uint32_t offset = address - section.address; if (offset > section.size - static_cast(sizeof(uint32_t))) { continue; } if (section.data) { uint32_t word = 0; std::memcpy(&word, section.data + offset, sizeof(word)); return word; } } throw std::runtime_error("Invalid address for readWord: " + std::to_string(address)); } uint8_t *ElfParser::getSectionData(const std::string §ionName) const { for (const auto §ion : m_sections) { if (section.name == sectionName) { return section.data; } } return nullptr; } uint32_t ElfParser::getSectionAddress(const std::string §ionName) const { for (const auto §ion : m_sections) { if (section.name == sectionName) { return section.address; } } return 0; } uint32_t ElfParser::getSectionSize(const std::string §ionName) const { for (const auto §ion : m_sections) { if (section.name == sectionName) { return section.size; } } return 0; } void ElfParser::debugAddress(uint32_t address) const { for (const auto §ion : m_sections) { if (address < section.address || address >= (section.address + section.size)) { continue; } const uint32_t offset = address - section.address; std::printf( "Address 0x%08X -> section '%s'\n" " section.address=0x%08X section.size=0x%08X section.offset=0x%08X\n" " isCode=%d isData=%d isBSS=%d isReadOnly=%d data=%p\n" " offsetInSection=0x%08X\n", address, section.name.c_str(), section.address, section.size, section.offset, section.isCode ? 1 : 0, section.isData ? 1 : 0, section.isBSS ? 1 : 0, section.isReadOnly ? 1 : 0, (void *)section.data, offset); if (!section.data) { std::printf(" section.data == nullptr (possible SHT_NOBITS/BSS)\n"); return; } const uint32_t dumpStart = (offset >= 16) ? (offset - 16) : 0; const uint32_t dumpEnd = std::min(section.size, offset + 32); std::printf(" bytes around address:\n "); for (uint32_t dumpOffset = dumpStart; dumpOffset < dumpEnd; ++dumpOffset) { std::printf("%02X ", section.data[dumpOffset]); } std::printf("\n"); return; } std::printf("Address 0x%08X not covered by any section in m_sections\n", address); } uint32_t ElfParser::getEntryPoint() const { return static_cast(m_elf->get_entry()); } void ElfParser::setReporter(RecompilerReporter *reporter) { m_reporter = reporter; } bool ElfParser::loadGhidraFunctionMap(const std::string &mapPath) { if (mapPath.empty()) { return false; } m_hasLoadedGhidraMap = false; m_ghidraMapStarts.clear(); std::ifstream file(mapPath); if (!file.is_open()) { if (m_reporter) { m_reporter->warning("ghidra-map", "Could not open Ghidra function map: " + mapPath); } return false; } std::string line; if (!std::getline(file, line)) { return false; } int count = 0; int skippedNonExecutable = 0; int skippedInvalidRange = 0; std::unordered_set mapStarts; while (std::getline(file, line)) { if (line.empty()) continue; std::stringstream ss(line); std::string name, startStr, endStr, sizeStr; if (!std::getline(ss, name, ',') || !std::getline(ss, startStr, ',') || !std::getline(ss, endStr, ',') || !std::getline(ss, sizeStr, ',')) { continue; } try { uint32_t start = std::stoul(startStr, nullptr, 0); uint32_t end = std::stoul(endStr, nullptr, 0); const Section *section = FindFunctionSectionByAddress(m_sections, start); if (!section) { ++skippedNonExecutable; continue; } end = ClampFunctionEndToSection(section, start, end); if (end <= start) { ++skippedInvalidRange; continue; } Function func{}; func.name = name; func.start = start; func.end = end; func.isRecompiled = false; func.isStub = false; func.isSkipped = false; m_extraFunctions.push_back(std::move(func)); mapStarts.insert(start); count++; } catch (...) { continue; } } if (count > 0) { m_hasLoadedGhidraMap = true; m_ghidraMapStarts = mapStarts; if (m_reporter) { m_reporter->info("ghidra-map", "Loaded " + std::to_string(count) + " functions from Ghidra map"); } if (skippedNonExecutable > 0) { if (m_reporter) { m_reporter->warning("ghidra-map", "Ignored " + std::to_string(skippedNonExecutable) + " Ghidra function(s) outside executable sections."); } } if (skippedInvalidRange > 0) { if (m_reporter) { m_reporter->warning("ghidra-map", "Ignored " + std::to_string(skippedInvalidRange) + " Ghidra function(s) with invalid ranges after section clamping."); } } m_extraFunctions.erase( std::remove_if(m_extraFunctions.begin(), m_extraFunctions.end(), [&](const Function &func) { return IsAutoGeneratedName(func.name) && !mapStarts.contains(func.start); }), m_extraFunctions.end()); std::sort(m_extraFunctions.begin(), m_extraFunctions.end(), [](const Function &a, const Function &b) { if (a.start != b.start) { return a.start < b.start; } const bool aAuto = IsAutoGeneratedName(a.name); const bool bAuto = IsAutoGeneratedName(b.name); if (aAuto != bAuto) { return !aAuto; } if (a.end != b.end) { return a.end > b.end; } return a.name < b.name; }); m_extraFunctions.erase( std::unique(m_extraFunctions.begin(), m_extraFunctions.end(), [](const Function &a, const Function &b) { if (a.start == b.start) { // pick the function with real name and not auto generated return true; } return false; }), m_extraFunctions.end()); return true; } if (skippedNonExecutable > 0 || skippedInvalidRange > 0) { if (m_reporter) { m_reporter->warning("ghidra-map", "Loaded 0 functions from Ghidra map after filtering (" + std::to_string(skippedNonExecutable) + " non-executable, " + std::to_string(skippedInvalidRange) + " invalid range)."); } } return false; } ElfParser::~ElfParser() = default; bool ElfParser::parse() { if (!m_elf->load(m_filePath)) { if (m_reporter) { m_reporter->error("elf", "Could not load ELF file: " + m_filePath); } return false; } // Check if this is a PS2 ELF (MIPS R5900) if (m_elf->get_machine() != ELFIO::EM_MIPS) { if (m_reporter) { m_reporter->error("elf", "Not a MIPS ELF file"); } return false; } loadSections(); loadSymbols(); loadRelocations(); loadDebugFunctions(); return true; } void ElfParser::loadSections() { m_sections.clear(); ELFIO::Elf_Half sec_num = m_elf->sections.size(); for (ELFIO::Elf_Half i = 0; i < sec_num; ++i) { ELFIO::section *psec = m_elf->sections[i]; Section section; section.name = psec->get_name(); section.address = psec->get_address(); section.size = psec->get_size(); section.offset = psec->get_offset(); section.isCode = isExecutableSection(psec); section.isData = isDataSection(psec); section.isBSS = (psec->get_type() == ELFIO::SHT_NOBITS); section.isReadOnly = !(psec->get_flags() & ELFIO::SHF_WRITE); if (psec->get_size() > 0 && psec->get_type() != ELFIO::SHT_NOBITS) { section.data = (uint8_t *)psec->get_data(); } else { section.data = nullptr; } m_sections.push_back(section); } if (m_sections.empty()) { AppendLoadSegmentsAsSections(*m_elf, m_sections); if (!m_sections.empty()) { if (m_reporter) { m_reporter->info("elf", "ELF has no section headers; using loadable segments as sections (" + std::to_string(m_sections.size()) + " entries)."); } } } } void ElfParser::loadSymbols() { m_symbols.clear(); for (ELFIO::Elf_Half i = 0; i < m_elf->sections.size(); ++i) { ELFIO::section *psec = m_elf->sections[i]; if (psec->get_type() == ELFIO::SHT_SYMTAB || psec->get_type() == ELFIO::SHT_DYNSYM) { if (psec->get_link() >= m_elf->sections.size()) { if (m_reporter) { m_reporter->warning("elf", "Symbol section link out of bounds: " + std::to_string(psec->get_link())); } continue; } ELFIO::symbol_section_accessor symbols(*m_elf, psec); ELFIO::Elf_Xword sym_num = symbols.get_symbols_num(); ELFIO::section *pstrSec = m_elf->sections[psec->get_link()]; ELFIO::string_section_accessor strings(pstrSec); for (ELFIO::Elf_Xword j = 0; j < sym_num; ++j) { std::string name; ELFIO::Elf64_Addr value; ELFIO::Elf_Xword size; unsigned char bind; unsigned char type; ELFIO::Elf_Half section_index; unsigned char other; symbols.get_symbol(j, name, value, size, bind, type, section_index, other); if (name.empty()) { continue; } Symbol symbol; symbol.name = name; symbol.address = static_cast(value); symbol.size = static_cast(size); symbol.isFunction = (type == ELFIO::STT_FUNC); symbol.isImported = section_index == ELFIO::SHN_UNDEF; symbol.isExported = (!symbol.isImported && bind == ELFIO::STB_GLOBAL); m_symbols.push_back(symbol); } } } } void ElfParser::loadRelocations() { m_relocations.clear(); for (ELFIO::Elf_Half i = 0; i < m_elf->sections.size(); ++i) { ELFIO::section *psec = m_elf->sections[i]; if (psec->get_type() == ELFIO::SHT_REL || psec->get_type() == ELFIO::SHT_RELA) { if (psec->get_link() >= m_elf->sections.size()) { if (m_reporter) { m_reporter->warning("elf", "Relocation section link out of bounds: " + std::to_string(psec->get_link())); } continue; } ELFIO::relocation_section_accessor relocs(*m_elf, psec); ELFIO::section *symSec = m_elf->sections[psec->get_link()]; if (symSec->get_link() >= m_elf->sections.size()) { if (m_reporter) { m_reporter->warning("elf", "Symbol section link out of bounds (in relocation): " + std::to_string(symSec->get_link())); } continue; } ELFIO::symbol_section_accessor symbols(*m_elf, symSec); ELFIO::section *strSec = m_elf->sections[symSec->get_link()]; ELFIO::string_section_accessor strings(strSec); for (ELFIO::Elf_Xword j = 0; j < relocs.get_entries_num(); ++j) { ELFIO::Elf64_Addr offset; ELFIO::Elf_Word symbol; ELFIO::Elf_Word type; ELFIO::Elf_Sxword addend; // Always use the 5-parameter version if (psec->get_type() == ELFIO::SHT_REL) { // Pass addend even for REL sections relocs.get_entry(j, offset, symbol, type, addend); // Reset addend for REL sections since it's not part of the section addend = 0; } else { relocs.get_entry(j, offset, symbol, type, addend); } Relocation reloc; reloc.offset = static_cast(offset); reloc.info = (symbol << 8) | (type & 0xFF); reloc.symbol = symbol; reloc.symbolName.clear(); reloc.type = type; reloc.addend = static_cast(addend); if (symbol < symbols.get_symbols_num()) { std::string symName; ELFIO::Elf64_Addr symValue = 0; ELFIO::Elf_Xword symSize = 0; unsigned char symBind = 0; unsigned char symType = 0; ELFIO::Elf_Half symSectionIndex = 0; unsigned char symOther = 0; if (symbols.get_symbol(symbol, symName, symValue, symSize, symBind, symType, symSectionIndex, symOther)) { reloc.symbolName = symName; } } m_relocations.push_back(reloc); } } } } void ElfParser::loadDebugFunctions() { m_extraFunctions.clear(); m_hasLoadedGhidraMap = false; m_ghidraMapStarts.clear(); if (HasDwarfSections(*m_elf)) { #if defined(_WIN32) const int fileDescriptor = _open(m_filePath.c_str(), _O_RDONLY | _O_BINARY); #else const int fileDescriptor = ::open(m_filePath.c_str(), O_RDONLY); #endif if (fileDescriptor >= 0) { Dwarf_Debug dbg = nullptr; Dwarf_Error error = nullptr; const int initResult = dwarf_init_b(fileDescriptor, DW_GROUPNUMBER_BASE, nullptr, nullptr, &dbg, &error); if (initResult == DW_DLV_OK) { for (;;) { Dwarf_Unsigned cuHeaderLength = 0; Dwarf_Half versionStamp = 0; Dwarf_Unsigned abbrevOffset = 0; Dwarf_Half addressSize = 0; Dwarf_Half lengthSize = 0; Dwarf_Half extensionSize = 0; Dwarf_Sig8 typeSignature = {0}; Dwarf_Unsigned typeOffset = 0; Dwarf_Unsigned nextCuHeader = 0; Dwarf_Half headerCuType = 0; Dwarf_Die cuDie = nullptr; const int cuResult = dwarf_next_cu_header_e( dbg, TRUE, &cuDie, &cuHeaderLength, &versionStamp, &abbrevOffset, &addressSize, &lengthSize, &extensionSize, &typeSignature, &typeOffset, &nextCuHeader, &headerCuType, &error); if (cuResult != DW_DLV_OK) { break; } if (cuDie != nullptr) { VisitDieTreeAndCollectFunctions(dbg, cuDie, this, m_extraFunctions); } } dwarf_finish(dbg); } #if defined(_WIN32) _close(fileDescriptor); #else ::close(fileDescriptor); #endif } } if (m_extraFunctions.empty()) { ScanJalTargetsFallback(this, m_extraFunctions); } std::sort(m_extraFunctions.begin(), m_extraFunctions.end(), [](const Function &a, const Function &b) { return a.start < b.start; }); m_extraFunctions.erase( std::unique(m_extraFunctions.begin(), m_extraFunctions.end(), [](const Function &a, const Function &b) { return a.start == b.start; }), m_extraFunctions.end()); } }