Skip to main content

vminer_core/
os.rs

1use crate::{PhysicalAddress, VirtualAddress, VmResult};
2use alloc::{format, string::String, vec::Vec};
3use core::ops::ControlFlow;
4
5#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
6pub struct Module(pub VirtualAddress);
7
8#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
9pub struct Thread(pub VirtualAddress);
10
11#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
12pub struct Process(pub VirtualAddress);
13
14#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
15pub struct Vma(pub VirtualAddress);
16
17#[derive(Debug, Clone, Copy)]
18pub struct VmaFlags(pub u64);
19
20impl VmaFlags {
21    pub const READ: Self = Self(0x1);
22    pub const WRITE: Self = Self(0x2);
23    pub const EXEC: Self = Self(0x4);
24
25    #[inline]
26    pub fn is_read(self) -> bool {
27        self.0 & Self::READ.0 != 0
28    }
29
30    #[inline]
31    pub fn is_write(self) -> bool {
32        self.0 & Self::WRITE.0 != 0
33    }
34
35    #[inline]
36    pub fn is_exec(self) -> bool {
37        self.0 & Self::EXEC.0 != 0
38    }
39}
40
41impl core::ops::BitOr for VmaFlags {
42    type Output = Self;
43
44    #[inline]
45    fn bitor(self, rhs: Self) -> Self {
46        Self(self.0 | rhs.0)
47    }
48}
49
50impl core::ops::BitOrAssign for VmaFlags {
51    #[inline]
52    fn bitor_assign(&mut self, rhs: Self) {
53        self.0 |= rhs.0
54    }
55}
56
57#[inline]
58fn find<'a, T: Copy>(
59    result: &'a mut Option<T>,
60    mut predicate: impl FnMut(T) -> VmResult<bool> + 'a,
61) -> impl FnMut(T) -> VmResult<ControlFlow<()>> + 'a {
62    move |item| {
63        Ok(if predicate(item)? {
64            *result = Some(item);
65            ControlFlow::Break(())
66        } else {
67            ControlFlow::Continue(())
68        })
69    }
70}
71
72#[inline]
73#[allow(clippy::needless_lifetimes)]
74fn push_to<'a, T>(vec: &'a mut Vec<T>) -> impl FnMut(T) -> VmResult<ControlFlow<()>> + 'a {
75    move |item| {
76        vec.push(item);
77        Ok(ControlFlow::Continue(()))
78    }
79}
80
81#[derive(Debug, Clone)]
82pub struct StackFrame {
83    pub start: Option<VirtualAddress>,
84    pub size: Option<u64>,
85    pub stack_pointer: VirtualAddress,
86    pub instruction_pointer: VirtualAddress,
87    pub module: Option<Module>,
88}
89
90pub trait Os: crate::HasVcpus {
91    fn read_virtual_memory(
92        &self,
93        mmu_addr: PhysicalAddress,
94        addr: VirtualAddress,
95        buf: &mut [u8],
96    ) -> VmResult<()>;
97
98    fn try_read_virtual_memory(
99        &self,
100        mmu_addr: PhysicalAddress,
101        addr: VirtualAddress,
102        buf: &mut [u8],
103    ) -> VmResult<()>;
104
105    fn read_process_memory(
106        &self,
107        _proc: Process,
108        mmu_addr: PhysicalAddress,
109        addr: VirtualAddress,
110        buf: &mut [u8],
111    ) -> VmResult<()> {
112        self.read_virtual_memory(mmu_addr, addr, buf)
113    }
114
115    fn try_read_process_memory(
116        &self,
117        _proc: Process,
118        mmu_addr: PhysicalAddress,
119        addr: VirtualAddress,
120        buf: &mut [u8],
121    ) -> VmResult<()> {
122        self.try_read_virtual_memory(mmu_addr, addr, buf)
123    }
124
125    fn read_kernel_memory(&self, addr: VirtualAddress, buf: &mut [u8]) -> VmResult<()> {
126        self.read_virtual_memory(self.kernel_pgd(), addr, buf)
127    }
128
129    fn kernel_pgd(&self) -> PhysicalAddress;
130
131    fn for_each_kernel_module(
132        &self,
133        f: &mut dyn FnMut(Module) -> VmResult<ControlFlow<()>>,
134    ) -> VmResult<()>;
135
136    fn init_process(&self) -> VmResult<Process>;
137    fn current_thread(&self, vcpu: crate::VcpuId) -> VmResult<Thread>;
138    fn current_process(&self, vcpu: crate::VcpuId) -> VmResult<Process> {
139        let thread = self.current_thread(vcpu)?;
140        self.thread_process(thread)
141    }
142    fn find_process_by_name(&self, name: &str) -> VmResult<Option<Process>> {
143        let mut proc = None;
144        self.for_each_process(&mut find(&mut proc, |p| Ok(self.process_name(p)? == name)))?;
145        Ok(proc)
146    }
147    fn find_process_by_id(&self, pid: u64) -> VmResult<Option<Process>> {
148        let mut proc = None;
149        self.for_each_process(&mut find(&mut proc, |p| Ok(self.process_id(p)? == pid)))?;
150        Ok(proc)
151    }
152
153    fn process_is_kernel(&self, proc: Process) -> VmResult<bool>;
154    fn process_id(&self, proc: Process) -> VmResult<u64>;
155    fn process_name(&self, proc: Process) -> VmResult<String>;
156    fn process_pgd(&self, proc: Process) -> VmResult<PhysicalAddress>;
157    fn process_path(&self, proc: Process) -> VmResult<Option<String>>;
158    fn process_parent(&self, proc: Process) -> VmResult<Process>;
159    fn process_parent_id(&self, proc: Process) -> VmResult<u64>;
160    fn process_for_each_child(
161        &self,
162        proc: Process,
163        f: &mut dyn FnMut(Process) -> VmResult<ControlFlow<()>>,
164    ) -> VmResult<()>;
165    fn process_collect_children(&self, proc: Process) -> VmResult<Vec<Process>> {
166        let mut procs = Vec::new();
167        self.process_for_each_child(proc, &mut push_to(&mut procs))?;
168        Ok(procs)
169    }
170    fn process_for_each_thread(
171        &self,
172        proc: Process,
173        f: &mut dyn FnMut(Thread) -> VmResult<ControlFlow<()>>,
174    ) -> VmResult<()>;
175    fn process_collect_threads(&self, proc: Process) -> VmResult<Vec<Thread>> {
176        let mut threads = Vec::new();
177        self.process_for_each_thread(proc, &mut push_to(&mut threads))?;
178        Ok(threads)
179    }
180    fn process_for_each_module(
181        &self,
182        proc: Process,
183        f: &mut dyn FnMut(Module) -> VmResult<ControlFlow<()>>,
184    ) -> VmResult<()>;
185
186    fn process_collect_modules(&self, proc: Process) -> VmResult<Vec<Module>> {
187        let mut modules = Vec::new();
188        self.process_for_each_module(proc, &mut push_to(&mut modules))?;
189        Ok(modules)
190    }
191    fn for_each_process(
192        &self,
193        f: &mut dyn FnMut(Process) -> VmResult<ControlFlow<()>>,
194    ) -> VmResult<()>;
195    fn collect_processes(&self) -> VmResult<Vec<Process>> {
196        let mut procs = Vec::new();
197        self.for_each_process(&mut push_to(&mut procs))?;
198        Ok(procs)
199    }
200    fn process_for_each_vma(
201        &self,
202        proc: Process,
203        f: &mut dyn FnMut(Vma) -> VmResult<ControlFlow<()>>,
204    ) -> VmResult<()>;
205    fn process_collect_vmas(&self, proc: Process) -> VmResult<Vec<Vma>> {
206        let mut vmas = Vec::new();
207        self.process_for_each_vma(proc, &mut push_to(&mut vmas))?;
208        Ok(vmas)
209    }
210
211    fn process_find_vma_by_address(
212        &self,
213        proc: Process,
214        addr: VirtualAddress,
215    ) -> VmResult<Option<Vma>> {
216        let mut vma = None;
217        self.process_for_each_vma(proc, &mut find(&mut vma, |v| self.vma_contains(v, addr)))?;
218        Ok(vma)
219    }
220
221    fn process_callstack(
222        &self,
223        proc: Process,
224        f: &mut dyn FnMut(&StackFrame) -> VmResult<ControlFlow<()>>,
225    ) -> VmResult<()> {
226        // Get pointers from the current CPU
227        #[allow(clippy::never_loop)]
228        let (instruction_pointer, stack_pointer, base_pointer) = 'res: loop {
229            for vcpu in self.iter_vcpus() {
230                if self.current_process(vcpu)? == proc {
231                    break 'res (
232                        self.instruction_pointer(vcpu)?,
233                        self.stack_pointer(vcpu)?,
234                        self.base_pointer(vcpu)?,
235                    );
236                }
237            }
238
239            return Err(crate::VmError::new("Not a running process"));
240        };
241
242        self.process_callstack_with_regs(proc, instruction_pointer, stack_pointer, base_pointer, f)
243    }
244
245    fn process_callstack_with_regs(
246        &self,
247        proc: Process,
248        instruction_pointer: VirtualAddress,
249        stack_pointer: VirtualAddress,
250        base_pointer: Option<VirtualAddress>,
251        f: &mut dyn FnMut(&StackFrame) -> VmResult<ControlFlow<()>>,
252    ) -> VmResult<()>;
253
254    fn thread_process(&self, thread: Thread) -> VmResult<Process>;
255    fn thread_id(&self, thread: Thread) -> VmResult<u64>;
256    fn thread_name(&self, thread: Thread) -> VmResult<Option<String>>;
257
258    fn vma_path(&self, vma: Vma) -> VmResult<Option<String>>;
259    fn vma_start(&self, vma: Vma) -> VmResult<VirtualAddress>;
260    fn vma_end(&self, vma: Vma) -> VmResult<VirtualAddress>;
261    fn vma_flags(&self, vma: Vma) -> VmResult<VmaFlags>;
262    fn vma_contains(&self, vma: Vma, addr: VirtualAddress) -> VmResult<bool> {
263        Ok(self.vma_start(vma)? <= addr && addr < self.vma_end(vma)?)
264    }
265
266    fn module_span(
267        &self,
268        module: Module,
269        proc: Process,
270    ) -> VmResult<(VirtualAddress, VirtualAddress)>;
271
272    #[inline]
273    fn module_contains(
274        &self,
275        module: Module,
276        proc: Process,
277        addr: VirtualAddress,
278    ) -> VmResult<bool> {
279        let (start, end) = self.module_span(module, proc)?;
280        Ok((start..end).contains(&addr))
281    }
282    fn module_name(&self, module: Module, proc: Process) -> VmResult<String>;
283    fn module_path(&self, module: Module, proc: Process) -> VmResult<String>;
284    fn find_module_by_address(
285        &self,
286        proc: Process,
287        addr: VirtualAddress,
288    ) -> VmResult<Option<Module>> {
289        let mut result = None;
290
291        {
292            let mut find = find(&mut result, |m| self.module_contains(m, proc, addr));
293            if addr.is_kernel() {
294                self.for_each_kernel_module(&mut find)?;
295            } else {
296                self.process_for_each_module(proc, &mut find)?;
297            }
298        }
299
300        Ok(result)
301    }
302
303    fn module_symbols(
304        &self,
305        proc: Process,
306        module: Module,
307    ) -> VmResult<Option<&crate::ModuleSymbols>>;
308
309    fn module_resolve_symbol_exact(
310        &self,
311        addr: VirtualAddress,
312        proc: Process,
313        module: Module,
314    ) -> VmResult<Option<&str>> {
315        let syms = match self.module_symbols(proc, module)? {
316            Some(syms) => syms,
317            None => return Ok(None),
318        };
319
320        let (mod_start, mod_end) = self.module_span(module, proc)?;
321        if !(mod_start..mod_end).contains(&addr) {
322            return Err(crate::VmError::new("address not in module"));
323        }
324        let addr = VirtualAddress((addr - mod_start) as u64);
325
326        Ok(syms.get_symbol(addr))
327    }
328
329    fn module_resolve_symbol(
330        &self,
331        addr: VirtualAddress,
332        proc: Process,
333        module: Module,
334    ) -> VmResult<Option<(&str, u64)>> {
335        let syms = match self.module_symbols(proc, module)? {
336            Some(syms) => syms,
337            None => return Ok(None),
338        };
339
340        let (mod_start, mod_end) = self.module_span(module, proc)?;
341        if !(mod_start..mod_end).contains(&addr) {
342            return Err(crate::VmError::new("address not in module"));
343        }
344        let addr = VirtualAddress((addr - mod_start) as u64);
345
346        Ok(syms.get_symbol_inexact(addr))
347    }
348
349    fn resolve_symbol_exact(&self, addr: VirtualAddress, proc: Process) -> VmResult<Option<&str>> {
350        match self.find_module_by_address(proc, addr)? {
351            Some(module) => self.module_resolve_symbol_exact(addr, proc, module),
352            None => Ok(None),
353        }
354    }
355
356    fn resolve_symbol(&self, addr: VirtualAddress, proc: Process) -> VmResult<Option<(&str, u64)>> {
357        match self.find_module_by_address(proc, addr)? {
358            Some(module) => self.module_resolve_symbol(addr, proc, module),
359            None => Ok(None),
360        }
361    }
362
363    fn format_symbol(
364        &self,
365        proc: Process,
366        addr: VirtualAddress,
367        demangle: bool,
368    ) -> VmResult<String> {
369        match self.find_module_by_address(proc, addr)? {
370            Some(module) => self.format_symbol_with_module(proc, module, addr, None, demangle),
371            None => format_symbol_without_module(self, proc, addr, None),
372        }
373    }
374
375    fn format_symbol_with_module(
376        &self,
377        proc: Process,
378        module: Module,
379        addr: VirtualAddress,
380        fun_start: Option<VirtualAddress>,
381        demangle: bool,
382    ) -> VmResult<String> {
383        let (mod_start, _) = self.module_span(module, proc)?;
384        let mod_name = self.module_name(module, proc)?;
385
386        let symbol = match fun_start {
387            Some(fun_start) => self
388                .module_resolve_symbol_exact(fun_start, proc, module)
389                .map(|s| s.map(|s| (s, (addr - fun_start) as u64))),
390            None => self.module_resolve_symbol(addr, proc, module),
391        };
392        let symbol = symbol.unwrap_or_else(|err| {
393            log::error!("{err}");
394            None
395        });
396
397        fn do_format(
398            addr: VirtualAddress,
399            fun_start: Option<VirtualAddress>,
400            symbol: Option<(&str, u64)>,
401            mod_name: String,
402            mod_start: VirtualAddress,
403            demangle: bool,
404        ) -> String {
405            let symbol = symbol.map(|(s, o)| {
406                (
407                    if demangle {
408                        crate::symbols::demangle(s)
409                    } else {
410                        alloc::borrow::Cow::Borrowed(s)
411                    },
412                    o,
413                )
414            });
415
416            match (symbol, fun_start) {
417                (Some((symbol, 0)), _) => format!("{mod_name}!{symbol}"),
418                (Some((symbol, offset)), _) => format!("{mod_name}!{symbol}+{offset:#x}"),
419                (None, Some(fun_start)) => match addr - fun_start {
420                    0 => format!("{mod_name}!{:#x}", fun_start - mod_start),
421                    offset => format!("{mod_name}!{:#x}+{offset:#x}", fun_start - mod_start),
422                },
423                (None, None) => format!("{mod_name}!{:#x}", addr - mod_start),
424            }
425        }
426
427        Ok(do_format(
428            addr, fun_start, symbol, mod_name, mod_start, demangle,
429        ))
430    }
431
432    fn format_stackframe_symbol(
433        &self,
434        proc: Process,
435        frame: &StackFrame,
436        demangle: bool,
437    ) -> VmResult<String> {
438        let addr = frame.instruction_pointer;
439        match frame.module {
440            Some(module) => {
441                self.format_symbol_with_module(proc, module, addr, frame.start, demangle)
442            }
443            None => format_symbol_without_module(self, proc, addr, frame.start),
444        }
445    }
446}
447
448fn format_symbol_without_module<O: Os + ?Sized>(
449    os: &O,
450    proc: Process,
451    addr: VirtualAddress,
452    fun_start: Option<VirtualAddress>,
453) -> VmResult<String> {
454    let vma_start = match os.process_find_vma_by_address(proc, addr)? {
455        Some(vma) => Some(os.vma_start(vma)?),
456        None => None,
457    };
458
459    fn do_format(
460        addr: VirtualAddress,
461        fun_start: Option<VirtualAddress>,
462        vma_start: Option<VirtualAddress>,
463    ) -> String {
464        let fun_start = fun_start.map(|start| (start, addr - start));
465
466        match (vma_start, fun_start) {
467            (Some(vma_start), Some((fun_start, 0))) => {
468                let fun_offset = vma_start - fun_start;
469                format!("{vma_start:#x}!{fun_offset:#x}")
470            }
471            (Some(vma_start), Some((fun_start, offset))) => {
472                let fun_offset = vma_start - fun_start;
473                format!("{vma_start:#x}!{fun_offset:#x}+{offset:#x}")
474            }
475            (Some(vma_start), None) => format!("{vma_start:#x}!{:#x}", addr - vma_start),
476            (None, Some((fun_start, 0))) => format!("{fun_start:#x}"),
477            (None, Some((fun_start, offset))) => format!("{fun_start:#x}+{offset:#x}"),
478            (None, None) => format!("{addr:#x}"),
479        }
480    }
481
482    Ok(do_format(addr, fun_start, vma_start))
483}