mirror of
https://github.com/Noratrieb/uwuwind.git
synced 2026-01-14 08:35:09 +01:00
decode CIE
This commit is contained in:
parent
e3246ede0b
commit
c4dd3a7153
5 changed files with 194 additions and 37 deletions
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
47
src/dwarf/parse/tests.rs
Normal 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]
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue