Skip to content

Commit

Permalink
fn: Add CFI superimposition
Browse files Browse the repository at this point in the history
  • Loading branch information
kevin-lesenechal committed Apr 9, 2023
1 parent 8d889ad commit 6119f88
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 28 deletions.
4 changes: 4 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ pub struct FnArgs {
/// always parsed as hexadecimal, and can start with `0x` or not.
#[arg(long, short = 'a')]
pub address: bool,

/// Superimpose call-frame information extracted from `.eh_frame`.
#[arg(long)]
pub cfi: bool,
}

#[derive(Args, Debug, Clone, Default)]
Expand Down
59 changes: 34 additions & 25 deletions src/eh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,35 +181,38 @@ fn print_fde_header<R: Reader<Offset = usize>>(
}
}

struct EhInstrContext {
cfa_reg: Register,
cfa_off: u64,
loc: u64,
data_align: i64,
sp: SizePrint,
pub struct EhInstrContext {
pub cfa_reg: Register,
pub cfa_off: u64,
pub loc: u64,
pub data_align: i64,
pub sp: SizePrint,
}

impl EhInstrContext {
fn print<R: Reader>(&mut self, instr: CallFrameInstruction<R>) {
pub fn print<R: Reader>(&mut self, instr: CallFrameInstruction<R>) {
use CallFrameInstruction::*;

match instr {
SetLoc { address } => {
println!(
"DW_CFA_set_loc({address})\tloc = {address}",
"{:30} loc = {address}",
format!("DW_CFA_set_loc({address})")
);
},
AdvanceLoc { delta } => {
self.loc += delta as u64;
println!(
"DW_CFA_advance_loc({delta})\tloc += {delta}\tloc = {}",
"{:30} loc += {delta}\tloc = {}",
format!("DW_CFA_advance_loc({delta})"),
self.sp.hex(self.loc),
);
},
DefCfa { register, offset } => {
println!(
"DW_CFA_def_cfa({}, {offset})\t\tcfa = %{} + {offset}",
register.0, register_name(register),
"{:30} cfa = %{} + {offset}",
format!("DW_CFA_def_cfa({}, {offset})", register.0),
register_name(register),
);
self.cfa_reg = register;
self.cfa_off = offset;
Expand All @@ -221,14 +224,16 @@ impl EhInstrContext {
},
DefCfaRegister { register } => {
println!(
"DW_CFA_def_cfa_register({})\tcfa = %{} + \x1b[90m{}\x1b[0m",
register.0, register_name(register), self.cfa_off,
"{:30} cfa = %{} + \x1b[90m{}\x1b[0m",
format!("DW_CFA_def_cfa_register({})", register.0),
register_name(register), self.cfa_off,
);
self.cfa_reg = register;
},
DefCfaOffset { offset } => {
println!(
"DW_CFA_def_cfa_offset({offset})\tcfa = \x1b[90m%{}\x1b[0m + {offset}",
"{:30} cfa = \x1b[90m%{}\x1b[0m + {offset}",
format!("DW_CFA_def_cfa_offset({offset})"),
register_name(self.cfa_reg),
);
self.cfa_off = offset;
Expand All @@ -241,23 +246,26 @@ impl EhInstrContext {
},
Undefined { register } => {
println!(
"DW_CFA_undefined({})\t\t%{} @ ??? (unrecoverable)",
register.0, register_name(register),
"{:30} %{} @ ??? (unrecoverable)",
format!("DW_CFA_undefined({})", register.0),
register_name(register),
);
},
SameValue { register } => {
println!(
"DW_CFA_same_value({})\t\t%{} untouched",
register.0, register_name(register),
"{:30} %{} untouched",
format!("DW_CFA_same_value({})", register.0),
register_name(register),
);
},
Offset { register, factored_offset } => {
let off = factored_offset as i64 * self.data_align;
println!(
"DW_CFA_offset({}, {factored_offset})\t\t%{} @ cfa {} {}",
register.0, register_name(register),
"{:30} %{} @ cfa {} {}",
format!("DW_CFA_offset({}, {factored_offset})", register.0),
register_name(register),
if off < 0 { "−" } else { "+" },
off.abs()
off.abs(),
);
},
OffsetExtendedSf { register, factored_offset } => {
Expand All @@ -277,8 +285,8 @@ impl EhInstrContext {
},
Register { dest_register, src_register } => {
println!(
"DW_CFA_register({}, {})\t%{} = %{}",
dest_register.0, src_register.0,
"{:30} %{} = %{}",
format!("DW_CFA_register({}, {})", dest_register.0, src_register.0),
register_name(dest_register), register_name(src_register),
);
},
Expand All @@ -297,8 +305,9 @@ impl EhInstrContext {
},
Restore { register } => {
println!(
"DW_CFA_restore({})\t\t%{} @ (initial rule)",
register.0, register_name(register),
"{:30} %{} @ (initial rule)",
format!("DW_CFA_restore({})", register.0),
register_name(register),
);
},
RememberState => println!("DW_CFA_remember_state()"),
Expand Down
122 changes: 119 additions & 3 deletions src/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@
* any later version. See LICENSE file for more information. *
******************************************************************************/

use std::cell::RefCell;
use std::collections::HashMap;
use goblin::elf::Elf;
use anyhow::{anyhow, Context, Result};
use gimli::{BaseAddresses, CallFrameInstruction, EhFrame, EndianSlice,
FrameDescriptionEntry, LittleEndian, Register, SectionBaseAddresses,
UnwindSection};
use goblin::container::Container;
use goblin::elf::sym::STT_FUNC;
use iced_x86::{Decoder, DecoderOptions, Formatter, FormatterOutput,
Expand All @@ -19,8 +23,10 @@ use iced_x86::{Decoder, DecoderOptions, Formatter, FormatterOutput,
use rustc_demangle::demangle;

use crate::args::FnArgs;
use crate::eh::EhInstrContext;
use crate::elf::{find_symbol, find_symbol_by_addr, symbol_file_offset};
use crate::print::SizePrint;
use crate::sections::find_section;
use crate::sym::sym_type;

pub fn do_fn(elf: &Elf, bytes: &[u8], args: &FnArgs) -> Result<()> {
Expand Down Expand Up @@ -53,7 +59,10 @@ pub fn do_fn(elf: &Elf, bytes: &[u8], args: &FnArgs) -> Result<()> {
let content = &bytes[file_off..(file_off + sym.st_size as usize)];

println!("\x1b[97m{sym_name}:\x1b[0m");
disassemble(elf, sym.st_value, content);

let mut opts = DisassOptions::default();
opts.cfi = args.cfi;
disassemble(elf, bytes, sym.st_value, content, opts);

Ok(())
}
Expand Down Expand Up @@ -105,7 +114,13 @@ impl FormatterOutput for ColorOutput {
}
}

fn disassemble(elf: &Elf, ip: u64, content: &[u8]) {
#[derive(Default)]
struct DisassOptions {
intel_syntax: bool,
cfi: bool,
}

fn disassemble(elf: &Elf, bytes: &[u8], ip: u64, content: &[u8], opts: DisassOptions) {
let container = elf.header.container().unwrap_or(Container::Big);
let bitness = match container {
Container::Big => 64,
Expand Down Expand Up @@ -133,7 +148,7 @@ fn disassemble(elf: &Elf, ip: u64, content: &[u8]) {
});

let mut output = ColorOutput;
let mut formatter: Box<dyn Formatter> = if false {
let mut formatter: Box<dyn Formatter> = if opts.intel_syntax {
Box::new(IntelFormatter::with_options(
Some(sym_resolver),
None,
Expand All @@ -150,11 +165,17 @@ fn disassemble(elf: &Elf, ip: u64, content: &[u8]) {
formatter.options_mut().set_space_between_memory_add_operators(true);
formatter.options_mut().set_gas_space_after_memory_operand_comma(true);

let mut eh = opts.cfi.then(|| EhFnCtx::new(elf, bytes, ip)).flatten();

while decoder.can_decode() {
let instr = decoder.decode();
let start_index = (instr.ip() - ip) as usize;
let bytes = &content[start_index..(start_index + instr.len())];

if let Some(ref mut eh) = eh {
eh.at_ip(instr.ip());
}

print!("{} \x1b[97m│\x1b[0m ", sp.hex(instr.ip()));

let col_w;
Expand All @@ -180,3 +201,98 @@ fn disassemble(elf: &Elf, ip: u64, content: &[u8]) {
println!("\x1b[0m");
}
}

struct EhFnCtx<'a> {
base_addrs: &'static BaseAddresses,
eh: EhFrame<EndianSlice<'a, LittleEndian>>,
fde: FrameDescriptionEntry<EndianSlice<'a, LittleEndian>>,
instr_ctx: RefCell<EhInstrContext>,
curr_loc: RefCell<u64>,
cie_shown: bool,
instr_index: usize,
}

impl<'a> EhFnCtx<'a> {
fn new(elf: &Elf, bytes: &'a [u8], ip: u64) -> Option<Self> {
let container = elf.header.container().unwrap_or(Container::Big);
let sp = SizePrint::new(container);

let eh_frame = find_section(elf, ".eh_frame")?;
let eh = EhFrame::new(&bytes[eh_frame.file_range()?], LittleEndian);

let base_addrs = Box::leak(Box::new(BaseAddresses {
eh_frame_hdr: SectionBaseAddresses::default(),
eh_frame: SectionBaseAddresses {
section: Some(eh_frame.sh_addr),
text: None,
data: None,
},
}));

let fde = eh.fde_for_address(
&base_addrs,
ip,
|section, bases, offset| section.cie_from_offset(bases, offset),
).ok()?;

let instr_ctx = EhInstrContext {
cfa_reg: Register(0),
cfa_off: 0,
loc: fde.initial_address(),
data_align: fde.cie().data_alignment_factor(),
sp,
};
let curr_loc = instr_ctx.loc;

Some(EhFnCtx {
base_addrs,
eh,
fde,
instr_ctx: RefCell::new(instr_ctx),
curr_loc: RefCell::new(curr_loc),
cie_shown: false,
instr_index: 0,
})
}

fn at_ip(&mut self, ip: u64) {
if !self.cie_shown {
let mut iter = self.fde.cie().instructions(&self.eh, self.base_addrs);
while let Ok(Some(instr)) = iter.next() {
self.print_instr(instr);
}
self.cie_shown = true;
}

let mut iter = self.fde.instructions(&self.eh, self.base_addrs);
for _ in 0..self.instr_index {
if iter.next().ok().flatten().is_none() {
return;
}
}

while let Ok(Some(instr)) = iter.next() {
if ip < *self.curr_loc.borrow() {
break;
}
self.print_instr(instr);
self.instr_index += 1;
}
}

fn print_instr(
&self,
instr: CallFrameInstruction<EndianSlice<'a, LittleEndian>>,
) {
match instr {
CallFrameInstruction::Nop => (),
CallFrameInstruction::AdvanceLoc { delta } => {
*self.curr_loc.borrow_mut() += delta as u64;
},
_ => {
print!("\x1b[35m[CFI]\x1b[0m ");
self.instr_ctx.borrow_mut().print(instr.clone());
}
}
}
}

0 comments on commit 6119f88

Please sign in to comment.