164 lines
5.2 KiB
Rust
164 lines
5.2 KiB
Rust
use alloc::borrow::Cow;
|
|
use alloc::sync::Arc;
|
|
use std::fs::File;
|
|
use std::path::PathBuf;
|
|
|
|
use object::Object;
|
|
|
|
use crate::{LookupContinuation, LookupResult};
|
|
|
|
#[cfg(unix)]
|
|
fn convert_path<R: gimli::Reader<Endian = gimli::RunTimeEndian>>(
|
|
r: &R,
|
|
) -> Result<PathBuf, gimli::Error> {
|
|
use std::ffi::OsStr;
|
|
use std::os::unix::ffi::OsStrExt;
|
|
let bytes = r.to_slice()?;
|
|
let s = OsStr::from_bytes(&bytes);
|
|
Ok(PathBuf::from(s))
|
|
}
|
|
|
|
#[cfg(not(unix))]
|
|
fn convert_path<R: gimli::Reader<Endian = gimli::RunTimeEndian>>(
|
|
r: &R,
|
|
) -> Result<PathBuf, gimli::Error> {
|
|
let bytes = r.to_slice()?;
|
|
let s = std::str::from_utf8(&bytes).map_err(|_| gimli::Error::BadUtf8)?;
|
|
Ok(PathBuf::from(s))
|
|
}
|
|
|
|
fn load_section<'data: 'file, 'file, O, R, F>(
|
|
id: gimli::SectionId,
|
|
file: &'file O,
|
|
endian: R::Endian,
|
|
loader: &mut F,
|
|
) -> Result<R, gimli::Error>
|
|
where
|
|
O: object::Object<'data, 'file>,
|
|
R: gimli::Reader<Endian = gimli::RunTimeEndian>,
|
|
F: FnMut(Cow<'data, [u8]>, R::Endian) -> R,
|
|
{
|
|
use object::ObjectSection;
|
|
|
|
let data = id
|
|
.dwo_name()
|
|
.and_then(|dwo_name| {
|
|
file.section_by_name(dwo_name)
|
|
.and_then(|section| section.uncompressed_data().ok())
|
|
})
|
|
.unwrap_or(Cow::Borrowed(&[]));
|
|
Ok(loader(data, endian))
|
|
}
|
|
|
|
/// A simple builtin split DWARF loader.
|
|
pub struct SplitDwarfLoader<R, F>
|
|
where
|
|
R: gimli::Reader<Endian = gimli::RunTimeEndian>,
|
|
F: FnMut(Cow<'_, [u8]>, R::Endian) -> R,
|
|
{
|
|
loader: F,
|
|
dwarf_package: Option<gimli::DwarfPackage<R>>,
|
|
}
|
|
|
|
impl<R, F> SplitDwarfLoader<R, F>
|
|
where
|
|
R: gimli::Reader<Endian = gimli::RunTimeEndian>,
|
|
F: FnMut(Cow<'_, [u8]>, R::Endian) -> R,
|
|
{
|
|
fn load_dwarf_package(loader: &mut F, path: Option<PathBuf>) -> Option<gimli::DwarfPackage<R>> {
|
|
let mut path = path.map(Ok).unwrap_or_else(std::env::current_exe).ok()?;
|
|
let dwp_extension = path
|
|
.extension()
|
|
.map(|previous_extension| {
|
|
let mut previous_extension = previous_extension.to_os_string();
|
|
previous_extension.push(".dwp");
|
|
previous_extension
|
|
})
|
|
.unwrap_or_else(|| "dwp".into());
|
|
path.set_extension(dwp_extension);
|
|
let file = File::open(&path).ok()?;
|
|
let map = unsafe { memmap2::Mmap::map(&file).ok()? };
|
|
let dwp = object::File::parse(&*map).ok()?;
|
|
|
|
let endian = if dwp.is_little_endian() {
|
|
gimli::RunTimeEndian::Little
|
|
} else {
|
|
gimli::RunTimeEndian::Big
|
|
};
|
|
|
|
let empty = loader(Cow::Borrowed(&[]), endian);
|
|
gimli::DwarfPackage::load(
|
|
|section_id| load_section(section_id, &dwp, endian, loader),
|
|
empty,
|
|
)
|
|
.ok()
|
|
}
|
|
|
|
/// Create a new split DWARF loader.
|
|
pub fn new(mut loader: F, path: Option<PathBuf>) -> SplitDwarfLoader<R, F> {
|
|
let dwarf_package = SplitDwarfLoader::load_dwarf_package(&mut loader, path);
|
|
SplitDwarfLoader {
|
|
loader,
|
|
dwarf_package,
|
|
}
|
|
}
|
|
|
|
/// Run the provided `LookupResult` to completion, loading any necessary
|
|
/// split DWARF along the way.
|
|
pub fn run<L>(&mut self, mut l: LookupResult<L>) -> L::Output
|
|
where
|
|
L: LookupContinuation<Buf = R>,
|
|
{
|
|
loop {
|
|
let (load, continuation) = match l {
|
|
LookupResult::Output(output) => break output,
|
|
LookupResult::Load { load, continuation } => (load, continuation),
|
|
};
|
|
|
|
let mut r: Option<Arc<gimli::Dwarf<_>>> = None;
|
|
if let Some(dwp) = self.dwarf_package.as_ref() {
|
|
if let Ok(Some(cu)) = dwp.find_cu(load.dwo_id, &load.parent) {
|
|
r = Some(Arc::new(cu));
|
|
}
|
|
}
|
|
|
|
if r.is_none() {
|
|
let mut path = PathBuf::new();
|
|
if let Some(p) = load.comp_dir.as_ref() {
|
|
if let Ok(p) = convert_path(p) {
|
|
path.push(p);
|
|
}
|
|
}
|
|
|
|
if let Some(p) = load.path.as_ref() {
|
|
if let Ok(p) = convert_path(p) {
|
|
path.push(p);
|
|
}
|
|
}
|
|
|
|
if let Ok(file) = File::open(&path) {
|
|
if let Ok(map) = unsafe { memmap2::Mmap::map(&file) } {
|
|
if let Ok(file) = object::File::parse(&*map) {
|
|
let endian = if file.is_little_endian() {
|
|
gimli::RunTimeEndian::Little
|
|
} else {
|
|
gimli::RunTimeEndian::Big
|
|
};
|
|
|
|
r = gimli::Dwarf::load(|id| {
|
|
load_section(id, &file, endian, &mut self.loader)
|
|
})
|
|
.ok()
|
|
.map(|mut dwo_dwarf| {
|
|
dwo_dwarf.make_dwo(&load.parent);
|
|
Arc::new(dwo_dwarf)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
l = continuation.resume(r);
|
|
}
|
|
}
|
|
}
|