blob: dd308840f5c01450d5afa9c873da6de9bec7f2dc [file] [log] [blame]
use super::mystd::ffi::{OsStr, OsString};
use super::mystd::os::unix::ffi::OsStrExt;
use super::mystd::str;
use super::{gimli, Context, Endian, EndianSlice, Mapping, Path, Stash, Vec};
use alloc::sync::Arc;
use core::ops::Deref;
use object::read::archive::ArchiveFile;
use object::read::xcoff::{FileHeader, SectionHeader, XcoffFile, XcoffSymbol};
use object::Object as _;
use object::ObjectSection as _;
use object::ObjectSymbol as _;
use object::SymbolFlags;
#[cfg(target_pointer_width = "32")]
type Xcoff = object::xcoff::FileHeader32;
#[cfg(target_pointer_width = "64")]
type Xcoff = object::xcoff::FileHeader64;
impl Mapping {
pub fn new(path: &Path, member_name: &OsString) -> Option<Mapping> {
let map = super::mmap(path)?;
Mapping::mk(map, |data, stash| {
if member_name.is_empty() {
Context::new(stash, Object::parse(data)?, None, None)
} else {
let archive = ArchiveFile::parse(data).ok()?;
for member in archive
.members()
.filter_map(|m| m.ok())
.filter(|m| OsStr::from_bytes(m.name()) == member_name)
{
let member_data = member.data(data).ok()?;
if let Some(obj) = Object::parse(member_data) {
return Context::new(stash, obj, None, None);
}
}
None
}
})
}
}
struct ParsedSym<'a> {
address: u64,
size: u64,
name: &'a str,
}
pub struct Object<'a> {
syms: Vec<ParsedSym<'a>>,
file: XcoffFile<'a, Xcoff>,
}
pub struct Image {
pub offset: usize,
pub base: u64,
pub size: usize,
}
pub fn parse_xcoff(data: &[u8]) -> Option<Image> {
let mut offset = 0;
let header = Xcoff::parse(data, &mut offset).ok()?;
let _ = header.aux_header(data, &mut offset).ok()?;
let sections = header.sections(data, &mut offset).ok()?;
if let Some(section) = sections.iter().find(|s| {
if let Ok(name) = str::from_utf8(&s.s_name()[0..5]) {
name == ".text"
} else {
false
}
}) {
Some(Image {
offset: section.s_scnptr() as usize,
base: section.s_paddr() as u64,
size: section.s_size() as usize,
})
} else {
None
}
}
pub fn parse_image(path: &Path, member_name: &OsString) -> Option<Image> {
let map = super::mmap(path)?;
let data = map.deref();
if member_name.is_empty() {
return parse_xcoff(data);
} else {
let archive = ArchiveFile::parse(data).ok()?;
for member in archive
.members()
.filter_map(|m| m.ok())
.filter(|m| OsStr::from_bytes(m.name()) == member_name)
{
let member_data = member.data(data).ok()?;
if let Some(image) = parse_xcoff(member_data) {
return Some(image);
}
}
None
}
}
impl<'a> Object<'a> {
fn get_concrete_size(file: &XcoffFile<'a, Xcoff>, sym: &XcoffSymbol<'a, '_, Xcoff>) -> u64 {
match sym.flags() {
SymbolFlags::Xcoff {
n_sclass: _,
x_smtyp: _,
x_smclas: _,
containing_csect: Some(index),
} => {
if let Ok(tgt_sym) = file.symbol_by_index(index) {
Self::get_concrete_size(file, &tgt_sym)
} else {
0
}
}
_ => sym.size(),
}
}
fn parse(data: &'a [u8]) -> Option<Object<'a>> {
let file = XcoffFile::parse(data).ok()?;
let mut syms = file
.symbols()
.filter_map(|sym| {
let name = sym.name().map_or("", |v| v);
let address = sym.address();
let size = Self::get_concrete_size(&file, &sym);
if name == ".text" || name == ".data" {
// We don't want to include ".text" and ".data" symbols.
// If they are included, since their ranges cover other
// symbols, when searching a symbol for a given address,
// ".text" or ".data" is returned. That's not what we expect.
None
} else {
Some(ParsedSym {
address,
size,
name,
})
}
})
.collect::<Vec<_>>();
syms.sort_by_key(|s| s.address);
Some(Object { syms, file })
}
pub fn section(&self, _: &Stash, name: &str) -> Option<&'a [u8]> {
Some(self.file.section_by_name(name)?.data().ok()?)
}
pub fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> {
// Symbols, except ".text" and ".data", are sorted and are not overlapped each other,
// so we can just perform a binary search here.
let i = match self.syms.binary_search_by_key(&addr, |sym| sym.address) {
Ok(i) => i,
Err(i) => i.checked_sub(1)?,
};
let sym = self.syms.get(i)?;
if (sym.address..sym.address + sym.size).contains(&addr) {
// On AIX, for a function call, for example, `foo()`, we have
// two symbols `foo` and `.foo`. `foo` references the function
// descriptor and `.foo` references the function entry.
// See https://www.ibm.com/docs/en/xl-fortran-aix/16.1.0?topic=calls-linkage-convention-function
// for more information.
// We trim the prefix `.` here, so that the rust demangler can work
// properly.
Some(sym.name.trim_start_matches(".").as_bytes())
} else {
None
}
}
pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> {
None
}
}
pub(super) fn handle_split_dwarf<'data>(
_package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>,
_stash: &'data Stash,
_load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>,
) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> {
None
}