xref: /DragonOS/kernel/crates/kprobe/src/arch/x86/mod.rs (revision 7b0ef10895108a0de5ff5ef3d2f93f40cf2e33a5)
1 use crate::{KprobeBasic, KprobeBuilder, KprobeOps};
2 use alloc::string::ToString;
3 use alloc::sync::Arc;
4 use core::{
5     fmt::Debug,
6     ops::{Deref, DerefMut},
7 };
8 use yaxpeax_arch::LengthedInstruction;
9 
10 const EBREAK_INST: u8 = 0xcc; // x86_64: 0xcc
11 const MAX_INSTRUCTION_SIZE: usize = 15; // x86_64 max instruction length
12 
13 pub struct Kprobe {
14     basic: KprobeBasic,
15     point: Arc<X86KprobePoint>,
16 }
17 
18 #[derive(Debug)]
19 pub struct X86KprobePoint {
20     addr: usize,
21     old_instruction: [u8; MAX_INSTRUCTION_SIZE],
22     old_instruction_len: usize,
23 }
24 
25 impl Drop for X86KprobePoint {
26     fn drop(&mut self) {
27         let address = self.addr;
28         unsafe {
29             core::ptr::copy(
30                 self.old_instruction.as_ptr(),
31                 address as *mut u8,
32                 self.old_instruction_len,
33             );
34             core::arch::x86_64::_mm_mfence();
35         }
36         let decoder = yaxpeax_x86::amd64::InstDecoder::default();
37         let inst = decoder.decode_slice(&self.old_instruction).unwrap();
38         log::trace!(
39             "Kprobe::uninstall: address: {:#x}, old_instruction: {:?}",
40             address,
41             inst.to_string()
42         );
43     }
44 }
45 
46 impl Debug for Kprobe {
47     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
48         f.debug_struct("Kprobe")
49             .field("basic", &self.basic)
50             .field("point", &self.point)
51             .finish()
52     }
53 }
54 
55 impl Deref for Kprobe {
56     type Target = KprobeBasic;
57 
58     fn deref(&self) -> &Self::Target {
59         &self.basic
60     }
61 }
62 
63 impl DerefMut for Kprobe {
64     fn deref_mut(&mut self) -> &mut Self::Target {
65         &mut self.basic
66     }
67 }
68 
69 impl KprobeBuilder {
70     pub fn install(self) -> (Kprobe, Arc<X86KprobePoint>) {
71         let probe_point = match &self.probe_point {
72             Some(point) => point.clone(),
73             None => self.replace_inst(),
74         };
75         let kprobe = Kprobe {
76             basic: KprobeBasic::from(self),
77             point: probe_point.clone(),
78         };
79         (kprobe, probe_point)
80     }
81     /// # 安装kprobe
82     ///
83     /// 不同的架构下需要保存原指令,然后替换为断点指令
84     fn replace_inst(&self) -> Arc<X86KprobePoint> {
85         let address = self.symbol_addr + self.offset;
86         let mut inst_tmp = [0u8; MAX_INSTRUCTION_SIZE];
87         unsafe {
88             core::ptr::copy(
89                 address as *const u8,
90                 inst_tmp.as_mut_ptr(),
91                 MAX_INSTRUCTION_SIZE,
92             );
93         }
94         let decoder = yaxpeax_x86::amd64::InstDecoder::default();
95         let inst = decoder.decode_slice(&inst_tmp).unwrap();
96         let len = inst.len().to_const();
97         log::trace!("inst: {:?}, len: {:?}", inst.to_string(), len);
98         let point = Arc::new(X86KprobePoint {
99             addr: address,
100             old_instruction: inst_tmp,
101             old_instruction_len: len as usize,
102         });
103         unsafe {
104             core::ptr::write_volatile(address as *mut u8, EBREAK_INST);
105             core::arch::x86_64::_mm_mfence();
106         }
107         log::trace!(
108             "Kprobe::install: address: {:#x}, func_name: {:?}",
109             address,
110             self.symbol
111         );
112         point
113     }
114 }
115 
116 impl Kprobe {
117     pub fn probe_point(&self) -> &Arc<X86KprobePoint> {
118         &self.point
119     }
120 }
121 
122 impl KprobeOps for X86KprobePoint {
123     fn return_address(&self) -> usize {
124         self.addr + self.old_instruction_len
125     }
126     fn single_step_address(&self) -> usize {
127         self.old_instruction.as_ptr() as usize
128     }
129     fn debug_address(&self) -> usize {
130         self.old_instruction.as_ptr() as usize + self.old_instruction_len
131     }
132     fn break_address(&self) -> usize {
133         self.addr
134     }
135 }
136