Skip to content

Commit cd094c5

Browse files
Merge pull request #22 from wcampbell0x2a/highlight-register-regions
2 parents dafd953 + a832beb commit cd094c5

File tree

2 files changed

+144
-16
lines changed

2 files changed

+144
-16
lines changed

src/main.rs

+90-16
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::fs::File;
22
use std::io::{BufRead, BufReader, Read, Write};
33
use std::net::{SocketAddr, TcpStream};
44
use std::process::{Command, Stdio};
5+
use std::str::FromStr;
56
use std::sync::{Arc, Mutex};
67
use std::thread;
78
use std::time::Duration;
@@ -29,8 +30,9 @@ use Constraint::{Fill, Length, Min};
2930
mod mi;
3031
use mi::{
3132
data_disassemble, data_read_sp_bytes, join_registers, parse_asm_insns_values,
32-
parse_key_value_pairs, parse_register_names_values, parse_register_values, read_pc_value, Asm,
33-
MIResponse, Register,
33+
parse_key_value_pairs, parse_memory_mappings, parse_register_names_values,
34+
parse_register_values, read_pc_value, Asm, MIResponse, MemoryMapping, Register,
35+
MEMORY_MAP_START_STR,
3436
};
3537

3638
enum InputMode {
@@ -108,6 +110,7 @@ struct App {
108110
input: Input,
109111
input_mode: InputMode,
110112
messages: LimitedBuffer<String>,
113+
memory_map: Arc<Mutex<Option<Vec<MemoryMapping>>>>,
111114
current_pc: Arc<Mutex<u64>>, // TODO: replace with AtomicU64?
112115
output_scroll: usize,
113116
output: Arc<Mutex<Vec<String>>>,
@@ -166,6 +169,7 @@ impl App {
166169
messages: LimitedBuffer::new(10),
167170
current_pc: Arc::new(Mutex::new(0)),
168171
output_scroll: 0,
172+
memory_map: Arc::new(Mutex::new(None)),
169173
output: Arc::new(Mutex::new(Vec::new())),
170174
stream_output_prompt: Arc::new(Mutex::new(String::new())),
171175
register_changed: Arc::new(Mutex::new(vec![])),
@@ -213,6 +217,7 @@ fn main() -> Result<(), Box<dyn Error>> {
213217
let register_changed_arc = Arc::clone(&app.register_changed);
214218
let register_names_arc = Arc::clone(&app.register_names);
215219
let registers_arc = Arc::clone(&app.registers);
220+
let memory_map_arc = Arc::clone(&app.memory_map);
216221
let stack_arc = Arc::clone(&app.stack);
217222
let asm_arc = Arc::clone(&app.asm);
218223

@@ -229,6 +234,7 @@ fn main() -> Result<(), Box<dyn Error>> {
229234
gdb_stdin_arc,
230235
output_arc,
231236
stream_output_prompt_arc,
237+
memory_map_arc,
232238
)
233239
});
234240

@@ -258,7 +264,9 @@ fn gdb_interact(
258264
gdb_stdin_arc: Arc<Mutex<dyn Write + Send>>,
259265
output_arc: Arc<Mutex<Vec<String>>>,
260266
stream_output_prompt_arc: Arc<Mutex<String>>,
267+
memory_map_arc: Arc<Mutex<Option<Vec<MemoryMapping>>>>,
261268
) {
269+
let mut current_map = (false, String::new());
262270
let mut next_write = vec![String::new()];
263271
for line in gdb_stdout.lines() {
264272
if let Ok(line) = line {
@@ -278,7 +286,11 @@ fn gdb_interact(
278286
next_write.push("-data-list-register-names".to_string());
279287
// When a breakpoint is hit, query for register values
280288
next_write.push("-data-list-register-values x".to_string());
289+
// get a list of changed registers
281290
next_write.push("-data-list-changed-registers".to_string());
291+
// get the memory mapping
292+
next_write
293+
.push(r#"-interpreter-exec console "info proc mappings""#.to_string());
282294
}
283295
}
284296
MIResponse::ExecResult(status, kv) => {
@@ -299,6 +311,16 @@ fn gdb_interact(
299311
let mut regs = registers_arc.lock().unwrap();
300312
regs.clear();
301313
}
314+
if status == "done" {
315+
// Check if we were looking for a mapping
316+
// TODO: This should be an enum or something?
317+
if current_map.0 {
318+
let m = parse_memory_mappings(&current_map.1);
319+
let mut memory_map = memory_map_arc.lock().unwrap();
320+
*memory_map = Some(m);
321+
current_map = (false, String::new());
322+
}
323+
}
302324

303325
if let Some(value) = kv.get("value") {
304326
// This works b/c we only use this for PC, but will most likely
@@ -375,8 +397,23 @@ fn gdb_interact(
375397
}
376398
}
377399
MIResponse::StreamOutput(t, s) => {
378-
// let split: Vec<String> = s.to_string().split('\n').collect();
379-
let mut split: Vec<String> =
400+
// when we find the start of a memory map, we sent this
401+
// and it's quite noisy to the regular output so don't
402+
// include
403+
if s.starts_with("process") || s.starts_with("Mapped address spaces:") {
404+
// HACK: completely skip the following, as they are a side
405+
// effect of not having a GDB MI way of getting a memory map
406+
continue;
407+
}
408+
if s.trim_end() == MEMORY_MAP_START_STR {
409+
current_map.0 = true;
410+
}
411+
if current_map.0 {
412+
current_map.1.push_str(s);
413+
continue;
414+
}
415+
416+
let split: Vec<String> =
380417
s.split('\n').map(String::from).map(|a| a.trim_end().to_string()).collect();
381418
for s in split {
382419
if !s.is_empty() {
@@ -401,16 +438,20 @@ fn gdb_interact(
401438
}
402439
if !next_write.is_empty() {
403440
for w in &next_write {
404-
let mut stdin = gdb_stdin_arc.lock().unwrap();
405-
debug!("writing {}", w);
406-
writeln!(stdin, "{}", w).expect("Failed to send command");
441+
write_mi(&gdb_stdin_arc, w);
407442
}
408443
next_write.clear();
409444
}
410445
}
411446
}
412447
}
413448

449+
fn write_mi(gdb_stdin_arc: &Arc<Mutex<dyn Write + Send>>, w: &String) {
450+
let mut stdin = gdb_stdin_arc.lock().unwrap();
451+
debug!("writing {}", w);
452+
writeln!(stdin, "{}", w).expect("Failed to send command");
453+
}
454+
414455
fn run_app<B: Backend>(terminal: &mut Terminal<B>, app: &mut App) -> io::Result<()> {
415456
loop {
416457
terminal.draw(|f| ui(f, app))?;
@@ -841,16 +882,49 @@ fn draw_registers(app: &App, f: &mut Frame, register: Rect) {
841882
if let Ok(regs) = app.registers.lock() {
842883
for (i, (name, register)) in regs.iter().enumerate() {
843884
if let Some(reg) = register {
844-
if reg.value == Some("<unavailable>".to_string()) {
845-
continue;
846-
}
847-
let changed = reg_changed_lock.contains(&(i as u8));
848-
let mut addr = Cell::from(name.to_string()).style(Style::new().fg(PURPLE));
849-
let val = Cell::from(reg.value.clone().unwrap());
850-
if changed {
851-
addr = addr.style(Style::new().fg(RED));
885+
if let Some(reg_value) = &reg.value {
886+
if reg_value == &"<unavailable>".to_string() {
887+
continue;
888+
}
889+
let val = u64::from_str_radix(&reg_value[2..], 16).unwrap();
890+
let changed = reg_changed_lock.contains(&(i as u8));
891+
let mut addr = Cell::from(name.to_string()).style(Style::new().fg(PURPLE));
892+
893+
let mut is_stack = false;
894+
let mut is_heap = false;
895+
if val != 0 {
896+
// look through, add see if the value is part of the stack
897+
let memory_map = app.memory_map.lock().unwrap();
898+
if memory_map.is_some() {
899+
for r in memory_map.as_ref().unwrap() {
900+
if r.is_stack() {
901+
if r.contains(val) {
902+
is_stack = true;
903+
break;
904+
}
905+
}
906+
if r.is_heap() {
907+
if r.contains(val) {
908+
is_heap = true;
909+
break;
910+
}
911+
}
912+
}
913+
}
914+
}
915+
916+
let mut val = Cell::from(reg.value.clone().unwrap());
917+
if is_stack {
918+
val = val.style(Style::new().fg(BLUE))
919+
}
920+
if is_heap {
921+
val = val.style(Style::new().fg(PURPLE))
922+
}
923+
if changed {
924+
addr = addr.style(Style::new().fg(RED));
925+
}
926+
rows.push(Row::new(vec![addr, val]));
852927
}
853-
rows.push(Row::new(vec![addr, val]));
854928
}
855929
}
856930
}

src/mi.rs

+54
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,63 @@
11
use std::borrow::Cow;
22
use std::collections::HashMap;
3+
use std::str::FromStr;
34

45
use log::debug;
56
use regex::Regex;
67

8+
pub const MEMORY_MAP_START_STR: &str =
9+
" Start Addr End Addr Size Offset Perms objfile";
10+
11+
#[derive(Debug, Clone)]
12+
pub struct MemoryMapping {
13+
start_address: u64,
14+
end_address: u64,
15+
size: u64,
16+
offset: u64,
17+
permissions: String,
18+
path: String,
19+
}
20+
21+
impl MemoryMapping {
22+
pub fn is_stack(&self) -> bool {
23+
self.path == "[stack]"
24+
}
25+
26+
pub fn is_heap(&self) -> bool {
27+
self.path == "[heap]"
28+
}
29+
30+
pub fn contains(&self, addr: u64) -> bool {
31+
(addr > self.start_address) && (addr < self.end_address)
32+
}
33+
}
34+
35+
impl FromStr for MemoryMapping {
36+
type Err = String;
37+
38+
fn from_str(line: &str) -> Result<Self, Self::Err> {
39+
let parts: Vec<&str> = line.split_whitespace().collect();
40+
if parts.len() < 6 {
41+
return Err(format!("Invalid line format: {}", line));
42+
}
43+
44+
Ok(MemoryMapping {
45+
start_address: u64::from_str_radix(&parts[0][2..], 16)
46+
.map_err(|_| "Invalid start address")?,
47+
end_address: u64::from_str_radix(&parts[1][2..], 16)
48+
.map_err(|_| "Invalid end address")?,
49+
size: u64::from_str_radix(&parts[2][2..], 16).map_err(|_| "Invalid size")?,
50+
offset: u64::from_str_radix(&parts[3][2..], 16).map_err(|_| "Invalid offset")?,
51+
permissions: parts[4].to_string(),
52+
path: parts[5..].join(" "), // Combine the rest as the path
53+
})
54+
}
55+
}
56+
57+
pub fn parse_memory_mappings(input: &str) -> Vec<MemoryMapping> {
58+
input.lines().skip(1).filter_map(|line| line.parse::<MemoryMapping>().ok()).collect()
59+
}
60+
761
// Define Register struct to hold register data
862
#[derive(Debug, Clone)]
963
pub struct Register {

0 commit comments

Comments
 (0)