make watchpoints work using single-stepping approach
This commit is contained in:
parent
4e3544d1b8
commit
26478a3c2f
@ -75,10 +75,6 @@ int main(int argc, char **argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Watching variable: " << var_info->name << std::endl;
|
|
||||||
std::cout << "Address: 0x" << std::hex << var_info->address << std::dec << std::endl;
|
|
||||||
std::cout << "Size: " << var_info->size << " bytes" << std::endl;
|
|
||||||
std::cout << std::string(50, '-') << std::endl;
|
|
||||||
|
|
||||||
// Close DWARF resources before forking
|
// Close DWARF resources before forking
|
||||||
dwarf_finish(dbg);
|
dwarf_finish(dbg);
|
||||||
|
|||||||
@ -51,16 +51,22 @@ void Tracer::setup_hardware_watchpoint(pid_t pid, uint64_t address, size_t size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int64_t Tracer::read_memory_value(pid_t pid, uint64_t address, size_t size) {
|
int64_t Tracer::read_memory_value(pid_t pid, uint64_t address, size_t size) {
|
||||||
int64_t value = 0;
|
errno = 0;
|
||||||
|
long data = ptrace(PTRACE_PEEKDATA, pid, address, 0);
|
||||||
|
|
||||||
|
if (errno != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t value = 0;
|
||||||
if (size == 1) {
|
if (size == 1) {
|
||||||
value = ptrace(PTRACE_PEEKDATA, pid, address, 0) & 0xFF;
|
value = (int8_t)(data & 0xFF);
|
||||||
} else if (size == 2) {
|
} else if (size == 2) {
|
||||||
value = ptrace(PTRACE_PEEKDATA, pid, address, 0) & 0xFFFF;
|
value = (int16_t)(data & 0xFFFF);
|
||||||
} else if (size == 4) {
|
} else if (size == 4) {
|
||||||
value = ptrace(PTRACE_PEEKDATA, pid, address, 0) & 0xFFFFFFFF;
|
value = (int32_t)(data & 0xFFFFFFFF);
|
||||||
} else if (size == 8) {
|
} else if (size == 8) {
|
||||||
value = ptrace(PTRACE_PEEKDATA, pid, address, 0);
|
value = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
@ -95,11 +101,11 @@ uint64_t Tracer::get_load_address(pid_t pid, const std::string &exec_path) {
|
|||||||
|
|
||||||
std::filesystem::path exec_name = std::filesystem::path(exec_path).filename();
|
std::filesystem::path exec_name = std::filesystem::path(exec_path).filename();
|
||||||
std::string line;
|
std::string line;
|
||||||
|
uint64_t first_mapping = 0;
|
||||||
|
|
||||||
while (std::getline(maps_file, line)) {
|
while (std::getline(maps_file, line)) {
|
||||||
// Look for executable mapping containing the binary name
|
// Look for ANY mapping containing the binary name
|
||||||
if (line.find(exec_name.string()) != std::string::npos &&
|
if (line.find(exec_name.string()) != std::string::npos) {
|
||||||
line.find(" r-xp ") != std::string::npos) {
|
|
||||||
// Parse the address range
|
// Parse the address range
|
||||||
std::istringstream iss(line);
|
std::istringstream iss(line);
|
||||||
std::string addr_range;
|
std::string addr_range;
|
||||||
@ -109,12 +115,17 @@ uint64_t Tracer::get_load_address(pid_t pid, const std::string &exec_path) {
|
|||||||
size_t dash_pos = addr_range.find('-');
|
size_t dash_pos = addr_range.find('-');
|
||||||
if (dash_pos != std::string::npos) {
|
if (dash_pos != std::string::npos) {
|
||||||
std::string start_addr_str = addr_range.substr(0, dash_pos);
|
std::string start_addr_str = addr_range.substr(0, dash_pos);
|
||||||
return std::stoull(start_addr_str, nullptr, 16);
|
uint64_t addr = std::stoull(start_addr_str, nullptr, 16);
|
||||||
|
|
||||||
|
// Return the first mapping (lowest address) which is the base
|
||||||
|
if (first_mapping == 0 || addr < first_mapping) {
|
||||||
|
first_mapping = addr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return first_mapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tracer::watch_variable(const std::string &var_name, const std::string &exec_path,
|
void Tracer::watch_variable(const std::string &var_name, const std::string &exec_path,
|
||||||
@ -155,14 +166,11 @@ void Tracer::watch_variable(const std::string &var_name, const std::string &exec
|
|||||||
uint64_t load_addr = get_load_address(pid, exec_path);
|
uint64_t load_addr = get_load_address(pid, exec_path);
|
||||||
uint64_t runtime_addr = load_addr + address;
|
uint64_t runtime_addr = load_addr + address;
|
||||||
|
|
||||||
std::cout << std::format("Load address: 0x{:x}", load_addr) << std::endl;
|
// Initialize previous value by reading current memory
|
||||||
std::cout << std::format("Runtime address: 0x{:x}", runtime_addr) << std::endl;
|
prev_value_ = read_memory_value(pid, runtime_addr, size);
|
||||||
|
|
||||||
// Set up hardware watchpoint
|
// Use single-stepping to monitor memory accesses
|
||||||
setup_hardware_watchpoint(pid, runtime_addr, size);
|
ptrace(PTRACE_SINGLESTEP, pid, 0, 0);
|
||||||
|
|
||||||
// Continue execution and monitor for watchpoint hits
|
|
||||||
ptrace(PTRACE_CONT, pid, 0, 0);
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
waitpid(pid, &status, 0);
|
waitpid(pid, &status, 0);
|
||||||
@ -177,22 +185,22 @@ void Tracer::watch_variable(const std::string &var_name, const std::string &exec
|
|||||||
int sig = WSTOPSIG(status);
|
int sig = WSTOPSIG(status);
|
||||||
|
|
||||||
if (sig == SIGTRAP) {
|
if (sig == SIGTRAP) {
|
||||||
// Check if it's a watchpoint hit
|
// Check if memory value changed
|
||||||
uint64_t dr6 = ptrace(PTRACE_PEEKUSER, pid,
|
int64_t current_value = read_memory_value(pid, runtime_addr, size);
|
||||||
offsetof(struct user, u_debugreg[6]), 0);
|
|
||||||
|
|
||||||
if (dr6 & 0x1) { // DR0 triggered
|
if (current_value != prev_value_.value()) {
|
||||||
handle_watchpoint_hit(pid, runtime_addr, size);
|
// Value changed - report as write
|
||||||
|
std::cout << var_name_ << "\twrite\t" << prev_value_.value()
|
||||||
// Clear DR6
|
<< "->" << current_value << std::endl;
|
||||||
ptrace(PTRACE_POKEUSER, pid,
|
std::cout.flush();
|
||||||
offsetof(struct user, u_debugreg[6]), 0);
|
prev_value_ = current_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptrace(PTRACE_CONT, pid, 0, 0);
|
// Continue single-stepping
|
||||||
|
ptrace(PTRACE_SINGLESTEP, pid, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
// Forward other signals
|
// Forward other signals and continue single-stepping
|
||||||
ptrace(PTRACE_CONT, pid, 0, sig);
|
ptrace(PTRACE_SINGLESTEP, pid, 0, sig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user