This commit is contained in:
nora 2025-10-26 20:14:39 +01:00
parent ba5e53eea6
commit c0cf3786f4
6 changed files with 171 additions and 23 deletions

View file

@ -1,13 +1,9 @@
{ pkgs ? import <nixpkgs> { } }: pkgs.mkShell { { pkgs ? import <nixpkgs> { } }: pkgs.mkShell {
buildInputs = with pkgs; [ buildInputs = with pkgs; [
rustup
gcc gcc
clang_16 clang_21
llvmPackages_16.bintools llvmPackages_21.bintools
]; ];
shellHook = ''
export PATH=$PATH:''${CARGO_HOME:-~/.cargo}/bin
'';
packages = (with pkgs; [ packages = (with pkgs; [
gef gef
rust-bindgen rust-bindgen

View file

@ -43,6 +43,7 @@ struct EhFrameHeader {
eh_frame_ptr_enc: Encoding, eh_frame_ptr_enc: Encoding,
fde_count_enc: Encoding, fde_count_enc: Encoding,
table_enc: Encoding, table_enc: Encoding,
rest: (),
} }
#[instrument] #[instrument]
@ -93,9 +94,9 @@ pub(crate) fn eh_frame(addr: Addr) -> Option<*const u8> {
trace!("eh_frame_hdr: {:?}", header); trace!("eh_frame_hdr: {:?}", header);
let (read_size, eh_frame_ptr) = read_encoded(ptr, header.eh_frame_ptr_enc); let (read_size, eh_frame_ptr) = read_encoded(ptr, header.eh_frame_ptr_enc, None);
let ptr = ptr.add(read_size); let ptr = ptr.add(read_size);
let (_read_size, fde_count) = read_encoded(ptr, header.fde_count_enc); let (_read_size, fde_count) = read_encoded(ptr, header.fde_count_enc, None);
trace!("eh_frame: {eh_frame_ptr:?}"); trace!("eh_frame: {eh_frame_ptr:?}");
trace!("fde_count: {fde_count:?}"); trace!("fde_count: {fde_count:?}");
@ -108,3 +109,96 @@ pub(crate) fn eh_frame(addr: Addr) -> Option<*const u8> {
Some(eh_frame_ptr as *const u8) Some(eh_frame_ptr as *const u8)
} }
} }
#[instrument]
pub(crate) fn frame_info(addr: Addr) -> Option<()> {
unsafe {
let header_ptr = eh_frame_hdr_ptr(addr)?;
let eh_frame_header_addr = header_ptr.addr();
let header = header_ptr.read();
let ptr = (&raw const (*header_ptr).rest).cast::<u8>();
let (eh_frame_ptr_size, eh_frame_ptr) = read_encoded(ptr, header.eh_frame_ptr_enc, None);
let ptr = ptr.add(eh_frame_ptr_size);
let (fde_count_size, fde_count) =
read_encoded(ptr, header.fde_count_enc, Some(eh_frame_ptr));
trace!(?header.table_enc);
let table_ptr = ptr.add(fde_count_size);
let mut walk_table_ptr = table_ptr;
for i in 0..fde_count {
let (read, initial_loc) =
read_encoded(walk_table_ptr, header.table_enc, Some(eh_frame_header_addr));
walk_table_ptr = walk_table_ptr.add(read);
let (read, address) =
read_encoded(walk_table_ptr, header.table_enc, Some(eh_frame_header_addr));
walk_table_ptr = walk_table_ptr.add(read);
trace!(idx = ?i, "eh_frame_hdr table initial_loc={initial_loc:x} address={address:x}");
}
let table_half_entry_size = header.table_enc.size();
let mut base = 0;
let mut len = fde_count;
let found_fde;
loop {
if len == 1 {
found_fde = Some(base);
break;
}
let mid = base + len / 2;
let mid_ptr = table_ptr.byte_add(mid * table_half_entry_size * 2);
let (_, value) = read_encoded(mid_ptr, header.table_enc, Some(eh_frame_header_addr));
debug!(
?base,
?len,
?mid,
"binary searching for {addr:?}: {value:x}"
);
match addr.addr().cmp(&value) {
core::cmp::Ordering::Less => {
len = mid - base;
}
core::cmp::Ordering::Equal => {
found_fde = Some(mid);
break;
}
core::cmp::Ordering::Greater => {
len = len - (mid - base);
base = mid;
}
}
}
debug!("found FDE idx in binary search {found_fde:?}");
let fde_table_ptr = table_ptr.byte_add(found_fde.unwrap() * table_half_entry_size * 2);
let (_, fde_address) = read_encoded(
fde_table_ptr.byte_add(table_half_entry_size),
header.table_enc,
Some(eh_frame_header_addr),
);
trace!("found FDE at address {fde_address:x}");
let fde_ptr = core::ptr::with_exposed_provenance::<u8>(fde_address);
fde_ptr.read_volatile();
trace!("ptr is valid");
trace!("FDE offset to .eh_frame: {:x}", fde_ptr.addr() - (eh_frame_ptr));
let fde = crate::dwarf::parse::parse_fde_from_ptr(fde_ptr, eh_frame_ptr).unwrap();
todo!()
}
}

View file

@ -5,12 +5,13 @@
//! interpreter (yes, that shit is basically a programming language). //! interpreter (yes, that shit is basically a programming language).
//! See https://dwarfstd.org/doc/DWARF5.pdf §6.4 for more information if more information is desired. //! See https://dwarfstd.org/doc/DWARF5.pdf §6.4 for more information if more information is desired.
//! Note that https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html //! Note that https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
//! contains more details on the precise format, which is slightly different from .debug_frame from DWARF. //! contains more details on the precise format, which is slightly different
//! from .debug_frame from DWARF.
mod divination; mod divination;
mod parse; mod parse;
pub(crate) use divination::eh_frame; pub(crate) use divination::{eh_frame, frame_info};
/// The `.eh_frame` section contains a list of call frame information records. /// The `.eh_frame` section contains a list of call frame information records.
/// Each CFI contains a CIE followed be one or more FDE records. /// Each CFI contains a CIE followed be one or more FDE records.

View file

@ -362,23 +362,29 @@ pub unsafe fn parse_cfi(mut ptr: *const u8) {
struct Cursor<'a>(&'a [u8]); struct Cursor<'a>(&'a [u8]);
/// Returns `(read_size, value)` /// Returns `(read_size, value)`
pub(super) unsafe fn read_encoded(ptr: *const u8, encoding: Encoding) -> (usize, usize) { pub(super) unsafe fn read_encoded(
ptr: *const u8,
encoding: Encoding,
datarel_base: Option<usize>,
) -> (usize, usize) {
let (read_size, value) = match encoding.format() { let (read_size, value) = match encoding.format() {
ValueFormat::DW_EH_PE_uleb128 => todo!("uleb128"), ValueFormat::DW_EH_PE_uleb128 => todo!("uleb128"),
ValueFormat::DW_EH_PE_udata2 => (2, ptr.cast::<u16>().read_unaligned() as usize), ValueFormat::DW_EH_PE_udata2 => (2, ptr.cast::<u16>().read_unaligned() as usize),
ValueFormat::DW_EH_PE_udata4 => (4, ptr.cast::<u32>().read_unaligned() as usize), ValueFormat::DW_EH_PE_udata4 => (4, ptr.cast::<u32>().read_unaligned() as usize),
ValueFormat::DW_EH_PE_udata8 => (8, ptr.cast::<u64>().read_unaligned() as usize), ValueFormat::DW_EH_PE_udata8 => (8, ptr.cast::<u64>().read_unaligned() as usize),
ValueFormat::DW_EH_PE_sleb128 => todo!("sleb128"), ValueFormat::DW_EH_PE_sleb128 => todo!("sleb128"),
ValueFormat::DW_EH_PE_sdata2 => (2, ptr.cast::<i16>().read_unaligned() as usize), ValueFormat::DW_EH_PE_sdata2 => (2, ptr.cast::<i16>().read_unaligned() as isize as usize),
ValueFormat::DW_EH_PE_sdata4 => (4, ptr.cast::<i32>().read_unaligned() as usize), ValueFormat::DW_EH_PE_sdata4 => (4, ptr.cast::<i32>().read_unaligned() as isize as usize),
ValueFormat::DW_EH_PE_sdata8 => (8, ptr.cast::<i64>().read_unaligned() as usize), ValueFormat::DW_EH_PE_sdata8 => (8, ptr.cast::<i64>().read_unaligned() as isize as usize),
}; };
let value = match encoding.application() { let value = match encoding.application() {
ValueApplication::DW_EH_PE_absptr => value, ValueApplication::DW_EH_PE_absptr => value,
ValueApplication::DW_EH_PE_pcrel => ((value as isize) + (ptr as isize)) as usize, ValueApplication::DW_EH_PE_pcrel => ((value as isize) + (ptr as isize)) as usize,
ValueApplication::DW_EH_PE_textrel => todo!("textrel"), ValueApplication::DW_EH_PE_textrel => todo!("textrel"),
ValueApplication::DW_EH_PE_datarel => todo!("datarel"), ValueApplication::DW_EH_PE_datarel => {
((value as isize) + (datarel_base.unwrap() as isize)) as usize
}
ValueApplication::DW_EH_PE_funcrel => todo!("funcrel"), ValueApplication::DW_EH_PE_funcrel => todo!("funcrel"),
ValueApplication::DW_EH_PE_aligned => todo!("aligned"), ValueApplication::DW_EH_PE_aligned => todo!("aligned"),
}; };
@ -387,6 +393,7 @@ pub(super) unsafe fn read_encoded(ptr: *const u8, encoding: Encoding) -> (usize,
} }
#[derive(PartialEq, Clone, Copy)] #[derive(PartialEq, Clone, Copy)]
#[repr(transparent)]
pub(super) struct Encoding(u8); pub(super) struct Encoding(u8);
impl Encoding { impl Encoding {
fn format(&self) -> ValueFormat { fn format(&self) -> ValueFormat {
@ -413,6 +420,18 @@ impl Encoding {
v => panic!("invalid header value application: {v}"), v => panic!("invalid header value application: {v}"),
} }
} }
pub(crate) fn size(&self) -> usize {
match self.format() {
ValueFormat::DW_EH_PE_uleb128 => panic!("uleb128 has no known size"),
ValueFormat::DW_EH_PE_udata2 => 2,
ValueFormat::DW_EH_PE_udata4 => 4,
ValueFormat::DW_EH_PE_udata8 => 8,
ValueFormat::DW_EH_PE_sleb128 => panic!("sleb128 has no known size"),
ValueFormat::DW_EH_PE_sdata2 => 2,
ValueFormat::DW_EH_PE_sdata4 => 4,
ValueFormat::DW_EH_PE_sdata8 => 8,
}
}
} }
impl fmt::Debug for Encoding { impl fmt::Debug for Encoding {
@ -456,7 +475,8 @@ enum ValueApplication {
DW_EH_PE_pcrel = 0x10, DW_EH_PE_pcrel = 0x10,
/// Value is relative to the beginning of the .text section. /// Value is relative to the beginning of the .text section.
DW_EH_PE_textrel = 0x20, DW_EH_PE_textrel = 0x20,
/// Value is relative to the beginning of the .got or .eh_frame_hdr section. /// Value is relative to the beginning of the .got or .eh_frame_hdr
/// section.
DW_EH_PE_datarel = 0x30, DW_EH_PE_datarel = 0x30,
/// Value is relative to the beginning of the function. /// Value is relative to the beginning of the function.
DW_EH_PE_funcrel = 0x40, DW_EH_PE_funcrel = 0x40,
@ -556,10 +576,11 @@ unsafe fn parse_frame_info<'a>(
unsafe fn parse_frame_head<'a>(ptr: *const u8) -> Result<(u32, &'a [u8], *const u8)> { unsafe fn parse_frame_head<'a>(ptr: *const u8) -> Result<(u32, &'a [u8], *const u8)> {
let len = ptr.cast::<u32>().read(); let len = ptr.cast::<u32>().read();
if len == 0xffffffff { if len == 0xffffffff {
// be careful, if you handle this you need to adjust the callers offsets lol lmao
todo!("loooong dwarf, cannot handle."); todo!("loooong dwarf, cannot handle.");
} }
let data = &mut Cursor(core::slice::from_raw_parts(ptr.add(4), len as usize)); let data = &mut Cursor(core::slice::from_raw_parts(ptr.add(4), len as usize));
trace!("frame info entry: {:x?}", data.0); trace!("frame info entry (without len): {:x?}", data.0);
let cie_id = read_u32(data)?; let cie_id = read_u32(data)?;
let new_ptr = ptr.add(4).add(len as _); let new_ptr = ptr.add(4).add(len as _);
@ -606,6 +627,37 @@ fn parse_cie<'a>(data: &mut Cursor<'a>) -> Result<Cie<'a>> {
Ok(cie) Ok(cie)
} }
pub(crate) unsafe fn parse_fde_from_ptr<'a>(
ptr: *const u8,
eh_frame_base: usize,
) -> Result<Fde<'a>> {
let (fde_cie_id, fde_data, _) = parse_frame_head(ptr)?;
let fde_data = &mut Cursor(fde_data);
if fde_cie_id == 0 {
return Err(Error(format!("FDE's CIE Pointer is 0")));
}
trace!("FDE's CIE pointer: {fde_cie_id}");
let cie_ptr = ptr.byte_add(4 /* length */).byte_sub(fde_cie_id as usize);
trace!(
"CIE offset to .eh_frame: {:x}",
cie_ptr.addr() - (eh_frame_base)
);
let (cie_cie_id, cie_data, _) = parse_frame_head(cie_ptr)?;
if cie_cie_id != 0 {
return Err(Error(format!("CIE must have cie_id=0")));
}
let cie_data = &mut Cursor(cie_data);
let cie = parse_cie(cie_data)?;
let fde = parse_fde(fde_data, fde_cie_id, &cie)?;
Ok(fde)
}
#[instrument(skip(data))] #[instrument(skip(data))]
fn parse_fde<'a>(data: &mut Cursor<'a>, cie_id: u32, cie: &Cie<'_>) -> Result<Fde<'a>> { fn parse_fde<'a>(data: &mut Cursor<'a>, cie_id: u32, cie: &Cie<'_>) -> Result<Fde<'a>> {
trace!("FDE {:x?}", data.0); trace!("FDE {:x?}", data.0);
@ -621,10 +673,10 @@ fn parse_fde<'a>(data: &mut Cursor<'a>, cie_id: u32, cie: &Cie<'_>) -> Result<Fd
Error("pointer encoding not present in augmentation for CIE with FDEs".into()) Error("pointer encoding not present in augmentation for CIE with FDEs".into())
})?; })?;
let (read_size, pc_begin) = unsafe { read_encoded(data.0.as_ptr(), pointer_encoding) }; let (read_size, pc_begin) = unsafe { read_encoded(data.0.as_ptr(), pointer_encoding, None) };
data.0 = &data.0[read_size..]; data.0 = &data.0[read_size..];
let (read_size, pc_range) = unsafe { read_encoded(data.0.as_ptr(), pointer_encoding) }; let (read_size, pc_range) = unsafe { read_encoded(data.0.as_ptr(), pointer_encoding, None) };
data.0 = &data.0[read_size..]; data.0 = &data.0[read_size..];
// This is only present if the aug data of the CIE contains z. But that is // This is only present if the aug data of the CIE contains z. But that is
@ -682,7 +734,7 @@ fn parse_augmentation_data(string: &str, data: &[u8]) -> Result<AugmentationData
b'P' => { b'P' => {
trace!("P"); trace!("P");
let encoding = Encoding(read_u8(data)?); let encoding = Encoding(read_u8(data)?);
let (read_size, value) = unsafe { read_encoded(data.0.as_ptr(), encoding) }; let (read_size, value) = unsafe { read_encoded(data.0.as_ptr(), encoding, None) };
data.0 = &data.0[read_size..]; data.0 = &data.0[read_size..];
aug_data.personality = Some(value); aug_data.personality = Some(value);
} }

View file

@ -25,14 +25,20 @@ impl Addr {
fn voidptr(self) -> *const ffi::c_void { fn voidptr(self) -> *const ffi::c_void {
self.0.cast() self.0.cast()
} }
fn addr(&self) -> usize {
self.0.addr()
}
} }
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
pub unsafe extern "C" fn _UnwindRaiseException( pub unsafe extern "C-unwind" fn _UnwindRaiseException(
exception_object: *mut uw::_Unwind_Exception, exception_object: *mut uw::_Unwind_Exception,
) -> uw::_Unwind_Reason_Code { ) -> uw::_Unwind_Reason_Code {
let _span = info_span!("_UnwindRaiseException", ?exception_object).entered(); let _span = info_span!("_UnwindRaiseException", ?exception_object).entered();
let frame = crate::dwarf::frame_info(arch::get_rip());
let eh_frame = crate::dwarf::eh_frame(arch::get_rip()).unwrap(); let eh_frame = crate::dwarf::eh_frame(arch::get_rip()).unwrap();
crate::dwarf::uwutables(eh_frame); crate::dwarf::uwutables(eh_frame);

View file

@ -1,5 +1,4 @@
use tracing_subscriber::{layer::SubscriberExt, EnvFilter}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
use tracing_subscriber::util::SubscriberInitExt;
use uwuwind::uw; use uwuwind::uw;
#[repr(C)] #[repr(C)]