use std::fs;
use std::path::Path;
use dodo_bindgen_rust_ir::Item;
use object::{Object, ObjectSection};
const PREFIX: &[u8] = b"DODO_META_";
pub fn read(path: &Path) -> Vec<Item> {
let data =
fs::read(path).unwrap_or_else(|err| panic!("failed to read `{}`: {err}", path.display()));
let file = object::File::parse(&*data)
.unwrap_or_else(|err| panic!("failed to parse `{}`: {err}", path.display()));
let exports = file
.exports()
.unwrap_or_else(|err| panic!("failed to read exports of `{}`: {err}", path.display()));
let mut entries = Vec::new();
for export in exports {
let name = export.name();
let name = name
.strip_prefix(b"_")
.filter(|name| name.starts_with(PREFIX))
.unwrap_or(name);
if !name.starts_with(PREFIX) {
continue;
}
let name = String::from_utf8_lossy(name).into_owned();
let bytes = bytes_at(&file, export.address())
.unwrap_or_else(|| panic!("metadata symbol `{name}` points outside of any section"));
let item = Item::decode(bytes)
.unwrap_or_else(|err| panic!("failed to decode metadata symbol `{name}`: {err}"));
entries.push((name, item));
}
entries.sort_by(|(a, _), (b, _)| a.cmp(b));
entries.into_iter().map(|(_, item)| item).collect()
}
fn bytes_at<'data>(file: &object::File<'data>, address: u64) -> Option<&'data [u8]> {
file.sections().find_map(|section| {
let start = section.address();
let offset = address.checked_sub(start)?;
if offset >= section.size() {
return None;
}
section.data().ok()?.get(offset as usize..)
})
}