use std::error::Error;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use goblin::elf::program_header::PF_X;
use goblin::elf::program_header::PT_LOAD;
use goblin::elf::sym;
use goblin::elf::Elf;
use libbpf_rs::Map;
use libbpf_rs::MapCore;
use libbpf_rs::MapMut;
use libbpf_rs::Object;
use libbpf_rs::ObjectBuilder;
use libbpf_rs::OpenObject;
use libbpf_rs::ProgramMut;
pub fn get_test_object_path(filename: &str) -> PathBuf {
let mut path = PathBuf::new();
path.push(env!("CARGO_MANIFEST_DIR"));
path.push("tests/bin");
path.push(filename);
path
}
pub fn open_test_object(filename: &str) -> OpenObject {
let obj_path = get_test_object_path(filename);
let obj = ObjectBuilder::default()
.debug(true)
.open_file(obj_path)
.expect("failed to open object");
obj
}
pub fn get_test_object(filename: &str) -> Object {
open_test_object(filename)
.load()
.expect("failed to load object")
}
#[track_caller]
pub fn get_map<'obj>(object: &'obj Object, name: &str) -> Map<'obj> {
object
.maps()
.find(|map| map.name() == name)
.unwrap_or_else(|| panic!("failed to find map `{name}`"))
}
#[track_caller]
pub fn get_map_mut<'obj>(object: &'obj mut Object, name: &str) -> MapMut<'obj> {
object
.maps_mut()
.find(|map| map.name() == name)
.unwrap_or_else(|| panic!("failed to find map `{name}`"))
}
#[track_caller]
pub fn get_prog_mut<'obj>(object: &'obj mut Object, name: &str) -> ProgramMut<'obj> {
object
.progs_mut()
.find(|map| map.name() == name)
.unwrap_or_else(|| panic!("failed to find program `{name}`"))
}
pub fn with_ringbuffer<F>(map: &Map, action: F) -> i32
where
F: FnOnce(),
{
let mut value = 0i32;
{
let callback = |data: &[u8]| {
plain::copy_from_bytes(&mut value, data).expect("Wrong size");
0
};
let mut builder = libbpf_rs::RingBufferBuilder::new();
builder.add(map, callback).expect("failed to add ringbuf");
let mgr = builder.build().expect("failed to build");
action();
mgr.consume().expect("failed to consume ringbuf");
}
value
}
pub fn get_symbol_offset(binary_path: &Path, symbol_name: &str) -> Result<usize, Box<dyn Error>> {
let buffer = fs::read(binary_path)?;
let elf = Elf::parse(&buffer)?;
for sym in elf.dynsyms.iter() {
if sym.st_type() != sym::STT_FUNC {
continue;
}
if let Some(name) = elf.dynstrtab.get_at(sym.st_name) {
if name == symbol_name {
if let Some(offset) = check_symbol_offset(&elf, &sym) {
return Ok(offset);
}
}
}
}
for sym in elf.syms.iter() {
if sym.st_type() != sym::STT_FUNC {
continue;
}
if let Some(name) = elf.strtab.get_at(sym.st_name) {
if name == symbol_name {
if let Some(offset) = check_symbol_offset(&elf, &sym) {
return Ok(offset);
}
}
}
}
Err(format!("Symbol `{symbol_name}` not found in binary {binary_path:?}").into())
}
fn check_symbol_offset(elf: &Elf, sym: &sym::Sym) -> Option<usize> {
for phdr in elf.program_headers.iter() {
if phdr.p_type != PT_LOAD {
continue;
}
if phdr.p_flags & PF_X == 0 {
continue;
}
if (phdr.p_vaddr..phdr.p_vaddr + phdr.p_memsz).contains(&sym.st_value) {
let offset = sym.st_value - phdr.p_vaddr + phdr.p_offset;
return Some(offset as usize);
}
}
None
}