mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-09-26 16:59:35 -04:00
Feature/better analyzer v1 (#39)
* feat: pin dependencies * feat: dwarf parse * fix duplicated functions * feat: small code cleanup * feat: crash prevent * feat: modernize code * feat: split m_libFunctions from m_knownLibNames * feat: added ghidra loader * feat: auto bootstrap name for recompiler
This commit is contained in:
@@ -2,6 +2,466 @@
|
||||
#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.h>
|
||||
#include <dwarf.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
namespace
|
||||
{
|
||||
bool IsAutoGeneratedName(const std::string &name)
|
||||
{
|
||||
return name.rfind("sub_", 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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
outFunctions.push_back(std::move(func));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace ps2recomp
|
||||
{
|
||||
@@ -23,28 +483,96 @@ namespace ps2recomp
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
auto addOrMerge = [&](const Function &newFunction)
|
||||
{
|
||||
if (newFunction.start == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = indexByStart.find(newFunction.start);
|
||||
if (it == indexByStart.end())
|
||||
{
|
||||
indexByStart.emplace(newFunction.start, functions.size());
|
||||
functions.push_back(newFunction);
|
||||
return;
|
||||
}
|
||||
|
||||
Function &existing = functions[it->second];
|
||||
|
||||
if (!newFunction.name.empty())
|
||||
{
|
||||
if (existing.name.empty() || (IsAutoGeneratedName(existing.name) && !IsAutoGeneratedName(newFunction.name)))
|
||||
{
|
||||
existing.name = newFunction.name;
|
||||
}
|
||||
}
|
||||
|
||||
if (newFunction.end > existing.end)
|
||||
{
|
||||
existing.end = newFunction.end;
|
||||
}
|
||||
|
||||
existing.isStub = existing.isStub || newFunction.isStub;
|
||||
};
|
||||
|
||||
for (const auto &symbol : m_symbols)
|
||||
{
|
||||
if (symbol.isFunction && symbol.size > 0)
|
||||
if (!symbol.isFunction || symbol.isImported)
|
||||
{
|
||||
Function func;
|
||||
func.name = symbol.name;
|
||||
func.start = symbol.address;
|
||||
func.end = symbol.address + symbol.size;
|
||||
func.isRecompiled = false;
|
||||
func.isStub = false;
|
||||
|
||||
functions.push_back(func);
|
||||
continue;
|
||||
}
|
||||
Function func;
|
||||
func.name = symbol.name;
|
||||
func.start = symbol.address;
|
||||
func.end = (symbol.size > 0) ? (symbol.address + symbol.size) : 0;
|
||||
func.isRecompiled = false;
|
||||
func.isStub = 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];
|
||||
|
||||
if (func.end > func.start)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const Section *section = FindSectionByAddress(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;
|
||||
}
|
||||
|
||||
@@ -63,6 +591,11 @@ namespace ps2recomp
|
||||
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)
|
||||
@@ -94,7 +627,7 @@ namespace ps2recomp
|
||||
}
|
||||
|
||||
uint8_t *ElfParser::getSectionData(const std::string §ionName) const
|
||||
{
|
||||
{
|
||||
for (const auto §ion : m_sections)
|
||||
{
|
||||
if (section.name == sectionName)
|
||||
@@ -107,7 +640,7 @@ namespace ps2recomp
|
||||
}
|
||||
|
||||
uint32_t ElfParser::getSectionAddress(const std::string §ionName) const
|
||||
{
|
||||
{
|
||||
for (const auto §ion : m_sections)
|
||||
{
|
||||
if (section.name == sectionName)
|
||||
@@ -120,7 +653,7 @@ namespace ps2recomp
|
||||
}
|
||||
|
||||
uint32_t ElfParser::getSectionSize(const std::string §ionName) const
|
||||
{
|
||||
{
|
||||
for (const auto §ion : m_sections)
|
||||
{
|
||||
if (section.name == sectionName)
|
||||
@@ -137,6 +670,91 @@ namespace ps2recomp
|
||||
return static_cast<uint32_t>(m_elf->get_entry());
|
||||
}
|
||||
|
||||
bool ElfParser::loadGhidraFunctionMap(const std::string &mapPath)
|
||||
{
|
||||
if (mapPath.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ifstream file(mapPath);
|
||||
if (!file.is_open())
|
||||
{
|
||||
std::cerr << "Warning: Could not open Ghidra function map: " << mapPath << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
if (!std::getline(file, line))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
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);
|
||||
|
||||
Function func{};
|
||||
func.name = name;
|
||||
func.start = start;
|
||||
func.end = end;
|
||||
func.isRecompiled = false;
|
||||
func.isStub = false;
|
||||
|
||||
m_extraFunctions.push_back(std::move(func));
|
||||
count++;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
std::cout << "Loaded " << count << " functions from Ghidra map" << std::endl;
|
||||
|
||||
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)
|
||||
{
|
||||
if (a.start == b.start)
|
||||
{
|
||||
// pick the function with real name and not auto generated
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}),
|
||||
m_extraFunctions.end());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ElfParser::~ElfParser() = default;
|
||||
|
||||
bool ElfParser::parse()
|
||||
@@ -157,6 +775,7 @@ namespace ps2recomp
|
||||
loadSections();
|
||||
loadSymbols();
|
||||
loadRelocations();
|
||||
loadDebugFunctions();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -192,6 +811,16 @@ namespace ps2recomp
|
||||
|
||||
m_sections.push_back(section);
|
||||
}
|
||||
|
||||
if (m_sections.empty())
|
||||
{
|
||||
AppendLoadSegmentsAsSections(*m_elf, m_sections);
|
||||
if (!m_sections.empty())
|
||||
{
|
||||
std::cout << "Info: ELF has no section headers; using loadable segments as sections ("
|
||||
<< m_sections.size() << " entries)." << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ElfParser::loadSymbols()
|
||||
@@ -204,6 +833,12 @@ namespace ps2recomp
|
||||
|
||||
if (psec->get_type() == ELFIO::SHT_SYMTAB || psec->get_type() == ELFIO::SHT_DYNSYM)
|
||||
{
|
||||
if (psec->get_link() >= m_elf->sections.size())
|
||||
{
|
||||
std::cerr << "Warning: Symbol section link out of bounds: " << psec->get_link() << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
ELFIO::symbol_section_accessor symbols(*m_elf, psec);
|
||||
|
||||
ELFIO::Elf_Xword sym_num = symbols.get_symbols_num();
|
||||
@@ -223,8 +858,7 @@ namespace ps2recomp
|
||||
|
||||
symbols.get_symbol(j, name, value, size, bind, type, section_index, other);
|
||||
|
||||
// Skip empty symbols or those with invalid section index
|
||||
if (name.empty() || section_index == ELFIO::SHN_UNDEF)
|
||||
if (name.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -234,8 +868,9 @@ namespace ps2recomp
|
||||
symbol.address = static_cast<uint32_t>(value);
|
||||
symbol.size = static_cast<uint32_t>(size);
|
||||
symbol.isFunction = (type == ELFIO::STT_FUNC);
|
||||
symbol.isImported = (bind == ELFIO::STB_GLOBAL && section_index == ELFIO::SHN_UNDEF);
|
||||
symbol.isExported = (bind == ELFIO::STB_GLOBAL && section_index != ELFIO::SHN_UNDEF);
|
||||
|
||||
symbol.isImported = section_index == ELFIO::SHN_UNDEF;
|
||||
symbol.isExported = (!symbol.isImported && bind == ELFIO::STB_GLOBAL);
|
||||
|
||||
m_symbols.push_back(symbol);
|
||||
}
|
||||
@@ -253,9 +888,22 @@ namespace ps2recomp
|
||||
|
||||
if (psec->get_type() == ELFIO::SHT_REL || psec->get_type() == ELFIO::SHT_RELA)
|
||||
{
|
||||
if (psec->get_link() >= m_elf->sections.size())
|
||||
{
|
||||
std::cout << "Warning: Relocation section link out of bounds: " << psec->get_link() << std::endl;
|
||||
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())
|
||||
{
|
||||
std::cout << "Warning: Symbol section link out of bounds (in relocation): " << symSec->get_link() << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
ELFIO::symbol_section_accessor symbols(*m_elf, symSec);
|
||||
|
||||
ELFIO::section *strSec = m_elf->sections[symSec->get_link()];
|
||||
@@ -294,4 +942,91 @@ namespace ps2recomp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ElfParser::loadDebugFunctions()
|
||||
{
|
||||
m_extraFunctions.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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user