mirror of
https://github.com/Noratrieb/uwuwind.git
synced 2026-01-14 08:35:09 +01:00
nostd
This commit is contained in:
parent
2875f34693
commit
d1e561c665
7 changed files with 157 additions and 81 deletions
|
|
@ -1,5 +1,5 @@
|
|||
use core::arch::asm;
|
||||
use core::ffi;
|
||||
use std::arch::asm;
|
||||
|
||||
pub fn get_rbp() -> *const usize {
|
||||
let mut out;
|
||||
|
|
|
|||
69
src/dwarf/divination.rs
Normal file
69
src/dwarf/divination.rs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
//! # divination
|
||||
//!
|
||||
//! the practice of seeking knowledge of the future or the unknown by supernatural means.
|
||||
//!
|
||||
//! we ask supernatural means (the dynamic linker) for knowledge of the future
|
||||
//! (where we will find the dwarves)
|
||||
//!
|
||||
//! first, we ask the dynamic linker to give us the `.eh_frame` for the current binary using
|
||||
//! the GNU extension (`_dl_find_object`)[https://www.gnu.org/software/libc/manual/html_node/Dynamic-Linker-Introspection.html].
|
||||
//! then, we parse that as beautiful DWARF call frame information, as god (or rather, the x86-64 psABI) intended.
|
||||
|
||||
use core::ffi;
|
||||
|
||||
use crate::stdext::with_last_os_error_str;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
struct dl_find_object {
|
||||
dlfo_flags: ffi::c_ulonglong,
|
||||
dlfo_map_start: *const ffi::c_void,
|
||||
dlfo_map_end: *const ffi::c_void,
|
||||
dlf_link_map: *const ffi::c_void,
|
||||
dlfo_eh_frame: *const ffi::c_void,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn _dl_find_object(address: *const ffi::c_void, result: *mut dl_find_object) -> ffi::c_int;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct DwarfInfo {
|
||||
/// The text segment
|
||||
map: *const [u8],
|
||||
/// PT_GNU_EH_FRAME
|
||||
dwarf: *const u8,
|
||||
}
|
||||
|
||||
pub fn dwarf_info(addr: *const ffi::c_void) -> Option<DwarfInfo> {
|
||||
unsafe {
|
||||
let mut out = core::mem::zeroed();
|
||||
let ret = _dl_find_object(addr, &mut out);
|
||||
trace!("dl_find_object returned {ret}");
|
||||
if ret != 0 {
|
||||
with_last_os_error_str(|err| trace!("dl_find_object error: {err}"));
|
||||
return None;
|
||||
}
|
||||
if out.dlfo_eh_frame.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let text_len = out.dlfo_map_end as usize - out.dlfo_map_start as usize;
|
||||
trace!(
|
||||
"dwarf info; map: ({:p}, {:x}), dwarf: {:p}",
|
||||
out.dlfo_map_start,
|
||||
text_len,
|
||||
out.dlfo_eh_frame
|
||||
);
|
||||
|
||||
if !(out.dlfo_map_start..out.dlfo_map_end).contains(&addr) {
|
||||
trace!("dl_find_object returned object out of range for addr: {addr:p}");
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(DwarfInfo {
|
||||
map: core::ptr::slice_from_raw_parts(out.dlfo_map_start as _, text_len),
|
||||
dwarf: out.dlfo_eh_frame as _,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,72 +1,11 @@
|
|||
//! this implements the stuff necessary to get the uwutables for actual unwinding
|
||||
//!
|
||||
//! # how it works
|
||||
//! first, we ask the dynamic linker to give us the `.eh_frame` for the current binary using
|
||||
//! the GNU extension (`_dl_find_object`)[https://www.gnu.org/software/libc/manual/html_node/Dynamic-Linker-Introspection.html].
|
||||
//! then, we parse that as beautiful DWARF call frame information, as god (or rather, the x86-64 psABI) intended.
|
||||
//!
|
||||
//! for this we need a DWARF parser and a DWARF call frame information interpreter (yes, that shit is basically a programming
|
||||
//! language). See https://dwarfstd.org/doc/DWARF5.pdf for more information if more information is desired.
|
||||
|
||||
mod divination;
|
||||
mod parse;
|
||||
|
||||
use core::ffi;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
struct dl_find_object {
|
||||
dlfo_flags: ffi::c_ulonglong,
|
||||
dlfo_map_start: *const ffi::c_void,
|
||||
dlfo_map_end: *const ffi::c_void,
|
||||
dlf_link_map: *const ffi::c_void,
|
||||
dlfo_eh_frame: *const ffi::c_void,
|
||||
}
|
||||
|
||||
//
|
||||
extern "C" {
|
||||
fn _dl_find_object(address: *const ffi::c_void, result: *mut dl_find_object) -> ffi::c_int;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct DwarfInfo {
|
||||
/// The text segment
|
||||
map: *const [u8],
|
||||
/// PT_GNU_EH_FRAME
|
||||
dwarf: *const u8,
|
||||
}
|
||||
|
||||
pub fn dwarf_info(addr: *const ffi::c_void) -> Option<DwarfInfo> {
|
||||
unsafe {
|
||||
let mut out = core::mem::zeroed();
|
||||
let ret = _dl_find_object(addr, &mut out);
|
||||
trace!("dl_find_object returned {ret}");
|
||||
if ret != 0 {
|
||||
trace!("dl_find_object error: {}", std::io::Error::last_os_error());
|
||||
return None;
|
||||
}
|
||||
if out.dlfo_eh_frame.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let text_len = out.dlfo_map_end as usize - out.dlfo_map_start as usize;
|
||||
trace!(
|
||||
"dwarf info; map: ({:p}, {:x}), dwarf: {:p}",
|
||||
out.dlfo_map_start,
|
||||
text_len,
|
||||
out.dlfo_eh_frame
|
||||
);
|
||||
|
||||
if !(out.dlfo_map_start..out.dlfo_map_end).contains(&addr) {
|
||||
trace!("dl_find_object returned object out of range for addr: {addr:p}");
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(DwarfInfo {
|
||||
map: core::ptr::slice_from_raw_parts(out.dlfo_map_start as _, text_len),
|
||||
dwarf: out.dlfo_eh_frame as _,
|
||||
})
|
||||
}
|
||||
}
|
||||
pub use divination::{dwarf_info, DwarfInfo};
|
||||
|
||||
pub fn uwutables(_dwarf_info: DwarfInfo) {}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use std::ffi::CStr;
|
||||
use core::ffi::CStr;
|
||||
|
||||
pub fn identify(addr: usize) -> Option<&'static CStr> {
|
||||
unsafe {
|
||||
let mut info: libc::Dl_info = std::mem::zeroed();
|
||||
let mut info: libc::Dl_info = core::mem::zeroed();
|
||||
|
||||
libc::dladdr(addr as _, &mut info);
|
||||
|
||||
|
|
|
|||
21
src/lib.rs
21
src/lib.rs
|
|
@ -1,31 +1,30 @@
|
|||
#![no_std]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::sync::atomic::AtomicPtr;
|
||||
use core::sync::atomic::AtomicPtr;
|
||||
|
||||
macro_rules! trace {
|
||||
($($tt:tt)*) => {
|
||||
eprintln!("UWUWIND TRACE | uwuwind/{}:{}: {}", file!(), line!(), format_args!($($tt)*));
|
||||
};
|
||||
}
|
||||
// Get the macros into our local prelude.
|
||||
#[macro_use]
|
||||
mod stdext;
|
||||
|
||||
pub mod uw;
|
||||
|
||||
mod arch;
|
||||
mod dwarf;
|
||||
mod identify;
|
||||
|
||||
mod walk;
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
pub unsafe extern "C" fn _UnwindRaiseException(
|
||||
exception_object: *mut uw::_Unwind_Exception,
|
||||
) -> uw::_Unwind_Reason_Code {
|
||||
println!("someone raised an exception with addr {exception_object:p}");
|
||||
|
||||
trace!("someone raised an exception with addr {exception_object:p}");
|
||||
crate::dwarf::dwarf_info(arch::get_rip() as _);
|
||||
|
||||
// walk::fp::walk();
|
||||
|
||||
std::process::abort();
|
||||
stdext::abort();
|
||||
}
|
||||
|
||||
// This is normally provided by the language runtime through the unwind info block.
|
||||
|
|
@ -33,6 +32,6 @@ pub unsafe extern "C" fn _UnwindRaiseException(
|
|||
static PERSONALITY_ROUTINE: AtomicPtr<()> = AtomicPtr::new(core::ptr::null_mut());
|
||||
|
||||
pub unsafe fn set_personality_routine(routine: uw::PersonalityRoutine) {
|
||||
let ptr: *mut () = std::mem::transmute(routine);
|
||||
PERSONALITY_ROUTINE.store(ptr, std::sync::atomic::Ordering::Relaxed);
|
||||
let ptr: *mut () = core::mem::transmute(routine);
|
||||
PERSONALITY_ROUTINE.store(ptr, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
|
|
|||
68
src/stdext.rs
Normal file
68
src/stdext.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
use core::fmt::Write;
|
||||
use core::{ffi, fmt};
|
||||
|
||||
pub struct LibCStdoutWriter;
|
||||
|
||||
impl Write for LibCStdoutWriter {
|
||||
fn write_str(&mut self, mut s: &str) -> fmt::Result {
|
||||
loop {
|
||||
let r = unsafe { libc::write(libc::STDOUT_FILENO, s.as_ptr().cast(), s.len()) };
|
||||
if r < 0 {
|
||||
return Err(fmt::Error);
|
||||
}
|
||||
if r == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
s = &s[(r as usize)..];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(args: fmt::Arguments<'_>) -> fmt::Result {
|
||||
write!(LibCStdoutWriter, "{}", args)
|
||||
}
|
||||
|
||||
macro_rules! print {
|
||||
($($tt:tt)*) => {{
|
||||
$crate::stdext::print(format_args!($($tt)*)).unwrap();
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! trace {
|
||||
($($tt:tt)*) => {
|
||||
print!("UWUWIND TRACE | uwuwind/{}:{}: {}\n", file!(), line!(), format_args!($($tt)*))
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use trace;
|
||||
|
||||
pub(crate) fn abort() -> ! {
|
||||
// SAFETY: We abort.
|
||||
unsafe { libc::abort() };
|
||||
}
|
||||
|
||||
fn errno() -> i32 {
|
||||
// SAFETY: Surely errno_location would be valid, right?
|
||||
unsafe { *libc::__errno_location() }
|
||||
}
|
||||
|
||||
pub(crate) fn with_last_os_error_str<R>(f: impl FnOnce(&str) -> R) -> R {
|
||||
let mut buf: [u8; 512] = [0; 512];
|
||||
|
||||
extern "C" {
|
||||
// the libc crate only has the definition for the POSIX version, but a GNU system has the GNU version.
|
||||
fn strerror_r(errnum: i32, buf: *mut ffi::c_char, buflen: usize) -> i32;
|
||||
}
|
||||
|
||||
// SAFETY: Our buffer length is passed correctly
|
||||
let error = unsafe { libc::strerror_r(errno(), buf.as_mut_ptr().cast(), buf.len()) };
|
||||
// SAFETY: strerror_r writes the string to buf, even if it didnt write anything, we did zero init it.
|
||||
let cstr = if error != 0 {
|
||||
ffi::CStr::from_bytes_with_nul(b"<strerror_r returned an error>\n").unwrap()
|
||||
} else {
|
||||
unsafe { ffi::CStr::from_ptr(buf.as_ptr().cast()) }
|
||||
};
|
||||
f(cstr
|
||||
.to_str()
|
||||
.unwrap_or("<error message contained invalid utf8>"))
|
||||
}
|
||||
|
|
@ -1,21 +1,22 @@
|
|||
//! Test frame pointer walker. Not very good.
|
||||
|
||||
use crate::arch::get_rbp;
|
||||
use crate::stdext::trace;
|
||||
|
||||
pub(crate) unsafe fn walk() {
|
||||
let mut current_rbp = get_rbp();
|
||||
loop {
|
||||
println!("walk... rbp={current_rbp:p}");
|
||||
trace!("walk... rbp={current_rbp:p}");
|
||||
|
||||
let return_addr = current_rbp.add(1).read() as *const usize;
|
||||
println!("walk... return_addr={return_addr:p}");
|
||||
trace!("walk... return_addr={return_addr:p}");
|
||||
|
||||
println!(
|
||||
trace!(
|
||||
"walk... frame={:?}",
|
||||
crate::identify::identify(return_addr as usize)
|
||||
);
|
||||
|
||||
println!("no read yet");
|
||||
trace!("no read yet");
|
||||
|
||||
current_rbp = current_rbp.read() as *const usize;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue