From 108e81bab6d83c445ca9d70fdc5be55f588b5f21 Mon Sep 17 00:00:00 2001 From: chenzhiy2001 Date: Sat, 29 Jul 2023 21:07:29 -0700 Subject: [PATCH] Important Update: Support Modularized Uprobe --- .gitignore | 2 +- README.md | 2 + os/Cargo.toml | 5 + os/src/ebpf/mod.rs | 3 + os/src/ebpf/osutil.rs | 8 +- os/src/ebpf/tracepoints.rs | 189 +++++++++++++++++++++++++++++---- os/src/main.rs | 3 + os/src/mm/memory_set.rs | 39 ++++++- os/src/mm/mod.rs | 28 +++++ os/src/mm/page_table.rs | 5 + os/src/net/port_table.rs | 1 + os/src/probe/mod.rs | 3 + os/src/syscall/fs.rs | 6 ++ os/src/syscall/gui.rs | 1 + os/src/syscall/net.rs | 2 + os/src/syscall/process.rs | 9 +- os/src/syscall/sync.rs | 9 ++ os/src/syscall/thread.rs | 2 + os/src/task/id.rs | 6 ++ os/src/task/manager.rs | 1 + os/src/task/mod.rs | 24 ++++- os/src/task/process.rs | 18 +++- os/src/task/task.rs | 1 + os/src/trap/context.rs | 40 +------ os/src/trap/mod.rs | 14 +++ side-stub.py | 7 ++ user/build/app/matrix.rs | 4 + user/build/bin/matrix.bin | Bin 25464 -> 25464 bytes user/build/elf/matrix.elf | Bin 150240 -> 152648 bytes user/ebpf/kern/Makefile | 2 +- user/ebpf/kern/get_regs_user.c | 14 +++ user/ebpf/kern/uprobe.h | 56 ++++++++++ user/src/bin/matrix.rs | 4 + 33 files changed, 439 insertions(+), 69 deletions(-) create mode 100644 user/ebpf/kern/get_regs_user.c create mode 100644 user/ebpf/kern/uprobe.h diff --git a/.gitignore b/.gitignore index 852c161..f7984e6 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,4 @@ os/.gdb_history os/virt.out tools/ pushall.sh -.vscode/*.log +.vscode/*.log \ No newline at end of file diff --git a/README.md b/README.md index 2df01b0..5df2d1a 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ If you don't know Rust Language and try to learn it, please visit [Rust Learnin Official QQ group number: 735045051 +## notes on ebpf version +You need to run like this: `CC=riscv64-linux-musl-gcc make run` ## news - 23/06/2022: Version 3.6.0 is on the way! Now we directly update the code on chX branches, please periodically check if there are any updates. diff --git a/os/Cargo.toml b/os/Cargo.toml index 66acbc1..bcd5c5b 100644 --- a/os/Cargo.toml +++ b/os/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["Yifan Wu "] edition = "2021" + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] @@ -26,6 +27,10 @@ numeric-enum-macro = "0.2.0" dyn-fmt = { version = "0.3.0", default-features = false } downcast-rs = { version = "1.2", default-features = false } ebpf2rv = { git = "https://github.com/livingshade/ebpf2rv", rev = "ecfc526" } +ruprobes = { path = "../../../ruprobes", features = ["rCore-Tutorial"] } +trap_context_riscv = { git = "https://github.com/chenzhiy2001/trap_context_riscv"} +trapframe = { git = "https://github.com/rcore-os/trapframe-rs"} +spin = "0.5" [profile.release] debug = true diff --git a/os/src/ebpf/mod.rs b/os/src/ebpf/mod.rs index b80aff4..11c9854 100644 --- a/os/src/ebpf/mod.rs +++ b/os/src/ebpf/mod.rs @@ -32,6 +32,9 @@ pub enum BpfObject { Program(Arc), } +pub use osutil::os_copy_from_user; +pub use osutil::os_copy_to_user; + impl BpfObject { /// get the map pub fn is_map(&self) -> Option<&SharedBpfMap> { diff --git a/os/src/ebpf/osutil.rs b/os/src/ebpf/osutil.rs index 27aa02e..9fe4178 100644 --- a/os/src/ebpf/osutil.rs +++ b/os/src/ebpf/osutil.rs @@ -79,7 +79,9 @@ pub fn os_console_write_str(s: &str) -> i64 { /// # os_copy_from_user /// copy `len` bytes from user space addresss `usr_addr` to `kern_buf` -pub fn os_copy_from_user(usr_addr: usize, kern_buf: *mut u8, len: usize) -> i32 { +#[inline(never)] +#[no_mangle] +pub extern "C" fn os_copy_from_user(usr_addr: usize, kern_buf: *mut u8, len: usize) -> i32 { use crate::mm::translated_byte_buffer; use crate::task::current_user_token; let t = translated_byte_buffer(current_user_token(), usr_addr as *const u8, len); @@ -93,7 +95,9 @@ pub fn os_copy_from_user(usr_addr: usize, kern_buf: *mut u8, len: usize) -> i32 /// # os_copy_to_user /// copy `len` bytes to user space addresss `usr_addr` from `kern_buf` -pub fn os_copy_to_user(usr_addr: usize, kern_buf: *const u8, len: usize) -> i32 { +#[inline(never)] +#[no_mangle] +pub extern "C" fn os_copy_to_user(usr_addr: usize, kern_buf: *const u8, len: usize) -> i32 { use crate::mm::translated_byte_buffer; use crate::task::current_user_token; let dst = translated_byte_buffer(current_user_token(), usr_addr as *const u8, len); diff --git a/os/src/ebpf/tracepoints.rs b/os/src/ebpf/tracepoints.rs index 5e12933..419192d 100644 --- a/os/src/ebpf/tracepoints.rs +++ b/os/src/ebpf/tracepoints.rs @@ -1,18 +1,28 @@ //! eBPF tracepoints //! //! attach a program to hookpoints -//! -//! currently we only support Kprobe -use alloc::{collections::BTreeMap, vec}; +//! +//! currently we only support Kprobe and Uprobe_syncfunc + +use crate::probe::{arch::trapframe::TrapFrame, kprobes::unregister_kprobe}; +use alloc::string::{ToString, String}; use alloc::sync::Arc; use alloc::vec::Vec; +use alloc::{collections::BTreeMap, vec}; use lazy_static::lazy_static; -use crate::probe::{arch::trapframe::TrapFrame, kprobes::unregister_kprobe}; +use ruprobes::{uprobe_register, ProbePlace, ProbeType}; //todo where is unregister? +use spin::Mutex as spin_Mutex; use lock::Mutex; +use trapframe::{TrapFrame as UprobeCrateTrapframe, UserContext,GeneralRegs}; -use crate::{probe::{register_kprobe, register_kretprobe, KProbeArgs, KRetProbeArgs}}; -use super::{BpfObject::*, *, retcode::BpfErrorCode::{*, self}, retcode::*}; +use super::{ + retcode::BpfErrorCode::{self, *}, + retcode::*, + BpfObject::*, + *, +}; +use crate::probe::{register_kprobe, register_kretprobe, KProbeArgs, KRetProbeArgs}; #[repr(C)] #[derive(Clone, Copy, Debug)] @@ -25,14 +35,20 @@ pub struct KprobeAttachAttr { #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum TracepointType { + // = ProbType + ProbePlace in rCore-ebpf KProbe, KRetProbeEntry, KRetProbeExit, + UProbe_Insn, + URetProbeEntry_Insn, //javascript-level long names :( + URetProbeExit_Insn, + UProbe_SyncFunc, + URetProbeEntry_SyncFunc, + URetProbeExit_SyncFunc, } use TracepointType::*; - #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] /// tracepoint abstraction, currently only for Kprobe pub struct Tracepoint { @@ -82,7 +98,29 @@ impl KProbeBPFContext { KProbeBPFContext { ptype: t, paddr: probed_addr, - tf: tf.clone() + tf: tf.clone(), + } + } + + pub fn as_ptr(&self) -> *const u8 { + unsafe { core::mem::transmute(self) } + } +} + +#[repr(C)] +/// uProbe context are just registers, or Trapframe +struct UProbeBPFContext { + ptype: usize,//0 is syncfunc + paddr: usize, + tf: TrapFrame, +} + +impl UProbeBPFContext { + pub fn new(tf: &TrapFrame, probed_addr: usize, t: usize) -> Self { + UProbeBPFContext { + ptype: t, + paddr: probed_addr, + tf: tf.clone(), } } @@ -93,7 +131,7 @@ impl KProbeBPFContext { /// the handler function that passed to register kprobe fn kprobe_handler(tf: &mut TrapFrame, probed_addr: usize) -> isize { - let tracepoint = Tracepoint::new(KProbe, probed_addr); + let tracepoint: Tracepoint = Tracepoint::new(KProbe, probed_addr); let ctx = KProbeBPFContext::new(tf, probed_addr, 0); info!("run attached progs!"); run_attached_programs(&tracepoint, ctx.as_ptr()); @@ -102,6 +140,61 @@ fn kprobe_handler(tf: &mut TrapFrame, probed_addr: usize) -> isize { 0 } + +fn uprobe_syncfunc_handler(tf: &mut trap_context_riscv::TrapContext, probed_addr: usize) {//tag: uprobe_handler + let tracepoint:Tracepoint=Tracepoint::new(UProbe_SyncFunc, probed_addr); + let ctx: UProbeBPFContext = UProbeBPFContext::new(&tf,probed_addr,0); + info!("run attached progs in uprobe_syncfunc_handler!"); + run_attached_programs(&tracepoint, ctx.as_ptr()); + info!("run attached progs in uprobe_syncfunc_handler exit!"); +} + + + pub fn nonsense(cx:&T){//cx: &mut UserContext + //println!("I'm handler! I'm useless!"); + // println!{"pre_handler: spec:{:#x}", cx.sepc}; +} + +// /// trapframe and usercontext are basically the same, +// /// but trapframe is used in OS while usercontext is used in uprobe crate +// fn trapframe_to_usercontext (cx:&mut TrapFrame)->UserContext{ +// UserContext { general: +// GeneralRegs { +// zero:cx.x[0], +// ra:cx.x[1], +// sp: cx.x[2], +// gp: cx.x[3], +// tp: cx.x[4], +// t0: cx.x[5], +// t1: cx.x[6], +// t2: cx.x[7], +// s0: cx.x[8], +// s1: cx.x[9], +// a0: cx.x[10], +// a1: cx.x[11], +// a2: cx.x[12], +// a3: cx.x[13], +// a4: cx.x[14], +// a5: cx.x[15], +// a6: cx.x[16], +// a7: cx.x[17], +// s2: cx.x[18], +// s3: cx.x[19], +// s4: cx.x[20], +// s5: cx.x[21], +// s6: cx.x[22], +// s7: cx.x[23], +// s8: cx.x[24], +// s9: cx.x[25], +// s10: cx.x[26], +// s11: cx.x[27], +// t3: cx.x[28], +// t4: cx.x[29], +// t5: cx.x[30], +// t6: cx.x[31], +// }, sstatus: cx.sstatus.bits(), sepc: cx.sepc } +// } + /// unused fn kretprobe_entry_handler(tf: &mut TrapFrame, probed_addr: usize) -> isize { let tracepoint = Tracepoint::new(KRetProbeEntry, probed_addr); @@ -126,12 +219,30 @@ fn resolve_symbol(symbol: &str) -> Option { //symbol_to_addr(symbol) Some(crate::syscall::fs::sys_open as usize) } - /// parse tracepoint types -fn parse_tracepoint<'a>(target: &'a str) -> Result<(TracepointType, &'a str), BpfErrorCode> { - let pos = target.find('$').ok_or(EINVAL)?; - let type_str = &target[0..pos]; - let fn_name = &target[(pos + 1)..]; +fn parse_tracepoint<'a>( + target: &'a str, +) -> Result<(TracepointType, String, Option), BpfErrorCode> { + // in rCore-Tutorial we put name2addr outside of OS. so + // fn_name is actually just a string of address like "0x80200000" + // but in future we may consider put name2addr back to kernel, so + // we don't change fn_name variable name here. + let fn_name_not_exist_msg = "FN_NAME_NOT_EXIST_MSG".to_string(); + let parts: Vec = target.split("$").map(|s| s.to_string()).collect(); + let how_many_parts = parts.len(); + let type_str = &parts[0]; + let fn_name = if how_many_parts == 2 { + &parts[1] + } else if how_many_parts == 3 { + &parts[2] + } else { + &fn_name_not_exist_msg + }; + let user_program_path_not_exist_msg: String = "USER_PROGRAM_PATH_NOT_EXIST".to_string(); + let user_program_path = if how_many_parts == 3 { &parts[1] } else { &user_program_path_not_exist_msg }; + // let pos = target.find('$').ok_or(EINVAL)?; + // let type_str = &target[0..pos]; + // let fn_name = &target[(pos + 1)..]; // determine tracepoint type let tp_type: TracepointType; @@ -141,16 +252,32 @@ fn parse_tracepoint<'a>(target: &'a str) -> Result<(TracepointType, &'a str), Bp tp_type = KRetProbeEntry; } else if type_str.eq_ignore_ascii_case("kretprobe@exit") { tp_type = KRetProbeExit; + } else if type_str.eq_ignore_ascii_case("uprobe_insn") { + //this solution is ugly but works. maybe we can find better solutions later. + tp_type = UProbe_Insn; + } else if type_str.eq_ignore_ascii_case("uretprobe_insn@entry") { + tp_type = URetProbeEntry_Insn; + } else if type_str.eq_ignore_ascii_case("uretprobe_insn@exit") { + tp_type = URetProbeExit_Insn; + } else if type_str.eq_ignore_ascii_case("uprobe_syncfunc") { + tp_type = UProbe_SyncFunc; + } else if type_str.eq_ignore_ascii_case("uretprobe_syncfunc@entry") { + tp_type = URetProbeEntry_SyncFunc; + } else if type_str.eq_ignore_ascii_case("uretprobe_syncfunc@exit") { + tp_type = URetProbeExit_SyncFunc; } else { return Err(EINVAL); } - Ok((tp_type, fn_name)) + let return_ty_type = tp_type; + let return_fn_name = fn_name.clone(); + let return_user_program_path = user_program_path.clone(); + Ok((return_ty_type, return_fn_name, Some(return_user_program_path))) } /// # bpf_program_attach /// attach a program to a hookpoint /// # arguments -/// * target - a str the represent hookpoiint symbol +/// * target - a str that represents hookpoint symbol /// * prog_fd - the fd of the bpf program /// # prodecure /// * get the bpf program object by prog_fd @@ -163,17 +290,20 @@ pub fn bpf_program_attach(target: &str, prog_fd: u32) -> BpfResult { // check program fd let program = { let objs = BPF_OBJECTS.lock(); - + match objs.get(&prog_fd) { Some(bpf_obj) => { let shared_program = bpf_obj.is_program().unwrap(); Ok(shared_program.clone()) - }, + } _ => Err(ENOENT), } }?; - let (tp_type, fn_name) = parse_tracepoint(target)?; - let addr = resolve_symbol(fn_name).ok_or(ENOENT)?; + let (tp_type, addr_string, user_program_path) = parse_tracepoint(target)?; + //let addr = resolve_symbol(&fn_name).ok_or(ENOENT)?; + println!("addr string is {:?}", addr_string); + let addr:usize = usize::from_str_radix(&addr_string[2..], 16).unwrap(); + //let addr = addr_string.parse::().unwrap(); let tracepoint = Tracepoint::new(tp_type, addr); @@ -214,9 +344,26 @@ pub fn bpf_program_attach(target: &str, prog_fd: u32) -> BpfResult { map.insert(tracepoint, vec![program]); map.insert(dual_tp, vec![]); } + UProbe_Insn => todo!(), + URetProbeEntry_Insn => todo!(), + URetProbeExit_Insn => todo!(), + UProbe_SyncFunc => { //tag: uprobe_handler + uprobe_register(user_program_path.unwrap().to_string(), addr, Arc::new(spin_Mutex::new(uprobe_syncfunc_handler)),None, ruprobes::ProbeType::SyncFunc); + map.insert(tracepoint, vec![program]); + } + URetProbeEntry_SyncFunc => todo!(), + URetProbeExit_SyncFunc => todo!(), } } - trace!("bpf prog attached! tracepoint symbol:{} addr: {:x}", fn_name, addr); + // trace!( + // "bpf prog attached! tracepoint symbol:{} addr: {:x}", + // fn_name, + // addr + // ); + trace!( + "bpf prog attached! tracepoint addr: {:x}", + addr + ); Ok(0) } diff --git a/os/src/main.rs b/os/src/main.rs index c166e11..7190a20 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -4,6 +4,7 @@ #![feature(alloc_error_handler)] #![feature(map_first_last)] +use crate::console::print; //use crate::drivers::{GPU_DEVICE, KEYBOARD_DEVICE, MOUSE_DEVICE, INPUT_CONDVAR}; use crate::drivers::{GPU_DEVICE, KEYBOARD_DEVICE, MOUSE_DEVICE}; @@ -13,6 +14,8 @@ extern crate bitflags; extern crate log; #[macro_use] extern crate alloc; +#[macro_use] +extern crate ruprobes; #[path = "boards/qemu.rs"] mod board; diff --git a/os/src/mm/memory_set.rs b/os/src/mm/memory_set.rs index db57929..dd5b0ab 100644 --- a/os/src/mm/memory_set.rs +++ b/os/src/mm/memory_set.rs @@ -34,7 +34,7 @@ pub fn kernel_token() -> usize { } pub struct MemorySet { - page_table: PageTable, + pub page_table: PageTable, areas: Vec, } @@ -245,6 +245,28 @@ impl MemorySet { //*self = Self::new_bare(); self.areas.clear(); } + /// Test if [`start_addr`, `end_addr`) is a free area + pub fn test_free_area(&self, start_addr: usize, end_addr: usize) -> bool { + self.areas + .iter() + .find(|area| area.is_overlap_with(VirtAddr(start_addr), VirtAddr(end_addr))) + .is_none() + } + /// Find a free area with hint address `addr_hint` and length `len`. + /// Return the start address of found free area. + /// Used for mmap. + pub fn find_free_area(&self, addr_hint: usize, len: usize) -> VirtAddr { + // brute force: + // try each area's end address as the start + super::address::VirtAddr( + core::iter::once(addr_hint)//czy is this correct? + //.chain(self.areas.iter().map(|area| area.end_addr))//czy we dont have end_addr. what does this line do? what's an alternative solution? + //.map(|addr| (addr + PAGE_SIZE - 1) & !(PAGE_SIZE - 1)) // round up a page + .chain(self.areas.iter().map(|area|area.vpn_range.get_end().0+PAGE_SIZE))//should we -1? + .find(|&addr| self.test_free_area(addr, addr + len)) + .expect("failed to find free area ???") + ) + } } pub struct MapArea { @@ -336,6 +358,21 @@ impl MapArea { current_vpn.step(); } } + + /// Test whether this area is (page) overlap with area [`start_addr`, `end_addr`) + pub fn is_overlap_with(&self, start_addr: VirtAddr, end_addr: VirtAddr) -> bool { + // original from rCore-ebpf: + // let p0 = Page::of_addr(self.start_addr); + // let p1 = Page::of_addr(self.end_addr - 1) + 1; + // let p2 = Page::of_addr(start_addr); + // let p3 = Page::of_addr(end_addr - 1) + 1; + // if OS crashes, here should be the first place to check with. + let p0 = self.vpn_range.get_start(); + let p1 = VirtPageNum(self.vpn_range.get_end().0+PAGE_SIZE); //czy is this mathematically correct? + let p2 = start_addr.floor();//VirtPageNum::from(start_addr); + let p3 = start_addr.ceil();//VirtPageNum::from(end_addr.0+PAGE_SIZE);//Page::of_addr(end_addr - 1) + 1; + !(p1 <= p2 || p0 >= p3) + } } #[derive(Copy, Clone, PartialEq, Debug)] diff --git a/os/src/mm/mod.rs b/os/src/mm/mod.rs index 89b7a7b..2870a3d 100644 --- a/os/src/mm/mod.rs +++ b/os/src/mm/mod.rs @@ -20,3 +20,31 @@ pub fn init() { frame_allocator::init_frame_allocator(); KERNEL_SPACE.exclusive_access().activate(); } + +//todo put uprobe util funcs to one file +use core::arch::asm; +use MapType::Framed; +/// only use this for uprobe +#[no_mangle] +pub extern "C" fn get_new_page(addr: usize, len: usize) -> usize{ + println!("get_new_page"); + let binding = crate::task::current_process(); + // println!("Getting PCB in get_new_page"); + let mut current_proc = binding.inner_exclusive_access(); + let ebreak_addr = current_proc.memory_set.find_free_area(addr, len); + current_proc.memory_set.push(MapArea::new(ebreak_addr, VirtAddr(ebreak_addr.0+len), Framed, MapPermission::R | MapPermission::W| MapPermission::X| MapPermission::U), None); + unsafe {asm!("fence.i");} + ebreak_addr.0 +} +/// only use this for uprobe +#[no_mangle] +pub extern "C" fn set_writeable(addr: usize){ + println!("set_writable. addr is {:x}",addr); + let binding = crate::task::current_process(); + // println!("Getting PCB in set_writeable"); + let current_proc = binding.inner_exclusive_access(); + current_proc.memory_set.page_table.translate(VirtAddr(addr).floor()).unwrap().set_writable(); + //page_table_entry.bits = page_table_entry.bits | ((1 << 2) as usize);//(1 << 2) is PTEFlags::W; + println!("setted!"); + unsafe {asm!("fence.i");} +} \ No newline at end of file diff --git a/os/src/mm/page_table.rs b/os/src/mm/page_table.rs index dfaf4b6..033bd58 100644 --- a/os/src/mm/page_table.rs +++ b/os/src/mm/page_table.rs @@ -47,6 +47,11 @@ impl PageTableEntry { pub fn writable(&self) -> bool { (self.flags() & PTEFlags::W) != PTEFlags::empty() } + pub fn set_writable(&mut self){ + println!("before: {:x}",self.bits); + self.bits |= 0b111;//1<<2 ; + println!("after: {:x}",self.bits); + } pub fn executable(&self) -> bool { (self.flags() & PTEFlags::X) != PTEFlags::empty() } diff --git a/os/src/net/port_table.rs b/os/src/net/port_table.rs index bcda046..555354c 100644 --- a/os/src/net/port_table.rs +++ b/os/src/net/port_table.rs @@ -90,6 +90,7 @@ pub fn check_accept(port: u16, tcp_packet: &TCPPacket) -> Option<()> { pub fn accept_connection(_port: u16, tcp_packet: &TCPPacket, task: Arc) { let process = task.process.upgrade().unwrap(); + // println!("Getting PCB in src/net/port_table.rs accept_connection()"); let mut inner = process.inner_exclusive_access(); let fd = inner.alloc_fd(); diff --git a/os/src/probe/mod.rs b/os/src/probe/mod.rs index f35f80c..3e09a38 100644 --- a/os/src/probe/mod.rs +++ b/os/src/probe/mod.rs @@ -6,11 +6,14 @@ pub use osutils::init_osutils; use kprobes::{Handler, HandlerFn}; pub use arch::TrapFrame; use alloc::sync::Arc; +//use ruprobes::{ProbePlace, ProbeType, uprobe_register, uprobes_trap_handler}; #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] #[path = "arch/riscv/mod.rs"] pub mod arch; + + pub struct KProbeArgs { pub pre_handler: Arc, pub post_handler: Option>, diff --git a/os/src/syscall/fs.rs b/os/src/syscall/fs.rs index 2758825..b04d90d 100644 --- a/os/src/syscall/fs.rs +++ b/os/src/syscall/fs.rs @@ -6,6 +6,7 @@ use alloc::sync::Arc; pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize { let token = current_user_token(); let process = current_process(); + // println!("Getting PCB in src/syscall/fs.rs sys_write()"); let inner = process.inner_exclusive_access(); if fd >= inner.fd_table.len() { return -1; @@ -26,6 +27,7 @@ pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize { pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize { let token = current_user_token(); let process = current_process(); + // println!("Getting PCB in src/syscall/fs.rs sys_read()"); let inner = process.inner_exclusive_access(); if fd >= inner.fd_table.len() { return -1; @@ -48,6 +50,7 @@ pub fn sys_open(path: *const u8, flags: u32) -> isize { let token = current_user_token(); let path = translated_str(token, path); if let Some(inode) = open_file(path.as_str(), OpenFlags::from_bits(flags).unwrap()) { + // println!("Getting PCB in src/syscall/fs.rs sys_open()"); let mut inner = process.inner_exclusive_access(); let fd = inner.alloc_fd(); inner.fd_table[fd] = Some(inode); @@ -59,6 +62,7 @@ pub fn sys_open(path: *const u8, flags: u32) -> isize { pub fn sys_close(fd: usize) -> isize { let process = current_process(); + // println!("Getting PCB in src/syscall/fs.rs sys_close()"); let mut inner = process.inner_exclusive_access(); if fd >= inner.fd_table.len() { return -1; @@ -73,6 +77,7 @@ pub fn sys_close(fd: usize) -> isize { pub fn sys_pipe(pipe: *mut usize) -> isize { let process = current_process(); let token = current_user_token(); + // println!("Getting PCB in src/syscall/fs.rs sys_pipe()"); let mut inner = process.inner_exclusive_access(); let (pipe_read, pipe_write) = make_pipe(); let read_fd = inner.alloc_fd(); @@ -86,6 +91,7 @@ pub fn sys_pipe(pipe: *mut usize) -> isize { pub fn sys_dup(fd: usize) -> isize { let process = current_process(); + // println!("Getting PCB in src/syscall/fs.rs sys_dup()"); let mut inner = process.inner_exclusive_access(); if fd >= inner.fd_table.len() { return -1; diff --git a/os/src/syscall/gui.rs b/os/src/syscall/gui.rs index c5ba8a4..97c81d3 100644 --- a/os/src/syscall/gui.rs +++ b/os/src/syscall/gui.rs @@ -15,6 +15,7 @@ pub fn sys_framebuffer() -> isize { let pn_offset = fb_start_ppn.0 as isize - fb_start_vpn.0 as isize; let current_process = current_process(); + // println!("Getting PCB in src/syscall/gui.rs sys_framebuffer()"); let mut inner = current_process.inner_exclusive_access(); inner.memory_set.push( MapArea::new( diff --git a/os/src/syscall/net.rs b/os/src/syscall/net.rs index c16b9ce..52cbf63 100644 --- a/os/src/syscall/net.rs +++ b/os/src/syscall/net.rs @@ -7,6 +7,7 @@ use alloc::sync::Arc; // just support udp pub fn sys_connect(raddr: u32, lport: u16, rport: u16) -> isize { let process = current_process(); + // println!("Getting PCB in src/syscall/net.rs sys_connect()"); let mut inner = process.inner_exclusive_access(); let fd = inner.alloc_fd(); let udp_node = UDP::new(IPv4::from_u32(raddr), lport, rport); @@ -19,6 +20,7 @@ pub fn sys_listen(port: u16) -> isize { match listen(port) { Some(port_index) => { let process = current_process(); + // println!("Getting PCB in src/syscall/net.rs sys_listen()"); let mut inner = process.inner_exclusive_access(); let fd = inner.alloc_fd(); let port_fd = PortFd::new(port_index); diff --git a/os/src/syscall/process.rs b/os/src/syscall/process.rs index 7d5b67a..5d537fa 100644 --- a/os/src/syscall/process.rs +++ b/os/src/syscall/process.rs @@ -8,6 +8,7 @@ use crate::timer::get_time_ms; use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; +use ruprobes::uprobes_init; pub fn sys_exit(exit_code: i32) -> ! { exit_current_and_run_next(exit_code); @@ -32,6 +33,7 @@ pub fn sys_fork() -> isize { let new_process = current_process.fork(); let new_pid = new_process.getpid(); // modify trap context of new_task, because it returns immediately after switching + // println!("Getting PCB in src/syscall/process.rs sys_fork()"); let new_process_inner = new_process.inner_exclusive_access(); let task = new_process_inner.tasks[0].as_ref().unwrap(); let trap_cx = task.inner_exclusive_access().get_trap_cx(); @@ -59,7 +61,8 @@ pub fn sys_exec(path: *const u8, mut args: *const usize) -> isize { let all_data = app_inode.read_all(); let process = current_process(); let argc = args_vec.len(); - process.exec(all_data.as_slice(), args_vec); + process.exec(all_data.as_slice(), args_vec, path); + uprobes_init(); // return argc because cx.x[10] will be covered with it later argc as isize } else { @@ -73,6 +76,7 @@ pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize { let process = current_process(); // find a child process + // println!("Getting PCB in src/syscall/process.rs sys_waitpid()"); let mut inner = process.inner_exclusive_access(); if !inner .children @@ -84,6 +88,7 @@ pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize { } let pair = inner.children.iter().enumerate().find(|(_, p)| { // ++++ temporarily access child PCB exclusively + // println!("Getting PCB AGAIN in src/syscall/process.rs sys_waitpid()"); p.inner_exclusive_access().is_zombie && (pid == -1 || pid as usize == p.getpid()) // ++++ release child PCB }); @@ -93,6 +98,7 @@ pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize { assert_eq!(Arc::strong_count(&child), 1); let found_pid = child.getpid(); // ++++ temporarily access child PCB exclusively + // println!("Getting PCB AGAIN AGAIN in src/syscall/process.rs sys_waitpid()"); let exit_code = child.inner_exclusive_access().exit_code; // ++++ release child PCB *translated_refmut(inner.memory_set.token(), exit_code_ptr) = exit_code; @@ -106,6 +112,7 @@ pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize { pub fn sys_kill(pid: usize, signal: u32) -> isize { if let Some(process) = pid2process(pid) { if let Some(flag) = SignalFlags::from_bits(signal) { + // println!("Getting PCB in src/syscall/process.rs sys_kill()"); process.inner_exclusive_access().signals |= flag; 0 } else { diff --git a/os/src/syscall/sync.rs b/os/src/syscall/sync.rs index 1180669..2d66317 100644 --- a/os/src/syscall/sync.rs +++ b/os/src/syscall/sync.rs @@ -18,6 +18,7 @@ pub fn sys_mutex_create(blocking: bool) -> isize { } else { Some(Arc::new(MutexBlocking::new())) }; + // println!("Getting PCB in src/syscall/sync.rs sys_mutex_create()"); let mut process_inner = process.inner_exclusive_access(); if let Some(id) = process_inner .mutex_list @@ -36,6 +37,7 @@ pub fn sys_mutex_create(blocking: bool) -> isize { pub fn sys_mutex_lock(mutex_id: usize) -> isize { let process = current_process(); + // println!("Getting PCB in src/syscall/sync.rs sys_mutex_lock()"); let process_inner = process.inner_exclusive_access(); let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap()); drop(process_inner); @@ -46,6 +48,7 @@ pub fn sys_mutex_lock(mutex_id: usize) -> isize { pub fn sys_mutex_unlock(mutex_id: usize) -> isize { let process = current_process(); + // println!("Getting PCB in src/syscall/sync.rs sys_mutex_unlock()"); let process_inner = process.inner_exclusive_access(); let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap()); drop(process_inner); @@ -56,6 +59,7 @@ pub fn sys_mutex_unlock(mutex_id: usize) -> isize { pub fn sys_semaphore_create(res_count: usize) -> isize { let process = current_process(); + // println!("Getting PCB in src/syscall/sync.rs sys_semaphore_create()"); let mut process_inner = process.inner_exclusive_access(); let id = if let Some(id) = process_inner .semaphore_list @@ -77,6 +81,7 @@ pub fn sys_semaphore_create(res_count: usize) -> isize { pub fn sys_semaphore_up(sem_id: usize) -> isize { let process = current_process(); + // println!("Getting PCB in src/syscall/sync.rs sys_semaphore_up()"); let process_inner = process.inner_exclusive_access(); let sem = Arc::clone(process_inner.semaphore_list[sem_id].as_ref().unwrap()); drop(process_inner); @@ -86,6 +91,7 @@ pub fn sys_semaphore_up(sem_id: usize) -> isize { pub fn sys_semaphore_down(sem_id: usize) -> isize { let process = current_process(); + // println!("Getting PCB in src/syscall/sync.rs sys_semaphore_down()"); let process_inner = process.inner_exclusive_access(); let sem = Arc::clone(process_inner.semaphore_list[sem_id].as_ref().unwrap()); drop(process_inner); @@ -95,6 +101,7 @@ pub fn sys_semaphore_down(sem_id: usize) -> isize { pub fn sys_condvar_create() -> isize { let process = current_process(); + // println!("Getting PCB in src/syscall/sync.rs sys_condvar_create()"); let mut process_inner = process.inner_exclusive_access(); let id = if let Some(id) = process_inner .condvar_list @@ -116,6 +123,7 @@ pub fn sys_condvar_create() -> isize { pub fn sys_condvar_signal(condvar_id: usize) -> isize { let process = current_process(); + // println!("Getting PCB in src/syscall/sync.rs sys_condcvar_signal()"); let process_inner = process.inner_exclusive_access(); let condvar = Arc::clone(process_inner.condvar_list[condvar_id].as_ref().unwrap()); drop(process_inner); @@ -125,6 +133,7 @@ pub fn sys_condvar_signal(condvar_id: usize) -> isize { pub fn sys_condvar_wait(condvar_id: usize, mutex_id: usize) -> isize { let process = current_process(); + // println!("Getting PCB in src/syscall/sync.rs sys_condvar_wait()"); let process_inner = process.inner_exclusive_access(); let condvar = Arc::clone(process_inner.condvar_list[condvar_id].as_ref().unwrap()); let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap()); diff --git a/os/src/syscall/thread.rs b/os/src/syscall/thread.rs index 3955d9d..dfd1f6b 100644 --- a/os/src/syscall/thread.rs +++ b/os/src/syscall/thread.rs @@ -23,6 +23,7 @@ pub fn sys_thread_create(entry: usize, arg: usize) -> isize { let new_task_inner = new_task.inner_exclusive_access(); let new_task_res = new_task_inner.res.as_ref().unwrap(); let new_task_tid = new_task_res.tid; + // println!("Getting PCB in src/syscall/thread.rs sys_thread_create()"); let mut process_inner = process.inner_exclusive_access(); // add new thread to current process let tasks = &mut process_inner.tasks; @@ -59,6 +60,7 @@ pub fn sys_waittid(tid: usize) -> i32 { let task = current_task().unwrap(); let process = task.process.upgrade().unwrap(); let task_inner = task.inner_exclusive_access(); + // println!("Getting PCB in src/syscall/thread.rs sys_waittid()"); let mut process_inner = process.inner_exclusive_access(); // a thread cannot wait for itself if task_inner.res.as_ref().unwrap().tid == tid { diff --git a/os/src/task/id.rs b/os/src/task/id.rs index 23ddf7b..09b4f26 100644 --- a/os/src/task/id.rs +++ b/os/src/task/id.rs @@ -130,6 +130,7 @@ impl TaskUserRes { ustack_base: usize, alloc_user_res: bool, ) -> Self { + // println!("Getting PCB in src/task/id.rs TaskUserRes::new()"); let tid = process.inner_exclusive_access().alloc_tid(); let task_user_res = Self { tid, @@ -144,6 +145,7 @@ impl TaskUserRes { pub fn alloc_user_res(&self) { let process = self.process.upgrade().unwrap(); + // println!("Getting PCB in src/task/id.rs alloc_user_res()"); let mut process_inner = process.inner_exclusive_access(); // alloc user stack let ustack_bottom = ustack_bottom_from_tid(self.ustack_base, self.tid); @@ -166,6 +168,7 @@ impl TaskUserRes { fn dealloc_user_res(&self) { // dealloc tid let process = self.process.upgrade().unwrap(); + // println!("Getting PCB in src/task/id.rs dealloc_user_res()"); let mut process_inner = process.inner_exclusive_access(); // dealloc ustack manually let ustack_bottom_va: VirtAddr = ustack_bottom_from_tid(self.ustack_base, self.tid).into(); @@ -181,6 +184,7 @@ impl TaskUserRes { #[allow(unused)] pub fn alloc_tid(&mut self) { + // println!("Getting PCB in src/task/id.rs alloc_tid()"); self.tid = self .process .upgrade() @@ -191,6 +195,7 @@ impl TaskUserRes { pub fn dealloc_tid(&self) { let process = self.process.upgrade().unwrap(); + // println!("Getting PCB in src/task/id.rs dealloc_tid()"); let mut process_inner = process.inner_exclusive_access(); process_inner.dealloc_tid(self.tid); } @@ -201,6 +206,7 @@ impl TaskUserRes { pub fn trap_cx_ppn(&self) -> PhysPageNum { let process = self.process.upgrade().unwrap(); + // println!("Getting PCB in src/task/id.rs trap_cx_ppn()"); let process_inner = process.inner_exclusive_access(); let trap_cx_bottom_va: VirtAddr = trap_cx_bottom_from_tid(self.tid).into(); process_inner diff --git a/os/src/task/manager.rs b/os/src/task/manager.rs index 7672cf5..af9593d 100644 --- a/os/src/task/manager.rs +++ b/os/src/task/manager.rs @@ -35,6 +35,7 @@ pub fn add_task(task: Arc) { } pub fn wakeup_task(task: Arc) { + // println!("Getting PCB in src/task/manager.rs wakeup_task()"); let mut task_inner = task.inner_exclusive_access(); task_inner.task_status = TaskStatus::Ready; drop(task_inner); diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs index ca97e21..994fdf9 100644 --- a/os/src/task/mod.rs +++ b/os/src/task/mod.rs @@ -11,6 +11,7 @@ mod task; use self::id::TaskUserRes; use crate::fs::{open_file, OpenFlags}; use crate::sbi::shutdown; +use alloc::string::ToString; use alloc::{sync::Arc, vec::Vec}; use lazy_static::*; use manager::fetch_task; @@ -32,6 +33,7 @@ pub fn suspend_current_and_run_next() { let task = take_current_task().unwrap(); // ---- access current TCB exclusively + // println!("Getting PCB in src/task/mod.rs suspend_current_and_run_next()"); let mut task_inner = task.inner_exclusive_access(); let task_cx_ptr = &mut task_inner.task_cx as *mut TaskContext; // Change status to Ready @@ -48,6 +50,7 @@ pub fn suspend_current_and_run_next() { /// This function must be followed by a schedule pub fn block_current_task() -> *mut TaskContext { let task = take_current_task().unwrap(); + // println!("Getting PCB in src/task/mod.rs block_current_task()"); let mut task_inner = task.inner_exclusive_access(); task_inner.task_status = TaskStatus::Blocked; &mut task_inner.task_cx as *mut TaskContext @@ -89,6 +92,7 @@ pub fn exit_current_and_run_next(exit_code: i32) { } } remove_from_pid2process(pid); + // println!("Getting PCB in src/task/mod.rs exit_current_and_run_next()"); let mut process_inner = process.inner_exclusive_access(); // mark this process as a zombie process process_inner.is_zombie = true; @@ -97,8 +101,10 @@ pub fn exit_current_and_run_next(exit_code: i32) { { // move all child processes under init process + // println!("Getting PCB AGAIN in src/task/mod.rs exit_current_and_run_next()"); let mut initproc_inner = INITPROC.inner_exclusive_access(); for child in process_inner.children.iter() { + // println!("Getting PCB AGAIN AGAIN in src/task/mod.rs exit_current_and_run_next()"); child.inner_exclusive_access().parent = Some(Arc::downgrade(&INITPROC)); initproc_inner.children.push(child.clone()); } @@ -121,6 +127,7 @@ pub fn exit_current_and_run_next(exit_code: i32) { drop(process_inner); recycle_res.clear(); + // println!("Getting PCB AGAIN AGAIN AGAIN in src/task/mod.rs exit_current_and_run_next()"); let mut process_inner = process.inner_exclusive_access(); process_inner.children.clear(); // deallocate other data in user space i.e. program code/data section @@ -138,7 +145,7 @@ lazy_static! { pub static ref INITPROC: Arc = { let inode = open_file("initproc", OpenFlags::RDONLY).unwrap(); let v = inode.read_all(); - ProcessControlBlock::new(v.as_slice()) + ProcessControlBlock::new(v.as_slice(),"initproc".to_string()) }; } @@ -148,12 +155,27 @@ pub fn add_initproc() { pub fn check_signals_of_current() -> Option<(i32, &'static str)> { let process = current_process(); + // println!("Getting PCB in src/task/mod.rs check_signals_of_current()"); let process_inner = process.inner_exclusive_access(); process_inner.signals.check_error() } pub fn current_add_signal(signal: SignalFlags) { let process = current_process(); + // println!("Getting PCB in src/task/mod.rs current_add_signal()"); let mut process_inner = process.inner_exclusive_access(); process_inner.signals |= signal; } + +/// use this for uprobe +#[no_mangle] +pub extern "C" fn get_exec_path() -> alloc::string::String{ + println!("get_exec_path"); + // get path of current thread + let my_process = ¤t_task().unwrap().process; + let rrr = my_process.upgrade().unwrap(); + // println!("Getting PCB in src/task/mod.rs get_exec_path()"); + let ret=my_process.upgrade().unwrap().inner_exclusive_access().path.clone(); + println!("get_exec_path succeeded. path = {}", ret); + ret +} \ No newline at end of file diff --git a/os/src/task/process.rs b/os/src/task/process.rs index b3976b7..448862a 100644 --- a/os/src/task/process.rs +++ b/os/src/task/process.rs @@ -4,7 +4,7 @@ use super::TaskControlBlock; use super::{add_task, SignalFlags}; use super::{pid_alloc, PidHandle}; use crate::fs::{File, Stdin, Stdout}; -use crate::mm::{translated_refmut, MemorySet, KERNEL_SPACE}; +use crate::mm::{translated_refmut, MemorySet, KERNEL_SPACE, PageTable}; use crate::sync::{Condvar, Mutex, Semaphore, UPIntrFreeCell, UPIntrRefMut}; use crate::trap::{trap_handler, TrapContext}; use alloc::string::String; @@ -32,6 +32,7 @@ pub struct ProcessControlBlockInner { pub mutex_list: Vec>>, pub semaphore_list: Vec>>, pub condvar_list: Vec>>, + pub path: String, } impl ProcessControlBlockInner { @@ -71,7 +72,7 @@ impl ProcessControlBlock { self.inner.exclusive_access() } - pub fn new(elf_data: &[u8]) -> Arc { + pub fn new(elf_data: &[u8],path:String) -> Arc { // memory_set with elf program headers/trampoline/trap context/user stack let (memory_set, ustack_base, entry_point) = MemorySet::from_elf(elf_data); // allocate a pid @@ -99,6 +100,7 @@ impl ProcessControlBlock { mutex_list: Vec::new(), semaphore_list: Vec::new(), condvar_list: Vec::new(), + path:path, }) }, }); @@ -122,6 +124,7 @@ impl ProcessControlBlock { trap_handler as usize, ); // add main thread to the process + // println!("Getting PCB in src/task/process.rs PCB::new()"); let mut process_inner = process.inner_exclusive_access(); process_inner.tasks.push(Some(Arc::clone(&task))); drop(process_inner); @@ -132,12 +135,16 @@ impl ProcessControlBlock { } /// Only support processes with a single thread. - pub fn exec(self: &Arc, elf_data: &[u8], args: Vec) { + pub fn exec(self: &Arc, elf_data: &[u8], args: Vec, path:String) { + // println!("Getting PCB in src/task/process.rs PCB::exec()"); assert_eq!(self.inner_exclusive_access().thread_count(), 1); + // println!("Getting PCB AGAIN in src/task/process.rs PCB::exec()"); + self.inner_exclusive_access().path=path; // memory_set with elf program headers/trampoline/trap context/user stack let (memory_set, ustack_base, entry_point) = MemorySet::from_elf(elf_data); let new_token = memory_set.token(); // substitute memory_set + // println!("Getting PCB AGAIN AGAIN in src/task/process.rs PCB::exec()"); self.inner_exclusive_access().memory_set = memory_set; // then we alloc user resource for main thread again // since memory_set has been changed @@ -186,7 +193,9 @@ impl ProcessControlBlock { /// Only support processes with a single thread. pub fn fork(self: &Arc) -> Arc { + // println!("Getting PCB in src/task/process.rs PCB::fork()"); let mut parent = self.inner_exclusive_access(); + let parent_path = parent.path.clone(); assert_eq!(parent.thread_count(), 1); // clone parent's memory_set completely including trampoline/ustacks/trap_cxs let memory_set = MemorySet::from_existed_user(&parent.memory_set); @@ -218,12 +227,14 @@ impl ProcessControlBlock { mutex_list: Vec::new(), semaphore_list: Vec::new(), condvar_list: Vec::new(), + path: parent_path, }) }, }); // add child parent.children.push(Arc::clone(&child)); // create main thread of child process + // println!("Getting PCB AGAIN AGAIN in src/task/process.rs PCB::fork()"); let task = Arc::new(TaskControlBlock::new( Arc::clone(&child), parent @@ -237,6 +248,7 @@ impl ProcessControlBlock { // but mention that we allocate a new kstack here false, )); + // println!("Getting PCB AGAIN AGAIN AGAIN in src/task/process.rs PCB::fork()"); // attach task to child process let mut child_inner = child.inner_exclusive_access(); child_inner.tasks.push(Some(Arc::clone(&task))); diff --git a/os/src/task/task.rs b/os/src/task/task.rs index ffc5c0e..cd4be6c 100644 --- a/os/src/task/task.rs +++ b/os/src/task/task.rs @@ -22,6 +22,7 @@ impl TaskControlBlock { pub fn get_user_token(&self) -> usize { let process = self.process.upgrade().unwrap(); + // println!("Getting PCB in src/task/task.rs get_user_token()"); let inner = process.inner_exclusive_access(); inner.memory_set.token() } diff --git a/os/src/trap/context.rs b/os/src/trap/context.rs index cccc729..b1ad25e 100644 --- a/os/src/trap/context.rs +++ b/os/src/trap/context.rs @@ -1,39 +1,3 @@ -use riscv::register::sstatus::{self, Sstatus, SPP}; +extern crate trap_context_riscv; -#[repr(C)] -#[derive(Debug,Clone)] -pub struct TrapContext { - pub x: [usize; 32], - pub sstatus: Sstatus, - pub sepc: usize, - pub kernel_satp: usize, - pub kernel_sp: usize, - pub trap_handler: usize, -} - -impl TrapContext { - pub fn set_sp(&mut self, sp: usize) { - self.x[2] = sp; - } - pub fn app_init_context( - entry: usize, - sp: usize, - kernel_satp: usize, - kernel_sp: usize, - trap_handler: usize, - ) -> Self { - let mut sstatus = sstatus::read(); - // set CPU privilege to User after trapping back - sstatus.set_spp(SPP::User); - let mut cx = Self { - x: [0; 32], - sstatus, - sepc: entry, - kernel_satp, - kernel_sp, - trap_handler, - }; - cx.set_sp(sp); - cx - } -} +pub use trap_context_riscv::TrapContext as TrapContext; \ No newline at end of file diff --git a/os/src/trap/mod.rs b/os/src/trap/mod.rs index 6850fde..45575f3 100644 --- a/os/src/trap/mod.rs +++ b/os/src/trap/mod.rs @@ -1,7 +1,11 @@ mod context; +extern crate ruprobes; + use crate::config::TRAMPOLINE; //use crate::probe::kprobes_breakpoint_handler; +use ruprobes::uprobes_trap_handler; +use trapframe::{UserContext, GeneralRegs}; use crate::syscall::syscall; use crate::task::{ check_signals_of_current, current_add_signal, current_trap_cx, current_trap_cx_user_va, @@ -14,6 +18,7 @@ use riscv::register::{ scause::{self, Exception, Interrupt, Trap}, sie, sip, sscratch, sstatus, stval, stvec, }; +use ruprobes::*; global_asm!(include_str!("trap.S")); @@ -64,6 +69,15 @@ pub fn trap_handler() -> ! { let stval = stval::read(); // println!("into {:?}", scause.cause()); match scause.cause() { + Trap::Exception(Exception::Breakpoint) => { // uprobe + let mut cx = current_trap_cx(); + println!("[user] breakpoint at {:#x}", cx.sepc); + unsafe { + // This works but looks messy. We should use a clearer syntax + // TrapContext(from rCore-Tutorial) => UserContext (from rCore-Plus, supported by ruprobes) + uprobes_trap_handler(cx); + } + } Trap::Exception(Exception::UserEnvCall) => { // jump to next instruction anyway let mut cx = current_trap_cx(); diff --git a/side-stub.py b/side-stub.py index 9071972..2209e89 100644 --- a/side-stub.py +++ b/side-stub.py @@ -21,6 +21,7 @@ class SideStub(gdb.MICommand): -side-stub tracepoint-then-get-registers -side-stub tracepoint-then-get-memory -side-stub tracepoint-then-get-arguments + -side-stub tracepoint_user_program_then_get_registers """ def __init__(self): # 在构造函数中注册该命令的名字 @@ -59,6 +60,8 @@ def invoke(self, argv): elif (argv[0]=='tracepoint_then_get_arguments'): self.tracepoint_then_get_arguments(argv[1]) + elif (argv[0]=='tracepoint_user_program_then_get_registers'): + self.tracepoint_user_program_then_get_registers(argv[1],argv[2]) else: raise gdb.GdbError('输入参数数目不对,help side-stub以获得用法') @@ -73,6 +76,10 @@ def tracepoint_then_get_registers(self,symbol): self.ser.write(('$'+command+symbol+'#'+hex(sum(command.encode('ascii')) % 256)[2:]).encode('ascii')) + def tracepoint_user_program_then_get_registers(self,program_name,addr): + command = 'vTU' # it's obvious that there should NOT be : in program_name, + self.ser.write(('$'+command+program_name+':'+addr+'#'+hex(sum(command.encode('ascii')) % 256)[2:]).encode('ascii')) + def tracepoint_then_get_arguments(self,fn_name): pass def disconnect(self): diff --git a/user/build/app/matrix.rs b/user/build/app/matrix.rs index 9ebf48f..007e185 100644 --- a/user/build/app/matrix.rs +++ b/user/build/app/matrix.rs @@ -12,6 +12,7 @@ const N: usize = 10; static P: i32 = 10007; type Arr = [[i32; N]; N]; +#[inline(never)] fn work(times: isize) { let mut a: Arr = Default::default(); let mut b: Arr = Default::default(); @@ -46,6 +47,9 @@ fn work(times: isize) { #[no_mangle] pub fn main() -> i32 { + + println!("main is {:x}",main as usize); + println!("work is {:x}",work as usize); for _ in 0..NUM { let pid = fork(); if pid == 0 { diff --git a/user/build/bin/matrix.bin b/user/build/bin/matrix.bin index 930e8f759d44c47b3dfe85493e92e677ba1fda28..4baf772356c7795683a25fdd8227c19a78a65098 100755 GIT binary patch delta 5407 zcmbtX3s6&68b0@i8$tvG0=a;wm%NA(V~7Efy2vX*DB9NgXt&#LFovpaePSQ8c7sR| zTiQ{NHFc)bK`ThpuG2QwwbpjAE7aPVP9LLgyKOfVLUERMX~ixe)%|bIiM_zsnVsI5 zPax9PH?5 z73N-JRr087WMth6>|_cHwU#68Dz2ejB}ZvE3Z?C^`-;K=_Jj9~=#6*~2G18#PjD}0 zrU42e0PPEf5PkTl)n%H(0=eX2w+MAM|No@Z>`PYB=XZ00Rw>2LTLZUG=Tvl5pZa92M8qS0=9f*?roD z`$x5wsIl_BO&AfL26q77bN&xz@@es z@EH<(4&YKd8}JMXJ{xeU?J)r%T|$@z1gUKVJXL~c0WP%-fU70A9&o9x13X!RYXO(o z9zg?yBncrC2vS=BJVAob1YBy*06boTPX}CTX8;~6!KVTKi|wfs(t)6q5Ym7kwNn9C zNN^r-sjYV1&nd`G>3n+pPMN$z(A|u^ZiRmmGpG1ggpbKb z@L^6v@4a9hLOh(FDo($h&x2Opw*7TGIZ1+@5iCTp8c#qOqhsP*zl4+TIvLlw6)#i@ zZ0lD1fO0z1gtsf_GY$B-Qc!k`@}aPDec)^5c1GEGABoU)e_%~)2E)3S;QF`?Hn#)H zzowEm(1(R{C??eAl&`6V%t;7)xerpqja?P|d=QK-ufn?c1^N_Oz8vu@#iaX^Ws4&H zw&a~R{WZzBKE7Y$D8n3L^XT&oxg<&Zij_;pV9`(^&p)5R;X%zIz~C(_)r~R;5_X z2F^}xVsvd;IW2_WXnb;os7NNZ2%dcrKi-t6R(g%pG~hjnnQ1+~Wgu7oO%IXn?;*1N zJ%RIyYZ$gM0b5jRv01FTIKvxl>gDXFes+M=?v#*UyTZnCR>y}AM&u;D$f2cwxw8EZ!BJ$Vv7xN{vgri)VMt*JBjP} z_h+MH1O7qc)+gY;6obaVT7yJRo?z`Y^_vIafI^2@rPn8jkHU^DY*weH+pqZtjSBw| zuNOmwM*PE6JOMk^5n|<$5+@UUJb_ZBK$fTxx_hh@bduQ&`tSom5 zyZ;eVs{7w~G<9W0=o^2(5nc2yYotfsr*S_M!iBH#!)bps(Eqr&tZpCYb|{#DZt z*$L*WrV+YQ$UhKg_Yc+&Wi!XdOx-@n7kj-dcFs;Wv_50H9IG&YXZisIt`dRHHq!_Q z{Os>00vv-|W+$_)GJIn838nr8-T-0bh7tT|c023diiJ5wqyA}scXqY}(wsjaQ3H z-W7wF=BBd3qj+s@#@fH1dSbJDNHb_|Fa?{pWpbuog_ulucU?HAhucuouZ=x~UW#Q9 z@p9*Q9(1CQPE{!8IG$=GRn=+fm@Xbx9uEBX6$-t>Mr&@#!~?myzeevY2$0+#A)w*jMVi0phA&2>t<0cQKTq)H!14DZiOP{W>Fn%xTp(PCKlED zBTmZKS?WIE=)LE?ObU<>h#y&`Ea4myKgos+w47iUuF2Q2?tkFsd`(j8K6t{8gg?hf zQAM~TU#IT;oCrC-!dx8{3ciC!^B-WnAK~9G(0Dq%@b)?S%fOC#DWB65WtR`uAMVZF zq}Hrv`o(vhCe(pYaiY9e6GRF{^Kp8#ip-_^{i&Wh+8=mSSb;v9trF1l~r+!G|S^f!_iANhv=G-2#Xyj51|Ba{#B6 zEte;TV>_lJI!2=7K@$bdzcIi> zyk8CsBK%yg4NzLuk zD+&{x6~rBxg)R85in+{5yrY88%%_RM8(;X^Lb)1hpTuufEXp)i5(PLF!&f1-#al|e zP6pB{|H$OD-%e;#h!+K@9U1m94MtdSqT!cs&nix>B6d6-CD0Qra_$)?q&Qh6v5JRp zvsqQ;i4!w;9`C4{nfWe_Ag2)~E1aoX#9EdF*d@m}=5ze|vPHLCf?-=gdG}Hou}S8;xCslU6NdT5$EMa;7El@~RurZ#W;9{SV<{o#+4n delta 5083 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 BV>AE& diff --git a/user/build/elf/matrix.elf b/user/build/elf/matrix.elf index a04bff8ef5fc461be4942f1c527b8db4af5ba3cd..f69a8889b3ed5c5fce25803b2bdc3432d66609ef 100755 GIT binary patch literal 152648 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 literal 150240 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 -`XL7+cWksk(n#E$?y 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 tuZF%Jrdf8u)z5*erPVtW;NL1;g}+S!tf.regs[i], 0, 0); + } + bpf_trace_printk("#", 0, 0, 0); + bpf_trace_printk("00", 0, 0, 0); //todo: modulo 256 checksum + return 0; +} \ No newline at end of file diff --git a/user/ebpf/kern/uprobe.h b/user/ebpf/kern/uprobe.h new file mode 100644 index 0000000..6f22fd2 --- /dev/null +++ b/user/ebpf/kern/uprobe.h @@ -0,0 +1,56 @@ +#ifndef __LIBS_UPROBE_H__ +#define __LIBS_UPROBE_H__ + +typedef unsigned long long size_t; + +#define UPROBE_TYPE_UPROBE_SYNCFUNC 0 +// #define KRPOBE_TYPE_KRETPROBE_ENTRY 1 +// #define KPROBE_TYPE_KRETPROBE_EXIT 2 + +struct uprobe_bpf_ctx { + size_t ptype; + size_t paddr; + struct { + union { + size_t regs[32]; + struct { + size_t zero; + size_t ra; + size_t sp; + size_t gp; + size_t tp; + size_t t0; + size_t t1; + size_t t2; + size_t s0; + size_t s1; + size_t a0; + size_t a1; + size_t a2; + size_t a3; + size_t a4; + size_t a5; + size_t a6; + size_t a7; + size_t s2; + size_t s3; + size_t s4; + size_t s5; + size_t s6; + size_t s7; + size_t s8; + size_t s9; + size_t s10; + size_t s11; + size_t t3; + size_t t4; + size_t t5; + size_t t6; + } general; + }; + size_t sstatus; + size_t sepc; + } tf; +}; + +#endif \ No newline at end of file diff --git a/user/src/bin/matrix.rs b/user/src/bin/matrix.rs index 9ebf48f..007e185 100644 --- a/user/src/bin/matrix.rs +++ b/user/src/bin/matrix.rs @@ -12,6 +12,7 @@ const N: usize = 10; static P: i32 = 10007; type Arr = [[i32; N]; N]; +#[inline(never)] fn work(times: isize) { let mut a: Arr = Default::default(); let mut b: Arr = Default::default(); @@ -46,6 +47,9 @@ fn work(times: isize) { #[no_mangle] pub fn main() -> i32 { + + println!("main is {:x}",main as usize); + println!("work is {:x}",work as usize); for _ in 0..NUM { let pid = fork(); if pid == 0 {