|  | 
|  | 1 | +use super::mystd::ffi::{OsStr, OsString}; | 
|  | 2 | +use super::mystd::os::unix::ffi::OsStrExt; | 
|  | 3 | +use super::mystd::str; | 
|  | 4 | +use super::{gimli, Context, Endian, EndianSlice, Mapping, Path, Stash, Vec}; | 
|  | 5 | +use alloc::sync::Arc; | 
|  | 6 | +use core::ops::Deref; | 
|  | 7 | +use object::read::archive::ArchiveFile; | 
|  | 8 | +use object::read::xcoff::{FileHeader, SectionHeader, XcoffFile, XcoffSymbol}; | 
|  | 9 | +use object::Object as _; | 
|  | 10 | +use object::ObjectSection as _; | 
|  | 11 | +use object::ObjectSymbol as _; | 
|  | 12 | +use object::SymbolFlags; | 
|  | 13 | + | 
|  | 14 | +#[cfg(target_pointer_width = "32")] | 
|  | 15 | +type Xcoff = object::xcoff::FileHeader32; | 
|  | 16 | +#[cfg(target_pointer_width = "64")] | 
|  | 17 | +type Xcoff = object::xcoff::FileHeader64; | 
|  | 18 | + | 
|  | 19 | +impl Mapping { | 
|  | 20 | +    pub fn new(path: &Path, member_name: &OsString) -> Option<Mapping> { | 
|  | 21 | +        let map = super::mmap(path)?; | 
|  | 22 | +        Mapping::mk(map, |data, stash| { | 
|  | 23 | +            if member_name.is_empty() { | 
|  | 24 | +                Context::new(stash, Object::parse(data)?, None, None) | 
|  | 25 | +            } else { | 
|  | 26 | +                let archive = ArchiveFile::parse(data).ok()?; | 
|  | 27 | +                for member in archive | 
|  | 28 | +                    .members() | 
|  | 29 | +                    .filter_map(|m| m.ok()) | 
|  | 30 | +                    .filter(|m| OsStr::from_bytes(m.name()) == member_name) | 
|  | 31 | +                { | 
|  | 32 | +                    let member_data = member.data(data).ok()?; | 
|  | 33 | +                    if let Some(obj) = Object::parse(member_data) { | 
|  | 34 | +                        return Context::new(stash, obj, None, None); | 
|  | 35 | +                    } | 
|  | 36 | +                } | 
|  | 37 | +                None | 
|  | 38 | +            } | 
|  | 39 | +        }) | 
|  | 40 | +    } | 
|  | 41 | +} | 
|  | 42 | + | 
|  | 43 | +struct ParsedSym<'a> { | 
|  | 44 | +    address: u64, | 
|  | 45 | +    size: u64, | 
|  | 46 | +    name: &'a str, | 
|  | 47 | +} | 
|  | 48 | + | 
|  | 49 | +pub struct Object<'a> { | 
|  | 50 | +    syms: Vec<ParsedSym<'a>>, | 
|  | 51 | +    file: XcoffFile<'a, Xcoff>, | 
|  | 52 | +} | 
|  | 53 | + | 
|  | 54 | +pub struct Image { | 
|  | 55 | +    pub offset: usize, | 
|  | 56 | +    pub base: u64, | 
|  | 57 | +    pub size: usize, | 
|  | 58 | +} | 
|  | 59 | + | 
|  | 60 | +pub fn parse_xcoff(data: &[u8]) -> Option<Image> { | 
|  | 61 | +    let mut offset = 0; | 
|  | 62 | +    let header = Xcoff::parse(data, &mut offset).ok()?; | 
|  | 63 | +    let _ = header.aux_header(data, &mut offset).ok()?; | 
|  | 64 | +    let sections = header.sections(data, &mut offset).ok()?; | 
|  | 65 | +    if let Some(section) = sections.iter().find(|s| { | 
|  | 66 | +        if let Ok(name) = str::from_utf8(&s.s_name()[0..5]) { | 
|  | 67 | +            name == ".text" | 
|  | 68 | +        } else { | 
|  | 69 | +            false | 
|  | 70 | +        } | 
|  | 71 | +    }) { | 
|  | 72 | +        Some(Image { | 
|  | 73 | +            offset: section.s_scnptr() as usize, | 
|  | 74 | +            base: section.s_paddr() as u64, | 
|  | 75 | +            size: section.s_size() as usize, | 
|  | 76 | +        }) | 
|  | 77 | +    } else { | 
|  | 78 | +        None | 
|  | 79 | +    } | 
|  | 80 | +} | 
|  | 81 | + | 
|  | 82 | +pub fn parse_image(path: &Path, member_name: &OsString) -> Option<Image> { | 
|  | 83 | +    let map = super::mmap(path)?; | 
|  | 84 | +    let data = map.deref(); | 
|  | 85 | +    if member_name.is_empty() { | 
|  | 86 | +        return parse_xcoff(data); | 
|  | 87 | +    } else { | 
|  | 88 | +        let archive = ArchiveFile::parse(data).ok()?; | 
|  | 89 | +        for member in archive | 
|  | 90 | +            .members() | 
|  | 91 | +            .filter_map(|m| m.ok()) | 
|  | 92 | +            .filter(|m| OsStr::from_bytes(m.name()) == member_name) | 
|  | 93 | +        { | 
|  | 94 | +            let member_data = member.data(data).ok()?; | 
|  | 95 | +            if let Some(image) = parse_xcoff(member_data) { | 
|  | 96 | +                return Some(image); | 
|  | 97 | +            } | 
|  | 98 | +        } | 
|  | 99 | +        None | 
|  | 100 | +    } | 
|  | 101 | +} | 
|  | 102 | + | 
|  | 103 | +impl<'a> Object<'a> { | 
|  | 104 | +    fn get_concrete_size(file: &XcoffFile<'a, Xcoff>, sym: &XcoffSymbol<'a, '_, Xcoff>) -> u64 { | 
|  | 105 | +        match sym.flags() { | 
|  | 106 | +            SymbolFlags::Xcoff { | 
|  | 107 | +                n_sclass: _, | 
|  | 108 | +                x_smtyp: _, | 
|  | 109 | +                x_smclas: _, | 
|  | 110 | +                containing_csect: Some(index), | 
|  | 111 | +            } => { | 
|  | 112 | +                if let Ok(tgt_sym) = file.symbol_by_index(index) { | 
|  | 113 | +                    Self::get_concrete_size(file, &tgt_sym) | 
|  | 114 | +                } else { | 
|  | 115 | +                    0 | 
|  | 116 | +                } | 
|  | 117 | +            } | 
|  | 118 | +            _ => sym.size(), | 
|  | 119 | +        } | 
|  | 120 | +    } | 
|  | 121 | + | 
|  | 122 | +    fn parse(data: &'a [u8]) -> Option<Object<'a>> { | 
|  | 123 | +        let file = XcoffFile::parse(data).ok()?; | 
|  | 124 | +        let mut syms = file | 
|  | 125 | +            .symbols() | 
|  | 126 | +            .filter_map(|sym| { | 
|  | 127 | +                let name = sym.name().map_or("", |v| v); | 
|  | 128 | +                let address = sym.address(); | 
|  | 129 | +                let size = Self::get_concrete_size(&file, &sym); | 
|  | 130 | +                if name == ".text" || name == ".data" { | 
|  | 131 | +                    // We don't want to include ".text" and ".data" symbols. | 
|  | 132 | +                    // If they are included, since their ranges cover other | 
|  | 133 | +                    // symbols, when searching a symbol for a given address, | 
|  | 134 | +                    // ".text" or ".data" is returned. That's not what we expect. | 
|  | 135 | +                    None | 
|  | 136 | +                } else { | 
|  | 137 | +                    Some(ParsedSym { | 
|  | 138 | +                        address, | 
|  | 139 | +                        size, | 
|  | 140 | +                        name, | 
|  | 141 | +                    }) | 
|  | 142 | +                } | 
|  | 143 | +            }) | 
|  | 144 | +            .collect::<Vec<_>>(); | 
|  | 145 | +        syms.sort_by_key(|s| s.address); | 
|  | 146 | +        Some(Object { syms, file }) | 
|  | 147 | +    } | 
|  | 148 | + | 
|  | 149 | +    pub fn section(&self, _: &Stash, name: &str) -> Option<&'a [u8]> { | 
|  | 150 | +        Some(self.file.section_by_name(name)?.data().ok()?) | 
|  | 151 | +    } | 
|  | 152 | + | 
|  | 153 | +    pub fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> { | 
|  | 154 | +        // Symbols, except ".text" and ".data", are sorted and are not overlapped each other, | 
|  | 155 | +        // so we can just perform a binary search here. | 
|  | 156 | +        let i = match self.syms.binary_search_by_key(&addr, |sym| sym.address) { | 
|  | 157 | +            Ok(i) => i, | 
|  | 158 | +            Err(i) => i.checked_sub(1)?, | 
|  | 159 | +        }; | 
|  | 160 | +        let sym = self.syms.get(i)?; | 
|  | 161 | +        if (sym.address..sym.address + sym.size).contains(&addr) { | 
|  | 162 | +            // On AIX, for a function call, for example, `foo()`, we have | 
|  | 163 | +            // two symbols `foo` and `.foo`. `foo` references the function | 
|  | 164 | +            // descriptor and `.foo` references the function entry. | 
|  | 165 | +            // See https://www.ibm.com/docs/en/xl-fortran-aix/16.1.0?topic=calls-linkage-convention-function | 
|  | 166 | +            // for more information. | 
|  | 167 | +            // We trim the prefix `.` here, so that the rust demangler can work | 
|  | 168 | +            // properly. | 
|  | 169 | +            Some(sym.name.trim_start_matches(".").as_bytes()) | 
|  | 170 | +        } else { | 
|  | 171 | +            None | 
|  | 172 | +        } | 
|  | 173 | +    } | 
|  | 174 | + | 
|  | 175 | +    pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> { | 
|  | 176 | +        None | 
|  | 177 | +    } | 
|  | 178 | +} | 
|  | 179 | + | 
|  | 180 | +pub(super) fn handle_split_dwarf<'data>( | 
|  | 181 | +    _package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>, | 
|  | 182 | +    _stash: &'data Stash, | 
|  | 183 | +    _load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>, | 
|  | 184 | +) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> { | 
|  | 185 | +    None | 
|  | 186 | +} | 
0 commit comments