Skip to main content

bugstalker/debugger/
register.rs

1use crate::debugger::error::Error;
2use crate::debugger::error::Error::{Ptrace, RegisterNotFound};
3use gimli::Register as DwarfRegister;
4use nix::libc::user_regs_struct;
5use nix::sys;
6use nix::unistd::Pid;
7use smallvec::{SmallVec, smallvec};
8use strum_macros::Display;
9use strum_macros::EnumString;
10
11/// x86_64 registers.
12#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, EnumString, Display)]
13#[strum(serialize_all = "snake_case")]
14pub enum Register {
15    Rax,
16    Rbx,
17    Rcx,
18    Rdx,
19    Rdi,
20    Rsi,
21    Rbp,
22    Rsp,
23    R8,
24    R9,
25    R10,
26    R11,
27    R12,
28    R13,
29    R14,
30    R15,
31    Rip,
32    Eflags,
33    Cs,
34    OrigRax,
35    FsBase,
36    GsBase,
37    Fs,
38    Gs,
39    Ss,
40    Ds,
41    Es,
42}
43
44impl Register {
45    pub fn dwarf_register(self) -> Option<DwarfRegister> {
46        let register = match self {
47            Register::Rax => 0,
48            Register::Rdx => 1,
49            Register::Rcx => 2,
50            Register::Rbx => 3,
51            Register::Rsi => 4,
52            Register::Rdi => 5,
53            Register::Rbp => 6,
54            Register::Rsp => 7,
55            Register::R8 => 8,
56            Register::R9 => 9,
57            Register::R10 => 10,
58            Register::R11 => 11,
59            Register::R12 => 12,
60            Register::R13 => 13,
61            Register::R14 => 14,
62            Register::R15 => 15,
63            Register::Rip => 16,
64            Register::Eflags => 49,
65            Register::Es => 50,
66            Register::Cs => 51,
67            Register::Ss => 52,
68            Register::Ds => 53,
69            Register::Fs => 54,
70            Register::Gs => 55,
71            Register::FsBase => 58,
72            Register::GsBase => 59,
73            Register::OrigRax => return None,
74        };
75        Some(DwarfRegister(register))
76    }
77}
78
79impl From<gimli::Register> for Register {
80    fn from(value: gimli::Register) -> Self {
81        match value.0 as i32 {
82            -1 => Register::Rip,
83            //-1 => Register::OrigRax,
84            0 => Register::Rax,
85            1 => Register::Rdx,
86            2 => Register::Rcx,
87            3 => Register::Rbx,
88            4 => Register::Rsi,
89            5 => Register::Rdi,
90            6 => Register::Rbp,
91            7 => Register::Rsp,
92            8 => Register::R8,
93            9 => Register::R9,
94            10 => Register::R10,
95            11 => Register::R11,
96            12 => Register::R12,
97            13 => Register::R13,
98            14 => Register::R14,
99            15 => Register::R15,
100            49 => Register::Eflags,
101            50 => Register::Es,
102            51 => Register::Cs,
103            52 => Register::Ss,
104            53 => Register::Ds,
105            54 => Register::Fs,
106            55 => Register::Gs,
107            58 => Register::FsBase,
108            59 => Register::GsBase,
109            _ => {
110                panic!("unknown dwarf register number");
111            }
112        }
113    }
114}
115
116/// x86_64 register values.
117#[derive(Debug, Clone)]
118pub struct RegisterMap {
119    rax: u64,
120    rbx: u64,
121    rcx: u64,
122    rdx: u64,
123    rdi: u64,
124    rsi: u64,
125    rbp: u64,
126    rsp: u64,
127    r8: u64,
128    r9: u64,
129    r10: u64,
130    r11: u64,
131    r12: u64,
132    r13: u64,
133    r14: u64,
134    r15: u64,
135    rip: u64,
136    eflags: u64,
137    cs: u64,
138    orig_rax: u64,
139    fs_base: u64,
140    gs_base: u64,
141    fs: u64,
142    gs: u64,
143    ss: u64,
144    ds: u64,
145    es: u64,
146}
147
148impl From<user_regs_struct> for RegisterMap {
149    fn from(value: user_regs_struct) -> Self {
150        Self {
151            rax: value.rax,
152            rbx: value.rbx,
153            rcx: value.rcx,
154            rdx: value.rdx,
155            rdi: value.rdi,
156            rsi: value.rsi,
157            rbp: value.rbp,
158            rsp: value.rsp,
159            r8: value.r8,
160            r9: value.r9,
161            r10: value.r10,
162            r11: value.r11,
163            r12: value.r12,
164            r13: value.r13,
165            r14: value.r14,
166            r15: value.r15,
167            rip: value.rip,
168            eflags: value.eflags,
169            cs: value.cs,
170            orig_rax: value.orig_rax,
171            fs_base: value.fs_base,
172            gs_base: value.gs_base,
173            fs: value.fs,
174            gs: value.gs,
175            ss: value.ss,
176            ds: value.ds,
177            es: value.es,
178        }
179    }
180}
181
182impl From<RegisterMap> for user_regs_struct {
183    fn from(reg_map: RegisterMap) -> user_regs_struct {
184        user_regs_struct {
185            rax: reg_map.rax,
186            rbx: reg_map.rbx,
187            rcx: reg_map.rcx,
188            rdx: reg_map.rdx,
189            rdi: reg_map.rdi,
190            rsi: reg_map.rsi,
191            rbp: reg_map.rbp,
192            rsp: reg_map.rsp,
193            r8: reg_map.r8,
194            r9: reg_map.r9,
195            r10: reg_map.r10,
196            r11: reg_map.r11,
197            r12: reg_map.r12,
198            r13: reg_map.r13,
199            r14: reg_map.r14,
200            r15: reg_map.r15,
201            rip: reg_map.rip,
202            eflags: reg_map.eflags,
203            cs: reg_map.cs,
204            orig_rax: reg_map.orig_rax,
205            fs_base: reg_map.fs_base,
206            gs_base: reg_map.gs_base,
207            fs: reg_map.fs,
208            gs: reg_map.gs,
209            ss: reg_map.ss,
210            ds: reg_map.ds,
211            es: reg_map.es,
212        }
213    }
214}
215
216impl RegisterMap {
217    /// Return current register values for selected thread.
218    ///
219    /// # Arguments
220    ///
221    /// * `pid`: thread id.
222    pub fn current(pid: Pid) -> Result<Self, Error> {
223        let regs = sys::ptrace::getregs(pid).map_err(Ptrace)?;
224        Ok(regs.into())
225    }
226
227    /// Return register value.
228    ///
229    /// # Arguments
230    ///
231    /// * `register`: target register.
232    pub fn value(&self, register: impl Into<Register>) -> u64 {
233        let register = register.into();
234        match register {
235            Register::Rax => self.rax,
236            Register::Rbx => self.rbx,
237            Register::Rcx => self.rcx,
238            Register::Rdx => self.rdx,
239            Register::Rdi => self.rdi,
240            Register::Rsi => self.rsi,
241            Register::Rbp => self.rbp,
242            Register::Rsp => self.rsp,
243            Register::R8 => self.r8,
244            Register::R9 => self.r9,
245            Register::R10 => self.r10,
246            Register::R11 => self.r11,
247            Register::R12 => self.r12,
248            Register::R13 => self.r13,
249            Register::R14 => self.r14,
250            Register::R15 => self.r15,
251            Register::Rip => self.rip,
252            Register::Eflags => self.eflags,
253            Register::Cs => self.cs,
254            Register::OrigRax => self.orig_rax,
255            Register::FsBase => self.fs_base,
256            Register::GsBase => self.gs_base,
257            Register::Fs => self.fs,
258            Register::Gs => self.gs,
259            Register::Ss => self.ss,
260            Register::Ds => self.ds,
261            Register::Es => self.es,
262        }
263    }
264
265    /// Set new register value.
266    ///
267    /// # Arguments
268    ///
269    /// * `register`: target register.
270    /// * `value`: new value.
271    pub fn update(&mut self, register: impl Into<Register>, value: u64) {
272        match register.into() {
273            Register::Rax => self.rax = value,
274            Register::Rbx => self.rbx = value,
275            Register::Rcx => self.rcx = value,
276            Register::Rdx => self.rdx = value,
277            Register::Rdi => self.rdi = value,
278            Register::Rsi => self.rsi = value,
279            Register::Rbp => self.rbp = value,
280            Register::Rsp => self.rsp = value,
281            Register::R8 => self.r8 = value,
282            Register::R9 => self.r9 = value,
283            Register::R10 => self.r10 = value,
284            Register::R11 => self.r11 = value,
285            Register::R12 => self.r12 = value,
286            Register::R13 => self.r13 = value,
287            Register::R14 => self.r14 = value,
288            Register::R15 => self.r15 = value,
289            Register::Rip => self.rip = value,
290            Register::Eflags => self.eflags = value,
291            Register::Cs => self.cs = value,
292            Register::OrigRax => self.orig_rax = value,
293            Register::FsBase => self.fs_base = value,
294            Register::GsBase => self.gs_base = value,
295            Register::Fs => self.fs = value,
296            Register::Gs => self.gs = value,
297            Register::Ss => self.ss = value,
298            Register::Ds => self.ds = value,
299            Register::Es => self.es = value,
300        };
301    }
302
303    /// Replace tread registers with values taken from this map.
304    ///
305    /// # Arguments
306    ///
307    /// * `pid`: target thread.
308    pub fn persist(self, pid: Pid) -> Result<(), Error> {
309        sys::ptrace::setregs(pid, self.into()).map_err(Ptrace)
310    }
311}
312
313/// x86_64 register values, using DWARF register number as index.
314#[derive(Debug, Clone)]
315pub struct DwarfRegisterMap(SmallVec<[Option<u64>; 0x80]>);
316
317impl DwarfRegisterMap {
318    /// Return register value.
319    ///
320    /// # Arguments
321    ///
322    /// * `register`: target register.
323    pub fn value(&self, register: gimli::Register) -> Result<u64, Error> {
324        self.0
325            .get(register.0 as usize)
326            .copied()
327            .and_then(|v| v)
328            .ok_or(RegisterNotFound(register))
329    }
330
331    /// Set new register value.
332    ///
333    /// # Arguments
334    ///
335    /// * `register`: target register.
336    /// * `value`: new value.
337    pub fn update(&mut self, register: gimli::Register, value: u64) {
338        self.0[register.0 as usize] = Some(value);
339    }
340
341    /// Update current registers from another map, preserving existing values
342    /// when the incoming map has no value.
343    ///
344    /// # Arguments
345    ///
346    /// * `other`: map that provides new register values.
347    pub fn update_from(&mut self, other: &Self) {
348        for (idx, value) in other.0.iter().enumerate() {
349            if let Some(value) = value {
350                self.0[idx] = Some(*value);
351            }
352        }
353    }
354}
355
356/// Mapping dwarf registers to machine registers.
357/// See https://docs.rs/gimli/0.13.0/gimli/struct.UnwindTableRow.html#method.register
358impl From<RegisterMap> for DwarfRegisterMap {
359    fn from(map: RegisterMap) -> Self {
360        let mut dwarf_map = smallvec![None; 0x80];
361        dwarf_map.insert(0, Some(map.rax));
362        dwarf_map.insert(1, Some(map.rdx));
363        dwarf_map.insert(2, Some(map.rcx));
364        dwarf_map.insert(3, Some(map.rbx));
365        dwarf_map.insert(4, Some(map.rsi));
366        dwarf_map.insert(5, Some(map.rdi));
367        dwarf_map.insert(6, Some(map.rbp));
368        dwarf_map.insert(7, Some(map.rsp));
369        dwarf_map.insert(8, Some(map.r8));
370        dwarf_map.insert(9, Some(map.r9));
371        dwarf_map.insert(10, Some(map.r10));
372        dwarf_map.insert(11, Some(map.r11));
373        dwarf_map.insert(12, Some(map.r12));
374        dwarf_map.insert(13, Some(map.r13));
375        dwarf_map.insert(14, Some(map.r14));
376        dwarf_map.insert(15, Some(map.r15));
377        dwarf_map.insert(16, Some(map.rip));
378        dwarf_map.insert(49, Some(map.eflags));
379        dwarf_map.insert(50, Some(map.es));
380        dwarf_map.insert(51, Some(map.cs));
381        dwarf_map.insert(52, Some(map.ss));
382        dwarf_map.insert(53, Some(map.ds));
383        dwarf_map.insert(54, Some(map.fs));
384        dwarf_map.insert(55, Some(map.gs));
385        dwarf_map.insert(58, Some(map.fs_base));
386        dwarf_map.insert(59, Some(map.gs_base));
387        DwarfRegisterMap(dwarf_map)
388    }
389}
390
391pub mod debug {
392    use crate::debugger::Error;
393    use crate::debugger::Error::Ptrace;
394    use bit_field::BitField;
395    use nix::sys;
396    use nix::sys::ptrace::AddressType;
397    use nix::unistd::Pid;
398    use std::ffi::c_void;
399    use std::fmt::{Display, Formatter};
400    use std::mem::offset_of;
401    use strum_macros::FromRepr;
402
403    /// Debug register representation.
404    #[repr(usize)]
405    #[derive(Clone, Copy, Debug, PartialEq, FromRepr)]
406    pub enum DebugRegisterNumber {
407        DR0,
408        DR1,
409        DR2,
410        DR3,
411    }
412
413    pub type DebugAddressRegister = usize;
414
415    #[derive(Clone, Copy, PartialEq, Debug)]
416    pub struct DebugStatusRegister(usize);
417
418    macro_rules! impl_trap {
419        ($fn_name: ident, $trap: path) => {
420            #[doc = "Return true if breakpoint X condition was detected."]
421            #[doc = "Reset the corresponding flag."]
422            pub fn $fn_name(&mut self) -> bool {
423                let is_set = (self.0 & $trap) == $trap;
424                self.0 &= !$trap;
425                is_set
426            }
427        };
428    }
429
430    impl DebugStatusRegister {
431        /// Breakpoint condition 0 was detected.
432        const TRAP0: usize = 1;
433        /// Breakpoint condition 1 was detected.
434        const TRAP1: usize = 1 << 1;
435        /// Breakpoint condition 2 was detected.
436        const TRAP2: usize = 1 << 2;
437        /// Breakpoint condition 3 was detected.
438        const TRAP3: usize = 1 << 3;
439
440        impl_trap!(trap0, Self::TRAP0);
441        impl_trap!(trap1, Self::TRAP1);
442        impl_trap!(trap2, Self::TRAP2);
443        impl_trap!(trap3, Self::TRAP3);
444
445        /// Return debug register number and flush it if breakpoint was hit.
446        pub fn detect_and_flush(&mut self) -> Option<DebugRegisterNumber> {
447            let dr = if self.trap0() {
448                DebugRegisterNumber::DR0
449            } else if self.trap1() {
450                DebugRegisterNumber::DR1
451            } else if self.trap2() {
452                DebugRegisterNumber::DR2
453            } else if self.trap3() {
454                DebugRegisterNumber::DR3
455            } else {
456                return None;
457            };
458            Some(dr)
459        }
460    }
461
462    #[derive(Clone, Copy, PartialEq, Debug)]
463    pub struct DebugControlRegister(usize);
464
465    impl DebugControlRegister {
466        /// Enable detection of exact instruction causing a data breakpoint condition for the current task.
467        /// This is not supported by `x86_64` processors,
468        /// but is recommended to be enabled for backward and forward compatibility.
469        const LOCAL_EXACT_BREAKPOINT_ENABLE_BIT: usize = 8;
470        /// Enable detection of exact instruction causing a data breakpoint condition for all tasks.
471        /// This is not supported by `x86_64` processors,
472        /// but is recommended to be enabled for backward and forward compatibility.
473        const GLOBAL_EXACT_BREAKPOINT_ENABLE_BIT: usize = 9;
474
475        /// Return true if breakpoint enabled.
476        ///
477        /// # Arguments
478        ///
479        /// * `dr_num`: address debug register number
480        /// * `global`: whether the breakpoint is global or local
481        #[inline(always)]
482        pub fn dr_enabled(&self, dr: DebugRegisterNumber, global: bool) -> bool {
483            let dr = dr as usize;
484            let idx = if global { dr * 2 + 1 } else { dr * 2 };
485            debug_assert!(idx <= 7);
486            self.0.get_bit(idx)
487        }
488
489        /// Configures a breakpoint condition and size for the associated breakpoint.
490        ///
491        /// # Arguments
492        ///
493        /// * `dr`: address debug register number
494        /// * `cond`: breakpoint condition
495        /// * `size`: breakpoint size
496        #[inline(always)]
497        pub fn configure_bp(
498            &mut self,
499            dr: DebugRegisterNumber,
500            cond: BreakCondition,
501            size: BreakSize,
502        ) {
503            let dr = dr as usize;
504            // set condition
505            let idx = 16 + (dr * 4);
506            self.0.set_bits(idx..=idx + 1, cond as usize);
507            // set size
508            let idx = 18 + (dr * 4);
509            self.0.set_bits(idx..=idx + 1, size as usize);
510        }
511
512        /// Enable/disable a breakpoint either as global or local.
513        ///
514        /// # Arguments
515        /// * `dr_num` - address debug register to enable/disable
516        /// * `global` - whether the breakpoint is global or local
517        /// * `enable` - whether to enable or disable the breakpoint
518        #[inline(always)]
519        pub fn set_dr(&mut self, dr: DebugRegisterNumber, global: bool, enable: bool) {
520            let dr = dr as usize;
521            let idx = if global { dr * 2 + 1 } else { dr * 2 };
522            self.0.set_bit(idx, enable);
523
524            let detection_bit = if global {
525                Self::GLOBAL_EXACT_BREAKPOINT_ENABLE_BIT
526            } else {
527                Self::LOCAL_EXACT_BREAKPOINT_ENABLE_BIT
528            };
529
530            if enable {
531                self.0.set_bit(detection_bit, true);
532            } else {
533                let all_disabled = [0, 1, 2, 3].iter().all(|&n| {
534                    !self.dr_enabled(
535                        DebugRegisterNumber::from_repr(n).expect("infallible"),
536                        global,
537                    )
538                });
539                if all_disabled {
540                    self.0.set_bit(detection_bit, false);
541                }
542            }
543        }
544    }
545
546    #[derive(PartialEq, Debug)]
547    pub struct HardwareDebugState {
548        /// Four (dr0, dr1, dr2, dr3 for x86_64) address debug registers.
549        pub address_regs: [DebugAddressRegister; 4],
550        /// Debug status register.
551        pub dr6: DebugStatusRegister,
552        /// Debug control register.
553        pub dr7: DebugControlRegister,
554    }
555
556    impl HardwareDebugState {
557        /// Return the current state of hardware debug registers.
558        ///
559        /// # Arguments
560        ///
561        /// * `pid`: thread id for which state is loaded
562        pub fn current(pid: Pid) -> Result<Self, Error> {
563            use nix::libc::user;
564
565            fn get_dr(pid: Pid, num: usize) -> Result<usize, Error> {
566                let offset = offset_of!(user, u_debugreg) + num * 8;
567                Ok(sys::ptrace::read_user(pid, offset as AddressType).map_err(Ptrace)? as usize)
568            }
569
570            Ok(Self {
571                address_regs: [
572                    get_dr(pid, 0)?,
573                    get_dr(pid, 1)?,
574                    get_dr(pid, 2)?,
575                    get_dr(pid, 3)?,
576                ],
577                dr6: DebugStatusRegister(get_dr(pid, 6)?),
578                dr7: DebugControlRegister(get_dr(pid, 7)?),
579            })
580        }
581
582        /// Synchronize state and debug registers.
583        ///
584        /// # Arguments
585        ///
586        /// * `pid`: thread id into which registers data is saved
587        pub fn sync(&self, pid: Pid) -> Result<(), Error> {
588            fn set_dr(pid: Pid, num: usize, data: usize) -> Result<(), Error> {
589                let offset = offset_of!(nix::libc::user, u_debugreg);
590                let offset = offset + num * 8;
591                unsafe {
592                    sys::ptrace::write_user(pid, offset as AddressType, data as *mut c_void)
593                        .map_err(Ptrace)
594                }
595            }
596
597            for (reg_num, val) in self.address_regs.iter().enumerate() {
598                set_dr(pid, reg_num, *val)?;
599            }
600            set_dr(pid, 6, self.dr6.0)?;
601            set_dr(pid, 7, self.dr7.0)?;
602            Ok(())
603        }
604    }
605
606    /// Specifies the breakpoint condition for a corresponding breakpoint.
607    ///
608    /// Instruction and i/o read-write conditions are unused and aren't presented here.
609    #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd)]
610    pub enum BreakCondition {
611        /// 01 — Break on data writes only.
612        DataWrites = 0b01,
613        /// 11 — Break on data reads or writes but not instruction fetches.
614        DataReadsWrites = 0b11,
615    }
616
617    impl Display for BreakCondition {
618        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
619            match self {
620                BreakCondition::DataWrites => f.write_str("w"),
621                BreakCondition::DataReadsWrites => f.write_str("rw"),
622            }
623        }
624    }
625
626    /// Specify the size of the memory location at the address specified in the
627    /// corresponding breakpoint address register (DR0 through DR3).
628    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
629    pub enum BreakSize {
630        /// 1-byte length.
631        Bytes1 = 0b00,
632        /// 2-byte length.
633        Bytes2 = 0b01,
634        /// 8 byte length (or undefined, on older processors).
635        Bytes8 = 0b10,
636        /// 4-byte length.
637        Bytes4 = 0b11,
638    }
639
640    impl TryFrom<u8> for BreakSize {
641        type Error = Error;
642
643        fn try_from(value: u8) -> Result<Self, Self::Error> {
644            let size = match value {
645                1 => BreakSize::Bytes1,
646                2 => BreakSize::Bytes2,
647                4 => BreakSize::Bytes4,
648                8 => BreakSize::Bytes8,
649                _ => return Err(Error::WatchpointWrongSize),
650            };
651            Ok(size)
652        }
653    }
654
655    impl Display for BreakSize {
656        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
657            match self {
658                BreakSize::Bytes1 => f.write_str("1b"),
659                BreakSize::Bytes2 => f.write_str("2b"),
660                BreakSize::Bytes8 => f.write_str("8b"),
661                BreakSize::Bytes4 => f.write_str("4b"),
662            }
663        }
664    }
665}