1 use alloc::sync::Arc; 2 use core::{ 3 arch::riscv64::sfence_vma_all, 4 fmt::Debug, 5 ops::{Deref, DerefMut}, 6 }; 7 8 use crate::{KprobeBasic, KprobeBuilder, KprobeOps}; 9 const EBREAK_INST: u32 = 0x00100073; // ebreak 10 const C_EBREAK_INST: u32 = 0x9002; // c.ebreak 11 const INSN_LENGTH_MASK: u16 = 0x3; 12 const INSN_LENGTH_32: u16 = 0x3; 13 14 #[derive(Debug)] 15 pub struct Kprobe { 16 basic: KprobeBasic, 17 point: Arc<Rv64KprobePoint>, 18 } 19 20 #[derive(Debug)] 21 enum OpcodeTy { 22 Inst16(u16), 23 Inst32(u32), 24 } 25 #[derive(Debug)] 26 pub struct Rv64KprobePoint { 27 addr: usize, 28 old_instruction: OpcodeTy, 29 inst_tmp: [u8; 8], 30 } 31 32 impl Deref for Kprobe { 33 type Target = KprobeBasic; 34 deref(&self) -> &Self::Target35 fn deref(&self) -> &Self::Target { 36 &self.basic 37 } 38 } 39 40 impl DerefMut for Kprobe { deref_mut(&mut self) -> &mut Self::Target41 fn deref_mut(&mut self) -> &mut Self::Target { 42 &mut self.basic 43 } 44 } 45 46 impl Kprobe { probe_point(&self) -> &Arc<Rv64KprobePoint>47 pub fn probe_point(&self) -> &Arc<Rv64KprobePoint> { 48 &self.point 49 } 50 } 51 52 impl Drop for Rv64KprobePoint { drop(&mut self)53 fn drop(&mut self) { 54 let address = self.addr; 55 match self.old_instruction { 56 OpcodeTy::Inst16(inst_16) => unsafe { 57 core::ptr::write(address as *mut u16, inst_16); 58 }, 59 OpcodeTy::Inst32(inst_32) => unsafe { 60 core::ptr::write(address as *mut u32, inst_32); 61 }, 62 } 63 unsafe { 64 sfence_vma_all(); 65 } 66 log::trace!( 67 "Kprobe::uninstall: address: {:#x}, old_instruction: {:?}", 68 address, 69 self.old_instruction 70 ); 71 } 72 } 73 74 impl KprobeBuilder { install(self) -> (Kprobe, Arc<Rv64KprobePoint>)75 pub fn install(self) -> (Kprobe, Arc<Rv64KprobePoint>) { 76 let probe_point = match &self.probe_point { 77 Some(point) => point.clone(), 78 None => self.replace_inst(), 79 }; 80 let kprobe = Kprobe { 81 basic: KprobeBasic::from(self), 82 point: probe_point.clone(), 83 }; 84 (kprobe, probe_point) 85 } 86 /// # 安装kprobe 87 /// 88 /// 不同的架构下需要保存原指令,然后替换为断点指令 replace_inst(&self) -> Arc<Rv64KprobePoint>89 fn replace_inst(&self) -> Arc<Rv64KprobePoint> { 90 let address = self.symbol_addr + self.offset; 91 let inst_16 = unsafe { core::ptr::read(address as *const u16) }; 92 // See https://elixir.bootlin.com/linux/v6.10.2/source/arch/riscv/kernel/probes/kprobes.c#L68 93 let is_inst_16 = if (inst_16 & INSN_LENGTH_MASK) == INSN_LENGTH_32 { 94 false 95 } else { 96 true 97 }; 98 let mut point = Rv64KprobePoint { 99 old_instruction: OpcodeTy::Inst16(0), 100 inst_tmp: [0; 8], 101 addr: address, 102 }; 103 let inst_tmp_ptr = point.inst_tmp.as_ptr() as usize; 104 if is_inst_16 { 105 point.old_instruction = OpcodeTy::Inst16(inst_16); 106 unsafe { 107 core::ptr::write(address as *mut u16, C_EBREAK_INST as u16); 108 // inst_16 :0-16 109 // c.ebreak:16-32 110 core::ptr::write(inst_tmp_ptr as *mut u16, inst_16); 111 core::ptr::write((inst_tmp_ptr + 2) as *mut u16, C_EBREAK_INST as u16); 112 } 113 } else { 114 let inst_32 = unsafe { core::ptr::read(address as *const u32) }; 115 point.old_instruction = OpcodeTy::Inst32(inst_32); 116 unsafe { 117 core::ptr::write(address as *mut u32, EBREAK_INST); 118 // inst_32 :0-32 119 // ebreak :32-64 120 core::ptr::write(inst_tmp_ptr as *mut u32, inst_32); 121 core::ptr::write((inst_tmp_ptr + 4) as *mut u32, EBREAK_INST); 122 } 123 } 124 unsafe { 125 sfence_vma_all(); 126 } 127 log::trace!( 128 "Kprobe::install: address: {:#x}, func_name: {:?}, opcode: {:x?}", 129 address, 130 self.symbol, 131 point.old_instruction 132 ); 133 Arc::new(point) 134 } 135 } 136 137 impl KprobeOps for Rv64KprobePoint { return_address(&self) -> usize138 fn return_address(&self) -> usize { 139 let address = self.addr; 140 match self.old_instruction { 141 OpcodeTy::Inst16(_) => address + 2, 142 OpcodeTy::Inst32(_) => address + 4, 143 } 144 } single_step_address(&self) -> usize145 fn single_step_address(&self) -> usize { 146 self.inst_tmp.as_ptr() as usize 147 } debug_address(&self) -> usize148 fn debug_address(&self) -> usize { 149 match self.old_instruction { 150 OpcodeTy::Inst16(_) => self.inst_tmp.as_ptr() as usize + 2, 151 OpcodeTy::Inst32(_) => self.inst_tmp.as_ptr() as usize + 4, 152 } 153 } break_address(&self) -> usize154 fn break_address(&self) -> usize { 155 self.addr 156 } 157 } 158