decode CIE

This commit is contained in:
nora 2023-06-14 21:07:07 +02:00
parent e3246ede0b
commit c4dd3a7153
5 changed files with 194 additions and 37 deletions

View file

@ -23,6 +23,7 @@ struct dl_find_object {
dlfo_map_start: *const ffi::c_void, dlfo_map_start: *const ffi::c_void,
dlfo_map_end: *const ffi::c_void, dlfo_map_end: *const ffi::c_void,
dlf_link_map: *const ffi::c_void, dlf_link_map: *const ffi::c_void,
/// A pointer to the `PT_GNU_EH_FRAME` segment (the `.eh_frame_hdr` section).
dlfo_eh_frame: *const ffi::c_void, dlfo_eh_frame: *const ffi::c_void,
} }
@ -30,12 +31,6 @@ extern "C" {
fn _dl_find_object(address: *const ffi::c_void, result: *mut dl_find_object) -> ffi::c_int; fn _dl_find_object(address: *const ffi::c_void, result: *mut dl_find_object) -> ffi::c_int;
} }
#[derive(Debug, Clone, Copy)]
pub struct DwarfInfo {
/// A pointer to the `PT_GNU_EH_FRAME` segment (the `.eh_frame_hdr` section).
pub(crate) eh_frame_hdr: *const u8,
}
/// The `.eh_frame_hdr` section. /// The `.eh_frame_hdr` section.
/// See <https://refspecs.linuxfoundation.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html> /// See <https://refspecs.linuxfoundation.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html>
/// and <https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html>. /// and <https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html>.
@ -78,7 +73,7 @@ fn eh_frame_hdr_ptr(addr: Addr) -> Option<*const EhFrameHeader> {
} }
} }
pub(crate) fn dwarf_info(addr: Addr) -> Option<DwarfInfo> { pub(crate) fn eh_frame(addr: Addr) -> Option<*const u8> {
unsafe { unsafe {
let ptr = eh_frame_hdr_ptr(addr)?; let ptr = eh_frame_hdr_ptr(addr)?;
let header = ptr.read(); let header = ptr.read();
@ -99,10 +94,12 @@ pub(crate) fn dwarf_info(addr: Addr) -> Option<DwarfInfo> {
trace!( trace!(
"eh_frame start: {:x?}", "eh_frame start: {:x?}",
core::slice::from_raw_parts(eh_frame_ptr as *const u8, 10) core::slice::from_raw_parts(eh_frame_ptr as *const u8, 15)
); );
crate::stdext::abort(); crate::dwarf::uwutables(eh_frame_ptr as *const u8);
Some(eh_frame_ptr as *const u8)
} }
} }
@ -118,8 +115,6 @@ unsafe fn read_encoded(ptr: *const u8, encoding: Encoding) -> (*const u8, usize)
ValueFormat::DW_EH_PE_sdata8 => (ptr.add(8), ptr.cast::<i64>().read_unaligned() as usize), ValueFormat::DW_EH_PE_sdata8 => (ptr.add(8), ptr.cast::<i64>().read_unaligned() as usize),
}; };
trace!("{ptr:p}, {value}");
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,

View file

@ -6,11 +6,13 @@
mod divination; mod divination;
mod parse; mod parse;
pub(crate) use divination::dwarf_info; pub(crate) use divination::eh_frame;
/// The `.eh_frame` section contains a list of call frame information records.
/// Each CFI contains a CIE followed be one or more FDE records.
pub unsafe fn uwutables(eh_frame: *const u8) { pub unsafe fn uwutables(eh_frame: *const u8) {
trace!("getting uwutables from {:p}", eh_frame); trace!("getting uwutables from {:p}", eh_frame);
unsafe { unsafe {
parse::parse_cie(eh_frame); parse::parse_cfi(eh_frame);
} }
} }

View file

@ -23,6 +23,19 @@
//! and the rule to find the value for the previous frame. //! and the rule to find the value for the previous frame.
#![allow(non_upper_case_globals)] #![allow(non_upper_case_globals)]
#[cfg(test)]
mod tests;
use core::ffi::CStr;
use alloc::{format, string::String};
/// The dwarf is invalid. This is fatal and should never happen.
#[derive(Debug)]
pub struct Error(String);
type Result<T, E = Error> = core::result::Result<T, E>;
#[derive(Debug)] #[derive(Debug)]
pub struct Expr; pub struct Expr;
@ -70,16 +83,8 @@ impl ILeb128 {
} }
/// Common Information Entry /// Common Information Entry
#[derive(Debug, PartialEq)]
pub struct Cie<'a> { pub struct Cie<'a> {
/// A constant that gives the number of bytes of the CIE structure, not including
/// the length field itself (see Section 7.2.2 on page 184). The size of the length
/// field plus the value of length must be an integral multiple of the address size.
pub length: usize,
/// A constant that is used to distinguish CIEs from FDEs.
pub cie_id: Id,
/// A version number (see Section 7.24 on page 238). This number is specific to
/// the call frame information and is independent of the DWARF version number.
pub version: u8,
/// A null-terminated UTF-8 string that identifies the augmentation to this CIE or /// A null-terminated UTF-8 string that identifies the augmentation to this CIE or
/// to the FDEs that use it. If a reader encounters an augmentation string that is /// to the FDEs that use it. If a reader encounters an augmentation string that is
/// unexpected, then only the following fields can be read: /// unexpected, then only the following fields can be read:
@ -96,24 +101,22 @@ pub struct Cie<'a> {
/// Because the .debug_frame section is useful independently of any .debug_info /// Because the .debug_frame section is useful independently of any .debug_info
/// section, the augmentation string always uses UTF-8 encoding. /// section, the augmentation string always uses UTF-8 encoding.
pub augmentation: &'a str, pub augmentation: &'a str,
/// The size of a target address in this CIE and any FDEs that use it, in bytes. If a
/// compilation unit exists for this frame, its address size must match the address
/// size here.
pub address_size: u8,
/// The size of a segment selector in this CIE and any FDEs that use it, in bytes.
pub segment_selector_size: u8,
/// A constant that is factored out of all advance location instructions (see /// A constant that is factored out of all advance location instructions (see
/// Section 6.4.2.1 on page 177). The resulting value is /// Section 6.4.2.1 on page 177). The resulting value is
/// (operand * code_alignment_factor). /// (operand * code_alignment_factor).
pub code_alignment_factor: ULeb128, pub code_alignment_factor: u128,
/// A constant that is factored out of certain offset instructions (see /// A constant that is factored out of certain offset instructions (see
/// Sections 6.4.2.2 on page 177 and 6.4.2.3 on page 179). The resulting value is /// Sections 6.4.2.2 on page 177 and 6.4.2.3 on page 179). The resulting value is
/// (operand * data_alignment_factor). /// (operand * data_alignment_factor).
pub data_alignment_factor: ILeb128, pub data_alignment_factor: i128,
/// An unsigned LEB128 constant that indicates which column in the rule table /// An unsigned LEB128 constant that indicates which column in the rule table
/// represents the return address of the function. Note that this column might not /// represents the return address of the function. Note that this column might not
/// correspond to an actual machine register. /// correspond to an actual machine register.
pub return_address_register: ULeb128, pub return_address_register: u128,
/// A block of data whose contents are defined by the contents of the Augmentation
/// String as described below. This field is only present if the Augmentation String
/// contains the character 'z'. The size of this data is given by the Augentation Length.
pub augmentation_data: Option<&'a [u8]>,
/// A sequence of rules that are interpreted to create the initial setting of each /// A sequence of rules that are interpreted to create the initial setting of each
/// column in the table. /// column in the table.
/// The default rule for all columns before interpretation of the initial instructions /// The default rule for all columns before interpretation of the initial instructions
@ -323,9 +326,118 @@ pub enum Instruction {
Nop, Nop,
} }
pub unsafe fn parse_cie(ptr: *const u8) { pub unsafe fn parse_cfi(ptr: *const u8) {
let len = *ptr.cast::<u32>(); parse_cie(ptr).unwrap();
trace!("{:x}", len); }
struct Cursor<'a>(&'a [u8]);
fn read_bytes<'a>(data: &mut Cursor<'a>, amount: usize) -> Result<&'a [u8]> {
if data.0.len() < amount {
return Err(Error(format!(
"index out of bounds, tried to read {amount} bytes from {}",
data.0.len()
)));
} else {
let result = &data.0[..amount];
data.0 = &data.0[amount..];
Ok(result)
}
}
fn read_u32(data: &mut Cursor<'_>) -> Result<u32> {
let int = read_bytes(data, 4)?;
Ok(u32::from_le_bytes(int.try_into().unwrap()))
}
fn read_u8(data: &mut Cursor<'_>) -> Result<u8> {
let int = read_bytes(data, 1)?;
Ok(int[0])
}
fn read_utf8_cstr<'a>(data: &mut Cursor<'a>) -> Result<&'a str> {
let cstr: &CStr = CStr::from_bytes_until_nul(data.0)
.map_err(|_| Error("no null terminator found for string".into()))?;
let utf8 = cstr
.to_str()
.map_err(|e| Error(format!("invalid utf8: {e:?}")))?;
data.0 = &data.0[(utf8.len() + 1)..];
Ok(utf8)
}
fn read_uleb128(data: &mut Cursor<'_>) -> Result<u128> {
let mut result = 0;
let mut shift = 0;
loop {
let byte = read_u8(data)?;
result |= ((byte & 0b0111_1111) << shift) as u128;
if (byte >> 7) == 0 {
break;
}
shift += 7;
}
Ok(result)
}
fn read_ileb128(data: &mut Cursor<'_>) -> Result<i128> {
let mut result = 0;
let mut shift = 0;
let size = 8;
let sign_bit_set = loop {
let byte = read_u8(data)?;
result |= ((byte & 0b0111_1111) << shift) as i128;
shift += 7;
if (byte >> 7) == 0 {
let sign_bit_set = ((byte >> 6) & 1) == 1;
break sign_bit_set;
}
};
if (shift < size) && sign_bit_set {
result |= -(1 << shift);
}
Ok(result)
}
unsafe fn parse_cie<'a>(ptr: *const u8) -> Result<Cie<'a>> {
let len = ptr.cast::<u32>().read();
if len == 0xffffffff {
todo!("loooong dwarf, cannot handle.");
}
let data = &mut Cursor(core::slice::from_raw_parts(ptr.add(4), len as usize));
trace!("{:x?}", data.0);
let cie_id = read_u32(data)?;
if cie_id != 0 {
return Err(Error(format!("not a CIE"))); // TODO: we need to parse an FDE here.
}
let version = read_u8(data)?;
if version != 1 {
return Err(Error(format!("version must be 1: {version}")));
}
let augmentation = read_utf8_cstr(data)?;
let code_alignment_factor = read_uleb128(data)?;
let data_alignment_factor = read_ileb128(data)?;
let return_address_register = read_uleb128(data)?;
let augmentation_data = if augmentation.starts_with('z') {
let aug_len = read_uleb128(data)?;
let aug_data = read_bytes(data, aug_len as usize)?;
Some(aug_data)
} else {
None
};
let initial_instructions = data.0;
let cie = Cie {
augmentation,
code_alignment_factor,
data_alignment_factor,
return_address_register,
augmentation_data,
initial_instructions,
};
trace!("total_len={len} {cie:#?}");
Ok(cie)
} }
pub(super) struct InstrIter { pub(super) struct InstrIter {

47
src/dwarf/parse/tests.rs Normal file
View file

@ -0,0 +1,47 @@
use crate::dwarf::parse::Cie;
#[test]
fn parse_simple_cie() {
#[rustfmt::skip]
let data = [
0x14, 0, 0, 0,
0, 0, 0, 0, 1,
0x7a, 0x52, 0, 1,
0x78, 0x10, 1,
0x1b, 0xc, 7, 8,
0x90, 1, 0, 0,
];
let cie = unsafe { super::parse_cie(data.as_ptr()) }.unwrap();
assert_eq!(
cie,
Cie {
augmentation: "zR",
code_alignment_factor: 1,
data_alignment_factor: -8,
return_address_register: 16,
augmentation_data: Some(b"\x1B"),
initial_instructions: &[0xc, 7, 8, 0x90, 1, 0, 0]
}
);
// llvm-dwarfdump output:
/*
00000000 00000014 00000000 CIE
Format: DWARF32
Version: 1
Augmentation: "zR"
Code alignment factor: 1
Data alignment factor: -8
Return address column: 16
Augmentation data: 1B
DW_CFA_def_cfa: RSP +8
DW_CFA_offset: RIP -8
DW_CFA_nop:
DW_CFA_nop:
CFA=RSP+8: RIP=[CFA-8]
*/
}

View file

@ -1,6 +1,8 @@
#![no_std] #![cfg_attr(not(test), no_std)]
#![allow(dead_code)] #![allow(dead_code)]
extern crate alloc;
use core::{ffi, sync::atomic::AtomicPtr}; use core::{ffi, sync::atomic::AtomicPtr};
// Get the macros into our local prelude. // Get the macros into our local prelude.
@ -29,8 +31,7 @@ pub unsafe extern "C" fn _UnwindRaiseException(
exception_object: *mut uw::_Unwind_Exception, exception_object: *mut uw::_Unwind_Exception,
) -> uw::_Unwind_Reason_Code { ) -> uw::_Unwind_Reason_Code {
trace!("someone raised an exception with addr {exception_object:p}"); trace!("someone raised an exception with addr {exception_object:p}");
let _di = crate::dwarf::dwarf_info(arch::get_rip()).unwrap(); let _ = crate::dwarf::eh_frame(arch::get_rip()).unwrap();
crate::dwarf::uwutables(core::ptr::null());
stdext::abort(); stdext::abort();
} }