use crate::Core;
use crate::sys::{self, R2RustAddrLine, R2RustBinIdentity, R2RustMapIdentity, R2RustSymbol};
use std::ffi::{CStr, CString};
use std::os::raw::c_void;
use std::ptr;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BinIdentity(R2RustBinIdentity);
impl BinIdentity {
pub fn current(core: Core) -> Option<Self> {
let mut identity = R2RustBinIdentity {
bin_file: 0,
bin_object: 0,
bin_file_id: 0,
};
unsafe {
sys::r2_rust_current_bin_identity(core.as_ptr(), &mut identity)
.then_some(Self(identity))
}
}
pub fn is_current(self, core: Core) -> bool {
unsafe { sys::r2_rust_bin_identity_matches(core.as_ptr(), &self.0) }
}
pub const fn file_id(self) -> u32 {
self.0.bin_file_id
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SymbolKind {
NoType,
Object,
Function,
}
impl SymbolKind {
const fn raw(self) -> i32 {
match self {
Self::NoType => sys::R2_RUST_SYMBOL_NOTYPE,
Self::Object => sys::R2_RUST_SYMBOL_OBJECT,
Self::Function => sys::R2_RUST_SYMBOL_FUNCTION,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Symbol {
pub name: String,
pub paddr: Option<u64>,
pub vaddr: u64,
pub size: u32,
pub ordinal: u32,
pub kind: SymbolKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SymbolAdd {
Added,
Duplicate,
Failed,
}
#[derive(Debug, Clone, Copy)]
pub struct SymbolTransaction {
identity: BinIdentity,
initial_len: usize,
added: usize,
}
impl SymbolTransaction {
pub fn begin(core: Core, identity: BinIdentity) -> Option<Self> {
let initial_len = unsafe { sys::r2_rust_symbol_count(core.as_ptr(), &identity.0) };
(initial_len != usize::MAX).then_some(Self {
identity,
initial_len,
added: 0,
})
}
pub fn add(&mut self, core: Core, symbol: &Symbol) -> SymbolAdd {
let Some((name, raw)) = raw_symbol(symbol) else {
return SymbolAdd::Failed;
};
let _name = name;
unsafe {
if sys::r2_rust_symbol_exists(core.as_ptr(), &self.identity.0, &raw) {
return SymbolAdd::Duplicate;
}
if sys::r2_rust_symbol_add(core.as_ptr(), &self.identity.0, &raw) {
self.added += 1;
SymbolAdd::Added
} else {
SymbolAdd::Failed
}
}
}
pub const fn added(self) -> usize {
self.added
}
pub fn can_rollback(self, core: Core) -> bool {
let current_len = unsafe { sys::r2_rust_symbol_count(core.as_ptr(), &self.identity.0) };
current_len == self.initial_len.saturating_add(self.added)
}
pub fn rollback(self, core: Core) -> bool {
self.can_rollback(core)
&& unsafe {
sys::r2_rust_symbols_truncate(core.as_ptr(), &self.identity.0, self.initial_len)
}
}
}
fn raw_symbol(symbol: &Symbol) -> Option<(CString, R2RustSymbol)> {
let name = CString::new(symbol.name.as_bytes()).ok()?;
let raw = R2RustSymbol {
name: name.as_ptr(),
paddr: symbol.paddr.unwrap_or(u64::MAX),
vaddr: symbol.vaddr,
size: symbol.size,
ordinal: symbol.ordinal,
kind: symbol.kind.raw(),
};
Some((name, raw))
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AddrLine {
pub addr: u64,
pub file: String,
pub path: Option<String>,
pub line: u32,
pub column: u32,
}
#[derive(Debug, Clone)]
pub struct AddrLineSnapshot {
identity: BinIdentity,
rows: Vec<AddrLine>,
}
impl AddrLineSnapshot {
pub fn capture(core: Core, identity: BinIdentity) -> Option<Self> {
let mut rows = Vec::<AddrLine>::new();
let ok = unsafe {
sys::r2_rust_addrline_foreach(
core.as_ptr(),
&identity.0,
collect_addrline,
(&mut rows as *mut Vec<AddrLine>).cast(),
)
};
ok.then_some(Self { identity, rows })
}
pub fn rows(&self) -> &[AddrLine] {
&self.rows
}
pub fn restore(&self, core: Core) -> bool {
replace_addrlines(core, self.identity, &self.rows)
}
}
unsafe extern "C" fn collect_addrline(user: *mut c_void, raw: *const R2RustAddrLine) -> bool {
let Some(rows) = (unsafe { user.cast::<Vec<AddrLine>>().as_mut() }) else {
return false;
};
let Some(raw) = (unsafe { raw.as_ref() }) else {
return false;
};
let Some(file) = (unsafe { c_string(raw.file) }) else {
return true;
};
rows.push(AddrLine {
addr: raw.addr,
file,
path: unsafe { c_string(raw.path) },
line: raw.line,
column: raw.column,
});
true
}
unsafe fn c_string(raw: *const std::os::raw::c_char) -> Option<String> {
(!raw.is_null()).then(|| unsafe { CStr::from_ptr(raw).to_string_lossy().into_owned() })
}
pub fn replace_addrlines(core: Core, identity: BinIdentity, rows: &[AddrLine]) -> bool {
let Some(previous) = AddrLineSnapshot::capture(core, identity) else {
return false;
};
if replace_addrlines_unchecked(core, identity, rows) {
true
} else {
let _ = replace_addrlines_unchecked(core, identity, previous.rows());
false
}
}
fn replace_addrlines_unchecked(core: Core, identity: BinIdentity, rows: &[AddrLine]) -> bool {
if !identity.is_current(core)
|| !unsafe { sys::r2_rust_addrline_reset(core.as_ptr(), &identity.0) }
{
return false;
}
rows.iter().all(|row| add_addrline(core, identity, row))
}
pub fn add_addrline(core: Core, identity: BinIdentity, row: &AddrLine) -> bool {
let Ok(file) = CString::new(row.file.as_bytes()) else {
return false;
};
let path = match row.path.as_deref() {
Some(path) => match CString::new(path.as_bytes()) {
Ok(path) => Some(path),
Err(_) => return false,
},
None => None,
};
let raw = R2RustAddrLine {
addr: row.addr,
file: file.as_ptr(),
path: path.as_ref().map_or(ptr::null(), |path| path.as_ptr()),
line: row.line,
column: row.column,
};
unsafe { sys::r2_rust_addrline_add(core.as_ptr(), &identity.0, &raw) }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MapIdentity(R2RustMapIdentity);
impl MapIdentity {
pub fn is_current(self, core: Core) -> bool {
unsafe { sys::r2_rust_map_identity_matches(core.as_ptr(), &self.0) }
}
pub const fn begin(self) -> u64 {
self.0.begin
}
pub const fn delta(self) -> u64 {
self.0.delta
}
pub const fn is_executable(self) -> bool {
self.0.perm & 1 != 0
}
}
fn empty_map_identity() -> R2RustMapIdentity {
R2RustMapIdentity {
map_id: 0,
fd: -1,
perm: 0,
begin: 0,
size: 0,
delta: 0,
}
}
pub fn resolve_paddr(core: Core, paddr: u64) -> Option<(u64, MapIdentity)> {
let mut vaddr = 0;
let mut identity = empty_map_identity();
unsafe {
sys::r2_rust_map_for_paddr(core.as_ptr(), paddr, &mut vaddr, &mut identity)
.then_some((vaddr, MapIdentity(identity)))
}
}
pub fn resolve_vaddr(core: Core, vaddr: u64) -> Option<(u64, MapIdentity)> {
let mut paddr = 0;
let mut identity = empty_map_identity();
unsafe {
sys::r2_rust_map_for_vaddr(core.as_ptr(), vaddr, &mut paddr, &mut identity)
.then_some((paddr, MapIdentity(identity)))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FunctionAdd {
Added,
Existing,
Failed,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct OwnedFunction {
addr: u64,
name: String,
}
#[derive(Debug, Clone, Default)]
pub struct FunctionTransaction {
functions: Vec<OwnedFunction>,
}
impl FunctionTransaction {
pub const fn begin() -> Self {
Self {
functions: Vec::new(),
}
}
pub fn add(&mut self, core: Core, addr: u64, name: &str) -> FunctionAdd {
if unsafe { sys::r2_rust_function_exists(core.as_ptr(), addr) } {
return FunctionAdd::Existing;
}
let Ok(name_c) = CString::new(name) else {
return FunctionAdd::Failed;
};
if unsafe { sys::r2_rust_function_analyze(core.as_ptr(), addr, name_c.as_ptr()) } {
self.functions.push(OwnedFunction {
addr,
name: name.to_owned(),
});
FunctionAdd::Added
} else {
FunctionAdd::Failed
}
}
pub fn added(&self) -> usize {
self.functions.len()
}
pub fn can_rollback(&self, core: Core) -> bool {
self.functions.iter().all(|function| {
CString::new(function.name.as_bytes()).is_ok_and(|name| unsafe {
sys::r2_rust_function_matches(core.as_ptr(), function.addr, name.as_ptr())
})
})
}
pub fn rollback(&self, core: Core) -> bool {
if !self.can_rollback(core) {
return false;
}
self.functions.iter().rev().all(|function| {
let name = CString::new(function.name.as_bytes()).expect("function name was validated");
unsafe { sys::r2_rust_function_delete(core.as_ptr(), function.addr, name.as_ptr()) }
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum XrefKind {
Data,
}
impl XrefKind {
const fn raw(self) -> i32 {
match self {
Self::Data => b'd' as i32,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum XrefAdd {
Added,
Existing,
Failed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct OwnedXref {
from: u64,
to: u64,
kind: XrefKind,
}
#[derive(Debug, Clone, Default)]
pub struct XrefTransaction {
xrefs: Vec<OwnedXref>,
}
impl XrefTransaction {
pub const fn begin() -> Self {
Self { xrefs: Vec::new() }
}
pub fn add(&mut self, core: Core, from: u64, to: u64, kind: XrefKind) -> XrefAdd {
let raw = kind.raw();
if unsafe { sys::r2_rust_xref_exists(core.as_ptr(), from, to, raw) } {
return XrefAdd::Existing;
}
if unsafe { sys::r2_rust_xref_add(core.as_ptr(), from, to, raw) } {
self.xrefs.push(OwnedXref { from, to, kind });
XrefAdd::Added
} else {
XrefAdd::Failed
}
}
pub fn added(&self) -> usize {
self.xrefs.len()
}
pub fn can_rollback(&self, core: Core) -> bool {
self.xrefs.iter().all(|xref| unsafe {
sys::r2_rust_xref_exists(core.as_ptr(), xref.from, xref.to, xref.kind.raw())
})
}
pub fn rollback(&self, core: Core) -> bool {
if !self.can_rollback(core) {
return false;
}
self.xrefs.iter().rev().all(|xref| unsafe {
sys::r2_rust_xref_delete(core.as_ptr(), xref.from, xref.to, xref.kind.raw())
})
}
}