mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-09-26 08:51:05 -04:00
5196a6672a
* feat: added guestBranchKind enum to categorize branch types feat: added missingFunctionPolicy enum to define behaviors for missing function scenarios refactor: added handle guest branches and report missing functions feat lookupFunction to utilize new dispatch logic and improve error handling for unregistered functions * fix: fix test conflict * feat: added debug sound driver logs * feat: emmiter for return * feat: added recompiler reporter feat: added strict diagnostics flag for heavy debug calls * feat: staticc table insted of hashmap for runtime * feat: back file to ignore * feat: explode code across helpers and classes * feat: update codegen test feat: better guest nop check * feat: fix link problem on linux * feat: fix Segmentation fault
1437 lines
45 KiB
C++
1437 lines
45 KiB
C++
#include "ps2recomp/elf_parser.h"
|
|
#include "ps2recomp/recompiler_reporter.h"
|
|
#include "ps2recomp/types.h"
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
#include <unordered_set>
|
|
#define NOMINMAX
|
|
|
|
#include <fcntl.h>
|
|
|
|
#if defined(_WIN32)
|
|
#include <io.h>
|
|
#include <direct.h>
|
|
#include <windows.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
#include "libdwarf_private.h"
|
|
#include <libdwarf.h>
|
|
#include <dwarf.h>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
|
|
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<ps2recomp::Section> §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<uint32_t>(vaddr);
|
|
load.size = static_cast<uint32_t>(fileSize);
|
|
load.offset = static_cast<uint32_t>(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<uint8_t *>(
|
|
reinterpret_cast<const uint8_t *>(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<uint32_t>(vaddr + fileSize);
|
|
bss.size = static_cast<uint32_t>(memSize - fileSize);
|
|
bss.offset = static_cast<uint32_t>(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<ps2recomp::Section> §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<ps2recomp::Section> §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<ps2recomp::Section> §ions)
|
|
{
|
|
for (const auto §ion : sections)
|
|
{
|
|
if (section.isCode)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const ps2recomp::Section *FindFunctionSectionByAddress(const std::vector<ps2recomp::Section> §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<uint64_t>(section->address) + static_cast<uint64_t>(section->size);
|
|
const uint32_t sectionEnd = (sectionEnd64 > 0xFFFFFFFFull)
|
|
? 0xFFFFFFFFu
|
|
: static_cast<uint32_t>(sectionEnd64);
|
|
|
|
uint32_t end = requestedEnd;
|
|
if (end == 0 || end > sectionEnd)
|
|
{
|
|
end = sectionEnd;
|
|
}
|
|
|
|
if (end <= start)
|
|
{
|
|
const uint64_t minimumEnd64 = static_cast<uint64_t>(start) + 4ull;
|
|
if (minimumEnd64 <= sectionEnd64)
|
|
{
|
|
end = static_cast<uint32_t>(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<uint32_t>(lowPc);
|
|
outHighPc = static_cast<uint32_t>(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<uint32_t>(lowPc);
|
|
outHighPc = static_cast<uint32_t>(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<uint32_t>(minPc);
|
|
outHighPc = static_cast<uint32_t>(maxPc);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void VisitDieTreeAndCollectFunctions(
|
|
Dwarf_Debug dbg,
|
|
Dwarf_Die rootDie,
|
|
ps2recomp::ElfParser *parser,
|
|
std::vector<ps2recomp::Function> &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<ps2recomp::Function> &outFunctions)
|
|
{
|
|
std::unordered_set<uint32_t> 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<uint32_t> 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<Function> ElfParser::extractFunctions() const
|
|
{
|
|
std::vector<Function> functions;
|
|
functions.reserve(m_symbols.size() + m_extraFunctions.size());
|
|
|
|
std::unordered_map<uint32_t, size_t> indexByStart;
|
|
indexByStart.reserve(functions.capacity());
|
|
|
|
// Symbol table sizes are authoritative wwhen exist
|
|
std::unordered_map<uint32_t, uint32_t> 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<uint64_t>(symbol.address) + static_cast<uint64_t>(symbol.size);
|
|
uint32_t symbolEnd = (symbolEnd64 > 0xFFFFFFFFull)
|
|
? 0xFFFFFFFFu
|
|
: static_cast<uint32_t>(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<std::pair<uint32_t, uint32_t>> 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<uint32_t, uint32_t> &a, const std::pair<uint32_t, uint32_t> &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<uint32_t, uint32_t> &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<uint64_t>(symbol.address) + static_cast<uint64_t>(symbol.size);
|
|
func.end = (end64 > 0xFFFFFFFFull) ? 0xFFFFFFFFu : static_cast<uint32_t>(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<Symbol> ElfParser::extractSymbols()
|
|
{
|
|
return m_symbols;
|
|
}
|
|
|
|
std::vector<Section> ElfParser::getSections()
|
|
{
|
|
return m_sections;
|
|
}
|
|
|
|
std::vector<Relocation> ElfParser::getRelocations()
|
|
{
|
|
return m_relocations;
|
|
}
|
|
|
|
std::vector<Function> 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<uint32_t>(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<uint32_t>(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<uint32_t> 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<uint32_t>(value);
|
|
symbol.size = static_cast<uint32_t>(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<uint32_t>(offset);
|
|
reloc.info = (symbol << 8) | (type & 0xFF);
|
|
reloc.symbol = symbol;
|
|
reloc.symbolName.clear();
|
|
reloc.type = type;
|
|
reloc.addend = static_cast<int32_t>(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());
|
|
}
|
|
}
|