disassembly and new font
This commit is contained in:
@@ -6,6 +6,8 @@
|
||||
#include <lldb/API/SBFrame.h>
|
||||
#include <lldb/API/SBValueList.h>
|
||||
#include <lldb/API/SBValue.h>
|
||||
#include <lldb/API/SBInstructionList.h>
|
||||
#include <lldb/API/SBInstruction.h>
|
||||
#include <filesystem>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
@@ -114,9 +116,11 @@ void LLDBBackend::handle_state_change(lldb::StateType state)
|
||||
|
||||
this->check_thread_changes();
|
||||
this->check_frame_changes();
|
||||
this->check_data_changes();
|
||||
|
||||
if (state == StateType::eStateStopped)
|
||||
{
|
||||
_state = TargetState::paused;
|
||||
this->send_state_change(TargetState::paused,
|
||||
StateChangeReason::initial_entry);
|
||||
return;
|
||||
@@ -131,10 +135,13 @@ void LLDBBackend::handle_state_change(lldb::StateType state)
|
||||
this->check_reg_changes();
|
||||
this->check_thread_changes();
|
||||
this->check_frame_changes();
|
||||
this->check_data_changes();
|
||||
_state = TargetState::paused;
|
||||
this->send_state_change(TargetState::paused, StateChangeReason::unknown);
|
||||
break;
|
||||
case eStateRunning:
|
||||
case eStateStepping:
|
||||
_state = TargetState::running;
|
||||
this->send_state_change(TargetState::running, StateChangeReason::unknown);
|
||||
return;
|
||||
default: printf("Unknown StateType %u encountered\n", state); exit(1);
|
||||
@@ -304,9 +311,13 @@ void LLDBBackend::dump_threads()
|
||||
stream.GetData());
|
||||
}
|
||||
|
||||
auto pc = frame.GetPC();
|
||||
|
||||
stream.Clear();
|
||||
auto sc =
|
||||
frame.GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol);
|
||||
//auto sc =
|
||||
// frame.GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol);
|
||||
auto sc = _target.ResolveSymbolContextForAddress(
|
||||
SBAddress{pc, _target}, eSymbolContextFunction | eSymbolContextSymbol);
|
||||
|
||||
uint64_t start, end;
|
||||
if (sc.GetFunction().IsValid())
|
||||
@@ -322,7 +333,7 @@ void LLDBBackend::dump_threads()
|
||||
end = sym.GetEndAddress().GetLoadAddress(_target);
|
||||
} else
|
||||
{
|
||||
start = frame.GetPC();
|
||||
start = pc;
|
||||
end = start + 0x100;
|
||||
}
|
||||
|
||||
@@ -338,6 +349,18 @@ void LLDBBackend::dump_threads()
|
||||
printf("InstList: %.*s\n", static_cast<int>(stream.GetSize()),
|
||||
stream.GetData());
|
||||
|
||||
auto inst = inst_list.GetInstructionAtIndex(0);
|
||||
const auto *comment = inst.GetComment(_target);
|
||||
if (comment && *comment != '\0')
|
||||
{
|
||||
printf("Selfprint: %s%s ; %s\n", inst.GetMnemonic(_target),
|
||||
inst.GetOperands(_target), comment);
|
||||
} else
|
||||
{
|
||||
printf("Selfprint: %s%s\n", inst.GetMnemonic(_target),
|
||||
inst.GetOperands(_target));
|
||||
}
|
||||
|
||||
//printf("Disasm: %s\n", frame.Disassemble());
|
||||
} else
|
||||
{
|
||||
@@ -360,7 +383,10 @@ bool LLDBBackend::step_into()
|
||||
}
|
||||
|
||||
// TODO: figure out what the runmodes mean
|
||||
thread.StepInto();
|
||||
// TODO: this is source step
|
||||
// thread.StepInto();
|
||||
|
||||
thread.StepInstruction(false);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -378,7 +404,10 @@ bool LLDBBackend::step_over()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
thread.StepOver();
|
||||
|
||||
//thread.StepOver();
|
||||
|
||||
thread.StepInstruction(true);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -712,6 +741,288 @@ void LLDBBackend::check_frame_changes()
|
||||
}
|
||||
}
|
||||
|
||||
void LLDBBackend::add_data_node(const data::DataNode &node)
|
||||
{
|
||||
std::lock_guard g{_data_lock};
|
||||
|
||||
if (_data_dag.nodes.contains(node.id))
|
||||
{
|
||||
printf("Double ID in DAG\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
_data_nodes.push_back(node);
|
||||
_data_dag.add_node(node.id);
|
||||
|
||||
// add child nodes
|
||||
switch (node.type)
|
||||
{
|
||||
using enum data::DataNode::Type;
|
||||
|
||||
case data_source:
|
||||
// nothing to do rn
|
||||
break;
|
||||
case disassemble:
|
||||
{
|
||||
auto src_id = std::get<data::Disassemble>(node.data).src_id;
|
||||
if (!_data_dag.nodes.contains(src_id))
|
||||
{
|
||||
printf("Invalid add sequence\n");
|
||||
exit(1);
|
||||
}
|
||||
_data_dag.add_edge(node.id, src_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_dag_linear_valid = false;
|
||||
|
||||
// TODO: just insert a dummy event?
|
||||
if (_state == TargetState::paused)
|
||||
{
|
||||
this->check_data_changes();
|
||||
}
|
||||
}
|
||||
|
||||
void LLDBBackend::remove_data_node(size_t id)
|
||||
{
|
||||
std::lock_guard g{_data_lock};
|
||||
|
||||
_data_dag.remove_node(id);
|
||||
auto it = std::find_if(_data_nodes.begin(), _data_nodes.end(),
|
||||
[id](const auto &el) { return el.id == id; });
|
||||
if (it == _data_nodes.end())
|
||||
{
|
||||
printf("Could not find node for DAG element\n");
|
||||
exit(1);
|
||||
}
|
||||
_data_nodes.erase(it);
|
||||
|
||||
auto cache_it =
|
||||
std::find_if(_cached_data_results.begin(), _cached_data_results.end(),
|
||||
[id](const auto &el) { return el.id == id; });
|
||||
if (cache_it != _cached_data_results.end())
|
||||
{
|
||||
_cached_data_results.erase(cache_it);
|
||||
}
|
||||
|
||||
_dag_linear_valid = false;
|
||||
}
|
||||
|
||||
void LLDBBackend::check_data_changes()
|
||||
{
|
||||
if (!_dag_linear_valid)
|
||||
{
|
||||
_dag_linear.clear();
|
||||
_data_dag.linearize(_dag_linear);
|
||||
_dag_linear_valid = true;
|
||||
}
|
||||
|
||||
for (auto id : _dag_linear)
|
||||
{
|
||||
const auto &node_it =
|
||||
std::find_if(_data_nodes.begin(), _data_nodes.end(),
|
||||
[id](const auto &el) { return el.id == id; });
|
||||
if (node_it == _data_nodes.end())
|
||||
{
|
||||
printf("Could not find node for DAG element\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const auto &result_it =
|
||||
std::find_if(_cached_data_results.begin(), _cached_data_results.end(),
|
||||
[id](const auto &el) { return el.id == id; });
|
||||
auto new_res = this->calc_data_res(*node_it);
|
||||
|
||||
// TODO: for disasm it would be better to check if the bytes have changed
|
||||
auto should_send = true;
|
||||
if (result_it != _cached_data_results.end())
|
||||
{
|
||||
if (new_res.success == result_it->success
|
||||
&& new_res.type == result_it->type)
|
||||
{
|
||||
if (new_res.data.size() == result_it->data.size())
|
||||
{
|
||||
if (!std::memcmp(new_res.data.data(), result_it->data.data(),
|
||||
new_res.data.size()))
|
||||
{
|
||||
should_send = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!should_send)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result_it == _cached_data_results.end())
|
||||
{
|
||||
_cached_data_results.push_back(new_res);
|
||||
} else
|
||||
{
|
||||
*result_it = new_res;
|
||||
}
|
||||
|
||||
// TODO: queue and send at once to prevent UI lag?
|
||||
this->send_data_result(
|
||||
BackToFront::DataResult{.result = std::move(new_res)});
|
||||
}
|
||||
}
|
||||
|
||||
dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
|
||||
{
|
||||
switch (node.type)
|
||||
{
|
||||
using enum data::DataNode::Type;
|
||||
|
||||
case data_source:
|
||||
{
|
||||
const auto &src_data = std::get<data::DataSource>(node.data);
|
||||
switch (src_data.type)
|
||||
{
|
||||
using enum data::DataSource::Type;
|
||||
|
||||
case reg:
|
||||
{
|
||||
const auto &info = std::get<data::DataSource::Reg>(src_data.data);
|
||||
if (info.set >= _reg_sets.size()
|
||||
|| info.idx >= _reg_sets[info.set].values.size())
|
||||
{
|
||||
return data::DataResult{
|
||||
.id = node.id,
|
||||
.success = false,
|
||||
};
|
||||
}
|
||||
// TODO: these indices *could* (very not likely) be incorrect
|
||||
// TODO: for now, pretend every register is u64
|
||||
return data::DataResult{
|
||||
.id = node.id,
|
||||
.success = true,
|
||||
.type = data::TypeInfo{.type = data::TypeInfo::Type::u64},
|
||||
.data = _reg_sets[info.set].values[info.idx]};
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case disassemble:
|
||||
{
|
||||
using namespace lldb;
|
||||
size_t addr_id = std::get<data::Disassemble>(node.data).src_id;
|
||||
const auto res_it =
|
||||
std::find_if(_cached_data_results.begin(), _cached_data_results.end(),
|
||||
[addr_id](const auto &el) { return el.id == addr_id; });
|
||||
if (res_it == _cached_data_results.end() || !res_it->success)
|
||||
{
|
||||
return data::DataResult{.id = node.id, .success = false};
|
||||
}
|
||||
|
||||
// TODO: for now only accept u64
|
||||
if (res_it->type.type != data::TypeInfo::Type::u64)
|
||||
{
|
||||
return data::DataResult{.id = node.id, .success = false};
|
||||
}
|
||||
|
||||
const auto pc = *reinterpret_cast<uint64_t *>(res_it->data.data());
|
||||
|
||||
auto sc = _target.ResolveSymbolContextForAddress(
|
||||
SBAddress{pc, _target}, eSymbolContextFunction | eSymbolContextSymbol);
|
||||
|
||||
uint64_t start, end;
|
||||
if (sc.GetFunction().IsValid())
|
||||
{
|
||||
auto fn = sc.GetFunction();
|
||||
start = fn.GetStartAddress().GetLoadAddress(_target);
|
||||
end = fn.GetEndAddress().GetLoadAddress(_target);
|
||||
} else if (sc.GetSymbol().IsValid()
|
||||
&& sc.GetSymbol().GetStartAddress().IsValid())
|
||||
{
|
||||
auto sym = sc.GetSymbol();
|
||||
start = sym.GetStartAddress().GetLoadAddress(_target);
|
||||
end = sym.GetEndAddress().GetLoadAddress(_target);
|
||||
} else
|
||||
{
|
||||
start = pc;
|
||||
end = start + 0x100;
|
||||
}
|
||||
|
||||
auto buf = std::vector<uint8_t>{};
|
||||
buf.resize(end - start);
|
||||
auto err = SBError{};
|
||||
_target.ReadMemory(SBAddress{start, _target}, buf.data(), buf.size(),
|
||||
err);
|
||||
|
||||
auto inst_list = _target.GetInstructionsWithFlavor(
|
||||
start, "intel", buf.data(), buf.size());
|
||||
|
||||
// TODO: for now the instlist is serialized in a custom format
|
||||
// use a type structure for it later?
|
||||
// it's basically an array of
|
||||
// struct Inst {
|
||||
// uint64_t addr;
|
||||
// uint8_t mnem_len;
|
||||
// uint8_t op_len;
|
||||
// uint8_t comment_len;
|
||||
// uint8_t inst_len;
|
||||
// char mnem[];
|
||||
// char op[];
|
||||
// char comment[];
|
||||
// };
|
||||
|
||||
std::vector<uint8_t> out{};
|
||||
|
||||
for (size_t i = 0; i < inst_list.GetSize(); ++i)
|
||||
{
|
||||
auto inst = inst_list.GetInstructionAtIndex(i);
|
||||
const auto mnem = std::string_view{inst.GetMnemonic(_target)};
|
||||
const auto op = std::string_view{inst.GetOperands(_target)};
|
||||
const auto comm = std::string_view{inst.GetComment(_target)};
|
||||
auto addr = inst.GetAddress().GetLoadAddress(_target);
|
||||
const auto len = inst.GetByteSize();
|
||||
|
||||
SBStream stream{};
|
||||
inst.GetDescription(stream);
|
||||
printf("Got inst: %.*s\n", stream.GetSize(), stream.GetData());
|
||||
|
||||
if (mnem.size() > 255 || op.size() > 255 || comm.size() > 255)
|
||||
{
|
||||
printf("Instruction length limits exceeded:\n");
|
||||
auto stream = SBStream{};
|
||||
inst.GetDescription(stream);
|
||||
printf("%.*s\n", static_cast<int>(stream.GetSize()),
|
||||
stream.GetData());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
size_t insert_idx = out.size();
|
||||
out.resize(out.size() + 8 + 4 + mnem.size() + op.size() + comm.size());
|
||||
*reinterpret_cast<uint64_t *>(&out[insert_idx]) = addr;
|
||||
*reinterpret_cast<uint8_t *>(&out[insert_idx + 8]) = mnem.size();
|
||||
*reinterpret_cast<uint8_t *>(&out[insert_idx + 9]) = op.size();
|
||||
*reinterpret_cast<uint8_t *>(&out[insert_idx + 10]) = comm.size();
|
||||
*reinterpret_cast<uint8_t *>(&out[insert_idx + 11]) = len;
|
||||
insert_idx += 12;
|
||||
std::copy(mnem.begin(), mnem.end(), out.begin() + insert_idx);
|
||||
insert_idx += mnem.size();
|
||||
std::copy(op.begin(), op.end(), out.begin() + insert_idx);
|
||||
insert_idx += op.size();
|
||||
std::copy(comm.begin(), comm.end(), out.begin() + insert_idx);
|
||||
}
|
||||
|
||||
return data::DataResult{
|
||||
.id = node.id,
|
||||
.success = true,
|
||||
.type = data::TypeInfo{.type = data::TypeInfo::Type::custom},
|
||||
.data = std::move(out)};
|
||||
}
|
||||
}
|
||||
|
||||
printf("Unhandled data type\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
Reg output for x64
|
||||
Got register set General Purpose Registers
|
||||
|
||||
Reference in New Issue
Block a user