use qcode::{
address_index::{AddressIndex, AddressTarget},
context::Context,
value::literal::SymbolicRef,
};
pub fn resolve_addresses(ctx: &mut Context) {
let addresses = AddressIndex::analyze(ctx);
let pairs: Vec<(_, u64)> = ctx
.shared
.values
.literals
.iter()
.map(|item| (item.id, item.value))
.collect();
for (id, value) in pairs {
let addr = value;
let symbolic = match addresses.get(addr) {
Some(AddressTarget::Function(function)) => Some(SymbolicRef::Function(function)),
Some(AddressTarget::Block(block)) => Some(SymbolicRef::Block(block)),
None => None,
};
if let Some(sym) = symbolic {
ctx.shared.values.literals[id].symbolic = Some(sym);
}
}
}
pub fn resolve_strings(ctx: &mut Context, read_cstring: impl Fn(u64) -> Option<Vec<u8>>) {
let pairs: Vec<_> = ctx
.shared
.values
.literals
.iter()
.filter(|item| item.symbolic.is_none())
.map(|item| (item.id, item.value))
.collect();
for (id, value) in pairs {
let Some(bytes) = read_cstring(value) else {
continue;
};
if bytes.is_empty() {
continue;
}
let s = String::from_utf8(bytes).expect("printable ASCII is valid UTF-8");
ctx.shared.values.literals[id].symbolic = Some(SymbolicRef::String(s));
}
}
#[cfg(test)]
mod tests {
use super::resolve_strings;
use qcode::{context::Context, value::literal::SymbolicRef};
fn hello_at_0x1000(addr: u64) -> Option<Vec<u8>> {
(addr == 0x1000).then(|| b"hello".to_vec())
}
#[test]
fn resolve_strings_annotates_printable_cstrings() {
let mut ctx = Context::new();
let lit_id = ctx.get_const(0x1000, 8).id;
resolve_strings(&mut ctx, hello_at_0x1000);
assert!(matches!(
&ctx.shared.values.literals[lit_id].symbolic,
Some(SymbolicRef::String(s)) if s == "hello"
));
}
#[test]
fn resolve_strings_leaves_unreadable_addresses_alone() {
let mut ctx = Context::new();
let lit_id = ctx.get_const(0x2000, 8).id;
resolve_strings(&mut ctx, hello_at_0x1000);
assert!(ctx.shared.values.literals[lit_id].symbolic.is_none());
}
}