mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-18 11:13:57 +00:00
Support threads with GDB (#90)
This commit is contained in:
@@ -22,6 +22,19 @@ namespace gdb_stub
|
||||
assert(condition);
|
||||
}
|
||||
|
||||
struct debugging_state
|
||||
{
|
||||
std::optional<uint32_t> continuation_thread{};
|
||||
};
|
||||
|
||||
struct debugging_context
|
||||
{
|
||||
connection_handler& connection;
|
||||
debugging_handler& handler;
|
||||
debugging_state& state;
|
||||
async_handler& async;
|
||||
};
|
||||
|
||||
network::tcp_client_socket accept_client(const network::address& bind_address)
|
||||
{
|
||||
network::tcp_server_socket server{bind_address.get_family()};
|
||||
@@ -49,79 +62,107 @@ namespace gdb_stub
|
||||
return {name, args};
|
||||
}
|
||||
|
||||
void handle_features(connection_handler& connection, debugging_handler& handler, const std::string_view payload)
|
||||
void send_xfer_data(connection_handler& connection, const std::string& args, const std::string_view data)
|
||||
{
|
||||
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);
|
||||
rt_assert(sscanf_s(args.c_str(), "%zx,%zx", &offset, &length) == 2);
|
||||
|
||||
const auto target_description = handler.get_target_description(file);
|
||||
|
||||
if (offset >= target_description.size())
|
||||
if (offset >= data.size())
|
||||
{
|
||||
connection.send_reply("l");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto remaining = target_description.size() - offset;
|
||||
const auto remaining = data.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);
|
||||
const auto sub_region = data.substr(offset, real_length);
|
||||
|
||||
connection.send_reply((is_end ? "l" : "m") + sub_region);
|
||||
std::string reply = is_end ? "l" : "m";
|
||||
reply.append(sub_region);
|
||||
|
||||
connection.send_reply(reply);
|
||||
}
|
||||
|
||||
void process_xfer(connection_handler& connection, debugging_handler& handler, const std::string_view payload)
|
||||
void handle_features(const debugging_context& c, const std::string_view payload)
|
||||
{
|
||||
const auto [command, args] = split_string(payload, ':');
|
||||
|
||||
if (command != "read")
|
||||
{
|
||||
c.connection.send_reply({});
|
||||
return;
|
||||
}
|
||||
|
||||
const auto [file, data] = split_string(args, ':');
|
||||
const auto target_description = c.handler.get_target_description(file);
|
||||
send_xfer_data(c.connection, std::string(data), target_description);
|
||||
}
|
||||
|
||||
void process_xfer(const debugging_context& c, const std::string_view payload)
|
||||
{
|
||||
auto [name, args] = split_string(payload, ':');
|
||||
|
||||
if (name == "features")
|
||||
{
|
||||
handle_features(connection, handler, args);
|
||||
handle_features(c, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
connection.send_reply({});
|
||||
c.connection.send_reply({});
|
||||
}
|
||||
}
|
||||
|
||||
void process_query(connection_handler& connection, debugging_handler& handler, const std::string_view payload)
|
||||
void process_query(const debugging_context& c, const std::string_view payload)
|
||||
{
|
||||
const auto [name, args] = split_string(payload, ':');
|
||||
|
||||
if (name == "Supported")
|
||||
{
|
||||
connection.send_reply("PacketSize=1024;qXfer:features:read+");
|
||||
c.connection.send_reply("PacketSize=1024;qXfer:features:read+");
|
||||
}
|
||||
else if (name == "Attached")
|
||||
{
|
||||
connection.send_reply("1");
|
||||
c.connection.send_reply("1");
|
||||
}
|
||||
else if (name == "Xfer")
|
||||
{
|
||||
process_xfer(connection, handler, args);
|
||||
process_xfer(c, args);
|
||||
}
|
||||
else if (name == "Symbol")
|
||||
{
|
||||
connection.send_reply("OK");
|
||||
c.connection.send_reply("OK");
|
||||
}
|
||||
else if (name == "C")
|
||||
{
|
||||
const auto thread_id = c.handler.get_current_thread_id();
|
||||
c.connection.send_reply("QC" + utils::string::to_hex_number(thread_id));
|
||||
}
|
||||
else if (name == "sThreadInfo")
|
||||
{
|
||||
c.connection.send_reply("l");
|
||||
}
|
||||
else if (name == "fThreadInfo")
|
||||
{
|
||||
std::string reply{};
|
||||
const auto ids = c.handler.get_thread_ids();
|
||||
|
||||
for (const auto id : ids)
|
||||
{
|
||||
reply.push_back(reply.empty() ? 'm' : ',');
|
||||
reply.append(utils::string::to_hex_number(id));
|
||||
}
|
||||
|
||||
c.connection.send_reply(reply);
|
||||
}
|
||||
else
|
||||
{
|
||||
connection.send_reply({});
|
||||
c.connection.send_reply({});
|
||||
}
|
||||
}
|
||||
|
||||
void process_action(connection_handler& connection, const action a)
|
||||
void process_action(const connection_handler& connection, const action a)
|
||||
{
|
||||
if (a == action::shutdown)
|
||||
{
|
||||
@@ -150,70 +191,103 @@ namespace gdb_stub
|
||||
return handler.delete_breakpoint(type, address, size);
|
||||
}
|
||||
|
||||
void handle_breakpoint(connection_handler& connection, debugging_handler& handler, const std::string& data,
|
||||
const bool set)
|
||||
void handle_breakpoint(const debugging_context& c, const std::string& data, const bool set)
|
||||
{
|
||||
uint32_t type{};
|
||||
uint64_t addr{};
|
||||
size_t kind{};
|
||||
rt_assert(sscanf_s(data.c_str(), "%x,%" PRIX64 ",%zx", &type, &addr, &kind) == 3);
|
||||
|
||||
const auto res = change_breakpoint(handler, set, translate_breakpoint_type(type), addr, kind);
|
||||
connection.send_reply(res ? "OK" : "E01");
|
||||
const auto res = change_breakpoint(c.handler, set, translate_breakpoint_type(type), addr, kind);
|
||||
c.connection.send_reply(res ? "OK" : "E01");
|
||||
}
|
||||
|
||||
void continue_execution(connection_handler& connection, async_handler& async, debugging_handler& handler)
|
||||
void signal_stop(const debugging_context& c)
|
||||
{
|
||||
async.run();
|
||||
process_action(connection, handler.run());
|
||||
async.pause();
|
||||
connection.send_reply("S05");
|
||||
const auto id = c.handler.get_current_thread_id();
|
||||
const auto hex_id = utils::string::to_hex_number(id);
|
||||
c.connection.send_reply("T05thread:" + hex_id + ";");
|
||||
}
|
||||
|
||||
void singlestep_execution(connection_handler& connection, debugging_handler& handler)
|
||||
void apply_continuation_thread(const debugging_context& c)
|
||||
{
|
||||
process_action(connection, handler.singlestep());
|
||||
connection.send_reply("S05");
|
||||
if (c.state.continuation_thread)
|
||||
{
|
||||
c.handler.switch_to_thread(*c.state.continuation_thread);
|
||||
c.state.continuation_thread = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
void handle_v_packet(connection_handler& connection, async_handler& async, debugging_handler& handler,
|
||||
const std::string_view data)
|
||||
void resume_execution(const debugging_context& c, const bool single_step)
|
||||
{
|
||||
apply_continuation_thread(c);
|
||||
|
||||
action a{};
|
||||
|
||||
if (single_step)
|
||||
{
|
||||
a = c.handler.singlestep();
|
||||
}
|
||||
else
|
||||
{
|
||||
c.async.run();
|
||||
a = c.handler.run();
|
||||
c.async.pause();
|
||||
}
|
||||
|
||||
process_action(c.connection, a);
|
||||
signal_stop(c);
|
||||
}
|
||||
|
||||
void store_continuation_thread(const debugging_context& c, const std::string_view thread_string)
|
||||
{
|
||||
if (thread_string.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t thread_id{};
|
||||
rt_assert(sscanf_s(std::string(thread_string).c_str(), "%x", &thread_id) == 1);
|
||||
c.state.continuation_thread = thread_id;
|
||||
}
|
||||
|
||||
void handle_v_packet(const debugging_context& c, const std::string_view data)
|
||||
{
|
||||
const auto [name, args] = split_string(data, ':');
|
||||
|
||||
if (name == "Cont?")
|
||||
{
|
||||
connection.send_reply("vCont;s;c");
|
||||
c.connection.send_reply("vCont;s;c");
|
||||
}
|
||||
else if (name == "Cont;s")
|
||||
else if (name == "Cont;s" || name == "Cont;c")
|
||||
{
|
||||
singlestep_execution(connection, handler);
|
||||
}
|
||||
else if (name == "Cont;c")
|
||||
{
|
||||
continue_execution(connection, async, handler);
|
||||
const auto singlestep = name[5] == 's';
|
||||
const auto [thread, _] = split_string(args, ':');
|
||||
|
||||
store_continuation_thread(c, thread);
|
||||
resume_execution(c, singlestep);
|
||||
}
|
||||
else
|
||||
{
|
||||
connection.send_reply({});
|
||||
c.connection.send_reply({});
|
||||
}
|
||||
}
|
||||
|
||||
void read_registers(connection_handler& connection, debugging_handler& handler)
|
||||
void read_registers(const debugging_context& c)
|
||||
{
|
||||
std::string response{};
|
||||
std::vector<std::byte> data{};
|
||||
data.resize(handler.get_max_register_size());
|
||||
data.resize(c.handler.get_max_register_size());
|
||||
|
||||
const auto registers = handler.get_register_count();
|
||||
const auto registers = c.handler.get_register_count();
|
||||
|
||||
for (size_t i = 0; i < registers; ++i)
|
||||
{
|
||||
const auto size = handler.read_register(i, data.data(), data.size());
|
||||
const auto size = c.handler.read_register(i, data.data(), data.size());
|
||||
|
||||
if (!size)
|
||||
{
|
||||
connection.send_reply("E01");
|
||||
c.connection.send_reply("E01");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -221,64 +295,62 @@ namespace gdb_stub
|
||||
response.append(utils::string::to_hex_string(register_data));
|
||||
}
|
||||
|
||||
connection.send_reply(response);
|
||||
c.connection.send_reply(response);
|
||||
}
|
||||
|
||||
void write_registers(connection_handler& connection, debugging_handler& handler, const std::string_view payload)
|
||||
void write_registers(const debugging_context& c, const std::string_view payload)
|
||||
{
|
||||
const auto data = utils::string::from_hex_string(payload);
|
||||
|
||||
const auto registers = handler.get_register_count();
|
||||
const auto register_size = handler.get_max_register_size();
|
||||
const auto registers = c.handler.get_register_count();
|
||||
const auto register_size = c.handler.get_max_register_size();
|
||||
|
||||
size_t offset = 0;
|
||||
for (size_t i = 0; i < registers; ++i)
|
||||
{
|
||||
if (offset >= data.size())
|
||||
{
|
||||
connection.send_reply("E01");
|
||||
c.connection.send_reply("E01");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto max_size = std::min(register_size, data.size() - offset);
|
||||
const auto size = handler.write_register(i, data.data() + offset, max_size);
|
||||
const auto size = c.handler.write_register(i, data.data() + offset, max_size);
|
||||
|
||||
offset += size;
|
||||
|
||||
if (!size)
|
||||
{
|
||||
connection.send_reply("E01");
|
||||
c.connection.send_reply("E01");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
connection.send_reply("OK");
|
||||
c.connection.send_reply("OK");
|
||||
}
|
||||
|
||||
void read_single_register(connection_handler& connection, debugging_handler& handler,
|
||||
const std::string& payload)
|
||||
void read_single_register(const debugging_context& c, const std::string& payload)
|
||||
{
|
||||
size_t reg{};
|
||||
rt_assert(sscanf_s(payload.c_str(), "%zx", ®) == 1);
|
||||
|
||||
std::vector<std::byte> data{};
|
||||
data.resize(handler.get_max_register_size());
|
||||
data.resize(c.handler.get_max_register_size());
|
||||
|
||||
const auto size = handler.read_register(reg, data.data(), data.size());
|
||||
const auto size = c.handler.read_register(reg, data.data(), data.size());
|
||||
|
||||
if (size)
|
||||
{
|
||||
const std::span register_data(data.data(), size);
|
||||
connection.send_reply(utils::string::to_hex_string(register_data));
|
||||
c.connection.send_reply(utils::string::to_hex_string(register_data));
|
||||
}
|
||||
else
|
||||
{
|
||||
connection.send_reply("E01");
|
||||
c.connection.send_reply("E01");
|
||||
}
|
||||
}
|
||||
|
||||
void write_single_register(connection_handler& connection, debugging_handler& handler,
|
||||
const std::string_view payload)
|
||||
void write_single_register(const debugging_context& c, const std::string_view payload)
|
||||
{
|
||||
const auto [reg, hex_data] = split_string(payload, '=');
|
||||
|
||||
@@ -286,11 +358,11 @@ namespace gdb_stub
|
||||
rt_assert(sscanf_s(std::string(reg).c_str(), "%zx", ®ister_index) == 1);
|
||||
|
||||
const auto data = utils::string::from_hex_string(hex_data);
|
||||
const auto res = handler.write_register(register_index, data.data(), data.size()) > 0;
|
||||
connection.send_reply(res ? "OK" : "E01");
|
||||
const auto res = c.handler.write_register(register_index, data.data(), data.size()) > 0;
|
||||
c.connection.send_reply(res ? "OK" : "E01");
|
||||
}
|
||||
|
||||
void read_memory(connection_handler& connection, debugging_handler& handler, const std::string& payload)
|
||||
void read_memory(const debugging_context& c, const std::string& payload)
|
||||
{
|
||||
uint64_t address{};
|
||||
size_t size{};
|
||||
@@ -298,24 +370,24 @@ namespace gdb_stub
|
||||
|
||||
if (size > 0x1000)
|
||||
{
|
||||
connection.send_reply("E01");
|
||||
c.connection.send_reply("E01");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::byte> data{};
|
||||
data.resize(size);
|
||||
|
||||
const auto res = handler.read_memory(address, data.data(), data.size());
|
||||
const auto res = c.handler.read_memory(address, data.data(), data.size());
|
||||
if (!res)
|
||||
{
|
||||
connection.send_reply("E01");
|
||||
c.connection.send_reply("E01");
|
||||
return;
|
||||
}
|
||||
|
||||
connection.send_reply(utils::string::to_hex_string(data));
|
||||
c.connection.send_reply(utils::string::to_hex_string(data));
|
||||
}
|
||||
|
||||
void write_memory(connection_handler& connection, debugging_handler& handler, const std::string_view payload)
|
||||
void write_memory(const debugging_context& c, const std::string_view payload)
|
||||
{
|
||||
const auto [info, hex_data] = split_string(payload, ':');
|
||||
|
||||
@@ -325,15 +397,15 @@ namespace gdb_stub
|
||||
|
||||
if (size > 0x1000)
|
||||
{
|
||||
connection.send_reply("E01");
|
||||
c.connection.send_reply("E01");
|
||||
return;
|
||||
}
|
||||
|
||||
auto data = utils::string::from_hex_string(hex_data);
|
||||
data.resize(size);
|
||||
|
||||
const auto res = handler.write_memory(address, data.data(), data.size());
|
||||
connection.send_reply(res ? "OK" : "E01");
|
||||
const auto res = c.handler.write_memory(address, data.data(), data.size());
|
||||
c.connection.send_reply(res ? "OK" : "E01");
|
||||
}
|
||||
|
||||
std::string decode_x_memory(const std::string_view payload)
|
||||
@@ -362,7 +434,7 @@ namespace gdb_stub
|
||||
return result;
|
||||
}
|
||||
|
||||
void write_x_memory(connection_handler& connection, debugging_handler& handler, const std::string_view payload)
|
||||
void write_x_memory(const debugging_context& c, const std::string_view payload)
|
||||
{
|
||||
const auto [info, encoded_data] = split_string(payload, ':');
|
||||
|
||||
@@ -372,97 +444,127 @@ namespace gdb_stub
|
||||
|
||||
if (size > 0x1000)
|
||||
{
|
||||
connection.send_reply("E01");
|
||||
c.connection.send_reply("E01");
|
||||
return;
|
||||
}
|
||||
|
||||
auto data = decode_x_memory(encoded_data);
|
||||
data.resize(size);
|
||||
|
||||
const auto res = handler.write_memory(address, data.data(), data.size());
|
||||
const auto res = c.handler.write_memory(address, data.data(), data.size());
|
||||
if (!res)
|
||||
{
|
||||
connection.send_reply("E01");
|
||||
c.connection.send_reply("E01");
|
||||
return;
|
||||
}
|
||||
|
||||
connection.send_reply("OK");
|
||||
c.connection.send_reply("OK");
|
||||
}
|
||||
|
||||
void handle_command(connection_handler& connection, async_handler& async, debugging_handler& handler,
|
||||
const uint8_t command, const std::string_view data)
|
||||
void switch_to_thread(const debugging_context& c, const std::string_view payload)
|
||||
{
|
||||
// printf("GDB command: %c -> %.*s\n", command, static_cast<int>(data.size()), data.data());
|
||||
if (payload.size() < 2)
|
||||
{
|
||||
c.connection.send_reply({});
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t id{};
|
||||
rt_assert(sscanf_s(std::string(payload.substr(1)).c_str(), "%x", &id) == 1);
|
||||
|
||||
const auto operation = payload[0];
|
||||
if (operation == 'c')
|
||||
{
|
||||
c.state.continuation_thread = id;
|
||||
c.connection.send_reply("OK");
|
||||
}
|
||||
else if (operation == 'g')
|
||||
{
|
||||
const auto res = id == 0 || c.handler.switch_to_thread(id);
|
||||
c.connection.send_reply(res ? "OK" : "E01");
|
||||
}
|
||||
else
|
||||
{
|
||||
c.connection.send_reply({});
|
||||
}
|
||||
}
|
||||
|
||||
void handle_command(const debugging_context& c, const uint8_t command, const std::string_view data)
|
||||
{
|
||||
printf("GDB command: %c -> %.*s\n", command, static_cast<int>(data.size()), data.data());
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case 'c':
|
||||
continue_execution(connection, async, handler);
|
||||
resume_execution(c, false);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
singlestep_execution(connection, handler);
|
||||
resume_execution(c, true);
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
process_query(connection, handler, data);
|
||||
process_query(c, data);
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
connection.close();
|
||||
c.connection.close();
|
||||
break;
|
||||
|
||||
case 'z':
|
||||
case 'Z':
|
||||
handle_breakpoint(connection, handler, std::string(data), command == 'Z');
|
||||
handle_breakpoint(c, std::string(data), command == 'Z');
|
||||
break;
|
||||
|
||||
case '?':
|
||||
connection.send_reply("S05");
|
||||
signal_stop(c);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
handle_v_packet(connection, async, handler, data);
|
||||
handle_v_packet(c, data);
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
read_registers(connection, handler);
|
||||
read_registers(c);
|
||||
break;
|
||||
|
||||
case 'G':
|
||||
write_registers(connection, handler, data);
|
||||
write_registers(c, data);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
read_single_register(connection, handler, std::string(data));
|
||||
read_single_register(c, std::string(data));
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
write_single_register(connection, handler, data);
|
||||
write_single_register(c, data);
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
read_memory(connection, handler, std::string(data));
|
||||
read_memory(c, std::string(data));
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
write_memory(connection, handler, data);
|
||||
write_memory(c, data);
|
||||
break;
|
||||
|
||||
case 'X':
|
||||
write_x_memory(connection, handler, data);
|
||||
write_x_memory(c, data);
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
switch_to_thread(c, data);
|
||||
break;
|
||||
|
||||
default:
|
||||
connection.send_reply({});
|
||||
c.connection.send_reply({});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void process_packet(connection_handler& connection, async_handler& async, debugging_handler& handler,
|
||||
const std::string_view packet)
|
||||
void process_packet(const debugging_context& c, const std::string_view packet)
|
||||
{
|
||||
connection.send_raw_data("+");
|
||||
c.connection.send_raw_data("+");
|
||||
|
||||
if (packet.empty())
|
||||
{
|
||||
@@ -470,7 +572,7 @@ namespace gdb_stub
|
||||
}
|
||||
|
||||
const auto command = packet.front();
|
||||
handle_command(connection, async, handler, command, packet.substr(1));
|
||||
handle_command(c, command, packet.substr(1));
|
||||
}
|
||||
|
||||
bool is_interrupt_packet(const std::optional<std::string>& data)
|
||||
@@ -502,8 +604,16 @@ namespace gdb_stub
|
||||
}
|
||||
}};
|
||||
|
||||
debugging_state state{};
|
||||
connection_handler connection{client};
|
||||
|
||||
debugging_context c{
|
||||
.connection = connection,
|
||||
.handler = handler,
|
||||
.state = state,
|
||||
.async = async,
|
||||
};
|
||||
|
||||
while (true)
|
||||
{
|
||||
const auto packet = connection.get_packet();
|
||||
@@ -512,7 +622,7 @@ namespace gdb_stub
|
||||
break;
|
||||
}
|
||||
|
||||
process_packet(connection, async, handler, *packet);
|
||||
process_packet(c, *packet);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -43,6 +43,11 @@ namespace gdb_stub
|
||||
virtual void on_interrupt() = 0;
|
||||
|
||||
virtual std::string get_target_description(std::string_view file) = 0;
|
||||
|
||||
virtual bool switch_to_thread(uint32_t thread_id) = 0;
|
||||
|
||||
virtual uint32_t get_current_thread_id() = 0;
|
||||
virtual std::vector<uint32_t> get_thread_ids() = 0;
|
||||
};
|
||||
|
||||
bool run_gdb_stub(const network::address& bind_address, debugging_handler& handler);
|
||||
|
||||
@@ -40,6 +40,34 @@ class win_x64_gdb_stub_handler : public x64_gdb_stub_handler
|
||||
return gdb_stub::action::resume;
|
||||
}
|
||||
|
||||
uint32_t get_current_thread_id() override
|
||||
{
|
||||
return this->win_emu_->current_thread().id;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> get_thread_ids() override
|
||||
{
|
||||
const auto& threads = this->win_emu_->process().threads;
|
||||
|
||||
std::vector<uint32_t> ids{};
|
||||
ids.reserve(threads.size());
|
||||
|
||||
for (const auto& t : threads | std::views::values)
|
||||
{
|
||||
if (!t.is_terminated())
|
||||
{
|
||||
ids.push_back(t.id);
|
||||
}
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
bool switch_to_thread(const uint32_t thread_id) override
|
||||
{
|
||||
return this->win_emu_->activate_thread(thread_id);
|
||||
}
|
||||
|
||||
private:
|
||||
windows_emulator* win_emu_{};
|
||||
};
|
||||
|
||||
@@ -250,6 +250,16 @@ class x64_gdb_stub_handler : public gdb_stub::debugging_handler
|
||||
return entry->second;
|
||||
}
|
||||
|
||||
uint32_t get_current_thread_id() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> get_thread_ids() override
|
||||
{
|
||||
return {this->get_current_thread_id()};
|
||||
}
|
||||
|
||||
private:
|
||||
x64_emulator* emu_{};
|
||||
|
||||
|
||||
@@ -525,14 +525,39 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
bool switch_to_thread(windows_emulator& win_emu, emulator_thread& thread)
|
||||
emulator_thread* get_thread_by_id(process_context& process, const uint32_t id)
|
||||
{
|
||||
for (auto& t : process.threads | std::views::values)
|
||||
{
|
||||
if (t.id == id)
|
||||
{
|
||||
return &t;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool switch_to_thread(windows_emulator& win_emu, emulator_thread& thread, const bool force = false)
|
||||
{
|
||||
if (thread.is_terminated())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& emu = win_emu.emu();
|
||||
auto& context = win_emu.process();
|
||||
|
||||
if (!thread.is_thread_ready(win_emu))
|
||||
const auto is_ready = thread.is_thread_ready(win_emu);
|
||||
|
||||
if (!is_ready)
|
||||
{
|
||||
return false;
|
||||
if (!force)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
win_emu.yield_thread();
|
||||
}
|
||||
|
||||
auto* active_thread = context.active_thread;
|
||||
@@ -553,7 +578,6 @@ namespace
|
||||
|
||||
thread.restore(emu);
|
||||
thread.setup_if_necessary(emu, context);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -863,6 +887,17 @@ void windows_emulator::perform_thread_switch()
|
||||
}
|
||||
}
|
||||
|
||||
bool windows_emulator::activate_thread(const uint32_t id)
|
||||
{
|
||||
const auto thread = get_thread_by_id(this->process(), id);
|
||||
if (!thread)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return switch_to_thread(*this, *thread, true);
|
||||
}
|
||||
|
||||
void windows_emulator::on_instruction_execution(const uint64_t address)
|
||||
{
|
||||
auto& process = this->process();
|
||||
|
||||
@@ -124,6 +124,7 @@ class windows_emulator
|
||||
|
||||
void yield_thread();
|
||||
void perform_thread_switch();
|
||||
bool activate_thread(uint32_t id);
|
||||
|
||||
bool time_is_relative() const
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user