This commit is contained in:
nora 2023-05-07 10:59:19 +02:00
parent ca89c63adc
commit 4ae7bf8ad5
20 changed files with 387 additions and 19 deletions

5
.cargo/config.toml Normal file
View file

@ -0,0 +1,5 @@
[target.x86_64-unknown-linux-gnu]
rustflags = [
"-Cforce-frame-pointers=yes", # for the uwuwind fp stack walker
"-Clink-arg=-Wl,-E" # for dladdr
]

4
Cargo.lock generated
View file

@ -205,9 +205,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.139" version = "0.2.140"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"

View file

@ -53,7 +53,7 @@ struct SectionTable {
name: String, name: String,
#[tabled(rename = "type")] #[tabled(rename = "type")]
r#type: ShType, r#type: ShType,
size: Hex, size: u64,
offset: Offset, offset: Offset,
flags: ShFlags, flags: ShFlags,
} }
@ -134,7 +134,7 @@ fn print_file(opts: &Opts, path: &Path) -> anyhow::Result<()> {
if opts.section_headers { if opts.section_headers {
println!("\nSections"); println!("\nSections");
let sections = elf let mut sections = elf
.section_headers()? .section_headers()?
.iter() .iter()
.map(|sh| { .map(|sh| {
@ -142,13 +142,15 @@ fn print_file(opts: &Opts, path: &Path) -> anyhow::Result<()> {
Ok(SectionTable { Ok(SectionTable {
name, name,
r#type: sh.r#type, r#type: sh.r#type,
size: Addr(sh.size), size: sh.size,
offset: sh.offset, offset: sh.offset,
flags: sh.flags, flags: sh.flags,
}) })
}) })
.collect::<Result<Vec<_>, ElfReadError>>()?; .collect::<Result<Vec<_>, ElfReadError>>()?;
sections.sort_by(|a, b| b.size.cmp(&a.size));
print_table(Table::new(sections)); print_table(Table::new(sections));
} }

View file

@ -26,10 +26,10 @@ pub struct Opts {
#[derive(Debug)] #[derive(Debug)]
struct SymDef<'a> { struct SymDef<'a> {
name: &'a BStr, _name: &'a BStr,
defined_in: u32, defined_in: u32,
/// `shndx` from ELF /// `shndx` from ELF
refers_to_section: SectionIdx, _refers_to_section: SectionIdx,
} }
struct LinkCtxt<'a> { struct LinkCtxt<'a> {
@ -112,9 +112,9 @@ impl<'a> LinkCtxt<'a> {
} }
Entry::Vacant(entry) => { Entry::Vacant(entry) => {
entry.insert(SymDef { entry.insert(SymDef {
name, _name: name,
defined_in: elf_idx as u32, defined_in: elf_idx as u32,
refers_to_section: e_sym.shndx, _refers_to_section: e_sym.shndx,
}); });
} }
} }

1
uwuwind/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

72
uwuwind/Cargo.lock generated Normal file
View file

@ -0,0 +1,72 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "gimli"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
dependencies = [
"fallible-iterator",
"indexmap",
"stable_deref_trait",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "indexmap"
version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "libc"
version = "0.2.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
[[package]]
name = "memmap2"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327"
dependencies = [
"libc",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "uwuwind"
version = "0.1.0"
dependencies = [
"gimli",
"libc",
"memmap2",
]

View file

@ -1,3 +1,5 @@
[workspace]
[package] [package]
name = "uwuwind" name = "uwuwind"
version = "0.1.0" version = "0.1.0"
@ -6,3 +8,12 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
gimli = "0.27.2"
libc = { version = "0.2.140", default-features = false, features = ["extra_traits"] }
memmap2 = "0.5.10"
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"

61
uwuwind/a.cpp Normal file
View file

@ -0,0 +1,61 @@
bool findUnwindSections(int_t targetAddr, UnwindInfoSections &info)
{
// Use DLFO_STRUCT_HAS_EH_DBASE to determine the existence of
// `_dl_find_object`. Use _LIBUNWIND_SUPPORT_DWARF_INDEX, because libunwind
// support for _dl_find_object on other unwind formats is not implemented,
// yet.
#if defined(DLFO_STRUCT_HAS_EH_DBASE) & defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
// We expect `_dl_find_object` to return PT_GNU_EH_FRAME.
#if DLFO_EH_SEGMENT_TYPE != PT_GNU_EH_FRAME
#error _dl_find_object retrieves an unexpected section type
#endif
// We look-up `dl_find_object` dynamically at runtime to ensure backwards
// compatibility with earlier version of glibc not yet providing it. On older
// systems, we gracefully fallback to `dl_iterate_phdr`. Cache the pointer
// so we only look it up once. Do manual lock to avoid _cxa_guard_acquire.
static decltype(_dl_find_object) *dlFindObject;
static bool dlFindObjectChecked = false;
if (!dlFindObjectChecked)
{
dlFindObject = reinterpret_cast<decltype(_dl_find_object) *>(
dlsym(RTLD_DEFAULT, "_dl_find_object"));
dlFindObjectChecked = true;
}
// Try to find the unwind info using `dl_find_object`
dl_find_object findResult;
if (dlFindObject && dlFindObject((void *)targetAddr, &findResult) == 0)
{
if (findResult.dlfo_eh_frame == nullptr)
{
// Found an entry for `targetAddr`, but there is no unwind info.
return false;
}
info.dso_base = reinterpret_cast<uintptr_t>(findResult.dlfo_map_start);
info.text_segment_length = static_cast<size_t>(
(char *)findResult.dlfo_map_end - (char *)findResult.dlfo_map_start);
// Record the start of PT_GNU_EH_FRAME.
info.dwarf_index_section =
reinterpret_cast<uintptr_t>(findResult.dlfo_eh_frame);
// `_dl_find_object` does not give us the size of PT_GNU_EH_FRAME.
// Setting length to `SIZE_MAX` effectively disables all range checks.
info.dwarf_index_section_length = SIZE_MAX;
EHHeaderParser<LocalAddressSpace>::EHHeaderInfo hdrInfo;
if (!EHHeaderParser<LocalAddressSpace>::decodeEHHdr(
*this, info.dwarf_index_section, info.dwarf_index_section_length,
hdrInfo))
{
return false;
}
// Record the start of the FDE and use SIZE_MAX to indicate that we do
// not know the end address.
info.dwarf_section = hdrInfo.eh_frame_ptr;
info.dwarf_section_length = SIZE_MAX;
return true;
}
#endif
dl_iterate_cb_data cb_data = {this, &info, targetAddr};
int found = dl_iterate_phdr(findUnwindSectionsByPhdr, &cb_data);
return static_cast<bool>(found);
}

26
uwuwind/src/arch.rs Normal file
View file

@ -0,0 +1,26 @@
use core::ffi;
use std::arch::asm;
pub fn get_rbp() -> *const usize {
let mut out;
unsafe {
asm!(
"mov {out}, rbp",
out = out(reg) out,
options(nostack, readonly)
);
}
out
}
pub fn get_rip() -> *const ffi::c_void {
let mut out;
unsafe {
asm!(
"lea {out}, [rip]",
out = out(reg) out,
options(nostack, readonly),
);
}
out
}

57
uwuwind/src/dwarf.rs Normal file
View file

@ -0,0 +1,57 @@
use core::ffi;
use std::{ffi::CStr, fmt::Debug};
use gimli::UnwindTable;
#[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)]
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 {
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
);
Some(DwarfInfo {
map: core::ptr::slice_from_raw_parts(out.dlfo_map_start as _, text_len),
dwarf: out.dlfo_eh_frame as _,
})
}
}
pub fn uwutables() {
let UnwindTabley
}

36
uwuwind/src/identify.rs Normal file
View file

@ -0,0 +1,36 @@
use std::ffi::CStr;
pub fn identify(addr: usize) -> Option<&'static CStr> {
unsafe {
let mut info: libc::Dl_info = std::mem::zeroed();
libc::dladdr(addr as _, &mut info);
if !info.dli_sname.is_null() {
let sym_name = CStr::from_ptr(info.dli_sname);
return Some(sym_name);
}
/*
let parse = |str| usize::from_str_radix(str, 16).ok();
let maps = std::fs::read_to_string("/proc/self/maps").unwrap();
maps.lines()
.filter_map(|line| {
let mut ws = line.split_ascii_whitespace();
let addr_range = ws.next()?;
let mut addr_range = addr_range.split("-");
let addr_range = (parse(addr_range.next()?)?)..(parse(addr_range.next()?)?);
if addr_range.contains(&addr) {
ws.nth(4).map(ToOwned::to_owned)
} else {
None
}
})
.next()
*/
None
}
}

View file

@ -1 +1,38 @@
pub mod api; #![allow(dead_code)]
use std::sync::atomic::AtomicPtr;
macro_rules! trace {
($($tt:tt)*) => {
eprintln!("UWUWIND TRACE | uwuwind/{}:{}: {}", file!(), line!(), format_args!($($tt)*));
};
}
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}");
crate::dwarf::dwarf_info(arch::get_rip() as _);
// walk::fp::walk();
std::process::abort();
}
// This is normally provided by the language runtime through the unwind info block.
// We don't want to access that usually because Rust messes with it :(.
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);
}

35
uwuwind/src/main.rs Normal file
View file

@ -0,0 +1,35 @@
use uwuwind::uw;
#[repr(C)]
struct Exception {
_uwe: uw::_Unwind_Exception,
uwu: &'static str,
}
fn main() {
unsafe {
uwuwind::set_personality_routine(personality_routine);
let exception = Box::into_raw(Box::new(Exception {
_uwe: uw::_Unwind_Exception {
exception_class: 123456,
exception_cleanup0: |_, _| {},
private_1: 0,
private_2: 0,
},
uwu: "meow :3",
}));
uwuwind::_UnwindRaiseException(exception.cast::<uw::_Unwind_Exception>());
}
}
fn personality_routine(
_version: i32,
_actions: uw::_UnwindAction,
_exception_class: u64,
_exception_object: *mut uw::_Unwind_Exception,
_context: *mut uw::_Unwind_Context,
) -> uw::_Unwind_Reason_Code {
uw::_Unwind_Reason_Code::_URC_NO_REASON
}

View file

@ -1,14 +1,5 @@
#![allow(nonstandard_style)] // Closely follow the spec here #![allow(nonstandard_style)] // Closely follow the spec here
use std::process::abort;
pub extern "C" fn _UnwindRaiseException(
exception_object: *mut _Unwind_Exception,
) -> _Unwind_Reason_Code {
println!("someone raised an exception with addr {exception_object:p}");
abort();
}
#[repr(C)] #[repr(C)]
pub enum _Unwind_Reason_Code { pub enum _Unwind_Reason_Code {
_URC_NO_REASON = 0, _URC_NO_REASON = 0,

22
uwuwind/src/walk/fp.rs Normal file
View file

@ -0,0 +1,22 @@
//! Test frame pointer walker. Not very good.
use crate::arch::get_rbp;
pub(crate) unsafe fn walk() {
let mut current_rbp = get_rbp();
loop {
println!("walk... rbp={current_rbp:p}");
let return_addr = current_rbp.add(1).read() as *const usize;
println!("walk... return_addr={return_addr:p}");
println!(
"walk... frame={:?}",
crate::identify::identify(return_addr as usize)
);
println!("no read yet");
current_rbp = current_rbp.read() as *const usize;
}
}

1
uwuwind/src/walk/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod fp;

BIN
uwuwind/test Executable file

Binary file not shown.

8
uwuwind/test.c Normal file
View file

@ -0,0 +1,8 @@
#include<stdio.h>
#define __USE_GNU
#include<dlfcn.h>
#include<elf.h>
int main() {
printf("%d", PT_GNU_EH_FRAME);
}

3
uwuwind/test.rs Normal file
View file

@ -0,0 +1,3 @@
fn main() {
panic!();
}

View file