Provide correct GDB target description (#92)

This commit is contained in:
Maurice Heumann
2025-01-19 12:24:33 +01:00
committed by GitHub
10 changed files with 451 additions and 68 deletions

View File

@@ -109,7 +109,7 @@ namespace utils::string
return to_hex_string(&i, sizeof(Integer), uppercase);
}
inline std::string to_hex_string(const std::span<std::byte> data, const bool uppercase = false)
inline std::string to_hex_string(const std::span<const std::byte> data, const bool uppercase = false)
{
return to_hex_string(data.data(), data.size(), uppercase);
}

View File

@@ -12,8 +12,8 @@ struct cpu_interface
virtual void start(uint64_t start, uint64_t end = 0, std::chrono::nanoseconds timeout = {}, size_t count = 0) = 0;
virtual void stop() = 0;
virtual void read_raw_register(int reg, void* value, size_t size) = 0;
virtual void write_raw_register(int reg, const void* value, size_t size) = 0;
virtual size_t read_raw_register(int reg, void* value, size_t size) = 0;
virtual size_t write_raw_register(int reg, const void* value, size_t size) = 0;
virtual std::vector<std::byte> save_registers() = 0;
virtual void restore_registers(const std::vector<std::byte>& register_data) = 0;

View File

@@ -20,14 +20,14 @@ class typed_emulator : public emulator
this->start(this->read_instruction_pointer(), 0, timeout, count);
}
void write_register(registers reg, const void* value, const size_t size)
size_t write_register(registers reg, const void* value, const size_t size)
{
this->write_raw_register(static_cast<int>(reg), value, size);
return this->write_raw_register(static_cast<int>(reg), value, size);
}
void read_register(registers reg, void* value, const size_t size)
size_t read_register(registers reg, void* value, const size_t size)
{
this->read_raw_register(static_cast<int>(reg), value, size);
return this->read_raw_register(static_cast<int>(reg), value, size);
}
template <typename T = pointer_type>
@@ -90,6 +90,6 @@ class typed_emulator : public emulator
private:
emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) override = 0;
void read_raw_register(int reg, void* value, size_t size) override = 0;
void write_raw_register(int reg, const void* value, size_t size) override = 0;
size_t read_raw_register(int reg, void* value, size_t size) override = 0;
size_t write_raw_register(int reg, const void* value, size_t size) override = 0;
};

View File

@@ -49,15 +49,45 @@ namespace gdb_stub
return {name, args};
}
void handle_features(connection_handler& connection, debugging_handler& handler, const std::string_view payload)
{
const auto [command, args] = split_string(payload, ':');
if (command != "read")
{
connection.send_reply({});
return;
}
const auto [file, data] = split_string(args, ':');
size_t offset{}, length{};
rt_assert(sscanf_s(std::string(data).c_str(), "%zx,%zx", &offset, &length) == 2);
const auto target_description = handler.get_target_description(file);
if (offset >= target_description.size())
{
connection.send_reply("l");
return;
}
const auto remaining = target_description.size() - offset;
const auto real_length = std::min(remaining, length);
const auto is_end = real_length == remaining;
const auto sub_region = target_description.substr(offset, real_length);
connection.send_reply((is_end ? "l" : "m") + sub_region);
}
void process_xfer(connection_handler& connection, debugging_handler& handler, const std::string_view payload)
{
auto [name, args] = split_string(payload, ':');
if (name == "features")
{
connection.send_reply("l<target version=\"1.0\"><architecture>" //
+ handler.get_target_description() //
+ "</architecture></target>");
handle_features(connection, handler, args);
}
else
{
@@ -179,16 +209,16 @@ namespace gdb_stub
for (size_t i = 0; i < registers; ++i)
{
memset(data.data(), 0, data.size());
const auto res = handler.read_register(i, data.data(), data.size());
const auto size = handler.read_register(i, data.data(), data.size());
if (!res)
if (!size)
{
connection.send_reply("E01");
return;
}
response.append(utils::string::to_hex_string(data));
const std::span register_data(data.data(), size);
response.append(utils::string::to_hex_string(register_data));
}
connection.send_reply(response);
@@ -201,20 +231,21 @@ namespace gdb_stub
const auto registers = handler.get_register_count();
const auto register_size = handler.get_max_register_size();
size_t offset = 0;
for (size_t i = 0; i < registers; ++i)
{
const auto offset = i * register_size;
const auto end_offset = offset + register_size;
if (data.size() < end_offset)
if (offset >= data.size())
{
connection.send_reply("E01");
return;
}
const auto res = handler.write_register(i, data.data() + offset, register_size);
const auto max_size = std::min(register_size, data.size() - offset);
const auto size = handler.write_register(i, data.data() + offset, max_size);
if (!res)
offset += size;
if (!size)
{
connection.send_reply("E01");
return;
@@ -233,11 +264,12 @@ namespace gdb_stub
std::vector<std::byte> data{};
data.resize(handler.get_max_register_size());
const auto res = handler.read_register(reg, data.data(), data.size());
const auto size = handler.read_register(reg, data.data(), data.size());
if (res)
if (size)
{
connection.send_reply(utils::string::to_hex_string(data));
const std::span register_data(data.data(), size);
connection.send_reply(utils::string::to_hex_string(register_data));
}
else
{
@@ -253,12 +285,8 @@ namespace gdb_stub
size_t register_index{};
rt_assert(sscanf_s(std::string(reg).c_str(), "%zx", &register_index) == 1);
const auto register_size = handler.get_max_register_size();
const auto data = utils::string::from_hex_string(hex_data);
const auto res = register_size <= data.size() && //
handler.write_register(register_index, data.data(), register_size);
const auto res = handler.write_register(register_index, data.data(), data.size()) > 0;
connection.send_reply(res ? "OK" : "E01");
}

View File

@@ -31,8 +31,8 @@ namespace gdb_stub
virtual size_t get_register_count() = 0;
virtual size_t get_max_register_size() = 0;
virtual bool read_register(size_t reg, void* data, size_t max_length) = 0;
virtual bool write_register(size_t reg, const void* data, size_t size) = 0;
virtual size_t read_register(size_t reg, void* data, size_t max_length) = 0;
virtual size_t write_register(size_t reg, const void* data, size_t size) = 0;
virtual bool read_memory(uint64_t address, void* data, size_t length) = 0;
virtual bool write_memory(uint64_t address, const void* data, size_t length) = 0;
@@ -42,7 +42,7 @@ namespace gdb_stub
virtual void on_interrupt() = 0;
virtual std::string get_target_description() = 0;
virtual std::string get_target_description(std::string_view file) = 0;
};
bool run_gdb_stub(const network::address& bind_address, debugging_handler& handler);

View File

@@ -301,9 +301,9 @@ namespace unicorn
uce(uc_emu_stop(*this));
}
void write_raw_register(const int reg, const void* value, const size_t size) override
size_t write_raw_register(const int reg, const void* value, const size_t size) override
{
size_t result_size = size;
auto result_size = size;
uce(uc_reg_write2(*this, reg, value, &result_size));
if (size < result_size)
@@ -311,9 +311,11 @@ namespace unicorn
throw std::runtime_error("Register size mismatch: " + std::to_string(size) +
" != " + std::to_string(result_size));
}
return result_size;
}
void read_raw_register(const int reg, void* value, const size_t size) override
size_t read_raw_register(const int reg, void* value, const size_t size) override
{
size_t result_size = size;
memset(value, 0, size);
@@ -324,6 +326,8 @@ namespace unicorn
throw std::runtime_error("Register size mismatch: " + std::to_string(size) +
" != " + std::to_string(result_size));
}
return result_size;
}
void map_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb,

View File

@@ -40,11 +40,6 @@ class win_x64_gdb_stub_handler : public x64_gdb_stub_handler
return gdb_stub::action::resume;
}
std::string get_target_description() override
{
return "i386:x86-64";
}
private:
windows_emulator* win_emu_{};
};

View File

@@ -1,20 +1,13 @@
#pragma once
#include <x64_emulator.hpp>
#include "scoped_hook.hpp"
#include <utils/concurrency.hpp>
#include <gdb-stub/gdb_stub.hpp>
inline std::vector gdb_registers{
x64_register::rax, x64_register::rbx, x64_register::rcx, x64_register::rdx, x64_register::rsi, x64_register::rdi,
x64_register::rbp, x64_register::rsp, x64_register::r8, x64_register::r9, x64_register::r10, x64_register::r11,
x64_register::r12, x64_register::r13, x64_register::r14, x64_register::r15, x64_register::rip, x64_register::rflags,
/*x64_register::cs,
x64_register::ss,
x64_register::ds,
x64_register::es,
x64_register::fs,
x64_register::gs,*/
};
#include "scoped_hook.hpp"
#include "x64_register_mapping.hpp"
#include "x64_target_descriptions.hpp"
inline memory_operation map_breakpoint_type(const gdb_stub::breakpoint_type type)
{
@@ -103,46 +96,81 @@ class x64_gdb_stub_handler : public gdb_stub::debugging_handler
size_t get_max_register_size() override
{
// return 256 / 8;
return 64 / 8;
return 512 / 8;
}
bool read_register(const size_t reg, void* data, const size_t max_length) override
size_t read_register(const size_t reg, void* data, const size_t max_length) override
{
try
{
if (reg >= gdb_registers.size())
{
// TODO: Fix
return true;
return 0;
}
this->emu_->read_register(gdb_registers[reg], data, max_length);
return true;
const auto real_reg = gdb_registers[reg];
auto size = this->emu_->read_register(real_reg.reg, data, max_length);
if (real_reg.offset)
{
size -= *real_reg.offset;
memcpy(data, static_cast<uint8_t*>(data) + *real_reg.offset, size);
}
const auto result_size = real_reg.expected_size.value_or(size);
if (result_size > size)
{
memset(static_cast<uint8_t*>(data) + size, 0, result_size - size);
}
return result_size;
}
catch (...)
{
// TODO: Fix
return true;
return 0;
}
}
bool write_register(const size_t reg, const void* data, const size_t size) override
size_t write_register(const size_t reg, const void* data, const size_t size) override
{
try
{
if (reg >= gdb_registers.size())
{
// TODO: Fix
return true;
return 0;
}
this->emu_->write_register(gdb_registers[reg], data, size);
return true;
const auto real_reg = gdb_registers[reg];
size_t written_size = 0;
if (real_reg.offset)
{
std::vector<std::byte> full_data{};
full_data.resize(this->get_max_register_size());
written_size = this->emu_->read_register(real_reg.reg, full_data.data(), full_data.size());
if (written_size < *real_reg.offset)
{
return 0;
}
memcpy(full_data.data() + *real_reg.offset, data, written_size - *real_reg.offset);
this->emu_->write_register(real_reg.reg, full_data.data(), written_size);
written_size -= *real_reg.offset;
}
else
{
written_size = this->emu_->write_register(real_reg.reg, data, size);
}
return real_reg.expected_size.value_or(written_size);
}
catch (...)
{
return false;
return 0;
}
}
@@ -211,6 +239,17 @@ class x64_gdb_stub_handler : public gdb_stub::debugging_handler
this->emu_->stop();
}
std::string get_target_description(const std::string_view file) override
{
const auto entry = x64_target_descriptions.find(file);
if (entry == x64_target_descriptions.end())
{
return {};
}
return entry->second;
}
private:
x64_emulator* emu_{};

View File

@@ -0,0 +1,102 @@
#pragma once
#include <cstddef>
#include <optional>
#include <x64_register.hpp>
struct register_entry
{
x64_register reg;
std::optional<size_t> expected_size;
std::optional<size_t> offset;
register_entry(const x64_register reg = x64_register::invalid,
const std::optional<size_t> expected_size = std::nullopt,
const std::optional<size_t> offset = std::nullopt)
: reg(reg),
expected_size(expected_size),
offset(offset)
{
}
};
inline std::vector<register_entry> gdb_registers{
x64_register::rax,
x64_register::rbx,
x64_register::rcx,
x64_register::rdx,
x64_register::rsi,
x64_register::rdi,
x64_register::rbp,
x64_register::rsp,
x64_register::r8,
x64_register::r9,
x64_register::r10,
x64_register::r11,
x64_register::r12,
x64_register::r13,
x64_register::r14,
x64_register::r15,
x64_register::rip,
x64_register::eflags,
{x64_register::cs, 4},
{x64_register::ss, 4},
{x64_register::ds, 4},
{x64_register::es, 4},
{x64_register::fs, 4},
{x64_register::gs, 4},
x64_register::st0,
x64_register::st1,
x64_register::st2,
x64_register::st3,
x64_register::st4,
x64_register::st5,
x64_register::st6,
x64_register::st7,
{x64_register::fpcw, 4}, // fctrl
{x64_register::fpsw, 4}, // fstat
{x64_register::fptag, 4}, // ftag
{x64_register::fcs, 4}, // fiseg
{x64_register::fip, 4}, // fioff
{x64_register::fds, 4}, // foseg
{x64_register::fdp, 4}, // fooff
{x64_register::fop, 4}, // fop
x64_register::xmm0,
x64_register::xmm1,
x64_register::xmm2,
x64_register::xmm3,
x64_register::xmm4,
x64_register::xmm5,
x64_register::xmm6,
x64_register::xmm7,
x64_register::xmm8,
x64_register::xmm9,
x64_register::xmm10,
x64_register::xmm11,
x64_register::xmm12,
x64_register::xmm13,
x64_register::xmm14,
x64_register::xmm15,
x64_register::mxcsr,
x64_register::fs_base,
x64_register::gs_base,
{x64_register::ymm0, 16, 16},
{x64_register::ymm1, 16, 16},
{x64_register::ymm2, 16, 16},
{x64_register::ymm3, 16, 16},
{x64_register::ymm4, 16, 16},
{x64_register::ymm5, 16, 16},
{x64_register::ymm6, 16, 16},
{x64_register::ymm7, 16, 16},
{x64_register::ymm8, 16, 16},
{x64_register::ymm9, 16, 16},
{x64_register::ymm10, 16, 16},
{x64_register::ymm11, 16, 16},
{x64_register::ymm12, 16, 16},
{x64_register::ymm13, 16, 16},
{x64_register::ymm14, 16, 16},
{x64_register::ymm15, 16, 16},
};

View File

@@ -0,0 +1,215 @@
#pragma once
#include <map>
#include <string>
inline std::map<std::string, std::string, std::less<>> x64_target_descriptions{
{
"target.xml",
R"__xml__(<?xml version="1.0"?>
<!-- Copyright (C) 2010-2018 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!-- AMD64 with AVX -->
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target>
<architecture>i386:x86-64</architecture>
<xi:include href="64bit-core.xml"/>
<xi:include href="64bit-sse.xml"/>
<xi:include href="64bit-segments.xml"/>
<xi:include href="64bit-avx.xml"/>
</target>
)__xml__",
},
{
"64bit-core.xml",
R"__xml__(<?xml version="1.0"?>
<!-- Copyright (C) 2010-2018 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.i386.core">
<flags id="i386_eflags" size="4">
<field name="CF" start="0" end="0"/>
<field name="" start="1" end="1"/>
<field name="PF" start="2" end="2"/>
<field name="AF" start="4" end="4"/>
<field name="ZF" start="6" end="6"/>
<field name="SF" start="7" end="7"/>
<field name="TF" start="8" end="8"/>
<field name="IF" start="9" end="9"/>
<field name="DF" start="10" end="10"/>
<field name="OF" start="11" end="11"/>
<field name="NT" start="14" end="14"/>
<field name="RF" start="16" end="16"/>
<field name="VM" start="17" end="17"/>
<field name="AC" start="18" end="18"/>
<field name="VIF" start="19" end="19"/>
<field name="VIP" start="20" end="20"/>
<field name="ID" start="21" end="21"/>
</flags>
<reg name="rax" bitsize="64" type="int64"/>
<reg name="rbx" bitsize="64" type="int64"/>
<reg name="rcx" bitsize="64" type="int64"/>
<reg name="rdx" bitsize="64" type="int64"/>
<reg name="rsi" bitsize="64" type="int64"/>
<reg name="rdi" bitsize="64" type="int64"/>
<reg name="rbp" bitsize="64" type="data_ptr"/>
<reg name="rsp" bitsize="64" type="data_ptr"/>
<reg name="r8" bitsize="64" type="int64"/>
<reg name="r9" bitsize="64" type="int64"/>
<reg name="r10" bitsize="64" type="int64"/>
<reg name="r11" bitsize="64" type="int64"/>
<reg name="r12" bitsize="64" type="int64"/>
<reg name="r13" bitsize="64" type="int64"/>
<reg name="r14" bitsize="64" type="int64"/>
<reg name="r15" bitsize="64" type="int64"/>
<reg name="rip" bitsize="64" type="code_ptr"/>
<reg name="eflags" bitsize="32" type="i386_eflags"/>
<reg name="cs" bitsize="32" type="int32"/>
<reg name="ss" bitsize="32" type="int32"/>
<reg name="ds" bitsize="32" type="int32"/>
<reg name="es" bitsize="32" type="int32"/>
<reg name="fs" bitsize="32" type="int32"/>
<reg name="gs" bitsize="32" type="int32"/>
<reg name="st0" bitsize="80" type="i387_ext"/>
<reg name="st1" bitsize="80" type="i387_ext"/>
<reg name="st2" bitsize="80" type="i387_ext"/>
<reg name="st3" bitsize="80" type="i387_ext"/>
<reg name="st4" bitsize="80" type="i387_ext"/>
<reg name="st5" bitsize="80" type="i387_ext"/>
<reg name="st6" bitsize="80" type="i387_ext"/>
<reg name="st7" bitsize="80" type="i387_ext"/>
<reg name="fctrl" bitsize="32" type="int" group="float"/>
<reg name="fstat" bitsize="32" type="int" group="float"/>
<reg name="ftag" bitsize="32" type="int" group="float"/>
<reg name="fiseg" bitsize="32" type="int" group="float"/>
<reg name="fioff" bitsize="32" type="int" group="float"/>
<reg name="foseg" bitsize="32" type="int" group="float"/>
<reg name="fooff" bitsize="32" type="int" group="float"/>
<reg name="fop" bitsize="32" type="int" group="float"/>
</feature>
)__xml__",
},
{
"64bit-sse.xml",
R"__xml__(<?xml version="1.0"?>
<!-- Copyright (C) 2010-2018 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.i386.sse">
<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>
<flags id="i386_mxcsr" size="4">
<field name="IE" start="0" end="0"/>
<field name="DE" start="1" end="1"/>
<field name="ZE" start="2" end="2"/>
<field name="OE" start="3" end="3"/>
<field name="UE" start="4" end="4"/>
<field name="PE" start="5" end="5"/>
<field name="DAZ" start="6" end="6"/>
<field name="IM" start="7" end="7"/>
<field name="DM" start="8" end="8"/>
<field name="ZM" start="9" end="9"/>
<field name="OM" start="10" end="10"/>
<field name="UM" start="11" end="11"/>
<field name="PM" start="12" end="12"/>
<field name="FZ" start="15" end="15"/>
</flags>
<reg name="xmm0" bitsize="128" type="vec128" regnum="40"/>
<reg name="xmm1" bitsize="128" type="vec128"/>
<reg name="xmm2" bitsize="128" type="vec128"/>
<reg name="xmm3" bitsize="128" type="vec128"/>
<reg name="xmm4" bitsize="128" type="vec128"/>
<reg name="xmm5" bitsize="128" type="vec128"/>
<reg name="xmm6" bitsize="128" type="vec128"/>
<reg name="xmm7" bitsize="128" type="vec128"/>
<reg name="xmm8" bitsize="128" type="vec128"/>
<reg name="xmm9" bitsize="128" type="vec128"/>
<reg name="xmm10" bitsize="128" type="vec128"/>
<reg name="xmm11" bitsize="128" type="vec128"/>
<reg name="xmm12" bitsize="128" type="vec128"/>
<reg name="xmm13" bitsize="128" type="vec128"/>
<reg name="xmm14" bitsize="128" type="vec128"/>
<reg name="xmm15" bitsize="128" type="vec128"/>
<reg name="mxcsr" bitsize="32" type="i386_mxcsr" group="vector"/>
</feature>
)__xml__",
},
{
"64bit-segments.xml",
R"__xml__(<?xml version="1.0"?>
<!-- Copyright (C) 2016-2018 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.i386.segments">
<reg name="fs_base" bitsize="64" type="int"/>
<reg name="gs_base" bitsize="64" type="int"/>
</feature>
)__xml__",
},
{
"64bit-avx.xml",
R"__xml__(<?xml version="1.0"?>
<!-- Copyright (C) 2010-2018 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.i386.avx">
<reg name="ymm0h" bitsize="128" type="uint128"/>
<reg name="ymm1h" bitsize="128" type="uint128"/>
<reg name="ymm2h" bitsize="128" type="uint128"/>
<reg name="ymm3h" bitsize="128" type="uint128"/>
<reg name="ymm4h" bitsize="128" type="uint128"/>
<reg name="ymm5h" bitsize="128" type="uint128"/>
<reg name="ymm6h" bitsize="128" type="uint128"/>
<reg name="ymm7h" bitsize="128" type="uint128"/>
<reg name="ymm8h" bitsize="128" type="uint128"/>
<reg name="ymm9h" bitsize="128" type="uint128"/>
<reg name="ymm10h" bitsize="128" type="uint128"/>
<reg name="ymm11h" bitsize="128" type="uint128"/>
<reg name="ymm12h" bitsize="128" type="uint128"/>
<reg name="ymm13h" bitsize="128" type="uint128"/>
<reg name="ymm14h" bitsize="128" type="uint128"/>
<reg name="ymm15h" bitsize="128" type="uint128"/>
</feature>
)__xml__",
},
};