| 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 |
| } |