mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-07 09:00:17 +02:00
We already have an equivalent header within FEXCore that's header only, so we can adapt it to conform for both cases, allowing for removal of one of them.
208 lines
6.1 KiB
C++
208 lines
6.1 KiB
C++
// SPDX-License-Identifier: MIT
|
|
/*
|
|
$info$
|
|
tags: glue|gdbserver
|
|
desc: Provides a gdb interface to the guest state
|
|
$end_info$
|
|
*/
|
|
|
|
#include "GdbServer/Info.h"
|
|
|
|
#include <FEXCore/Core/CoreState.h>
|
|
#include <FEXCore/Core/X86Enums.h>
|
|
#include <FEXCore/fextl/fmt.h>
|
|
#include <FEXCore/fextl/sstream.h>
|
|
#include <FEXCore/Utils/CompilerDefs.h>
|
|
#include <FEXCore/Utils/FileLoading.h>
|
|
#include <FEXCore/Utils/LogManager.h>
|
|
#include <FEXCore/Utils/StringUtils.h>
|
|
|
|
#include <array>
|
|
#include <string_view>
|
|
|
|
namespace FEX::GDB::Info {
|
|
constexpr std::array<std::string_view, 22> FlagNames = {
|
|
"CF", "", "PF", "", "AF", "", "ZF", "SF", "TF", "IF", "DF", "OF", "IOPL", "", "NT", "", "RF", "VM", "AC", "VIF", "VIP", "ID",
|
|
};
|
|
|
|
const std::string_view& GetFlagName(unsigned Bit) {
|
|
LOGMAN_THROW_A_FMT(Bit < 22, "Bit position too large");
|
|
return FlagNames[Bit];
|
|
}
|
|
|
|
std::string_view GetGRegName(unsigned Reg) {
|
|
switch (Reg) {
|
|
case FEXCore::X86State::REG_RAX: return "rax";
|
|
case FEXCore::X86State::REG_RBX: return "rbx";
|
|
case FEXCore::X86State::REG_RCX: return "rcx";
|
|
case FEXCore::X86State::REG_RDX: return "rdx";
|
|
case FEXCore::X86State::REG_RSP: return "rsp";
|
|
case FEXCore::X86State::REG_RBP: return "rbp";
|
|
case FEXCore::X86State::REG_RSI: return "rsi";
|
|
case FEXCore::X86State::REG_RDI: return "rdi";
|
|
case FEXCore::X86State::REG_R8: return "r8";
|
|
case FEXCore::X86State::REG_R9: return "r9";
|
|
case FEXCore::X86State::REG_R10: return "r10";
|
|
case FEXCore::X86State::REG_R11: return "r11";
|
|
case FEXCore::X86State::REG_R12: return "r12";
|
|
case FEXCore::X86State::REG_R13: return "r13";
|
|
case FEXCore::X86State::REG_R14: return "r14";
|
|
case FEXCore::X86State::REG_R15: return "r15";
|
|
default: FEX_UNREACHABLE;
|
|
}
|
|
}
|
|
|
|
fextl::string GetThreadName(uint32_t PID, uint32_t ThreadID) {
|
|
const auto ThreadFile = fextl::fmt::format("/proc/{}/task/{}/comm", PID, ThreadID);
|
|
fextl::string ThreadName;
|
|
FEXCore::FileLoading::LoadFile(ThreadName, ThreadFile);
|
|
// Trim out the potential newline, breaks GDB if it exists.
|
|
return FEXCore::StringUtils::Trim(ThreadName);
|
|
}
|
|
|
|
fextl::string BuildOSXML() {
|
|
fextl::ostringstream xml;
|
|
|
|
xml << "<?xml version='1.0'?>\n";
|
|
|
|
xml << "<!DOCTYPE target SYSTEM \"osdata.dtd\">\n";
|
|
xml << "<osdata type=\"processes\">";
|
|
// XXX
|
|
xml << "</osdata>";
|
|
|
|
xml << std::flush;
|
|
|
|
return xml.str();
|
|
}
|
|
|
|
fextl::string BuildTargetXML(bool Is64Bit) {
|
|
fextl::ostringstream xml;
|
|
|
|
xml << "<?xml version='1.0'?>\n";
|
|
xml << "<!DOCTYPE target SYSTEM 'gdb-target.dtd'>\n";
|
|
xml << "<target>\n";
|
|
if (Is64Bit) {
|
|
xml << "<architecture>i386:x86-64</architecture>\n";
|
|
} else {
|
|
xml << "<architecture>i386</architecture>\n";
|
|
}
|
|
xml << "<osabi>GNU/Linux</osabi>\n";
|
|
xml << "<feature name='org.gnu.gdb.i386.core'>\n";
|
|
|
|
xml << "<flags id='fex_eflags' size='4'>\n";
|
|
// flags register
|
|
for (int i = 0; i < 22; i++) {
|
|
auto name = GDB::Info::GetFlagName(i);
|
|
if (name.empty()) {
|
|
continue;
|
|
}
|
|
xml << "\t<field name='" << name << "' start='" << i << "' end='" << i << "' />\n";
|
|
}
|
|
xml << "</flags>\n";
|
|
|
|
int32_t TargetSize {};
|
|
auto reg = [&](std::string_view name, std::string_view type, int size) {
|
|
TargetSize += size;
|
|
xml << "<reg name='" << name << "' bitsize='" << size << "' type='" << type << "' />" << std::endl;
|
|
};
|
|
|
|
// Register ordering.
|
|
// We want to just memcpy our x86 state to gdb, so we tell it the ordering.
|
|
|
|
// GPRs
|
|
for (uint32_t i = 0; i < FEXCore::Core::CPUState::NUM_GPRS; i++) {
|
|
reg(GDB::Info::GetGRegName(i), "int64", 64);
|
|
}
|
|
|
|
reg("rip", "code_ptr", 64);
|
|
|
|
reg("eflags", "fex_eflags", 32);
|
|
|
|
// Fake registers which GDB requires, but we don't support;
|
|
// We stick them past the end of our cpu state.
|
|
|
|
// non-userspace segment registers
|
|
reg("cs", "int32", 32);
|
|
reg("ss", "int32", 32);
|
|
reg("ds", "int32", 32);
|
|
reg("es", "int32", 32);
|
|
|
|
reg("fs", "int32", 32);
|
|
reg("gs", "int32", 32);
|
|
|
|
// x87 stack
|
|
for (int i = 0; i < 8; i++) {
|
|
reg(fextl::fmt::format("st{}", i), "i387_ext", 80);
|
|
}
|
|
|
|
// x87 control
|
|
reg("fctrl", "int32", 32);
|
|
reg("fstat", "int32", 32);
|
|
reg("ftag", "int32", 32);
|
|
reg("fiseg", "int32", 32);
|
|
reg("fioff", "int32", 32);
|
|
reg("foseg", "int32", 32);
|
|
reg("fooff", "int32", 32);
|
|
reg("fop", "int32", 32);
|
|
|
|
|
|
xml << "</feature>\n";
|
|
xml << "<feature name='org.gnu.gdb.i386.sse'>\n";
|
|
xml <<
|
|
R"(<vector id="v4f" type="ieee_single" count="4"/>
|
|
<vector id="v2d" type="ieee_double" count="2"/>
|
|
<vector id="v16i8" type="int8" count="16"/>
|
|
<vector id="v8i16" type="int16" count="8"/>
|
|
<vector id="v4i32" type="int32" count="4"/>
|
|
<vector id="v2i64" type="int64" count="2"/>
|
|
<union id="vec128">
|
|
<field name="v4_float" type="v4f"/>
|
|
<field name="v2_double" type="v2d"/>
|
|
<field name="v16_int8" type="v16i8"/>
|
|
<field name="v8_int16" type="v8i16"/>
|
|
<field name="v4_int32" type="v4i32"/>
|
|
<field name="v2_int64" type="v2i64"/>
|
|
<field name="uint128" type="uint128"/>
|
|
</union>
|
|
)";
|
|
|
|
// SSE regs
|
|
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_XMMS; i++) {
|
|
reg(fextl::fmt::format("xmm{}", i), "vec128", 128);
|
|
}
|
|
|
|
reg("mxcsr", "int", 32);
|
|
|
|
xml << "</feature>\n";
|
|
|
|
xml << "<feature name='org.gnu.gdb.i386.avx'>";
|
|
xml <<
|
|
R"(<vector id="v4f" type="ieee_single" count="4"/>
|
|
<vector id="v2d" type="ieee_double" count="2"/>
|
|
<vector id="v16i8" type="int8" count="16"/>
|
|
<vector id="v8i16" type="int16" count="8"/>
|
|
<vector id="v4i32" type="int32" count="4"/>
|
|
<vector id="v2i64" type="int64" count="2"/>
|
|
<union id="vec128">
|
|
<field name="v4_float" type="v4f"/>
|
|
<field name="v2_double" type="v2d"/>
|
|
<field name="v16_int8" type="v16i8"/>
|
|
<field name="v8_int16" type="v8i16"/>
|
|
<field name="v4_int32" type="v4i32"/>
|
|
<field name="v2_int64" type="v2i64"/>
|
|
<field name="uint128" type="uint128"/>
|
|
</union>
|
|
)";
|
|
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_XMMS; i++) {
|
|
reg(fmt::format("ymm{}h", i), "vec128", 128);
|
|
}
|
|
xml << "</feature>\n";
|
|
|
|
xml << "</target>";
|
|
xml << std::flush;
|
|
|
|
return xml.str();
|
|
}
|
|
|
|
} // namespace FEX::GDB::Info
|