Merge pull request #1787 from FEX-Emu/skmp/gdb-jit-integration

gdb: jit integration
This commit is contained in:
Stefanos Kornilios Mitsis Poiitidis 2022-06-27 11:57:40 +00:00 committed by GitHub
commit 4449b60459
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 815 additions and 35 deletions

View File

@ -1,6 +1,9 @@
cmake_minimum_required(VERSION 3.14)
project(FEX)
INCLUDE (CheckIncludeFiles)
CHECK_INCLUDE_FILES ("gdb/jit-reader.h" HAVE_GDB_JIT_READER_H)
option(BUILD_TESTS "Build unit tests to ensure sanity" TRUE)
option(BUILD_FEX_LINUX_TESTS "Build FEXLinuxTests, requires g++/g++-multilib or g++-x86-64-linux-gnu/g++-multilib-x86-64-linux-gnu" FALSE)
option(BUILD_THUNKS "Build thunks" FALSE)
@ -13,6 +16,7 @@ option(ENABLE_MOLD "Enable linking with mold" FALSE)
option(ENABLE_ASAN "Enables Clang ASAN" FALSE)
option(ENABLE_TSAN "Enables Clang TSAN" FALSE)
option(ENABLE_ASSERTIONS "Enables assertions in build" FALSE)
option(ENABLE_GDB_SYMBOLS "Enables GDBSymbols integration support" ${HAVE_GDB_JIT_READER_H})
option(ENABLE_VISUAL_DEBUGGER "Enables the visual debugger for compiling" FALSE)
option(ENABLE_STRICT_WERROR "Enables stricter -Werror for CI" FALSE)
option(ENABLE_WERROR "Enables -Werror" FALSE)
@ -44,6 +48,12 @@ if (ENABLE_ASSERTIONS)
add_definitions(-DASSERTIONS_ENABLED=1)
endif()
if (ENABLE_GDB_SYMBOLS)
message(STATUS "GDBSymbols support enabled")
add_definitions(-DGDB_SYMBOLS_ENABLED=1)
endif()
if (ENABLE_INTERPRETER)
message(STATUS "Interpreter enabled")
add_definitions(-DINTERPRETER_ENABLED=1)

View File

@ -118,6 +118,7 @@ set (SRCS
Interface/Core/X86Tables/X87Tables.cpp
Interface/Core/X86Tables/XOPTables.cpp
Interface/HLE/Thunks/Thunks.cpp
Interface/GDBJIT/GDBJIT.cpp
Interface/IR/AOTIR.cpp
Interface/IR/IRDumper.cpp
Interface/IR/IRParser.cpp

View File

@ -208,6 +208,16 @@
"Useful for determining hot blocks of code",
"Has some file writing overhead per JIT block"
]
},
"GDBSymbols": {
"Type": "bool",
"Default": "false",
"Desc": [
"Integrates with GDB using the JIT interface.",
"Needs the fex jit loader in GDB, which can be loaded via `jit-reader-load libFEXGDBReader.so.`",
"Also needs x86_64-linux-gnu-objdump in PATH.",
"Can be very slow."
]
}
},
"Logging": {

View File

@ -156,6 +156,7 @@ namespace FEXCore::Context {
void SetSyscallHandler(FEXCore::Context::Context *CTX, FEXCore::HLE::SyscallHandler *Handler) {
CTX->SyscallHandler = Handler;
CTX->SourcecodeResolver = Handler->GetSourcecodeResolver();
}
FEXCore::CPUID::FunctionResults RunCPUIDFunction(FEXCore::Context::Context *CTX, uint32_t Function, uint32_t Leaf) {

View File

@ -49,6 +49,8 @@ namespace CPU {
namespace HLE {
struct SyscallArguments;
class SyscallHandler;
class SourcecodeResolver;
struct SourcecodeMap;
}
}
@ -107,6 +109,7 @@ namespace FEXCore::Context {
FEX_CONFIG_OPT(GlobalJITNaming, GLOBALJITNAMING);
FEX_CONFIG_OPT(LibraryJITNaming, LIBRARYJITNAMING);
FEX_CONFIG_OPT(BlockJITNaming, BLOCKJITNAMING);
FEX_CONFIG_OPT(GDBSymbols, GDBSYMBOLS);
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
FEX_CONFIG_OPT(CacheObjectCodeCompilation, CACHEOBJECTCODECOMPILATION);
FEX_CONFIG_OPT(x87ReducedPrecision, X87REDUCEDPRECISION);
@ -130,6 +133,7 @@ namespace FEXCore::Context {
FEXCore::CPUIDEmu CPUID;
FEXCore::HLE::SyscallHandler *SyscallHandler{};
FEXCore::HLE::SourcecodeResolver *SourcecodeResolver{};
std::unique_ptr<FEXCore::ThunkHandler> ThunkHandler;
std::unique_ptr<FEXCore::CPU::Dispatcher> Dispatcher;
@ -195,7 +199,7 @@ namespace FEXCore::Context {
uint64_t StartAddr;
uint64_t Length;
};
[[nodiscard]] GenerateIRResult GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
[[nodiscard]] GenerateIRResult GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, bool ExtendedDebugInfo);
struct CompileCodeResult {
void* CompiledCode;

View File

@ -7,6 +7,7 @@ desc: Glues Frontend, OpDispatcher and IR Opts & Compilation, LookupCache, Dispa
$end_info$
*/
#include <cstdint>
#include "Interface/Context/Context.h"
#include "Interface/Core/LookupCache.h"
#include "Interface/Core/Core.h"
@ -33,6 +34,7 @@ $end_info$
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/Debug/X86Tables.h>
#include <FEXCore/HLE/SyscallHandler.h>
#include <FEXCore/HLE/SourcecodeResolver.h>
#include <FEXCore/HLE/Linux/ThreadManagement.h>
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IREmitter.h>
@ -50,7 +52,6 @@ $end_info$
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstdint>
#include <filesystem>
#include <functional>
#include <fstream>
@ -75,6 +76,7 @@ $end_info$
#include <vector>
#include <xxhash.h>
namespace FEXCore::CPU {
bool CreateCPUCore(FEXCore::Context::Context *CTX) {
// This should be used for generating things that are shared between threads
@ -715,7 +717,7 @@ namespace FEXCore::Context {
}
}
Context::GenerateIRResult Context::GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
Context::GenerateIRResult Context::GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, bool ExtendedDebugInfo) {
Thread->OpDispatcher->ReownOrClaimBuffer();
Thread->OpDispatcher->ResetWorkingList();
@ -770,6 +772,10 @@ namespace FEXCore::Context {
DecodedInfo = &Block.DecodedInstructions[i];
bool IsLocked = DecodedInfo->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_LOCK;
if (ExtendedDebugInfo) {
Thread->OpDispatcher->_GuestOpcode(Block.Entry + BlockInstructionsLength - GuestRIP);
}
if (Config.SMCChecks == FEXCore::Config::CONFIG_SMC_FULL) {
auto ExistingCodePtr = reinterpret_cast<uint64_t*>(Block.Entry + BlockInstructionsLength);
@ -896,18 +902,26 @@ namespace FEXCore::Context {
auto CompiledCode = Thread->CPUBackend->RelocateJITObjectCode(GuestRIP, CodeCacheEntry);
if (CompiledCode) {
return {
.CompiledCode = CompiledCode,
.IRData = nullptr, // No IR data generated
.DebugData = nullptr, // nullptr here ensures that code serialization doesn't occur on from cache read
.RAData = nullptr, // No RA data generated
.GeneratedIR = false, // nullptr here ensures IR cache mechanisms won't run
.StartAddr = 0, // Unused
.Length = 0, // Unused
.CompiledCode = CompiledCode,
.IRData = nullptr, // No IR data generated
.DebugData = nullptr, // nullptr here ensures that code serialization doesn't occur on from cache read
.RAData = nullptr, // No RA data generated
.GeneratedIR = false, // nullptr here ensures IR cache mechanisms won't run
.StartAddr = 0, // Unused
.Length = 0, // Unused
};
}
}
}
if (SourcecodeResolver && Config.GDBSymbols()) {
auto AOTIRCacheEntry = SyscallHandler->LookupAOTIRCacheEntry(GuestRIP);
if (AOTIRCacheEntry.Entry && !AOTIRCacheEntry.Entry->ContainsCode) {
AOTIRCacheEntry.Entry->SourcecodeMap =
SourcecodeResolver->GenerateMap(AOTIRCacheEntry.Entry->Filename, AOTIRCacheEntry.Entry->FileId);
}
}
// AOT IR bookkeeping and cache
{
auto [IRCopy, RACopy, DebugDataCopy, _StartAddr, _Length, _GeneratedIR] = IRCaptureCache.PreGenerateIRFetch(GuestRIP, IRList);
@ -924,7 +938,7 @@ namespace FEXCore::Context {
if (IRList == nullptr) {
// Generate IR + Meta Info
auto [IRCopy, RACopy, TotalInstructions, TotalInstructionsLength, _StartAddr, _Length] = GenerateIR(Thread, GuestRIP);
auto [IRCopy, RACopy, TotalInstructions, TotalInstructionsLength, _StartAddr, _Length] = GenerateIR(Thread, GuestRIP, Config.GDBSymbols());
// Setup pointers to internal structures
IRList = IRCopy;
@ -1002,20 +1016,20 @@ namespace FEXCore::Context {
auto FragmentBasePtr = reinterpret_cast<uint8_t *>(CodePtr);
if (DebugData) {
auto GuestRIPLookup = this->SyscallHandler->LookupAOTIRCacheEntry(GuestRIP);
auto GuestRIPLookup = SyscallHandler->LookupAOTIRCacheEntry(GuestRIP);
if (DebugData->Subblocks.size()) {
for (auto& Subblock: DebugData->Subblocks) {
auto BlockBasePtr = FragmentBasePtr + Subblock.HostCodeOffset;
if (GuestRIPLookup.Entry) {
Symbols.Register(BlockBasePtr, DebugData->HostCodeSize, GuestRIPLookup.Entry->Filename, GuestRIP - GuestRIPLookup.Offset);
Symbols.Register(BlockBasePtr, DebugData->HostCodeSize, GuestRIPLookup.Entry->Filename, GuestRIP - GuestRIPLookup.VAFileStart);
} else {
Symbols.Register(BlockBasePtr, GuestRIP, Subblock.HostCodeSize);
}
Symbols.Register(BlockBasePtr, GuestRIP, Subblock.HostCodeSize);
}
}
} else {
if (GuestRIPLookup.Entry) {
Symbols.Register(FragmentBasePtr, DebugData->HostCodeSize, GuestRIPLookup.Entry->Filename, GuestRIP - GuestRIPLookup.Offset);
Symbols.Register(FragmentBasePtr, DebugData->HostCodeSize, GuestRIPLookup.Entry->Filename, GuestRIP - GuestRIPLookup.VAFileStart);
} else {
Symbols.Register(FragmentBasePtr, GuestRIP, DebugData->HostCodeSize);
}
@ -1278,4 +1292,4 @@ namespace FEXCore::Context {
Thread->FrontendDecoder->SetExternalBranches(ExternalBranches);
Thread->FrontendDecoder->SetSectionMaxAddress(SectionMaxAddress);
}
}
}

View File

@ -164,6 +164,7 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
REGISTER_OP(CODEBLOCK, NoOp);
REGISTER_OP(BEGINBLOCK, NoOp);
REGISTER_OP(ENDBLOCK, NoOp);
REGISTER_OP(GUESTOPCODE, NoOp);
REGISTER_OP(FENCE, Fence);
REGISTER_OP(BREAK, Break);
REGISTER_OP(PHI, NoOp);

View File

@ -672,6 +672,7 @@ void *Arm64JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IR
this->Entry = Entry;
this->RAData = RAData;
this->DebugData = DebugData;
#ifndef NDEBUG
LoadConstant(x0, Entry);

View File

@ -147,6 +147,7 @@ private:
void EmitDetectionString();
IR::RegisterAllocationPass *RAPass;
IR::RegisterAllocationData *RAData;
FEXCore::Core::DebugData *DebugData;
void ResetStack();
/**
@ -348,7 +349,7 @@ private:
DEF_OP(CacheLineZero);
///< Misc ops
DEF_OP(EndBlock);
DEF_OP(GuestOpcode);
DEF_OP(Fence);
DEF_OP(Break);
DEF_OP(Phi);

View File

@ -6,12 +6,19 @@ $end_info$
#include <syscall.h>
#include "Interface/Core/JIT/Arm64/JITClass.h"
#include "FEXCore/Debug/InternalThreadState.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void Arm64JITCore::Op_##x(IR::IROp_Header *IROp, IR::NodeID Node)
DEF_OP(GuestOpcode) {
auto Op = IROp->C<IR::IROp_GuestOpcode>();
// metadata
DebugData->GuestOpcodes.push_back({Op->GuestEntryOffset, GetCursorAddress<uint8_t*>() - GuestEntry});
}
DEF_OP(Fence) {
auto Op = IROp->C<IR::IROp_Fence>();
switch (Op->Fence) {
@ -232,6 +239,7 @@ void Arm64JITCore::RegisterMiscHandlers() {
REGISTER_OP(CODEBLOCK, NoOp);
REGISTER_OP(BEGINBLOCK, NoOp);
REGISTER_OP(ENDBLOCK, NoOp);
REGISTER_OP(GUESTOPCODE, GuestOpcode);
REGISTER_OP(FENCE, Fence);
REGISTER_OP(BREAK, Break);
REGISTER_OP(PHI, NoOp);

View File

@ -575,6 +575,7 @@ void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRLi
this->Entry = Entry;
this->RAData = RAData;
this->DebugData = DebugData;
// Fairly excessive buffer range to make sure we don't overflow
uint32_t BufferRange = SSACount * 16 + GDBEnabled * Dispatcher::MaxGDBPauseCheckSize;
@ -582,7 +583,7 @@ void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRLi
CTX->ClearCodeCache(ThreadState);
}
auto GuestEntry = getCurr<uint8_t*>();
GuestEntry = getCurr<uint8_t*>();
CursorEntry = getSize();
this->IR = IR;
@ -646,12 +647,13 @@ void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRLi
for (auto [BlockNode, BlockHeader] : IR->GetBlocks()) {
using namespace FEXCore::IR;
{
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
auto BlockIROp = BlockHeader->CW<IROp_CodeBlock>();
LOGMAN_THROW_A_FMT(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
auto BlockIROp = BlockHeader->CW<IROp_CodeBlock>();
LOGMAN_THROW_A_FMT(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
#endif
auto BlockStartHostCode = getCurr<uint8_t *>();
{
const auto Node = IR->GetID(BlockNode);
const auto IsTarget = JumpTargets.try_emplace(Node).first;
@ -706,6 +708,13 @@ void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRLi
OpHandler Handler = OpHandlers[IROp->Op];
(this->*Handler)(IROp, ID);
}
if (DebugData) {
DebugData->Subblocks.push_back({
static_cast<uint32_t>(BlockStartHostCode - GuestEntry),
static_cast<uint32_t>(getCurr<uint8_t *>() - BlockStartHostCode)
});
}
}
// Make sure last branch is generated. It certainly can't be eliminated here.

View File

@ -189,6 +189,7 @@ private:
IR::RegisterAllocationPass *RAPass;
FEXCore::IR::RegisterAllocationData *RAData;
FEXCore::Core::DebugData *DebugData;
#ifdef BLOCKSTATS
bool GetSamplingData {true};
@ -200,6 +201,11 @@ private:
void EmitDetectionString();
uint32_t SpillSlots{};
/**
* @brief Current guest RIP entrypoint
*/
uint8_t *GuestEntry{};
using SetCC = void (X86JITCore::*)(const Operand& op);
using CMovCC = void (X86JITCore::*)(const Reg& reg, const Operand& op);
using JCC = void (X86JITCore::*)(const Label& label, LabelType type);
@ -341,7 +347,7 @@ private:
DEF_OP(CacheLineZero);
///< Misc ops
DEF_OP(EndBlock);
DEF_OP(GuestOpcode);
DEF_OP(Fence);
DEF_OP(Break);
DEF_OP(Phi);

View File

@ -7,6 +7,7 @@ $end_info$
#include "Interface/Context/Context.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "FEXCore/Debug/InternalThreadState.h"
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Utils/LogManager.h>
@ -20,6 +21,12 @@ $end_info$
namespace FEXCore::CPU {
#define DEF_OP(x) void X86JITCore::Op_##x(IR::IROp_Header *IROp, IR::NodeID Node)
DEF_OP(GuestOpcode) {
auto Op = IROp->C<IR::IROp_GuestOpcode>();
// metadata
DebugData->GuestOpcodes.push_back({Op->GuestEntryOffset, getCurr<uint8_t*>() - GuestEntry});
}
DEF_OP(Fence) {
auto Op = IROp->C<IR::IROp_Fence>();
switch (Op->Fence) {
@ -178,6 +185,7 @@ void X86JITCore::RegisterMiscHandlers() {
REGISTER_OP(CODEBLOCK, NoOp);
REGISTER_OP(BEGINBLOCK, NoOp);
REGISTER_OP(ENDBLOCK, NoOp);
REGISTER_OP(GUESTOPCODE, GuestOpcode);
REGISTER_OP(FENCE, Fence);
REGISTER_OP(BREAK, Break);
REGISTER_OP(PHI, NoOp);

View File

@ -0,0 +1,127 @@
#include "GDBJIT.h"
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/HLE/SourcecodeResolver.h>
#include <FEXCore/Utils/LogManager.h>
#if defined(GDB_SYMBOLS_ENABLED)
#include <FEXCore/Debug/GDBReaderInterface.h>
extern "C" {
enum jit_actions_t { JIT_NOACTION = 0, JIT_REGISTER_FN, JIT_UNREGISTER_FN };
struct jit_code_entry {
jit_code_entry *next_entry;
jit_code_entry *prev_entry;
const char *symfile_addr;
uint64_t symfile_size;
};
struct jit_descriptor {
uint32_t version;
/* This type should be jit_actions_t, but we use uint32_t
to be explicit about the bitwidth. */
uint32_t action_flag;
jit_code_entry *relevant_entry;
jit_code_entry *first_entry;
};
/* Make sure to specify the version statically, because the
debugger may check the version before we can set it. */
constinit jit_descriptor __jit_debug_descriptor = {.version = 1};
/* GDB puts a breakpoint in this function. */
void __attribute__((noinline)) __jit_debug_register_code() {
asm volatile("" ::"r"(&__jit_debug_descriptor));
};
}
namespace FEXCore {
void GDBJITRegister(FEXCore::IR::AOTIRCacheEntry *Entry, uintptr_t VAFileStart,
uint64_t GuestRIP, uintptr_t HostEntry,
FEXCore::Core::DebugData *DebugData) {
auto map = Entry->SourcecodeMap.get();
if (map) {
auto FileOffset = GuestRIP - VAFileStart;
auto Sym = map->FindSymbolMapping(FileOffset);
std::string SymName = HLE::SourcecodeSymbolMapping::SymName(
Sym, Entry->Filename, HostEntry, FileOffset);
std::vector<gdb_line_mapping> Lines;
for (const auto &GuestOpcode : DebugData->GuestOpcodes) {
auto Line = map->FindLineMapping(GuestRIP + GuestOpcode.GuestEntryOffset -
VAFileStart);
if (Line) {
Lines.push_back(
{Line->LineNumber, HostEntry + GuestOpcode.HostEntryOffset});
}
}
size_t size = sizeof(info_t) + 1 * sizeof(blocks_t) +
Lines.size() * sizeof(gdb_line_mapping);
auto mem = (uint8_t *)malloc(size);
auto base = mem;
info_t *info = (info_t *)mem;
mem += sizeof(info_t);
strncpy(info->filename, map->SourceFile.c_str(), 511);
info->nblocks = 1;
auto blocks = (blocks_t *)mem;
info->blocks_ofs = mem - base;
mem += info->nblocks * sizeof(blocks_t);
for (int i = 0; i < info->nblocks; i++) {
strncpy(blocks[i].name, SymName.c_str(), 511);
blocks[i].start = HostEntry;
blocks[i].end = HostEntry + DebugData->HostCodeSize;
}
info->nlines = Lines.size();
auto lines = (gdb_line_mapping *)mem;
info->lines_ofs = mem - base;
mem += info->nlines * sizeof(gdb_line_mapping);
if (info->nlines) {
memcpy(lines, &Lines.at(0), info->nlines * sizeof(gdb_line_mapping));
}
auto entry = new jit_code_entry{0, 0, 0, 0};
entry->symfile_addr = (const char *)info;
entry->symfile_size = size;
if (__jit_debug_descriptor.first_entry) {
__jit_debug_descriptor.relevant_entry->next_entry = entry;
entry->prev_entry = __jit_debug_descriptor.relevant_entry;
} else {
__jit_debug_descriptor.first_entry = entry;
}
__jit_debug_descriptor.relevant_entry = entry;
__jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
__jit_debug_register_code();
}
}
} // namespace FEXCore
#else
namespace FEXCore {
void GDBJITRegister([[maybe_unused]] FEXCore::IR::AOTIRCacheEntry *Entry,
[[maybe_unused]] uintptr_t VAFileStart,
[[maybe_unused]] uint64_t GuestRIP,
[[maybe_unused]] uintptr_t HostEntry,
[[maybe_unused]] FEXCore::Core::DebugData *DebugData) {
ERROR_AND_DIE_FMT("GDBSymbols support not compiled in");
}
} // namespace FEXCore
#endif

View File

@ -0,0 +1,7 @@
#include <Interface/IR/AOTIR.h>
namespace FEXCore {
void GDBJITRegister(FEXCore::IR::AOTIRCacheEntry *Entry, uintptr_t VAFileStart, uint64_t GuestRIP, uintptr_t HostEntry, FEXCore::Core::DebugData *DebugData);
}

View File

@ -6,6 +6,7 @@
#include <FEXCore/Utils/Allocator.h>
#include <FEXCore/HLE/SyscallHandler.h>
#include <Interface/Core/LookupCache.h>
#include <Interface/GDBJIT/GDBJIT.h>
#include <cstddef>
#include <cstdint>
@ -17,6 +18,7 @@
#include <unistd.h>
#include <xxhash.h>
namespace FEXCore::IR {
AOTIRInlineEntry *AOTIRInlineIndex::GetInlineEntry(uint64_t DataOffset) {
uintptr_t This = (uintptr_t)this;
@ -242,9 +244,9 @@ namespace FEXCore::IR {
AOTIRCaptureCache::PreGenerateIRFetchResult AOTIRCaptureCache::PreGenerateIRFetch(uint64_t GuestRIP, FEXCore::IR::IRListView *IRList) {
auto AOTIRCacheEntry = CTX->SyscallHandler->LookupAOTIRCacheEntry(GuestRIP);
PreGenerateIRFetchResult Result{};
if (AOTIRCacheEntry.Entry) {
AOTIRCacheEntry.Entry->ContainsCode = true;
@ -253,7 +255,7 @@ namespace FEXCore::IR {
if (Mod != nullptr)
{
auto AOTEntry = Mod->Find(GuestRIP - AOTIRCacheEntry.Offset);
auto AOTEntry = Mod->Find(GuestRIP - AOTIRCacheEntry.VAFileStart);
if (AOTEntry) {
// verify hash
@ -291,8 +293,9 @@ namespace FEXCore::IR {
FEXCore::IR::IRListView *IRList,
FEXCore::Core::DebugData *DebugData,
bool GeneratedIR) {
// Both generated ir and LibraryJITName need a named region lookup
if (GeneratedIR || CTX->Config.LibraryJITNaming()) {
if (GeneratedIR || CTX->Config.LibraryJITNaming() || CTX->Config.GDBSymbols()) {
auto AOTIRCacheEntry = CTX->SyscallHandler->LookupAOTIRCacheEntry(GuestRIP);
@ -301,14 +304,18 @@ namespace FEXCore::IR {
CTX->Symbols.RegisterNamedRegion(CodePtr, DebugData->HostCodeSize, AOTIRCacheEntry.Entry->Filename);
}
if (CTX->Config.GDBSymbols()) {
GDBJITRegister(AOTIRCacheEntry.Entry, AOTIRCacheEntry.VAFileStart, GuestRIP, (uintptr_t)CodePtr, DebugData);
}
// Add to AOT cache if aot generation is enabled
if (GeneratedIR && RAData &&
(CTX->Config.AOTIRCapture() || CTX->Config.AOTIRGenerate())) {
auto hash = XXH3_64bits((void*)StartAddr, Length);
auto LocalRIP = GuestRIP - AOTIRCacheEntry.Offset;
auto LocalStartAddr = StartAddr - AOTIRCacheEntry.Offset;
auto LocalRIP = GuestRIP - AOTIRCacheEntry.VAFileStart;
auto LocalStartAddr = StartAddr - AOTIRCacheEntry.VAFileStart;
auto FileId = AOTIRCacheEntry.Entry->FileId;
// The underlying pointer and the unique_ptr deleter for RAData must
// be marshalled separately to the lambda below. Otherwise, the
@ -377,7 +384,7 @@ namespace FEXCore::IR {
std::unique_lock lk(AOTIRCacheLock);
auto Inserted = AOTIRCache.insert({fileid, AOTIRCacheEntry{0, 0, 0, fileid, filename, false}});
auto Inserted = AOTIRCache.insert({fileid, AOTIRCacheEntry { .FileId = fileid, .Filename = filename }});
auto Entry = &(Inserted.first->second);
LOGMAN_THROW_A_FMT(Entry->Array == nullptr, "Duplicate LoadAOTIRCacheEntry");

View File

@ -12,6 +12,7 @@
#include <unordered_map>
#include <shared_mutex>
#include <queue>
#include <FEXCore/HLE/SourcecodeResolver.h>
namespace FEXCore::Core {
struct DebugData;
@ -75,6 +76,7 @@ namespace FEXCore::IR {
AOTIRInlineIndex *Array;
void *FilePtr;
size_t Size;
std::unique_ptr<FEXCore::HLE::SourcecodeMap> SourcecodeMap;
std::string FileId;
std::string Filename;
bool ContainsCode;

View File

@ -181,6 +181,11 @@
"RAOverride": "0"
},
"GuestOpcode u32:$GuestEntryOffset": {
"Desc": ["Marks the beginning of a guest opcode"],
"HasSideEffects": true
},
"GPR = ValidateCode u64:$CodeOriginalLow, u64:$CodeOriginalhigh, i64:$Offset, u8:$CodeLength": {
"HasSideEffects": true,
"HasDest": true,

View File

@ -0,0 +1,22 @@
#include <cstddef>
#include <cstdint>
#include <gdb/jit-reader.h>
// everything is stored inline as it is marshaled cross process by gdb
struct blocks_t {
char name[512];
GDB_CORE_ADDR start;
GDB_CORE_ADDR end;
};
struct info_t {
char filename[512];
ptrdiff_t blocks_ofs;
ptrdiff_t lines_ofs;
int nblocks;
int nlines;
};

View File

@ -46,6 +46,11 @@ namespace FEXCore::Core {
uint32_t HostCodeSize;
};
struct DebugDataGuestOpcode {
uint64_t GuestEntryOffset;
ptrdiff_t HostEntryOffset;
};
/**
* @brief Contains debug data for a block of code for later debugger analysis
*
@ -54,6 +59,7 @@ namespace FEXCore::Core {
struct DebugData {
uint64_t HostCodeSize; ///< The size of the code generated in the host JIT
std::vector<DebugDataSubblock> Subblocks;
std::vector<DebugDataGuestOpcode> GuestOpcodes;
std::vector<FEXCore::CPU::Relocation> *Relocations;
};

View File

@ -0,0 +1,96 @@
#pragma once
#include <algorithm>
#include <string>
#include <vector>
#include <memory>
#include <filesystem>
#include <fmt/format.h>
namespace FEXCore::IR {
struct AOTIRCacheEntry;
}
namespace FEXCore::HLE {
struct SourcecodeLineMapping {
uintptr_t FileGuestBegin;
uintptr_t FileGuestEnd;
int LineNumber;
};
struct SourcecodeSymbolMapping {
uintptr_t FileGuestBegin;
uintptr_t FileGuestEnd;
std::string Name;
static std::string SymName(const SourcecodeSymbolMapping *Sym, const std::string &GuestFilename, uintptr_t HostEntry, uintptr_t FileBegin) {
if (Sym) {
auto SymOffset = FileBegin - Sym->FileGuestBegin;
if (SymOffset) {
return fmt::format("{}: {}+{} @{:x}", std::filesystem::path(GuestFilename).stem().string(), Sym->Name,
SymOffset, HostEntry);
} else {
return fmt::format("{}: {} @{:x}", std::filesystem::path(GuestFilename).stem().string(), Sym->Name,
HostEntry);
}
} else {
return fmt::format("{}: +{} @{:x}", std::filesystem::path(GuestFilename).stem().string(), FileBegin,
HostEntry);
}
}
};
struct SourcecodeMap {
std::string SourceFile;
std::vector<SourcecodeLineMapping> SortedLineMappings;
std::vector<SourcecodeSymbolMapping> SortedSymbolMappings;
template<typename F>
void IterateLineMappings(uintptr_t FileBegin, uintptr_t Size, const F &Callback) const {
auto Begin = FileBegin;
auto End = FileBegin + Size;
auto Found = std::lower_bound(SortedLineMappings.cbegin(), SortedLineMappings.cend(), Begin, [](const auto &Range, const auto Position) {
return Range.FileGuestEnd <= Position;
});
while (Found != SortedLineMappings.cend()) {
if (Found->FileGuestBegin < End && Found->FileGuestEnd > Begin) {
Callback(Found);
} else {
break;
}
Found++;
}
}
const SourcecodeLineMapping *FindLineMapping(uintptr_t FileBegin) const {
return Find(FileBegin, SortedLineMappings);
}
const SourcecodeSymbolMapping *FindSymbolMapping(uintptr_t FileBegin) const {
return Find(FileBegin, SortedSymbolMappings);
}
private:
template<typename VecT>
const typename VecT::value_type *Find(uintptr_t FileBegin, const VecT &SortedMappings) const {
auto Found = std::lower_bound(SortedMappings.cbegin(), SortedMappings.cend(), FileBegin, [](const auto &Range, const auto Position) {
return Range.FileGuestEnd <= Position;
});
if (Found != SortedMappings.end() && Found->FileGuestBegin <= FileBegin && Found->FileGuestEnd > FileBegin) {
return &(*Found);
} else {
return {};
}
}
};
class SourcecodeResolver {
public:
virtual std::unique_ptr<SourcecodeMap> GenerateMap(const std::string_view& GuestBinaryFile, const std::string_view& GuestBinaryFileId) = 0;
};
}

View File

@ -50,9 +50,11 @@ namespace FEXCore::HLE {
};
class SyscallHandler;
class SourcecodeResolver;
struct AOTIRCacheEntryLookupResult {
AOTIRCacheEntryLookupResult(FEXCore::IR::AOTIRCacheEntry *Entry, uintptr_t Offset, FHU::ScopedSignalMaskWithSharedLock &&lk)
: Entry(Entry), Offset(Offset), lk(std::move(lk))
AOTIRCacheEntryLookupResult(FEXCore::IR::AOTIRCacheEntry *Entry, uintptr_t VAFileStart, FHU::ScopedSignalMaskWithSharedLock &&lk)
: Entry(Entry), VAFileStart(VAFileStart), lk(std::move(lk))
{
}
@ -60,7 +62,7 @@ namespace FEXCore::HLE {
AOTIRCacheEntryLookupResult(AOTIRCacheEntryLookupResult&&) = default;
FEXCore::IR::AOTIRCacheEntry *Entry;
uintptr_t Offset;
uintptr_t VAFileStart;
friend class SyscallHandler;
protected:
@ -80,6 +82,7 @@ namespace FEXCore::HLE {
virtual void MarkGuestExecutableRange(uint64_t Start, uint64_t Length) { }
virtual AOTIRCacheEntryLookupResult LookupAOTIRCacheEntry(uint64_t GuestAddr) = 0;
virtual SourcecodeResolver *GetSourcecodeResolver() { return nullptr; }
protected:
SyscallOSABI OSABI;
};

View File

@ -6,6 +6,8 @@
#include <fcntl.h>
#include <unistd.h>
#include <FEXCore/Utils/LogManager.h>
#include "Linux/Utils/ELFContainer.h"
/*
@ -191,6 +193,36 @@ struct ELFParser {
return true;
}
ptrdiff_t FileToVA(off_t FileOffset) {
for (auto phdr : phdrs) {
if (phdr.p_offset <= FileOffset && (phdr.p_offset + phdr.p_filesz) > FileOffset) {
auto SectionFileOffset = FileOffset - phdr.p_offset;
if (SectionFileOffset < phdr.p_memsz) {
return SectionFileOffset + phdr.p_vaddr;
}
}
}
return {};
}
off_t VAToFile(ptrdiff_t VAOffset) {
for (auto phdr : phdrs) {
if (phdr.p_vaddr <= VAOffset && (phdr.p_vaddr + phdr.p_memsz) > VAOffset) {
auto SectionVAOffset = VAOffset - phdr.p_vaddr;
if (SectionVAOffset < phdr.p_filesz) {
return SectionVAOffset + phdr.p_offset;
}
}
}
return {};
}
bool ReadElf(const std::string &file) {
int NewFD = ::open(file.c_str(), O_RDONLY);

View File

@ -7,6 +7,7 @@ $end_info$
*/
#include "Linux/Utils/ELFContainer.h"
#include "Linux/Utils/ELFParser.h"
#include "Tests/LinuxSyscalls/LinuxAllocator.h"
#include "Tests/LinuxSyscalls/Syscalls.h"
@ -37,6 +38,7 @@ $end_info$
#include <filesystem>
#include <fstream>
#include <memory>
#include <regex>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
@ -700,4 +702,313 @@ void SyscallHandler::UnlockAfterFork() {
FM.GetFDLock()->unlock();
}
static bool isHEX(char c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
}
std::unique_ptr<FEXCore::HLE::SourcecodeMap> SyscallHandler::GenerateMap(const std::string_view& GuestBinaryFile, const std::string_view& GuestBinaryFileId) {
ELFParser GuestELF;
if (!GuestELF.ReadElf(std::string(GuestBinaryFile))) {
LogMan::Msg::DFmt("GenerateMap: '{}' is not an elf file?", GuestBinaryFile);
return {};
}
struct stat GuestBinaryFileStat;
if (stat(GuestBinaryFile.data(), &GuestBinaryFileStat)) {
LogMan::Msg::DFmt("GenerateMap: failed to stat '{}'", GuestBinaryFile);
return {};
}
std::error_code ec;
auto FexSrcPath = std::filesystem::path(FEXCore::Config::GetDataDirectory()) / "fexsrc";
std::filesystem::create_directories(FexSrcPath, ec);
if (ec) {
LogMan::Msg::DFmt("GenerateMap: failed to create_directories '{}'", FexSrcPath.string());
return {};
}
auto GuestSourceFile = (FexSrcPath / GuestBinaryFileId).string() + ".src";
struct stat GuestSourceFileStat;
if (stat(GuestSourceFile.data(), &GuestSourceFileStat) != 0 || GuestBinaryFileStat.st_mtime > GuestSourceFileStat.st_mtime) {
LogMan::Msg::DFmt("GenerateMap: Generating source for '{}'", GuestBinaryFile);
auto command = fmt::format("x86_64-linux-gnu-objdump -SC \'{}\' > '{}'", GuestBinaryFile, GuestSourceFile);
if (system(command.c_str()) != 0) {
LogMan::Msg::DFmt("GenerateMap: '{}' failed", command);
return {};
}
}
auto GuestIndexFile = (FexSrcPath / GuestBinaryFileId).string() + ".idx";
struct stat GuestIndexFileStat;
bool GenerateIndex = stat(GuestIndexFile.data(), &GuestIndexFileStat) != 0 || GuestSourceFileStat.st_mtime > GuestIndexFileStat.st_mtime;
if (!GenerateIndex) {
// Index file de-serialization
LogMan::Msg::DFmt("GenerateMap: Reading index '{}'", GuestIndexFile);
std::ifstream Stream(GuestIndexFile);
if (!Stream) {
LogMan::Msg::DFmt("GenerateMap: Failed to open '{}'", GuestIndexFile);
goto DoGenerate;
}
//"fexsrcindex0"
char filemagic[12];
Stream.read(filemagic, sizeof(filemagic));
if (memcmp(filemagic, "fexsrcindex0", sizeof(filemagic)) != 0) {
LogMan::Msg::DFmt("GenerateMap: '{}' has invalid magic '{}'", GuestIndexFile, filemagic);
goto DoGenerate;
}
auto rv = std::make_unique<FEXCore::HLE::SourcecodeMap>();
{
auto len = rv->SourceFile.size();
Stream.read((char*)&len, sizeof(len));
rv->SourceFile.resize(len);
Stream.read(rv->SourceFile.data(), len);
}
{
auto len = rv->SortedLineMappings.size();
Stream.read((char*)&len, sizeof(len));
rv->SortedLineMappings.resize(len);
for (auto &Mapping: rv->SortedLineMappings) {
Stream.read((char*)&Mapping.FileGuestBegin, sizeof(Mapping.FileGuestBegin));
Stream.read((char*)&Mapping.FileGuestEnd, sizeof(Mapping.FileGuestEnd));
Stream.read((char*)&Mapping.LineNumber, sizeof(Mapping.LineNumber));
}
}
{
auto len = rv->SortedSymbolMappings.size();
Stream.read((char*)&len, sizeof(len));
rv->SortedSymbolMappings.resize(len);
for (auto &Mapping: rv->SortedSymbolMappings) {
Stream.read((char*)&Mapping.FileGuestBegin, sizeof(Mapping.FileGuestBegin));
Stream.read((char*)&Mapping.FileGuestEnd, sizeof(Mapping.FileGuestEnd));
{
auto len = Mapping.Name.size();
Stream.read((char*)&len, sizeof(len));
Mapping.Name.resize(len);
Stream.read(Mapping.Name.data(), len);
}
}
}
LogMan::Msg::DFmt("GenerateMap: Finished reading index");
return rv;
} else {
// objdump output parsing, index generation, index file serialization
DoGenerate:
LogMan::Msg::DFmt("GenerateMap: Generating index for '{}'", GuestSourceFile);
std::ifstream Stream(GuestSourceFile);
if (!Stream) {
LogMan::Msg::DFmt("GenerateMap: Failed to open '{}'", GuestSourceFile);
}
std::ofstream IndexStream(GuestIndexFile);
if (!IndexStream) {
LogMan::Msg::DFmt("GenerateMap: Failed to open '{}' for writing", GuestIndexFile);
}
IndexStream.write("fexsrcindex0", strlen("fexsrcindex0"));
// objdump parsing
std::string Line;
int LineNum = 0;
bool PreviousLineWasEmpty = false;
uintptr_t LastSymbolOffset{};
uintptr_t CurrentSymbolOffset{};
std::string LastSymbolName;
uintptr_t LastOffset{};
uintptr_t CurrentOffset{};
int LastOffsetLine;
auto rv = std::make_unique<FEXCore::HLE::SourcecodeMap>();
rv->SourceFile = GuestSourceFile;
auto EndSymbol = [&] {
if (LastSymbolOffset) {
rv->SortedSymbolMappings.push_back({LastSymbolOffset, CurrentSymbolOffset, LastSymbolName});
// LogMan::Msg::DFmt("Ended Symbol {} - {:x}...{:x}", LastSymbolName, LastSymbolOffset, CurrentSymbolOffset);
}
LastSymbolOffset = {};
};
auto EndLine = [&] {
if (LastOffset) {
rv->SortedLineMappings.push_back({LastOffset, CurrentOffset, LastOffsetLine});
// LogMan::Msg::DFmt("Ended Line {} - {:x}...{:x}", LastOffsetLine, LastOffset, CurrentOffset);
}
LastOffset = {};
};
while (std::getline(Stream, Line)) {
LineNum++;
auto LineIsEmpty = Line.empty();
if (LineIsEmpty) {
PreviousLineWasEmpty = true;
} else {
// LogMan::Msg::DFmt("Line: '{}'", Line);
if (isHEX(Line[0])) {
std::string addr;
int offs = 1;
for (; !isspace(Line[offs]) && offs < Line.size(); offs++)
;
if (offs == Line.size())
continue;
if (offs != 8 && offs != 16)
continue;
auto VAOffset = std::strtoul(Line.substr(0, offs).c_str(), nullptr, 16);
auto FileOffset = GuestELF.VAToFile(VAOffset);
if (FileOffset == 0) {
LogMan::Msg::EFmt("File Offset {:x} did not map to file?! {}", VAOffset, Line);
}
CurrentSymbolOffset = FileOffset;
if (PreviousLineWasEmpty) {
EndSymbol();
}
LastSymbolOffset = CurrentSymbolOffset;
for (; Line[offs] != '<' && offs < Line.size(); offs++)
;
if (offs == Line.size())
continue;
offs++;
LastSymbolName = Line.substr(offs, Line.size() - 2 - offs);
// LogMan::Msg::DFmt("Symbol {} @ {:x} -> Line {}", LastSymbolName, LastSymbolOffset, LineNum);
} else if (isspace(Line[0])) {
int offs = 1;
for (; isspace(Line[offs]) && offs < Line.size(); offs++)
;
if (offs == Line.size())
continue;
int start = offs;
for (; Line[offs] != ':' && offs < Line.size(); offs++)
;
if (offs == Line.size())
continue;
if (Line[offs + 1] == '\t') {
auto VAOffsetStr = Line.substr(start, offs - start);
auto VAOffset = std::strtoul(VAOffsetStr.c_str(), nullptr, 16);
auto FileOffset = GuestELF.VAToFile(VAOffset);
if (FileOffset == 0) {
LogMan::Msg::EFmt("File Offset {:x} did not map to file?! {}", VAOffset, Line);
} else {
if (LastOffset > FileOffset) {
LogMan::Msg::EFmt("File Offset {:x} less than previous {:} ?! {}", FileOffset, LastOffset, Line);
}
CurrentOffset = FileOffset;
EndLine();
LastOffset = CurrentOffset;
LastOffsetLine = LineNum;
}
}
}
// something else -- keep going
}
}
CurrentOffset = LastOffset + 4;
CurrentSymbolOffset = CurrentOffset;
EndSymbol();
EndLine();
// Index post processing - entires are sorted for faster lookups
std::sort(rv->SortedLineMappings.begin(), rv->SortedLineMappings.end(),
[](const auto &lhs, const auto &rhs) { return lhs.FileGuestEnd <= rhs.FileGuestBegin; });
std::sort(rv->SortedSymbolMappings.begin(), rv->SortedSymbolMappings.end(),
[](const auto &lhs, const auto &rhs) { return lhs.FileGuestEnd <= rhs.FileGuestBegin; });
// Index serialization
{
auto len = rv->SourceFile.size();
IndexStream.write((const char*)&len, sizeof(len));
IndexStream.write(rv->SourceFile.c_str(), len);
}
{
auto len = rv->SortedLineMappings.size();
IndexStream.write((const char*)&len, sizeof(len));
for (const auto &Mapping: rv->SortedLineMappings) {
IndexStream.write((const char*)&Mapping.FileGuestBegin, sizeof(Mapping.FileGuestBegin));
IndexStream.write((const char*)&Mapping.FileGuestEnd, sizeof(Mapping.FileGuestEnd));
IndexStream.write((const char*)&Mapping.LineNumber, sizeof(Mapping.LineNumber));
}
}
{
auto len = rv->SortedSymbolMappings.size();
IndexStream.write((char*)&len, sizeof(len));
for (const auto &Mapping: rv->SortedSymbolMappings) {
IndexStream.write((const char*)&Mapping.FileGuestBegin, sizeof(Mapping.FileGuestBegin));
IndexStream.write((const char*)&Mapping.FileGuestEnd, sizeof(Mapping.FileGuestEnd));
{
auto len = Mapping.Name.size();
IndexStream.write((const char*)&len, sizeof(len));
IndexStream.write(Mapping.Name.c_str(), len);
}
}
}
LogMan::Msg::DFmt("GenerateMap: Finished generating index", GuestIndexFile);
return rv;
}
}
}

View File

@ -12,6 +12,7 @@ $end_info$
#include <FEXCore/Config/Config.h>
#include <FEXCore/HLE/SyscallHandler.h>
#include <FEXCore/HLE/SourcecodeResolver.h>
#include <FEXCore/IR/IR.h>
#include <FEXCore/Utils/CompilerDefs.h>
@ -83,7 +84,7 @@ struct ExecveAtArgs {
uint64_t ExecveHandler(const char *pathname, char* const* argv, char* const* envp, ExecveAtArgs *Args);
class SyscallHandler : public FEXCore::HLE::SyscallHandler {
class SyscallHandler : public FEXCore::HLE::SyscallHandler, FEXCore::HLE::SourcecodeResolver {
public:
virtual ~SyscallHandler();
@ -192,6 +193,8 @@ public:
///// FORK tracking /////
void LockBeforeFork();
void UnlockAfterFork();
SourcecodeResolver *GetSourcecodeResolver() override { return this; }
protected:
SyscallHandler(FEXCore::Context::Context *_CTX, FEX::HLE::SignalDelegator *_SignalDelegation);
@ -224,6 +227,8 @@ private:
#endif
std::unique_ptr<FEX::HLE::MemAllocator> Alloc32Handler{};
std::unique_ptr<FEXCore::HLE::SourcecodeMap> GenerateMap(const std::string_view& GuestBinaryFile, const std::string_view& GuestBinaryFileId) override;
///// VMA (Virtual Memory Area) tracking /////

View File

@ -173,7 +173,7 @@ FEXCore::HLE::AOTIRCacheEntryLookupResult SyscallHandler::LookupAOTIRCacheEntry(
if (Entry != _SyscallHandler->VMATracking.VMAs.end()) {
rv.Entry = Entry->second.Resource ? Entry->second.Resource->AOTIRCacheEntry : nullptr;
rv.Offset = Entry->second.Base - Entry->second.Offset;
rv.VAFileStart = Entry->second.Base - Entry->second.Offset;
}
return rv;

View File

@ -11,6 +11,9 @@ if (NOT TERMUX_BUILD)
add_subdirectory(FEXRootFSFetcher/)
endif()
if (ENABLE_GDB_SYMBOLS)
add_subdirectory(FEXGDBReader/)
endif()
add_subdirectory(FEXGetConfig/)
add_subdirectory(FEXServer/)

View File

@ -0,0 +1,15 @@
set(NAME FEXGDBReader)
set(SRCS FEXGDBReader.cpp)
add_library(${NAME} SHARED ${SRCS})
install(TARGETS ${NAME}
RUNTIME
LIBRARY DESTINATION /usr/lib/gdb
COMPONENT LIBRARY)
target_include_directories(${NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Source/)
target_include_directories(${NAME} PRIVATE ${CMAKE_BINARY_DIR}/generated)
# We don't actually link, but this is a nice way to get the include dirs
target_link_libraries(${NAME} PRIVATE Common)

View File

@ -0,0 +1,65 @@
#include <cstddef>
#include <cstdio>
#include <unordered_map>
#include <mutex>
#include <string>
#include <FEXCore/Debug/GDBReaderInterface.h>
GDB_DECLARE_GPL_COMPATIBLE_READER;
#define debugf(...)
extern "C" {
static enum gdb_status read_debug_info(struct gdb_reader_funcs *self, struct gdb_symbol_callbacks *cbs, void *memory, long memory_sz) {
info_t *info = (info_t *)memory;
blocks_t *blocks = (blocks_t *)(info->blocks_ofs + (long)memory);
gdb_line_mapping *lines = (gdb_line_mapping *)(info->lines_ofs + (long)memory);
debugf("info: %p\n", info);
debugf("info: s %p\n", info->filename);
debugf("info: s %s\n", info->filename);
debugf("info: l %d\n", info->nlines);
debugf("info: b %d\n", info->nblocks);
struct gdb_object *object = cbs->object_open(cbs);
struct gdb_symtab *symtab = cbs->symtab_open(cbs, object, info->filename);
for (int i = 0; i < info->nblocks; i++) {
debugf("info: %d\n", i);
debugf("info: %lx\n", blocks[i].start);
debugf("info: %lx\n", blocks[i].end);
debugf("info: %s\n", blocks[i].name);
cbs->block_open(cbs, symtab, NULL, blocks[i].start, blocks[i].end, blocks[i].name);
}
debugf("info: lines %d\n", info->nlines);
debugf("info: lines %p\n", lines);
for (int i = 0; i < info->nlines; i++) {
debugf("info: line: %d\n", i);
debugf("info: line pc: %lx\n", lines[i].pc);
debugf("info: line file: %d\n", lines[i].line);
}
cbs->line_mapping_add(cbs, symtab, info->nlines, lines);
// don't close here, symtab and object are cached
cbs->symtab_close(cbs, symtab);
cbs->object_close(cbs, object);
return GDB_SUCCESS;
}
enum gdb_status unwind_frame(struct gdb_reader_funcs *self, struct gdb_unwind_callbacks *cbs) { return GDB_SUCCESS; }
struct gdb_frame_id get_frame_id(struct gdb_reader_funcs *self, struct gdb_unwind_callbacks *cbs) {
struct gdb_frame_id frame = {0x1234000, 0};
return frame;
}
void destroy_reader(struct gdb_reader_funcs *self) {}
extern struct gdb_reader_funcs *gdb_init_reader(void) {
static struct gdb_reader_funcs funcs = {GDB_READER_INTERFACE_VERSION, NULL, read_debug_info, unwind_frame, get_frame_id, destroy_reader};
return &funcs;
}
}