From 218caa410aa38c29984be31a5229b9fa717560ee Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:19:13 +0200 Subject: Merging upstream version 1.68.2+dfsg1. Signed-off-by: Daniel Baumann --- vendor/object/src/read/any.rs | 90 ++++ vendor/object/src/read/archive.rs | 315 +++++++++++-- vendor/object/src/read/coff/symbol.rs | 8 + vendor/object/src/read/elf/comdat.rs | 2 +- vendor/object/src/read/elf/file.rs | 5 +- vendor/object/src/read/elf/relocation.rs | 56 ++- vendor/object/src/read/elf/segment.rs | 2 +- vendor/object/src/read/elf/symbol.rs | 15 +- vendor/object/src/read/macho/fat.rs | 2 + vendor/object/src/read/macho/file.rs | 2 + vendor/object/src/read/macho/load_command.rs | 5 + vendor/object/src/read/mod.rs | 25 +- vendor/object/src/read/pe/data_directory.rs | 28 +- vendor/object/src/read/pe/file.rs | 83 ++-- vendor/object/src/read/pe/import.rs | 114 +++++ vendor/object/src/read/pe/resource.rs | 30 +- vendor/object/src/read/xcoff/comdat.rs | 130 ++++++ vendor/object/src/read/xcoff/file.rs | 629 ++++++++++++++++++++++++++ vendor/object/src/read/xcoff/mod.rs | 21 + vendor/object/src/read/xcoff/relocation.rs | 128 ++++++ vendor/object/src/read/xcoff/section.rs | 426 ++++++++++++++++++ vendor/object/src/read/xcoff/segment.rs | 115 +++++ vendor/object/src/read/xcoff/symbol.rs | 634 +++++++++++++++++++++++++++ 23 files changed, 2753 insertions(+), 112 deletions(-) create mode 100644 vendor/object/src/read/xcoff/comdat.rs create mode 100644 vendor/object/src/read/xcoff/file.rs create mode 100644 vendor/object/src/read/xcoff/mod.rs create mode 100644 vendor/object/src/read/xcoff/relocation.rs create mode 100644 vendor/object/src/read/xcoff/section.rs create mode 100644 vendor/object/src/read/xcoff/segment.rs create mode 100644 vendor/object/src/read/xcoff/symbol.rs (limited to 'vendor/object/src/read') diff --git a/vendor/object/src/read/any.rs b/vendor/object/src/read/any.rs index 02e76dcdd..c390b21b6 100644 --- a/vendor/object/src/read/any.rs +++ b/vendor/object/src/read/any.rs @@ -12,6 +12,8 @@ use crate::read::macho; use crate::read::pe; #[cfg(feature = "wasm")] use crate::read::wasm; +#[cfg(feature = "xcoff")] +use crate::read::xcoff; use crate::read::{ self, Architecture, BinaryFormat, CodeView, ComdatKind, CompressedData, CompressedFileRange, Error, Export, FileFlags, FileKind, Import, Object, ObjectComdat, ObjectKind, ObjectMap, @@ -44,6 +46,10 @@ macro_rules! with_inner { $enum::Pe64(ref $var) => $body, #[cfg(feature = "wasm")] $enum::Wasm(ref $var) => $body, + #[cfg(feature = "xcoff")] + $enum::Xcoff32(ref $var) => $body, + #[cfg(feature = "xcoff")] + $enum::Xcoff64(ref $var) => $body, } }; } @@ -67,6 +73,10 @@ macro_rules! with_inner_mut { $enum::Pe64(ref mut $var) => $body, #[cfg(feature = "wasm")] $enum::Wasm(ref mut $var) => $body, + #[cfg(feature = "xcoff")] + $enum::Xcoff32(ref mut $var) => $body, + #[cfg(feature = "xcoff")] + $enum::Xcoff64(ref mut $var) => $body, } }; } @@ -91,6 +101,10 @@ macro_rules! map_inner { $from::Pe64(ref $var) => $to::Pe64($body), #[cfg(feature = "wasm")] $from::Wasm(ref $var) => $to::Wasm($body), + #[cfg(feature = "xcoff")] + $from::Xcoff32(ref $var) => $to::Xcoff32($body), + #[cfg(feature = "xcoff")] + $from::Xcoff64(ref $var) => $to::Xcoff64($body), } }; } @@ -115,6 +129,10 @@ macro_rules! map_inner_option { $from::Pe64(ref $var) => $body.map($to::Pe64), #[cfg(feature = "wasm")] $from::Wasm(ref $var) => $body.map($to::Wasm), + #[cfg(feature = "xcoff")] + $from::Xcoff32(ref $var) => $body.map($to::Xcoff32), + #[cfg(feature = "xcoff")] + $from::Xcoff64(ref $var) => $body.map($to::Xcoff64), } }; } @@ -138,6 +156,10 @@ macro_rules! map_inner_option_mut { $from::Pe64(ref mut $var) => $body.map($to::Pe64), #[cfg(feature = "wasm")] $from::Wasm(ref mut $var) => $body.map($to::Wasm), + #[cfg(feature = "xcoff")] + $from::Xcoff32(ref mut $var) => $body.map($to::Xcoff32), + #[cfg(feature = "xcoff")] + $from::Xcoff64(ref mut $var) => $body.map($to::Xcoff64), } }; } @@ -162,6 +184,10 @@ macro_rules! next_inner { $from::Pe64(ref mut iter) => iter.next().map($to::Pe64), #[cfg(feature = "wasm")] $from::Wasm(ref mut iter) => iter.next().map($to::Wasm), + #[cfg(feature = "xcoff")] + $from::Xcoff32(ref mut iter) => iter.next().map($to::Xcoff32), + #[cfg(feature = "xcoff")] + $from::Xcoff64(ref mut iter) => iter.next().map($to::Xcoff64), } }; } @@ -192,6 +218,10 @@ enum FileInternal<'data, R: ReadRef<'data>> { Pe64(pe::PeFile64<'data, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmFile<'data, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffFile32<'data, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffFile64<'data, R>), } impl<'data, R: ReadRef<'data>> File<'data, R> { @@ -214,6 +244,10 @@ impl<'data, R: ReadRef<'data>> File<'data, R> { FileKind::Pe64 => FileInternal::Pe64(pe::PeFile64::parse(data)?), #[cfg(feature = "coff")] FileKind::Coff => FileInternal::Coff(coff::CoffFile::parse(data)?), + #[cfg(feature = "xcoff")] + FileKind::Xcoff32 => FileInternal::Xcoff32(xcoff::XcoffFile32::parse(data)?), + #[cfg(feature = "xcoff")] + FileKind::Xcoff64 => FileInternal::Xcoff64(xcoff::XcoffFile64::parse(data)?), #[allow(unreachable_patterns)] _ => return Err(Error("Unsupported file format")), }; @@ -250,6 +284,8 @@ impl<'data, R: ReadRef<'data>> File<'data, R> { FileInternal::Pe32(_) | FileInternal::Pe64(_) => BinaryFormat::Pe, #[cfg(feature = "wasm")] FileInternal::Wasm(_) => BinaryFormat::Wasm, + #[cfg(feature = "xcoff")] + FileInternal::Xcoff32(_) | FileInternal::Xcoff64(_) => BinaryFormat::Xcoff, } } } @@ -468,6 +504,10 @@ where Pe64(pe::PeSegmentIterator64<'data, 'file, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmSegmentIterator<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffSegmentIterator32<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffSegmentIterator64<'data, 'file, R>), } impl<'data, 'file, R: ReadRef<'data>> Iterator for SegmentIterator<'data, 'file, R> { @@ -508,6 +548,10 @@ where Pe64(pe::PeSegment64<'data, 'file, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmSegment<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffSegment32<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffSegment64<'data, 'file, R>), } impl<'data, 'file, R: ReadRef<'data>> fmt::Debug for Segment<'data, 'file, R> { @@ -600,6 +644,10 @@ where Pe64(pe::PeSectionIterator64<'data, 'file, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmSectionIterator<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffSectionIterator32<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffSectionIterator64<'data, 'file, R>), } impl<'data, 'file, R: ReadRef<'data>> Iterator for SectionIterator<'data, 'file, R> { @@ -639,6 +687,10 @@ where Pe64(pe::PeSection64<'data, 'file, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmSection<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffSection32<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffSection64<'data, 'file, R>), } impl<'data, 'file, R: ReadRef<'data>> fmt::Debug for Section<'data, 'file, R> { @@ -771,6 +823,10 @@ where Pe64(pe::PeComdatIterator64<'data, 'file, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmComdatIterator<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffComdatIterator32<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffComdatIterator64<'data, 'file, R>), } impl<'data, 'file, R: ReadRef<'data>> Iterator for ComdatIterator<'data, 'file, R> { @@ -810,6 +866,10 @@ where Pe64(pe::PeComdat64<'data, 'file, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmComdat<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffComdat32<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffComdat64<'data, 'file, R>), } impl<'data, 'file, R: ReadRef<'data>> fmt::Debug for Comdat<'data, 'file, R> { @@ -885,6 +945,10 @@ where Pe64(pe::PeComdatSectionIterator64<'data, 'file, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmComdatSectionIterator<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffComdatSectionIterator32<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffComdatSectionIterator64<'data, 'file, R>), } impl<'data, 'file, R: ReadRef<'data>> Iterator for ComdatSectionIterator<'data, 'file, R> { @@ -947,6 +1011,10 @@ where Pe64((coff::CoffSymbolTable<'data, 'file, R>, PhantomData)), #[cfg(feature = "wasm")] Wasm((wasm::WasmSymbolTable<'data, 'file>, PhantomData)), + #[cfg(feature = "xcoff")] + Xcoff32((xcoff::XcoffSymbolTable32<'data, 'file, R>, PhantomData)), + #[cfg(feature = "xcoff")] + Xcoff64((xcoff::XcoffSymbolTable64<'data, 'file, R>, PhantomData)), } impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for SymbolTable<'data, 'file, R> {} @@ -1027,6 +1095,20 @@ where Pe64((coff::CoffSymbolIterator<'data, 'file, R>, PhantomData)), #[cfg(feature = "wasm")] Wasm((wasm::WasmSymbolIterator<'data, 'file>, PhantomData)), + #[cfg(feature = "xcoff")] + Xcoff32( + ( + xcoff::XcoffSymbolIterator32<'data, 'file, R>, + PhantomData, + ), + ), + #[cfg(feature = "xcoff")] + Xcoff64( + ( + xcoff::XcoffSymbolIterator64<'data, 'file, R>, + PhantomData, + ), + ), } impl<'data, 'file, R: ReadRef<'data>> Iterator for SymbolIterator<'data, 'file, R> { @@ -1090,6 +1172,10 @@ where Pe64((coff::CoffSymbol<'data, 'file, R>, PhantomData)), #[cfg(feature = "wasm")] Wasm((wasm::WasmSymbol<'data, 'file>, PhantomData)), + #[cfg(feature = "xcoff")] + Xcoff32((xcoff::XcoffSymbol32<'data, 'file, R>, PhantomData)), + #[cfg(feature = "xcoff")] + Xcoff64((xcoff::XcoffSymbol64<'data, 'file, R>, PhantomData)), } impl<'data, 'file, R: ReadRef<'data>> fmt::Debug for Symbol<'data, 'file, R> { @@ -1240,6 +1326,10 @@ where Pe64(pe::PeRelocationIterator<'data, 'file, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmRelocationIterator<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff32(xcoff::XcoffRelocationIterator32<'data, 'file, R>), + #[cfg(feature = "xcoff")] + Xcoff64(xcoff::XcoffRelocationIterator64<'data, 'file, R>), } impl<'data, 'file, R: ReadRef<'data>> Iterator for SectionRelocationIterator<'data, 'file, R> { diff --git a/vendor/object/src/read/archive.rs b/vendor/object/src/read/archive.rs index 0208878e4..f5aaa9b19 100644 --- a/vendor/object/src/read/archive.rs +++ b/vendor/object/src/read/archive.rs @@ -3,7 +3,7 @@ use core::convert::TryInto; use crate::archive; -use crate::read::{self, Error, ReadError, ReadRef}; +use crate::read::{self, Bytes, Error, ReadError, ReadRef}; /// The kind of archive format. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -23,15 +23,28 @@ pub enum ArchiveKind { Bsd64, /// The Windows COFF archive format. Coff, + /// The AIX big archive format. + AixBig, +} + +/// The list of members in the archive. +#[derive(Debug, Clone, Copy)] +enum Members<'data> { + Common { + offset: u64, + end_offset: u64, + }, + AixBig { + index: &'data [archive::AixMemberOffset], + }, } /// A partially parsed archive file. -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct ArchiveFile<'data, R: ReadRef<'data> = &'data [u8]> { data: R, - len: u64, - offset: u64, kind: ArchiveKind, + members: Members<'data>, symbols: (u64, u64), names: &'data [u8], } @@ -44,15 +57,23 @@ impl<'data, R: ReadRef<'data>> ArchiveFile<'data, R> { let magic = data .read_bytes(&mut tail, archive::MAGIC.len() as u64) .read_error("Invalid archive size")?; - if magic != &archive::MAGIC[..] { + + if magic == archive::AIX_BIG_MAGIC { + return Self::parse_aixbig(data); + } else if magic != archive::MAGIC { return Err(Error("Unsupported archive identifier")); } + let mut members_offset = tail; + let members_end_offset = len; + let mut file = ArchiveFile { data, - offset: tail, - len, kind: ArchiveKind::Unknown, + members: Members::Common { + offset: 0, + end_offset: 0, + }, symbols: (0, 0), names: &[], }; @@ -77,7 +98,7 @@ impl<'data, R: ReadRef<'data>> ArchiveFile<'data, R> { // GNU symbol table (unless we later determine this is COFF). file.kind = ArchiveKind::Gnu; file.symbols = member.file_range(); - file.offset = tail; + members_offset = tail; if tail < len { let member = ArchiveMember::parse(data, &mut tail, &[])?; @@ -85,55 +106,125 @@ impl<'data, R: ReadRef<'data>> ArchiveFile<'data, R> { // COFF linker member. file.kind = ArchiveKind::Coff; file.symbols = member.file_range(); - file.offset = tail; + members_offset = tail; if tail < len { let member = ArchiveMember::parse(data, &mut tail, &[])?; if member.name == b"//" { // COFF names table. file.names = member.data(data)?; - file.offset = tail; + members_offset = tail; } } } else if member.name == b"//" { // GNU names table. file.names = member.data(data)?; - file.offset = tail; + members_offset = tail; } } } else if member.name == b"/SYM64/" { // GNU 64-bit symbol table. file.kind = ArchiveKind::Gnu64; file.symbols = member.file_range(); - file.offset = tail; + members_offset = tail; if tail < len { let member = ArchiveMember::parse(data, &mut tail, &[])?; if member.name == b"//" { // GNU names table. file.names = member.data(data)?; - file.offset = tail; + members_offset = tail; } } } else if member.name == b"//" { // GNU names table. file.kind = ArchiveKind::Gnu; file.names = member.data(data)?; - file.offset = tail; + members_offset = tail; } else if member.name == b"__.SYMDEF" || member.name == b"__.SYMDEF SORTED" { // BSD symbol table. file.kind = ArchiveKind::Bsd; file.symbols = member.file_range(); - file.offset = tail; + members_offset = tail; } else if member.name == b"__.SYMDEF_64" || member.name == b"__.SYMDEF_64 SORTED" { // BSD 64-bit symbol table. file.kind = ArchiveKind::Bsd64; file.symbols = member.file_range(); - file.offset = tail; + members_offset = tail; } else { // TODO: This could still be a BSD file. We leave this as unknown for now. } } + file.members = Members::Common { + offset: members_offset, + end_offset: members_end_offset, + }; + Ok(file) + } + + fn parse_aixbig(data: R) -> read::Result { + let mut tail = 0; + + let file_header = data + .read::(&mut tail) + .read_error("Invalid AIX big archive file header")?; + // Caller already validated this. + debug_assert_eq!(file_header.magic, archive::AIX_BIG_MAGIC); + + let mut file = ArchiveFile { + data, + kind: ArchiveKind::AixBig, + members: Members::AixBig { index: &[] }, + symbols: (0, 0), + names: &[], + }; + + // Read the span of symbol table. + let symtbl64 = parse_u64_digits(&file_header.gst64off, 10) + .read_error("Invalid offset to 64-bit symbol table in AIX big archive")?; + if symtbl64 > 0 { + // The symbol table is also a file with header. + let member = ArchiveMember::parse_aixbig(data, symtbl64)?; + file.symbols = member.file_range(); + } else { + let symtbl = parse_u64_digits(&file_header.gstoff, 10) + .read_error("Invalid offset to symbol table in AIX big archive")?; + if symtbl > 0 { + // The symbol table is also a file with header. + let member = ArchiveMember::parse_aixbig(data, symtbl)?; + file.symbols = member.file_range(); + } + } + + // Big archive member index table lists file entries with offsets and names. + // To avoid potential infinite loop (members are double-linked list), the + // iterator goes through the index instead of real members. + let member_table_offset = parse_u64_digits(&file_header.memoff, 10) + .read_error("Invalid offset for member table of AIX big archive")?; + if member_table_offset == 0 { + // The offset would be zero if archive contains no file. + return Ok(file); + } + + // The member index table is also a file with header. + let member = ArchiveMember::parse_aixbig(data, member_table_offset)?; + let mut member_data = Bytes(member.data(data)?); + + // Structure of member index table: + // Number of entries (20 bytes) + // Offsets of each entry (20*N bytes) + // Names string table (the rest of bytes to fill size defined in header) + let members_count_bytes = member_data + .read_slice::(20) + .read_error("Missing member count in AIX big archive")?; + let members_count = parse_u64_digits(members_count_bytes, 10) + .and_then(|size| size.try_into().ok()) + .read_error("Invalid member count in AIX big archive")?; + let index = member_data + .read_slice::(members_count) + .read_error("Member count overflow in AIX big archive")?; + file.members = Members::AixBig { index }; + Ok(file) } @@ -150,8 +241,7 @@ impl<'data, R: ReadRef<'data>> ArchiveFile<'data, R> { pub fn members(&self) -> ArchiveMemberIterator<'data, R> { ArchiveMemberIterator { data: self.data, - offset: self.offset, - len: self.len, + members: self.members, names: self.names, } } @@ -161,8 +251,7 @@ impl<'data, R: ReadRef<'data>> ArchiveFile<'data, R> { #[derive(Debug)] pub struct ArchiveMemberIterator<'data, R: ReadRef<'data> = &'data [u8]> { data: R, - offset: u64, - len: u64, + members: Members<'data>, names: &'data [u8], } @@ -170,28 +259,55 @@ impl<'data, R: ReadRef<'data>> Iterator for ArchiveMemberIterator<'data, R> { type Item = read::Result>; fn next(&mut self) -> Option { - if self.offset >= self.len { - return None; - } - let member = ArchiveMember::parse(self.data, &mut self.offset, self.names); - if member.is_err() { - self.offset = self.len; + match &mut self.members { + Members::Common { + ref mut offset, + ref mut end_offset, + } => { + if *offset >= *end_offset { + return None; + } + let member = ArchiveMember::parse(self.data, offset, self.names); + if member.is_err() { + *offset = *end_offset; + } + Some(member) + } + Members::AixBig { ref mut index } => match **index { + [] => None, + [ref first, ref rest @ ..] => { + *index = rest; + let member = ArchiveMember::parse_aixbig_index(self.data, first); + if member.is_err() { + *index = &[]; + } + Some(member) + } + }, } - Some(member) } } +/// An archive member header. +#[derive(Debug, Clone, Copy)] +enum MemberHeader<'data> { + /// Common header used by many formats. + Common(&'data archive::Header), + /// AIX big archive header + AixBig(&'data archive::AixHeader), +} + /// A partially parsed archive member. #[derive(Debug)] pub struct ArchiveMember<'data> { - header: &'data archive::Header, + header: MemberHeader<'data>, name: &'data [u8], offset: u64, size: u64, } impl<'data> ArchiveMember<'data> { - /// Parse the archive member header, name, and file data. + /// Parse the member header, name, and file data in an archive with the common format. /// /// This reads the extended name (if any) and adjusts the file size. fn parse>( @@ -217,11 +333,11 @@ impl<'data> ArchiveMember<'data> { *offset = offset.saturating_add(1); } - let name = if header.name[0] == b'/' && (header.name[1] as char).is_digit(10) { + let name = if header.name[0] == b'/' && (header.name[1] as char).is_ascii_digit() { // Read file name from the names table. parse_sysv_extended_name(&header.name[1..], names) .read_error("Invalid archive extended name offset")? - } else if &header.name[..3] == b"#1/" && (header.name[3] as char).is_digit(10) { + } else if &header.name[..3] == b"#1/" && (header.name[3] as char).is_ascii_digit() { // Read file name from the start of the file data. parse_bsd_extended_name(&header.name[3..], data, &mut file_offset, &mut file_size) .read_error("Invalid archive extended name length")? @@ -236,17 +352,81 @@ impl<'data> ArchiveMember<'data> { }; Ok(ArchiveMember { - header, + header: MemberHeader::Common(header), name, offset: file_offset, size: file_size, }) } - /// Return the raw header. + /// Parse a member index entry in an AIX big archive, + /// and then parse the member header, name, and file data. + fn parse_aixbig_index>( + data: R, + index: &archive::AixMemberOffset, + ) -> read::Result { + let offset = parse_u64_digits(&index.0, 10) + .read_error("Invalid AIX big archive file member offset")?; + Self::parse_aixbig(data, offset) + } + + /// Parse the member header, name, and file data in an AIX big archive. + fn parse_aixbig>(data: R, mut offset: u64) -> read::Result { + // The format was described at + // https://www.ibm.com/docs/en/aix/7.3?topic=formats-ar-file-format-big + let header = data + .read::(&mut offset) + .read_error("Invalid AIX big archive member header")?; + let name_length = parse_u64_digits(&header.namlen, 10) + .read_error("Invalid AIX big archive member name length")?; + let name = data + .read_bytes(&mut offset, name_length) + .read_error("Invalid AIX big archive member name")?; + + // The actual data for a file member begins at the first even-byte boundary beyond the + // member header and continues for the number of bytes specified by the ar_size field. The + // ar command inserts null bytes for padding where necessary. + if offset & 1 != 0 { + offset = offset.saturating_add(1); + } + // Because of the even-byte boundary, we have to read and check terminator after header. + let terminator = data + .read_bytes(&mut offset, 2) + .read_error("Invalid AIX big archive terminator")?; + if terminator != archive::TERMINATOR { + return Err(Error("Invalid AIX big archive terminator")); + } + + let size = parse_u64_digits(&header.size, 10) + .read_error("Invalid archive member size in AIX big archive")?; + Ok(ArchiveMember { + header: MemberHeader::AixBig(header), + name, + offset, + size, + }) + } + + /// Return the raw header that is common to many archive formats. + /// + /// Returns `None` if this archive does not use the common header format. #[inline] - pub fn header(&self) -> &'data archive::Header { - self.header + pub fn header(&self) -> Option<&'data archive::Header> { + match self.header { + MemberHeader::Common(header) => Some(header), + _ => None, + } + } + + /// Return the raw header for AIX big archives. + /// + /// Returns `None` if this is not an AIX big archive. + #[inline] + pub fn aix_header(&self) -> Option<&'data archive::AixHeader> { + match self.header { + MemberHeader::AixBig(header) => Some(header), + _ => None, + } } /// Return the parsed file name. @@ -260,25 +440,37 @@ impl<'data> ArchiveMember<'data> { /// Parse the file modification timestamp from the header. #[inline] pub fn date(&self) -> Option { - parse_u64_digits(&self.header.date, 10) + match &self.header { + MemberHeader::Common(header) => parse_u64_digits(&header.date, 10), + MemberHeader::AixBig(header) => parse_u64_digits(&header.date, 10), + } } /// Parse the user ID from the header. #[inline] pub fn uid(&self) -> Option { - parse_u64_digits(&self.header.uid, 10) + match &self.header { + MemberHeader::Common(header) => parse_u64_digits(&header.uid, 10), + MemberHeader::AixBig(header) => parse_u64_digits(&header.uid, 10), + } } /// Parse the group ID from the header. #[inline] pub fn gid(&self) -> Option { - parse_u64_digits(&self.header.gid, 10) + match &self.header { + MemberHeader::Common(header) => parse_u64_digits(&header.gid, 10), + MemberHeader::AixBig(header) => parse_u64_digits(&header.gid, 10), + } } /// Parse the file mode from the header. #[inline] pub fn mode(&self) -> Option { - parse_u64_digits(&self.header.mode, 8) + match &self.header { + MemberHeader::Common(header) => parse_u64_digits(&header.mode, 8), + MemberHeader::AixBig(header) => parse_u64_digits(&header.mode, 8), + } } /// Return the offset and size of the file data. @@ -442,6 +634,19 @@ mod tests { 0000"; let archive = ArchiveFile::parse(&data[..]).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Coff); + + let data = b"\ + \n\ + 0 0 \ + 0 0 \ + 0 128 \ + 6 0 \ + 0 \0\0\0\0\0\0\0\0\0\0\0\0\ + \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ + \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ + \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::AixBig); } #[test] @@ -499,4 +704,36 @@ mod tests { assert!(members.next().is_none()); } + + #[test] + fn aix_names() { + let data = b"\ + \n\ + 396 0 0 \ + 128 262 0 \ + 4 262 0 \ + 1662610370 223 1 644 16 \ + 0123456789abcdef`\nord\n\ + 4 396 128 \ + 1662610374 223 1 644 16 \ + fedcba9876543210`\nrev\n\ + 94 0 262 \ + 0 0 0 0 0 \ + `\n2 128 \ + 262 0123456789abcdef\0fedcba9876543210\0"; + let data = &data[..]; + let archive = ArchiveFile::parse(data).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::AixBig); + let mut members = archive.members(); + + let member = members.next().unwrap().unwrap(); + assert_eq!(member.name(), b"0123456789abcdef"); + assert_eq!(member.data(data).unwrap(), &b"ord\n"[..]); + + let member = members.next().unwrap().unwrap(); + assert_eq!(member.name(), b"fedcba9876543210"); + assert_eq!(member.data(data).unwrap(), &b"rev\n"[..]); + + assert!(members.next().is_none()); + } } diff --git a/vendor/object/src/read/coff/symbol.rs b/vendor/object/src/read/coff/symbol.rs index c954c8a29..217e38fca 100644 --- a/vendor/object/src/read/coff/symbol.rs +++ b/vendor/object/src/read/coff/symbol.rs @@ -325,6 +325,14 @@ where pub(crate) symbol: &'data pe::ImageSymbol, } +impl<'data, 'file, R: ReadRef<'data>> CoffSymbol<'data, 'file, R> { + #[inline] + /// Get the raw `ImageSymbol` struct. + pub fn raw_symbol(&self) -> &'data pe::ImageSymbol { + self.symbol + } +} + impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for CoffSymbol<'data, 'file, R> {} impl<'data, 'file, R: ReadRef<'data>> ObjectSymbol<'data> for CoffSymbol<'data, 'file, R> { diff --git a/vendor/object/src/read/elf/comdat.rs b/vendor/object/src/read/elf/comdat.rs index c9f0076f9..7cee85bb4 100644 --- a/vendor/object/src/read/elf/comdat.rs +++ b/vendor/object/src/read/elf/comdat.rs @@ -34,7 +34,7 @@ where type Item = ElfComdat<'data, 'file, Elf, R>; fn next(&mut self) -> Option { - while let Some((_index, section)) = self.iter.next() { + for (_index, section) in self.iter.by_ref() { if let Some(comdat) = ElfComdat::parse(self.file, section) { return Some(comdat); } diff --git a/vendor/object/src/read/elf/file.rs b/vendor/object/src/read/elf/file.rs index e1f76a38c..259da7906 100644 --- a/vendor/object/src/read/elf/file.rs +++ b/vendor/object/src/read/elf/file.rs @@ -156,7 +156,8 @@ where self.header.e_machine(self.endian), self.header.is_class_64(), ) { - (elf::EM_AARCH64, _) => Architecture::Aarch64, + (elf::EM_AARCH64, true) => Architecture::Aarch64, + (elf::EM_AARCH64, false) => Architecture::Aarch64_Ilp32, (elf::EM_ARM, _) => Architecture::Arm, (elf::EM_AVR, _) => Architecture::Avr, (elf::EM_BPF, _) => Architecture::Bpf, @@ -175,7 +176,9 @@ where // This is either s390 or s390x, depending on the ELF class. // We only support the 64-bit variant s390x here. (elf::EM_S390, true) => Architecture::S390x, + (elf::EM_SBF, _) => Architecture::Sbf, (elf::EM_SPARCV9, true) => Architecture::Sparc64, + (elf::EM_XTENSA, false) => Architecture::Xtensa, _ => Architecture::Unknown, } } diff --git a/vendor/object/src/read/elf/relocation.rs b/vendor/object/src/read/elf/relocation.rs index 557b80efc..8443dbc75 100644 --- a/vendor/object/src/read/elf/relocation.rs +++ b/vendor/object/src/read/elf/relocation.rs @@ -240,19 +240,28 @@ fn parse_relocation( let mut encoding = RelocationEncoding::Generic; let is_mips64el = header.is_mips64el(endian); let (kind, size) = match header.e_machine(endian) { - elf::EM_AARCH64 => match reloc.r_type(endian, false) { - elf::R_AARCH64_ABS64 => (RelocationKind::Absolute, 64), - elf::R_AARCH64_ABS32 => (RelocationKind::Absolute, 32), - elf::R_AARCH64_ABS16 => (RelocationKind::Absolute, 16), - elf::R_AARCH64_PREL64 => (RelocationKind::Relative, 64), - elf::R_AARCH64_PREL32 => (RelocationKind::Relative, 32), - elf::R_AARCH64_PREL16 => (RelocationKind::Relative, 16), - elf::R_AARCH64_CALL26 => { - encoding = RelocationEncoding::AArch64Call; - (RelocationKind::PltRelative, 26) + elf::EM_AARCH64 => { + if header.is_type_64() { + match reloc.r_type(endian, false) { + elf::R_AARCH64_ABS64 => (RelocationKind::Absolute, 64), + elf::R_AARCH64_ABS32 => (RelocationKind::Absolute, 32), + elf::R_AARCH64_ABS16 => (RelocationKind::Absolute, 16), + elf::R_AARCH64_PREL64 => (RelocationKind::Relative, 64), + elf::R_AARCH64_PREL32 => (RelocationKind::Relative, 32), + elf::R_AARCH64_PREL16 => (RelocationKind::Relative, 16), + elf::R_AARCH64_CALL26 => { + encoding = RelocationEncoding::AArch64Call; + (RelocationKind::PltRelative, 26) + } + r_type => (RelocationKind::Elf(r_type), 0), + } + } else { + match reloc.r_type(endian, false) { + elf::R_AARCH64_P32_ABS32 => (RelocationKind::Absolute, 32), + r_type => (RelocationKind::Elf(r_type), 0), + } } - r_type => (RelocationKind::Elf(r_type), 0), - }, + } elf::EM_ARM => match reloc.r_type(endian, false) { elf::R_ARM_ABS32 => (RelocationKind::Absolute, 32), r_type => (RelocationKind::Elf(r_type), 0), @@ -304,6 +313,19 @@ fn parse_relocation( elf::EM_LOONGARCH => match reloc.r_type(endian, false) { elf::R_LARCH_32 => (RelocationKind::Absolute, 32), elf::R_LARCH_64 => (RelocationKind::Absolute, 64), + elf::R_LARCH_32_PCREL => (RelocationKind::Relative, 32), + elf::R_LARCH_B16 => { + encoding = RelocationEncoding::LoongArchBranch; + (RelocationKind::Relative, 16) + } + elf::R_LARCH_B21 => { + encoding = RelocationEncoding::LoongArchBranch; + (RelocationKind::Relative, 21) + } + elf::R_LARCH_B26 => { + encoding = RelocationEncoding::LoongArchBranch; + (RelocationKind::Relative, 26) + } r_type => (RelocationKind::Elf(r_type), 0), }, elf::EM_MIPS => match reloc.r_type(endian, is_mips64el) { @@ -372,6 +394,11 @@ fn parse_relocation( } r_type => (RelocationKind::Elf(r_type), 0), }, + elf::EM_SBF => match reloc.r_type(endian, false) { + elf::R_SBF_64_64 => (RelocationKind::Absolute, 64), + elf::R_SBF_64_32 => (RelocationKind::Absolute, 32), + r_type => (RelocationKind::Elf(r_type), 0), + }, elf::EM_SPARC | elf::EM_SPARC32PLUS | elf::EM_SPARCV9 => { match reloc.r_type(endian, false) { elf::R_SPARC_32 | elf::R_SPARC_UA32 => (RelocationKind::Absolute, 32), @@ -379,6 +406,11 @@ fn parse_relocation( r_type => (RelocationKind::Elf(r_type), 0), } } + elf::EM_XTENSA => match reloc.r_type(endian, false) { + elf::R_XTENSA_32 => (RelocationKind::Absolute, 32), + elf::R_XTENSA_32_PCREL => (RelocationKind::Relative, 32), + r_type => (RelocationKind::Elf(r_type), 0), + }, _ => (RelocationKind::Elf(reloc.r_type(endian, false)), 0), }; let sym = reloc.r_sym(endian, is_mips64el) as usize; diff --git a/vendor/object/src/read/elf/segment.rs b/vendor/object/src/read/elf/segment.rs index 874ea92b8..445893c8d 100644 --- a/vendor/object/src/read/elf/segment.rs +++ b/vendor/object/src/read/elf/segment.rs @@ -34,7 +34,7 @@ where type Item = ElfSegment<'data, 'file, Elf, R>; fn next(&mut self) -> Option { - while let Some(segment) = self.iter.next() { + for segment in self.iter.by_ref() { if segment.p_type(self.file.endian) == elf::PT_LOAD { return Some(ElfSegment { file: self.file, diff --git a/vendor/object/src/read/elf/symbol.rs b/vendor/object/src/read/elf/symbol.rs index f52eff20e..5d8d29f27 100644 --- a/vendor/object/src/read/elf/symbol.rs +++ b/vendor/object/src/read/elf/symbol.rs @@ -4,7 +4,6 @@ use core::fmt::Debug; use core::slice; use core::str; -use crate::elf; use crate::endian::{self, Endianness}; use crate::pod::Pod; use crate::read::util::StringTable; @@ -12,6 +11,7 @@ use crate::read::{ self, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, SectionIndex, SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapEntry, SymbolScope, SymbolSection, }; +use crate::{elf, U32}; use super::{FileHeader, SectionHeader, SectionTable}; @@ -28,7 +28,7 @@ where shndx_section: SectionIndex, symbols: &'data [Elf::Sym], strings: StringTable<'data, R>, - shndx: &'data [u32], + shndx: &'data [U32], } impl<'data, Elf: FileHeader, R: ReadRef<'data>> Default for SymbolTable<'data, Elf, R> { @@ -145,8 +145,8 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SymbolTable<'data, Elf, R> { /// Return the extended section index for the given symbol if present. #[inline] - pub fn shndx(&self, index: usize) -> Option { - self.shndx.get(index).copied() + pub fn shndx(&self, endian: Elf::Endian, index: usize) -> Option { + self.shndx.get(index).map(|x| x.get(endian)) } /// Return the section index for the given symbol. @@ -161,7 +161,7 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SymbolTable<'data, Elf, R> { match symbol.st_shndx(endian) { elf::SHN_UNDEF => Ok(None), elf::SHN_XINDEX => self - .shndx(index) + .shndx(endian, index) .read_error("Missing ELF symbol extended index") .map(|index| Some(SectionIndex(index as usize))), shndx if shndx < elf::SHN_LORESERVE => Ok(Some(SectionIndex(shndx.into()))), @@ -349,8 +349,9 @@ impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ObjectSymbol<'data> fn kind(&self) -> SymbolKind { match self.symbol.st_type() { elf::STT_NOTYPE if self.index.0 == 0 => SymbolKind::Null, + elf::STT_NOTYPE => SymbolKind::Label, elf::STT_OBJECT | elf::STT_COMMON => SymbolKind::Data, - elf::STT_FUNC => SymbolKind::Text, + elf::STT_FUNC | elf::STT_GNU_IFUNC => SymbolKind::Text, elf::STT_SECTION => SymbolKind::Section, elf::STT_FILE => SymbolKind::File, elf::STT_TLS => SymbolKind::Tls, @@ -369,7 +370,7 @@ impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ObjectSymbol<'data> } } elf::SHN_COMMON => SymbolSection::Common, - elf::SHN_XINDEX => match self.symbols.shndx(self.index.0) { + elf::SHN_XINDEX => match self.symbols.shndx(self.endian, self.index.0) { Some(index) => SymbolSection::Section(SectionIndex(index as usize)), None => SymbolSection::Unknown, }, diff --git a/vendor/object/src/read/macho/fat.rs b/vendor/object/src/read/macho/fat.rs index 6fc649f31..d4301b7e1 100644 --- a/vendor/object/src/read/macho/fat.rs +++ b/vendor/object/src/read/macho/fat.rs @@ -57,6 +57,8 @@ pub trait FatArch: Pod { macho::CPU_TYPE_X86 => Architecture::I386, macho::CPU_TYPE_X86_64 => Architecture::X86_64, macho::CPU_TYPE_MIPS => Architecture::Mips, + macho::CPU_TYPE_POWERPC => Architecture::PowerPc, + macho::CPU_TYPE_POWERPC64 => Architecture::PowerPc64, _ => Architecture::Unknown, } } diff --git a/vendor/object/src/read/macho/file.rs b/vendor/object/src/read/macho/file.rs index e028de3b9..ab8c05757 100644 --- a/vendor/object/src/read/macho/file.rs +++ b/vendor/object/src/read/macho/file.rs @@ -195,6 +195,8 @@ where macho::CPU_TYPE_X86 => Architecture::I386, macho::CPU_TYPE_X86_64 => Architecture::X86_64, macho::CPU_TYPE_MIPS => Architecture::Mips, + macho::CPU_TYPE_POWERPC => Architecture::PowerPc, + macho::CPU_TYPE_POWERPC64 => Architecture::PowerPc64, _ => Architecture::Unknown, } } diff --git a/vendor/object/src/read/macho/load_command.rs b/vendor/object/src/read/macho/load_command.rs index 29fab6e0e..10daf4ed1 100644 --- a/vendor/object/src/read/macho/load_command.rs +++ b/vendor/object/src/read/macho/load_command.rs @@ -77,6 +77,11 @@ impl<'data, E: Endian> LoadCommandData<'data, E> { .read_error("Invalid Mach-O command size") } + /// Raw bytes of this LoadCommand structure. + pub fn raw_data(&self) -> &'data [u8] { + self.data.0 + } + /// Parse a load command string value. /// /// Strings used by load commands are specified by offsets that are diff --git a/vendor/object/src/read/mod.rs b/vendor/object/src/read/mod.rs index 41d344111..91a5c05a5 100644 --- a/vendor/object/src/read/mod.rs +++ b/vendor/object/src/read/mod.rs @@ -22,7 +22,8 @@ pub use util::*; feature = "elf", feature = "macho", feature = "pe", - feature = "wasm" + feature = "wasm", + feature = "xcoff" ))] mod any; #[cfg(any( @@ -30,7 +31,8 @@ mod any; feature = "elf", feature = "macho", feature = "pe", - feature = "wasm" + feature = "wasm", + feature = "xcoff" ))] pub use any::*; @@ -49,12 +51,15 @@ pub mod macho; #[cfg(feature = "pe")] pub mod pe; -mod traits; -pub use traits::*; - #[cfg(feature = "wasm")] pub mod wasm; +#[cfg(feature = "xcoff")] +pub mod xcoff; + +mod traits; +pub use traits::*; + mod private { pub trait Sealed {} } @@ -176,6 +181,12 @@ pub enum FileKind { /// A Wasm file. #[cfg(feature = "wasm")] Wasm, + /// A 32-bit XCOFF file. + #[cfg(feature = "xcoff")] + Xcoff32, + /// A 64-bit XCOFF file. + #[cfg(feature = "xcoff")] + Xcoff64, } impl FileKind { @@ -236,6 +247,10 @@ impl FileKind { | [0x4c, 0x01, ..] // COFF x86-64 | [0x64, 0x86, ..] => FileKind::Coff, + #[cfg(feature = "xcoff")] + [0x01, 0xDF, ..] => FileKind::Xcoff32, + #[cfg(feature = "xcoff")] + [0x01, 0xF7, ..] => FileKind::Xcoff64, _ => return Err(Error("Unknown file magic")), }; Ok(kind) diff --git a/vendor/object/src/read/pe/data_directory.rs b/vendor/object/src/read/pe/data_directory.rs index 8c1955355..f5d98774e 100644 --- a/vendor/object/src/read/pe/data_directory.rs +++ b/vendor/object/src/read/pe/data_directory.rs @@ -3,7 +3,10 @@ use core::slice; use crate::read::{Error, ReadError, ReadRef, Result}; use crate::{pe, LittleEndian as LE}; -use super::{ExportTable, ImportTable, RelocationBlockIterator, ResourceDirectory, SectionTable}; +use super::{ + DelayLoadImportTable, ExportTable, ImportTable, RelocationBlockIterator, ResourceDirectory, + SectionTable, +}; /// The table of data directories in a PE file. #[derive(Debug, Clone, Copy)] @@ -105,6 +108,29 @@ impl<'data> DataDirectories<'data> { Ok(Some(ImportTable::new(section_data, section_va, import_va))) } + /// Returns the partially parsed delay-load import directory. + /// + /// `data` must be the entire file data. + pub fn delay_load_import_table>( + &self, + data: R, + sections: &SectionTable<'data>, + ) -> Result>> { + let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT) { + Some(data_dir) => data_dir, + None => return Ok(None), + }; + let import_va = data_dir.virtual_address.get(LE); + let (section_data, section_va) = sections + .pe_data_containing(data, import_va) + .read_error("Invalid import data dir virtual address")?; + Ok(Some(DelayLoadImportTable::new( + section_data, + section_va, + import_va, + ))) + } + /// Returns the blocks in the base relocation directory. /// /// `data` must be the entire file data. diff --git a/vendor/object/src/read/pe/file.rs b/vendor/object/src/read/pe/file.rs index 15b42074a..8dd85131a 100644 --- a/vendor/object/src/read/pe/file.rs +++ b/vendor/object/src/read/pe/file.rs @@ -304,49 +304,60 @@ where None => return Ok(None), }; let debug_data = data_dir.data(self.data, &self.common.sections).map(Bytes)?; - let debug_dir = debug_data - .read_at::(0) - .read_error("Invalid PE debug dir size")?; + let debug_data_size = data_dir.size.get(LE) as usize; - if debug_dir.typ.get(LE) != pe::IMAGE_DEBUG_TYPE_CODEVIEW { - return Ok(None); + let count = debug_data_size / mem::size_of::(); + let rem = debug_data_size % mem::size_of::(); + if rem != 0 || count < 1 { + return Err(Error("Invalid PE debug dir size")); } - let info = self - .data - .read_slice_at::( - debug_dir.pointer_to_raw_data.get(LE) as u64, - debug_dir.size_of_data.get(LE) as usize, - ) - .read_error("Invalid CodeView Info address")?; - - let mut info = Bytes(info); - - let sig = info - .read_bytes(4) - .read_error("Invalid CodeView signature")?; - if sig.0 != b"RSDS" { - return Ok(None); - } + let debug_dirs = debug_data + .read_slice_at::(0, count) + .read_error("Invalid PE debug dir size")?; + + for debug_dir in debug_dirs { + if debug_dir.typ.get(LE) != pe::IMAGE_DEBUG_TYPE_CODEVIEW { + continue; + } + + let info = self + .data + .read_slice_at::( + debug_dir.pointer_to_raw_data.get(LE) as u64, + debug_dir.size_of_data.get(LE) as usize, + ) + .read_error("Invalid CodeView Info address")?; + + let mut info = Bytes(info); + + let sig = info + .read_bytes(4) + .read_error("Invalid CodeView signature")?; + if sig.0 != b"RSDS" { + continue; + } - let guid: [u8; 16] = info - .read_bytes(16) - .read_error("Invalid CodeView GUID")? - .0 - .try_into() - .unwrap(); + let guid: [u8; 16] = info + .read_bytes(16) + .read_error("Invalid CodeView GUID")? + .0 + .try_into() + .unwrap(); - let age = info.read::>().read_error("Invalid CodeView Age")?; + let age = info.read::>().read_error("Invalid CodeView Age")?; - let path = info - .read_string() - .read_error("Invalid CodeView file path")?; + let path = info + .read_string() + .read_error("Invalid CodeView file path")?; - Ok(Some(CodeView { - path: ByteString(path), - guid, - age: age.get(LE), - })) + return Ok(Some(CodeView { + path: ByteString(path), + guid, + age: age.get(LE), + })); + } + Ok(None) } fn has_debug_symbols(&self) -> bool { diff --git a/vendor/object/src/read/pe/import.rs b/vendor/object/src/read/pe/import.rs index 809a96286..a5535dc36 100644 --- a/vendor/object/src/read/pe/import.rs +++ b/vendor/object/src/read/pe/import.rs @@ -216,3 +216,117 @@ impl ImageThunkData for pe::ImageThunkData32 { self.0.get(LE) & 0x7fff_ffff } } + +/// Information for parsing a PE delay-load import table. +#[derive(Debug, Clone)] +pub struct DelayLoadImportTable<'data> { + section_data: Bytes<'data>, + section_address: u32, + import_address: u32, +} + +impl<'data> DelayLoadImportTable<'data> { + /// Create a new delay load import table parser. + /// + /// The import descriptors start at `import_address`. + /// This table works in the same way the import table does: descriptors will be + /// parsed until a null entry. + /// + /// `section_data` should be from the section containing `import_address`, and + /// `section_address` should be the address of that section. Pointers within the + /// descriptors and thunks may point to anywhere within the section data. + pub fn new(section_data: &'data [u8], section_address: u32, import_address: u32) -> Self { + DelayLoadImportTable { + section_data: Bytes(section_data), + section_address, + import_address, + } + } + + /// Return an iterator for the import descriptors. + pub fn descriptors(&self) -> Result> { + let offset = self.import_address.wrapping_sub(self.section_address); + let mut data = self.section_data; + data.skip(offset as usize) + .read_error("Invalid PE delay-load import descriptor address")?; + Ok(DelayLoadDescriptorIterator { data }) + } + + /// Return a library name given its address. + /// + /// This address may be from [`pe::ImageDelayloadDescriptor::dll_name_rva`]. + pub fn name(&self, address: u32) -> Result<&'data [u8]> { + self.section_data + .read_string_at(address.wrapping_sub(self.section_address) as usize) + .read_error("Invalid PE import descriptor name") + } + + /// Return a list of thunks given its address. + /// + /// This address may be from the INT, i.e. from + /// [`pe::ImageDelayloadDescriptor::import_name_table_rva`]. + /// + /// Please note that others RVA values from [`pe::ImageDelayloadDescriptor`] are used + /// by the delay loader at runtime to store values, and thus do not point inside the same + /// section as the INT. Calling this function on those addresses will fail. + pub fn thunks(&self, address: u32) -> Result> { + let offset = address.wrapping_sub(self.section_address); + let mut data = self.section_data; + data.skip(offset as usize) + .read_error("Invalid PE delay load import thunk table address")?; + Ok(ImportThunkList { data }) + } + + /// Parse a thunk. + pub fn import(&self, thunk: Pe::ImageThunkData) -> Result> { + if thunk.is_ordinal() { + Ok(Import::Ordinal(thunk.ordinal())) + } else { + let (hint, name) = self.hint_name(thunk.address())?; + Ok(Import::Name(hint, name)) + } + } + + /// Return the hint and name at the given address. + /// + /// This address may be from [`pe::ImageThunkData32`] or [`pe::ImageThunkData64`]. + /// + /// The hint is an index into the export name pointer table in the target library. + pub fn hint_name(&self, address: u32) -> Result<(u16, &'data [u8])> { + let offset = address.wrapping_sub(self.section_address); + let mut data = self.section_data; + data.skip(offset as usize) + .read_error("Invalid PE delay load import thunk address")?; + let hint = data + .read::>() + .read_error("Missing PE delay load import thunk hint")? + .get(LE); + let name = data + .read_string() + .read_error("Missing PE delay load import thunk name")?; + Ok((hint, name)) + } +} + +/// A fallible iterator for the descriptors in the delay-load data directory. +#[derive(Debug, Clone)] +pub struct DelayLoadDescriptorIterator<'data> { + data: Bytes<'data>, +} + +impl<'data> DelayLoadDescriptorIterator<'data> { + /// Return the next descriptor. + /// + /// Returns `Ok(None)` when a null descriptor is found. + pub fn next(&mut self) -> Result> { + let import_desc = self + .data + .read::() + .read_error("Missing PE null delay-load import descriptor")?; + if import_desc.is_null() { + Ok(None) + } else { + Ok(Some(import_desc)) + } + } +} diff --git a/vendor/object/src/read/pe/resource.rs b/vendor/object/src/read/pe/resource.rs index bfbb609f5..e667f0d98 100644 --- a/vendor/object/src/read/pe/resource.rs +++ b/vendor/object/src/read/pe/resource.rs @@ -1,7 +1,8 @@ use alloc::string::String; +use core::char; use crate::read::{ReadError, ReadRef, Result}; -use crate::{pe, LittleEndian as LE, U16}; +use crate::{pe, LittleEndian as LE, U16Bytes}; /// The `.rsrc` section of a PE file. #[derive(Debug, Clone, Copy)] @@ -17,7 +18,7 @@ impl<'data> ResourceDirectory<'data> { /// Parses the root resource directory. pub fn root(&self) -> Result> { - ResourceDirectoryTable::parse(&self.data, 0) + ResourceDirectoryTable::parse(self.data, 0) } } @@ -92,13 +93,13 @@ impl pe::ImageResourceDirectoryEntry { ) -> Result> { if self.is_table() { ResourceDirectoryTable::parse(section.data, self.data_offset()) - .map(|t| ResourceDirectoryEntryData::Table(t)) + .map(ResourceDirectoryEntryData::Table) } else { section .data .read_at::(self.data_offset().into()) .read_error("Invalid resource entry") - .map(|d| ResourceDirectoryEntryData::Data(d)) + .map(ResourceDirectoryEntryData::Data) } } } @@ -143,22 +144,33 @@ pub struct ResourceName { impl ResourceName { /// Converts to a `String`. pub fn to_string_lossy(&self, directory: ResourceDirectory) -> Result { - let d = self.data(directory)?; - Ok(String::from_utf16_lossy(d)) + let d = self.data(directory)?.iter().map(|c| c.get(LE)); + + Ok(char::decode_utf16(d) + .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER)) + .collect::()) } /// Returns the string unicode buffer. - pub fn data<'data>(&self, directory: ResourceDirectory<'data>) -> Result<&'data [u16]> { + pub fn data<'data>( + &self, + directory: ResourceDirectory<'data>, + ) -> Result<&'data [U16Bytes]> { let mut offset = u64::from(self.offset); let len = directory .data - .read::>(&mut offset) + .read::>(&mut offset) .read_error("Invalid resource name offset")?; directory .data - .read_slice::(&mut offset, len.get(LE).into()) + .read_slice::>(&mut offset, len.get(LE).into()) .read_error("Invalid resource name length") } + + /// Returns the string buffer as raw bytes. + pub fn raw_data<'data>(&self, directory: ResourceDirectory<'data>) -> Result<&'data [u8]> { + self.data(directory).map(crate::pod::bytes_of_slice) + } } /// A resource name or ID. diff --git a/vendor/object/src/read/xcoff/comdat.rs b/vendor/object/src/read/xcoff/comdat.rs new file mode 100644 index 000000000..eeed2f54d --- /dev/null +++ b/vendor/object/src/read/xcoff/comdat.rs @@ -0,0 +1,130 @@ +//! XCOFF doesn't support the COMDAT section. + +use core::fmt::Debug; + +use crate::xcoff; + +use crate::read::{self, ComdatKind, ObjectComdat, ReadRef, Result, SectionIndex, SymbolIndex}; + +use super::{FileHeader, XcoffFile}; + +/// An iterator over the COMDAT section groups of a `XcoffFile32`. +pub type XcoffComdatIterator32<'data, 'file, R = &'data [u8]> = + XcoffComdatIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator over the COMDAT section groups of a `XcoffFile64`. +pub type XcoffComdatIterator64<'data, 'file, R = &'data [u8]> = + XcoffComdatIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator over the COMDAT section groups of a `XcoffFile`. +#[derive(Debug)] +pub struct XcoffComdatIterator<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + #[allow(unused)] + pub(crate) file: &'file XcoffFile<'data, Xcoff, R>, +} + +impl<'data, 'file, Xcoff, R> Iterator for XcoffComdatIterator<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + type Item = XcoffComdat<'data, 'file, Xcoff, R>; + + #[inline] + fn next(&mut self) -> Option { + None + } +} + +/// A COMDAT section group of a `XcoffFile32`. +pub type XcoffComdat32<'data, 'file, R = &'data [u8]> = + XcoffComdat<'data, 'file, xcoff::FileHeader32, R>; + +/// A COMDAT section group of a `XcoffFile64`. +pub type XcoffComdat64<'data, 'file, R = &'data [u8]> = + XcoffComdat<'data, 'file, xcoff::FileHeader64, R>; + +/// A COMDAT section group of a `XcoffFile`. +#[derive(Debug)] +pub struct XcoffComdat<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + #[allow(unused)] + file: &'file XcoffFile<'data, Xcoff, R>, +} + +impl<'data, 'file, Xcoff, R> read::private::Sealed for XcoffComdat<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Xcoff, R> ObjectComdat<'data> for XcoffComdat<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + type SectionIterator = XcoffComdatSectionIterator<'data, 'file, Xcoff, R>; + + #[inline] + fn kind(&self) -> ComdatKind { + unreachable!(); + } + + #[inline] + fn symbol(&self) -> SymbolIndex { + unreachable!(); + } + + #[inline] + fn name_bytes(&self) -> Result<&[u8]> { + unreachable!(); + } + + #[inline] + fn name(&self) -> Result<&str> { + unreachable!(); + } + + #[inline] + fn sections(&self) -> Self::SectionIterator { + unreachable!(); + } +} + +/// An iterator over the sections in a COMDAT section group of a `XcoffFile32`. +pub type XcoffComdatSectionIterator32<'data, 'file, R = &'data [u8]> = + XcoffComdatSectionIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator over the sections in a COMDAT section group of a `XcoffFile64`. +pub type XcoffComdatSectionIterator64<'data, 'file, R = &'data [u8]> = + XcoffComdatSectionIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator over the sections in a COMDAT section group of a `XcoffFile`. +#[derive(Debug)] +pub struct XcoffComdatSectionIterator<'data, 'file, Xcoff, R = &'data [u8]> +where + 'data: 'file, + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + #[allow(unused)] + file: &'file XcoffFile<'data, Xcoff, R>, +} + +impl<'data, 'file, Xcoff, R> Iterator for XcoffComdatSectionIterator<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + type Item = SectionIndex; + + fn next(&mut self) -> Option { + None + } +} diff --git a/vendor/object/src/read/xcoff/file.rs b/vendor/object/src/read/xcoff/file.rs new file mode 100644 index 000000000..bac9e7075 --- /dev/null +++ b/vendor/object/src/read/xcoff/file.rs @@ -0,0 +1,629 @@ +use core::fmt::Debug; +use core::mem; + +use alloc::vec::Vec; + +use crate::read::{self, Error, NoDynamicRelocationIterator, Object, ReadError, ReadRef, Result}; + +use crate::{ + xcoff, Architecture, BigEndian as BE, FileFlags, ObjectKind, ObjectSection, Pod, SectionIndex, + SymbolIndex, +}; + +use super::{ + CsectAux, FileAux, SectionHeader, SectionTable, Symbol, SymbolTable, XcoffComdat, + XcoffComdatIterator, XcoffSection, XcoffSectionIterator, XcoffSegment, XcoffSegmentIterator, + XcoffSymbol, XcoffSymbolIterator, XcoffSymbolTable, +}; + +/// A 32-bit XCOFF object file. +pub type XcoffFile32<'data, R = &'data [u8]> = XcoffFile<'data, xcoff::FileHeader32, R>; +/// A 64-bit XCOFF object file. +pub type XcoffFile64<'data, R = &'data [u8]> = XcoffFile<'data, xcoff::FileHeader64, R>; + +/// A partially parsed XCOFF file. +/// +/// Most of the functionality of this type is provided by the `Object` trait implementation. +#[derive(Debug)] +pub struct XcoffFile<'data, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + pub(super) data: R, + pub(super) header: &'data Xcoff, + pub(super) aux_header: Option<&'data Xcoff::AuxHeader>, + pub(super) sections: SectionTable<'data, Xcoff>, + pub(super) symbols: SymbolTable<'data, Xcoff, R>, +} + +impl<'data, Xcoff, R> XcoffFile<'data, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + /// Parse the raw XCOFF file data. + pub fn parse(data: R) -> Result { + let mut offset = 0; + let header = Xcoff::parse(data, &mut offset)?; + let aux_header = header.aux_header(data, &mut offset)?; + let sections = header.sections(data, &mut offset)?; + let symbols = header.symbols(data)?; + + Ok(XcoffFile { + data, + header, + aux_header, + sections, + symbols, + }) + } + + /// Returns the raw data. + pub fn data(&self) -> R { + self.data + } + + /// Returns the raw XCOFF file header. + pub fn raw_header(&self) -> &'data Xcoff { + self.header + } +} + +impl<'data, Xcoff, R> read::private::Sealed for XcoffFile<'data, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Xcoff, R> Object<'data, 'file> for XcoffFile<'data, Xcoff, R> +where + 'data: 'file, + Xcoff: FileHeader, + R: 'file + ReadRef<'data>, +{ + type Segment = XcoffSegment<'data, 'file, Xcoff, R>; + type SegmentIterator = XcoffSegmentIterator<'data, 'file, Xcoff, R>; + type Section = XcoffSection<'data, 'file, Xcoff, R>; + type SectionIterator = XcoffSectionIterator<'data, 'file, Xcoff, R>; + type Comdat = XcoffComdat<'data, 'file, Xcoff, R>; + type ComdatIterator = XcoffComdatIterator<'data, 'file, Xcoff, R>; + type Symbol = XcoffSymbol<'data, 'file, Xcoff, R>; + type SymbolIterator = XcoffSymbolIterator<'data, 'file, Xcoff, R>; + type SymbolTable = XcoffSymbolTable<'data, 'file, Xcoff, R>; + type DynamicRelocationIterator = NoDynamicRelocationIterator; + + fn architecture(&self) -> crate::Architecture { + if self.is_64() { + Architecture::PowerPc64 + } else { + Architecture::PowerPc + } + } + + fn is_little_endian(&self) -> bool { + false + } + + fn is_64(&self) -> bool { + self.header.is_type_64() + } + + fn kind(&self) -> ObjectKind { + let flags = self.header.f_flags(); + if flags & xcoff::F_EXEC != 0 { + ObjectKind::Executable + } else if flags & xcoff::F_SHROBJ != 0 { + ObjectKind::Dynamic + } else if flags & xcoff::F_RELFLG == 0 { + ObjectKind::Relocatable + } else { + ObjectKind::Unknown + } + } + + fn segments(&'file self) -> XcoffSegmentIterator<'data, 'file, Xcoff, R> { + XcoffSegmentIterator { file: self } + } + + fn section_by_name_bytes( + &'file self, + section_name: &[u8], + ) -> Option> { + self.sections() + .find(|section| section.name_bytes() == Ok(section_name)) + } + + fn section_by_index( + &'file self, + index: SectionIndex, + ) -> Result> { + let section = self.sections.section(index)?; + Ok(XcoffSection { + file: self, + section, + index, + }) + } + + fn sections(&'file self) -> XcoffSectionIterator<'data, 'file, Xcoff, R> { + XcoffSectionIterator { + file: self, + iter: self.sections.iter().enumerate(), + } + } + + fn comdats(&'file self) -> XcoffComdatIterator<'data, 'file, Xcoff, R> { + XcoffComdatIterator { file: self } + } + + fn symbol_table(&'file self) -> Option> { + if self.symbols.is_empty() { + return None; + } + Some(XcoffSymbolTable { + symbols: &self.symbols, + file: self, + }) + } + + fn symbol_by_index( + &'file self, + index: SymbolIndex, + ) -> Result> { + let symbol = self.symbols.symbol(index.0)?; + Ok(XcoffSymbol { + symbols: &self.symbols, + index, + symbol, + file: self, + }) + } + + fn symbols(&'file self) -> XcoffSymbolIterator<'data, 'file, Xcoff, R> { + XcoffSymbolIterator { + symbols: &self.symbols, + index: 0, + file: self, + } + } + + fn dynamic_symbol_table(&'file self) -> Option> { + None + } + + fn dynamic_symbols(&'file self) -> XcoffSymbolIterator<'data, 'file, Xcoff, R> { + // TODO: return the symbols in the STYP_LOADER section. + XcoffSymbolIterator { + file: self, + symbols: &self.symbols, + // Hack: don't return any. + index: self.symbols.len(), + } + } + + fn dynamic_relocations(&'file self) -> Option { + // TODO: return the relocations in the STYP_LOADER section. + None + } + + fn imports(&self) -> Result>> { + // TODO: return the imports in the STYP_LOADER section. + Ok(Vec::new()) + } + + fn exports(&self) -> Result>> { + // TODO: return the exports in the STYP_LOADER section. + Ok(Vec::new()) + } + + fn has_debug_symbols(&self) -> bool { + self.section_by_name(".debug").is_some() || self.section_by_name(".dwinfo").is_some() + } + + fn relative_address_base(&'file self) -> u64 { + 0 + } + + fn entry(&'file self) -> u64 { + if let Some(aux_header) = self.aux_header { + aux_header.o_entry().into() + } else { + 0 + } + } + + fn flags(&self) -> FileFlags { + FileFlags::Xcoff { + f_flags: self.header.f_flags(), + } + } +} + +/// A trait for generic access to `FileHeader32` and `FileHeader64`. +#[allow(missing_docs)] +pub trait FileHeader: Debug + Pod { + type Word: Into; + type AuxHeader: AuxHeader; + type SectionHeader: SectionHeader; + type Symbol: Symbol; + type FileAux: FileAux; + type CsectAux: CsectAux; + + /// Return true if this type is a 64-bit header. + fn is_type_64(&self) -> bool; + + fn f_magic(&self) -> u16; + fn f_nscns(&self) -> u16; + fn f_timdat(&self) -> u32; + fn f_symptr(&self) -> Self::Word; + fn f_nsyms(&self) -> u32; + fn f_opthdr(&self) -> u16; + fn f_flags(&self) -> u16; + + // Provided methods. + + /// Read the file header. + /// + /// Also checks that the magic field in the file header is a supported format. + fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> Result<&'data Self> { + let header = data + .read::(offset) + .read_error("Invalid XCOFF header size or alignment")?; + if !header.is_supported() { + return Err(Error("Unsupported XCOFF header")); + } + Ok(header) + } + + fn is_supported(&self) -> bool { + (self.is_type_64() && self.f_magic() == xcoff::MAGIC_64) + || (!self.is_type_64() && self.f_magic() == xcoff::MAGIC_32) + } + + /// Read the auxiliary file header. + fn aux_header<'data, R: ReadRef<'data>>( + &self, + data: R, + offset: &mut u64, + ) -> Result> { + let aux_header_size = self.f_opthdr(); + if self.f_flags() & xcoff::F_EXEC == 0 { + // No auxiliary header is required for an object file that is not an executable. + // TODO: Some AIX programs generate auxiliary headers for 32-bit object files + // that end after the data_start field. + *offset += u64::from(aux_header_size); + return Ok(None); + } + // Executables, however, must have auxiliary headers that include the + // full structure definitions. + if aux_header_size != mem::size_of::() as u16 { + *offset += u64::from(aux_header_size); + return Ok(None); + } + let aux_header = data + .read::(offset) + .read_error("Invalid XCOFF auxiliary header size")?; + Ok(Some(aux_header)) + } + + /// Read the section table. + #[inline] + fn sections<'data, R: ReadRef<'data>>( + &self, + data: R, + offset: &mut u64, + ) -> Result> { + SectionTable::parse(self, data, offset) + } + + /// Return the symbol table. + #[inline] + fn symbols<'data, R: ReadRef<'data>>(&self, data: R) -> Result> { + SymbolTable::parse(*self, data) + } +} + +impl FileHeader for xcoff::FileHeader32 { + type Word = u32; + type AuxHeader = xcoff::AuxHeader32; + type SectionHeader = xcoff::SectionHeader32; + type Symbol = xcoff::Symbol32; + type FileAux = xcoff::FileAux32; + type CsectAux = xcoff::CsectAux32; + + fn is_type_64(&self) -> bool { + false + } + + fn f_magic(&self) -> u16 { + self.f_magic.get(BE) + } + + fn f_nscns(&self) -> u16 { + self.f_nscns.get(BE) + } + + fn f_timdat(&self) -> u32 { + self.f_timdat.get(BE) + } + + fn f_symptr(&self) -> Self::Word { + self.f_symptr.get(BE) + } + + fn f_nsyms(&self) -> u32 { + self.f_nsyms.get(BE) + } + + fn f_opthdr(&self) -> u16 { + self.f_opthdr.get(BE) + } + + fn f_flags(&self) -> u16 { + self.f_flags.get(BE) + } +} + +impl FileHeader for xcoff::FileHeader64 { + type Word = u64; + type AuxHeader = xcoff::AuxHeader64; + type SectionHeader = xcoff::SectionHeader64; + type Symbol = xcoff::Symbol64; + type FileAux = xcoff::FileAux64; + type CsectAux = xcoff::CsectAux64; + + fn is_type_64(&self) -> bool { + true + } + + fn f_magic(&self) -> u16 { + self.f_magic.get(BE) + } + + fn f_nscns(&self) -> u16 { + self.f_nscns.get(BE) + } + + fn f_timdat(&self) -> u32 { + self.f_timdat.get(BE) + } + + fn f_symptr(&self) -> Self::Word { + self.f_symptr.get(BE) + } + + fn f_nsyms(&self) -> u32 { + self.f_nsyms.get(BE) + } + + fn f_opthdr(&self) -> u16 { + self.f_opthdr.get(BE) + } + + fn f_flags(&self) -> u16 { + self.f_flags.get(BE) + } +} + +#[allow(missing_docs)] +pub trait AuxHeader: Debug + Pod { + type Word: Into; + + fn o_vstamp(&self) -> u16; + fn o_tsize(&self) -> Self::Word; + fn o_dsize(&self) -> Self::Word; + fn o_bsize(&self) -> Self::Word; + fn o_entry(&self) -> Self::Word; + fn o_text_start(&self) -> Self::Word; + fn o_data_start(&self) -> Self::Word; + fn o_toc(&self) -> Self::Word; + fn o_snentry(&self) -> u16; + fn o_sntext(&self) -> u16; + fn o_sndata(&self) -> u16; + fn o_sntoc(&self) -> u16; + fn o_snloader(&self) -> u16; + fn o_snbss(&self) -> u16; + fn o_sntdata(&self) -> u16; + fn o_sntbss(&self) -> u16; + fn o_algntext(&self) -> u16; + fn o_algndata(&self) -> u16; + fn o_maxstack(&self) -> Self::Word; + fn o_maxdata(&self) -> Self::Word; + fn o_textpsize(&self) -> u8; + fn o_datapsize(&self) -> u8; + fn o_stackpsize(&self) -> u8; +} + +impl AuxHeader for xcoff::AuxHeader32 { + type Word = u32; + + fn o_vstamp(&self) -> u16 { + self.o_vstamp.get(BE) + } + + fn o_tsize(&self) -> Self::Word { + self.o_tsize.get(BE) + } + + fn o_dsize(&self) -> Self::Word { + self.o_dsize.get(BE) + } + + fn o_bsize(&self) -> Self::Word { + self.o_bsize.get(BE) + } + + fn o_entry(&self) -> Self::Word { + self.o_entry.get(BE) + } + + fn o_text_start(&self) -> Self::Word { + self.o_text_start.get(BE) + } + + fn o_data_start(&self) -> Self::Word { + self.o_data_start.get(BE) + } + + fn o_toc(&self) -> Self::Word { + self.o_toc.get(BE) + } + + fn o_snentry(&self) -> u16 { + self.o_snentry.get(BE) + } + + fn o_sntext(&self) -> u16 { + self.o_sntext.get(BE) + } + + fn o_sndata(&self) -> u16 { + self.o_sndata.get(BE) + } + + fn o_sntoc(&self) -> u16 { + self.o_sntoc.get(BE) + } + + fn o_snloader(&self) -> u16 { + self.o_snloader.get(BE) + } + + fn o_snbss(&self) -> u16 { + self.o_snbss.get(BE) + } + + fn o_sntdata(&self) -> u16 { + self.o_sntdata.get(BE) + } + + fn o_sntbss(&self) -> u16 { + self.o_sntbss.get(BE) + } + + fn o_algntext(&self) -> u16 { + self.o_algntext.get(BE) + } + + fn o_algndata(&self) -> u16 { + self.o_algndata.get(BE) + } + + fn o_maxstack(&self) -> Self::Word { + self.o_maxstack.get(BE) + } + + fn o_maxdata(&self) -> Self::Word { + self.o_maxdata.get(BE) + } + + fn o_textpsize(&self) -> u8 { + self.o_textpsize + } + + fn o_datapsize(&self) -> u8 { + self.o_datapsize + } + + fn o_stackpsize(&self) -> u8 { + self.o_stackpsize + } +} + +impl AuxHeader for xcoff::AuxHeader64 { + type Word = u64; + + fn o_vstamp(&self) -> u16 { + self.o_vstamp.get(BE) + } + + fn o_tsize(&self) -> Self::Word { + self.o_tsize.get(BE) + } + + fn o_dsize(&self) -> Self::Word { + self.o_dsize.get(BE) + } + + fn o_bsize(&self) -> Self::Word { + self.o_bsize.get(BE) + } + + fn o_entry(&self) -> Self::Word { + self.o_entry.get(BE) + } + + fn o_text_start(&self) -> Self::Word { + self.o_text_start.get(BE) + } + + fn o_data_start(&self) -> Self::Word { + self.o_data_start.get(BE) + } + + fn o_toc(&self) -> Self::Word { + self.o_toc.get(BE) + } + + fn o_snentry(&self) -> u16 { + self.o_snentry.get(BE) + } + + fn o_sntext(&self) -> u16 { + self.o_sntext.get(BE) + } + + fn o_sndata(&self) -> u16 { + self.o_sndata.get(BE) + } + + fn o_sntoc(&self) -> u16 { + self.o_sntoc.get(BE) + } + + fn o_snloader(&self) -> u16 { + self.o_snloader.get(BE) + } + + fn o_snbss(&self) -> u16 { + self.o_snbss.get(BE) + } + + fn o_sntdata(&self) -> u16 { + self.o_sntdata.get(BE) + } + + fn o_sntbss(&self) -> u16 { + self.o_sntbss.get(BE) + } + + fn o_algntext(&self) -> u16 { + self.o_algntext.get(BE) + } + + fn o_algndata(&self) -> u16 { + self.o_algndata.get(BE) + } + + fn o_maxstack(&self) -> Self::Word { + self.o_maxstack.get(BE) + } + + fn o_maxdata(&self) -> Self::Word { + self.o_maxdata.get(BE) + } + + fn o_textpsize(&self) -> u8 { + self.o_textpsize + } + + fn o_datapsize(&self) -> u8 { + self.o_datapsize + } + + fn o_stackpsize(&self) -> u8 { + self.o_stackpsize + } +} diff --git a/vendor/object/src/read/xcoff/mod.rs b/vendor/object/src/read/xcoff/mod.rs new file mode 100644 index 000000000..136e31073 --- /dev/null +++ b/vendor/object/src/read/xcoff/mod.rs @@ -0,0 +1,21 @@ +//! Support for reading AIX XCOFF files. +//! +//! Provides `XcoffFile` and related types which implement the `Object` trait. + +mod file; +pub use file::*; + +mod section; +pub use section::*; + +mod symbol; +pub use symbol::*; + +mod relocation; +pub use relocation::*; + +mod comdat; +pub use comdat::*; + +mod segment; +pub use segment::*; diff --git a/vendor/object/src/read/xcoff/relocation.rs b/vendor/object/src/read/xcoff/relocation.rs new file mode 100644 index 000000000..8107a2e82 --- /dev/null +++ b/vendor/object/src/read/xcoff/relocation.rs @@ -0,0 +1,128 @@ +use alloc::fmt; +use core::fmt::Debug; +use core::slice; + +use crate::pod::Pod; +use crate::{xcoff, BigEndian as BE, Relocation}; + +use crate::read::{ReadRef, RelocationEncoding, RelocationKind, RelocationTarget, SymbolIndex}; + +use super::{FileHeader, SectionHeader, XcoffFile}; + +/// An iterator over the relocations in a `XcoffSection32`. +pub type XcoffRelocationIterator32<'data, 'file, R = &'data [u8]> = + XcoffRelocationIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator over the relocations in a `XcoffSection64`. +pub type XcoffRelocationIterator64<'data, 'file, R = &'data [u8]> = + XcoffRelocationIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator over the relocations in a `XcoffSection`. +pub struct XcoffRelocationIterator<'data, 'file, Xcoff, R = &'data [u8]> +where + 'data: 'file, + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + #[allow(unused)] + pub(super) file: &'file XcoffFile<'data, Xcoff, R>, + pub(super) relocations: + slice::Iter<'data, <::SectionHeader as SectionHeader>::Rel>, +} + +impl<'data, 'file, Xcoff, R> Iterator for XcoffRelocationIterator<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + type Item = (u64, Relocation); + + fn next(&mut self) -> Option { + self.relocations.next().map(|relocation| { + let encoding = RelocationEncoding::Generic; + let (kind, addend) = match relocation.r_rtype() { + xcoff::R_POS + | xcoff::R_RL + | xcoff::R_RLA + | xcoff::R_BA + | xcoff::R_RBA + | xcoff::R_TLS => (RelocationKind::Absolute, 0), + xcoff::R_REL | xcoff::R_BR | xcoff::R_RBR => (RelocationKind::Relative, -4), + xcoff::R_TOC | xcoff::R_TOCL | xcoff::R_TOCU => (RelocationKind::Got, 0), + r_type => (RelocationKind::Xcoff(r_type), 0), + }; + let size = (relocation.r_rsize() & 0x3F) + 1; + let target = RelocationTarget::Symbol(SymbolIndex(relocation.r_symndx() as usize)); + ( + relocation.r_vaddr().into(), + Relocation { + kind, + encoding, + size, + target, + addend, + implicit_addend: true, + }, + ) + }) + } +} + +impl<'data, 'file, Xcoff, R> fmt::Debug for XcoffRelocationIterator<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("XcoffRelocationIterator").finish() + } +} + +/// A trait for generic access to `Rel32` and `Rel64`. +#[allow(missing_docs)] +pub trait Rel: Debug + Pod { + type Word: Into; + fn r_vaddr(&self) -> Self::Word; + fn r_symndx(&self) -> u32; + fn r_rsize(&self) -> u8; + fn r_rtype(&self) -> u8; +} + +impl Rel for xcoff::Rel32 { + type Word = u32; + + fn r_vaddr(&self) -> Self::Word { + self.r_vaddr.get(BE) + } + + fn r_symndx(&self) -> u32 { + self.r_symndx.get(BE) + } + + fn r_rsize(&self) -> u8 { + self.r_rsize + } + + fn r_rtype(&self) -> u8 { + self.r_rtype + } +} + +impl Rel for xcoff::Rel64 { + type Word = u64; + + fn r_vaddr(&self) -> Self::Word { + self.r_vaddr.get(BE) + } + + fn r_symndx(&self) -> u32 { + self.r_symndx.get(BE) + } + + fn r_rsize(&self) -> u8 { + self.r_rsize + } + + fn r_rtype(&self) -> u8 { + self.r_rtype + } +} diff --git a/vendor/object/src/read/xcoff/section.rs b/vendor/object/src/read/xcoff/section.rs new file mode 100644 index 000000000..0944e10c8 --- /dev/null +++ b/vendor/object/src/read/xcoff/section.rs @@ -0,0 +1,426 @@ +use core::fmt::Debug; +use core::{iter, result, slice, str}; + +use crate::{ + xcoff, BigEndian as BE, CompressedData, CompressedFileRange, Pod, SectionFlags, SectionKind, +}; + +use crate::read::{self, Error, ObjectSection, ReadError, ReadRef, Result, SectionIndex}; + +use super::{AuxHeader, FileHeader, Rel, XcoffFile, XcoffRelocationIterator}; + +/// An iterator over the sections of an `XcoffFile32`. +pub type XcoffSectionIterator32<'data, 'file, R = &'data [u8]> = + XcoffSectionIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator over the sections of an `XcoffFile64`. +pub type XcoffSectionIterator64<'data, 'file, R = &'data [u8]> = + XcoffSectionIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator over the sections of an `XcoffFile`. +#[derive(Debug)] +pub struct XcoffSectionIterator<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file XcoffFile<'data, Xcoff, R>, + pub(super) iter: iter::Enumerate>, +} + +impl<'data, 'file, Xcoff, R> Iterator for XcoffSectionIterator<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + type Item = XcoffSection<'data, 'file, Xcoff, R>; + + fn next(&mut self) -> Option { + self.iter.next().map(|(index, section)| XcoffSection { + index: SectionIndex(index), + file: self.file, + section, + }) + } +} + +/// A section of an `XcoffFile32`. +pub type XcoffSection32<'data, 'file, R = &'data [u8]> = + XcoffSection<'data, 'file, xcoff::FileHeader32, R>; +/// A section of an `XcoffFile64`. +pub type XcoffSection64<'data, 'file, R = &'data [u8]> = + XcoffSection<'data, 'file, xcoff::FileHeader64, R>; + +/// A section of an `XcoffFile`. +#[derive(Debug)] +pub struct XcoffSection<'data, 'file, Xcoff, R = &'data [u8]> +where + 'data: 'file, + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file XcoffFile<'data, Xcoff, R>, + pub(super) section: &'data Xcoff::SectionHeader, + pub(super) index: SectionIndex, +} + +impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> XcoffSection<'data, 'file, Xcoff, R> { + fn bytes(&self) -> Result<&'data [u8]> { + self.section + .data(self.file.data) + .read_error("Invalid XCOFF section offset or size") + } +} + +impl<'data, 'file, Xcoff, R> read::private::Sealed for XcoffSection<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Xcoff, R> ObjectSection<'data> for XcoffSection<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + type RelocationIterator = XcoffRelocationIterator<'data, 'file, Xcoff, R>; + + fn index(&self) -> SectionIndex { + self.index + } + + fn address(&self) -> u64 { + self.section.s_paddr().into() + } + + fn size(&self) -> u64 { + self.section.s_size().into() + } + + fn align(&self) -> u64 { + // The default section alignment is 4. + if let Some(aux_header) = self.file.aux_header { + match self.kind() { + SectionKind::Text => aux_header.o_algntext().into(), + SectionKind::Data => aux_header.o_algndata().into(), + _ => 4, + } + } else { + 4 + } + } + + fn file_range(&self) -> Option<(u64, u64)> { + self.section.file_range() + } + + fn data(&self) -> Result<&'data [u8]> { + self.bytes() + } + + fn data_range(&self, address: u64, size: u64) -> Result> { + Ok(read::util::data_range( + self.bytes()?, + self.address(), + address, + size, + )) + } + + fn compressed_file_range(&self) -> Result { + Ok(CompressedFileRange::none(self.file_range())) + } + + fn compressed_data(&self) -> Result> { + self.data().map(CompressedData::none) + } + + fn name_bytes(&self) -> read::Result<&[u8]> { + Ok(self.section.name()) + } + + fn name(&self) -> read::Result<&str> { + let name = self.name_bytes()?; + str::from_utf8(name) + .ok() + .read_error("Non UTF-8 XCOFF section name") + } + + fn segment_name_bytes(&self) -> Result> { + Ok(None) + } + + fn segment_name(&self) -> Result> { + Ok(None) + } + + fn kind(&self) -> SectionKind { + let section_type = self.section.s_flags() as u16; + if section_type & xcoff::STYP_TEXT != 0 { + SectionKind::Text + } else if section_type & xcoff::STYP_DATA != 0 { + SectionKind::Data + } else if section_type & xcoff::STYP_TDATA != 0 { + SectionKind::Tls + } else if section_type & xcoff::STYP_BSS != 0 { + SectionKind::UninitializedData + } else if section_type & xcoff::STYP_TBSS != 0 { + SectionKind::UninitializedTls + } else if section_type & (xcoff::STYP_DEBUG | xcoff::STYP_DWARF) != 0 { + SectionKind::Debug + } else if section_type & (xcoff::STYP_LOADER | xcoff::STYP_OVRFLO) != 0 { + SectionKind::Metadata + } else if section_type + & (xcoff::STYP_INFO | xcoff::STYP_EXCEPT | xcoff::STYP_PAD | xcoff::STYP_TYPCHK) + != 0 + { + SectionKind::Other + } else { + SectionKind::Unknown + } + } + + fn relocations(&self) -> Self::RelocationIterator { + let rel = self.section.relocations(self.file.data).unwrap_or(&[]); + XcoffRelocationIterator { + file: self.file, + relocations: rel.iter(), + } + } + + fn flags(&self) -> SectionFlags { + SectionFlags::Xcoff { + s_flags: self.section.s_flags(), + } + } + + fn uncompressed_data(&self) -> Result> { + self.compressed_data()?.decompress() + } +} + +/// The table of section headers in an XCOFF file. +#[derive(Debug, Clone, Copy)] +pub struct SectionTable<'data, Xcoff: FileHeader> { + sections: &'data [Xcoff::SectionHeader], +} + +impl<'data, Xcoff> Default for SectionTable<'data, Xcoff> +where + Xcoff: FileHeader, +{ + fn default() -> Self { + Self { sections: &[] } + } +} + +impl<'data, Xcoff> SectionTable<'data, Xcoff> +where + Xcoff: FileHeader, +{ + /// Parse the section table. + /// + /// `data` must be the entire file data. + /// `offset` must be after the optional file header. + pub fn parse>(header: &Xcoff, data: R, offset: &mut u64) -> Result { + let section_num = header.f_nscns(); + if section_num == 0 { + return Ok(SectionTable::default()); + } + let sections = data + .read_slice(offset, section_num as usize) + .read_error("Invalid XCOFF section headers")?; + Ok(SectionTable { sections }) + } + + /// Iterate over the section headers. + #[inline] + pub fn iter(&self) -> slice::Iter<'data, Xcoff::SectionHeader> { + self.sections.iter() + } + + /// Return true if the section table is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.sections.is_empty() + } + + /// The number of section headers. + #[inline] + pub fn len(&self) -> usize { + self.sections.len() + } + + /// Return the section header at the given index. + pub fn section(&self, index: SectionIndex) -> read::Result<&'data Xcoff::SectionHeader> { + self.sections + .get(index.0) + .read_error("Invalid XCOFF section index") + } +} + +/// A trait for generic access to `SectionHeader32` and `SectionHeader64`. +#[allow(missing_docs)] +pub trait SectionHeader: Debug + Pod { + type Word: Into; + type HalfWord: Into; + type Xcoff: FileHeader; + type Rel: Rel; + + fn s_name(&self) -> &[u8; 8]; + fn s_paddr(&self) -> Self::Word; + fn s_vaddr(&self) -> Self::Word; + fn s_size(&self) -> Self::Word; + fn s_scnptr(&self) -> Self::Word; + fn s_relptr(&self) -> Self::Word; + fn s_lnnoptr(&self) -> Self::Word; + fn s_nreloc(&self) -> Self::HalfWord; + fn s_nlnno(&self) -> Self::HalfWord; + fn s_flags(&self) -> u32; + + /// Return the section name. + fn name(&self) -> &[u8] { + let sectname = &self.s_name()[..]; + match memchr::memchr(b'\0', sectname) { + Some(end) => §name[..end], + None => sectname, + } + } + + /// Return the offset and size of the section in the file. + fn file_range(&self) -> Option<(u64, u64)> { + Some((self.s_scnptr().into(), self.s_size().into())) + } + + /// Return the section data. + /// + /// Returns `Ok(&[])` if the section has no data. + /// Returns `Err` for invalid values. + fn data<'data, R: ReadRef<'data>>(&self, data: R) -> result::Result<&'data [u8], ()> { + if let Some((offset, size)) = self.file_range() { + data.read_bytes_at(offset, size) + } else { + Ok(&[]) + } + } + + /// Read the relocations. + fn relocations<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [Self::Rel]>; +} + +impl SectionHeader for xcoff::SectionHeader32 { + type Word = u32; + type HalfWord = u16; + type Xcoff = xcoff::FileHeader32; + type Rel = xcoff::Rel32; + + fn s_name(&self) -> &[u8; 8] { + &self.s_name + } + + fn s_paddr(&self) -> Self::Word { + self.s_paddr.get(BE) + } + + fn s_vaddr(&self) -> Self::Word { + self.s_vaddr.get(BE) + } + + fn s_size(&self) -> Self::Word { + self.s_size.get(BE) + } + + fn s_scnptr(&self) -> Self::Word { + self.s_scnptr.get(BE) + } + + fn s_relptr(&self) -> Self::Word { + self.s_relptr.get(BE) + } + + fn s_lnnoptr(&self) -> Self::Word { + self.s_lnnoptr.get(BE) + } + + fn s_nreloc(&self) -> Self::HalfWord { + self.s_nreloc.get(BE) + } + + fn s_nlnno(&self) -> Self::HalfWord { + self.s_nlnno.get(BE) + } + + fn s_flags(&self) -> u32 { + self.s_flags.get(BE) + } + + /// Read the relocations in a XCOFF32 file. + /// + /// `data` must be the entire file data. + fn relocations<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [Self::Rel]> { + let reloc_num = self.s_nreloc() as usize; + // TODO: If more than 65,534 relocation entries are required, the field value will be 65535, + // and an STYP_OVRFLO section header will contain the actual count of relocation entries in + // the s_paddr field. + if reloc_num == 65535 { + return Err(Error("Overflow section is not supported yet.")); + } + data.read_slice_at(self.s_relptr().into(), reloc_num) + .read_error("Invalid XCOFF relocation offset or number") + } +} + +impl SectionHeader for xcoff::SectionHeader64 { + type Word = u64; + type HalfWord = u32; + type Xcoff = xcoff::FileHeader64; + type Rel = xcoff::Rel64; + + fn s_name(&self) -> &[u8; 8] { + &self.s_name + } + + fn s_paddr(&self) -> Self::Word { + self.s_paddr.get(BE) + } + + fn s_vaddr(&self) -> Self::Word { + self.s_vaddr.get(BE) + } + + fn s_size(&self) -> Self::Word { + self.s_size.get(BE) + } + + fn s_scnptr(&self) -> Self::Word { + self.s_scnptr.get(BE) + } + + fn s_relptr(&self) -> Self::Word { + self.s_relptr.get(BE) + } + + fn s_lnnoptr(&self) -> Self::Word { + self.s_lnnoptr.get(BE) + } + + fn s_nreloc(&self) -> Self::HalfWord { + self.s_nreloc.get(BE) + } + + fn s_nlnno(&self) -> Self::HalfWord { + self.s_nlnno.get(BE) + } + + fn s_flags(&self) -> u32 { + self.s_flags.get(BE) + } + + /// Read the relocations in a XCOFF64 file. + /// + /// `data` must be the entire file data. + fn relocations<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [Self::Rel]> { + data.read_slice_at(self.s_relptr(), self.s_nreloc() as usize) + .read_error("Invalid XCOFF relocation offset or number") + } +} diff --git a/vendor/object/src/read/xcoff/segment.rs b/vendor/object/src/read/xcoff/segment.rs new file mode 100644 index 000000000..49969438d --- /dev/null +++ b/vendor/object/src/read/xcoff/segment.rs @@ -0,0 +1,115 @@ +//! TODO: Support the segment for XCOFF when auxiliary file header and loader section is ready. + +use core::fmt::Debug; +use core::str; + +use crate::read::{self, ObjectSegment, ReadRef, Result}; +use crate::xcoff; + +use super::{FileHeader, XcoffFile}; + +/// An iterator over the segments of an `XcoffFile32`. +pub type XcoffSegmentIterator32<'data, 'file, R = &'data [u8]> = + XcoffSegmentIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator over the segments of an `XcoffFile64`. +pub type XcoffSegmentIterator64<'data, 'file, R = &'data [u8]> = + XcoffSegmentIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator over the segments of an `XcoffFile`. +#[derive(Debug)] +pub struct XcoffSegmentIterator<'data, 'file, Xcoff, R = &'data [u8]> +where + 'data: 'file, + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + #[allow(unused)] + pub(super) file: &'file XcoffFile<'data, Xcoff, R>, +} + +impl<'data, 'file, Xcoff, R> Iterator for XcoffSegmentIterator<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + type Item = XcoffSegment<'data, 'file, Xcoff, R>; + + fn next(&mut self) -> Option { + None + } +} + +/// A segment of an `XcoffFile32`. +pub type XcoffSegment32<'data, 'file, R = &'data [u8]> = + XcoffSegment<'data, 'file, xcoff::FileHeader32, R>; +/// A segment of an `XcoffFile64`. +pub type XcoffSegment64<'data, 'file, R = &'data [u8]> = + XcoffSegment<'data, 'file, xcoff::FileHeader64, R>; + +/// A loadable section of an `XcoffFile`. +#[derive(Debug)] +pub struct XcoffSegment<'data, 'file, Xcoff, R = &'data [u8]> +where + 'data: 'file, + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + #[allow(unused)] + pub(super) file: &'file XcoffFile<'data, Xcoff, R>, +} + +impl<'data, 'file, Xcoff, R> XcoffSegment<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Xcoff, R> read::private::Sealed for XcoffSegment<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Xcoff, R> ObjectSegment<'data> for XcoffSegment<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + fn address(&self) -> u64 { + unreachable!(); + } + + fn size(&self) -> u64 { + unreachable!(); + } + + fn align(&self) -> u64 { + unreachable!(); + } + + fn file_range(&self) -> (u64, u64) { + unreachable!(); + } + + fn data(&self) -> Result<&'data [u8]> { + unreachable!(); + } + + fn data_range(&self, _address: u64, _size: u64) -> Result> { + unreachable!(); + } + + fn name_bytes(&self) -> Result> { + unreachable!(); + } + + fn name(&self) -> Result> { + unreachable!(); + } + + fn flags(&self) -> crate::SegmentFlags { + unreachable!(); + } +} diff --git a/vendor/object/src/read/xcoff/symbol.rs b/vendor/object/src/read/xcoff/symbol.rs new file mode 100644 index 000000000..6738ad171 --- /dev/null +++ b/vendor/object/src/read/xcoff/symbol.rs @@ -0,0 +1,634 @@ +use alloc::fmt; +use core::convert::TryInto; +use core::fmt::Debug; +use core::marker::PhantomData; +use core::str; + +use crate::endian::{BigEndian as BE, U32Bytes}; +use crate::pod::Pod; +use crate::read::util::StringTable; +use crate::{bytes_of, xcoff, Object, ObjectSection, SectionKind}; + +use crate::read::{ + self, Bytes, Error, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Result, SectionIndex, + SymbolFlags, SymbolIndex, SymbolKind, SymbolScope, SymbolSection, +}; + +use super::{FileHeader, XcoffFile}; + +/// A table of symbol entries in an XCOFF file. +/// +/// Also includes the string table used for the symbol names. +#[derive(Debug)] +pub struct SymbolTable<'data, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + symbols: &'data [xcoff::SymbolBytes], + strings: StringTable<'data, R>, + header: PhantomData, +} + +impl<'data, Xcoff, R> Default for SymbolTable<'data, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + fn default() -> Self { + Self { + symbols: &[], + strings: StringTable::default(), + header: PhantomData, + } + } +} + +impl<'data, Xcoff, R> SymbolTable<'data, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + /// Parse the symbol table. + pub fn parse(header: Xcoff, data: R) -> Result { + let mut offset = header.f_symptr().into(); + let (symbols, strings) = if offset != 0 { + let symbols = data + .read_slice(&mut offset, header.f_nsyms() as usize) + .read_error("Invalid XCOFF symbol table offset or size")?; + + // Parse the string table. + // Note: don't update data when reading length; the length includes itself. + let length = data + .read_at::>(offset) + .read_error("Missing XCOFF string table")? + .get(BE); + let str_end = offset + .checked_add(length as u64) + .read_error("Invalid XCOFF string table length")?; + let strings = StringTable::new(data, offset, str_end); + + (symbols, strings) + } else { + (&[][..], StringTable::default()) + }; + + Ok(SymbolTable { + symbols, + strings, + header: PhantomData, + }) + } + + /// Return the symbol entry at the given index and offset. + pub fn get(&self, index: usize, offset: usize) -> Result<&'data T> { + let entry = index + .checked_add(offset) + .and_then(|x| self.symbols.get(x)) + .read_error("Invalid XCOFF symbol index")?; + let bytes = bytes_of(entry); + Bytes(bytes).read().read_error("Invalid XCOFF symbol data") + } + + /// Return the symbol at the given index. + pub fn symbol(&self, index: usize) -> Result<&'data Xcoff::Symbol> { + self.get::(index, 0) + } + + /// Return the file auxiliary symbol. + pub fn aux_file(&self, index: usize) -> Result<&'data Xcoff::FileAux> { + debug_assert!(self.symbol(index)?.has_aux_file()); + let aux_file = self.get::(index, 1)?; + if let Some(aux_type) = aux_file.x_auxtype() { + if aux_type != xcoff::AUX_FILE { + return Err(Error("Invalid index for file auxiliary symbol.")); + } + } + Ok(aux_file) + } + + /// Return the csect auxiliary symbol. + pub fn aux_csect(&self, index: usize, offset: usize) -> Result<&'data Xcoff::CsectAux> { + debug_assert!(self.symbol(index)?.has_aux_csect()); + let aux_csect = self.get::(index, offset)?; + if let Some(aux_type) = aux_csect.x_auxtype() { + if aux_type != xcoff::AUX_CSECT { + return Err(Error("Invalid index/offset for csect auxiliary symbol.")); + } + } + Ok(aux_csect) + } + + /// Return true if the symbol table is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.symbols.is_empty() + } + + /// The number of symbol table entries. + /// + /// This includes auxiliary symbol table entries. + #[inline] + pub fn len(&self) -> usize { + self.symbols.len() + } +} + +/// A symbol table of an `XcoffFile32`. +pub type XcoffSymbolTable32<'data, 'file, R = &'data [u8]> = + XcoffSymbolTable<'data, 'file, xcoff::FileHeader32, R>; +/// A symbol table of an `XcoffFile64`. +pub type XcoffSymbolTable64<'data, 'file, R = &'data [u8]> = + XcoffSymbolTable<'data, 'file, xcoff::FileHeader64, R>; + +/// A symbol table of an `XcoffFile`. +#[derive(Debug, Clone, Copy)] +pub struct XcoffSymbolTable<'data, 'file, Xcoff, R = &'data [u8]> +where + 'data: 'file, + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + pub(crate) file: &'file XcoffFile<'data, Xcoff, R>, + pub(super) symbols: &'file SymbolTable<'data, Xcoff, R>, +} + +impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> read::private::Sealed + for XcoffSymbolTable<'data, 'file, Xcoff, R> +{ +} + +impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> ObjectSymbolTable<'data> + for XcoffSymbolTable<'data, 'file, Xcoff, R> +{ + type Symbol = XcoffSymbol<'data, 'file, Xcoff, R>; + type SymbolIterator = XcoffSymbolIterator<'data, 'file, Xcoff, R>; + + fn symbols(&self) -> Self::SymbolIterator { + XcoffSymbolIterator { + file: self.file, + symbols: self.symbols, + index: 0, + } + } + + fn symbol_by_index(&self, index: SymbolIndex) -> read::Result { + let symbol = self.symbols.symbol(index.0)?; + Ok(XcoffSymbol { + file: self.file, + symbols: self.symbols, + index, + symbol, + }) + } +} + +/// An iterator over the symbols of an `XcoffFile32`. +pub type XcoffSymbolIterator32<'data, 'file, R = &'data [u8]> = + XcoffSymbolIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator over the symbols of an `XcoffFile64`. +pub type XcoffSymbolIterator64<'data, 'file, R = &'data [u8]> = + XcoffSymbolIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator over the symbols of an `XcoffFile`. +pub struct XcoffSymbolIterator<'data, 'file, Xcoff, R = &'data [u8]> +where + 'data: 'file, + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + pub(crate) file: &'file XcoffFile<'data, Xcoff, R>, + pub(super) symbols: &'file SymbolTable<'data, Xcoff, R>, + pub(super) index: usize, +} + +impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> fmt::Debug + for XcoffSymbolIterator<'data, 'file, Xcoff, R> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("XcoffSymbolIterator").finish() + } +} + +impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> Iterator + for XcoffSymbolIterator<'data, 'file, Xcoff, R> +{ + type Item = XcoffSymbol<'data, 'file, Xcoff, R>; + + fn next(&mut self) -> Option { + let index = self.index; + let symbol = self.symbols.symbol(index).ok()?; + // TODO: skip over the auxiliary symbols for now. + self.index += 1 + symbol.n_numaux() as usize; + Some(XcoffSymbol { + file: self.file, + symbols: self.symbols, + index: SymbolIndex(index), + symbol, + }) + } +} + +/// A symbol of an `XcoffFile32`. +pub type XcoffSymbol32<'data, 'file, R = &'data [u8]> = + XcoffSymbol<'data, 'file, xcoff::FileHeader32, R>; +/// A symbol of an `XcoffFile64`. +pub type XcoffSymbol64<'data, 'file, R = &'data [u8]> = + XcoffSymbol<'data, 'file, xcoff::FileHeader64, R>; + +/// A symbol of an `XcoffFile`. +#[derive(Debug, Clone, Copy)] +pub struct XcoffSymbol<'data, 'file, Xcoff, R = &'data [u8]> +where + 'data: 'file, + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + pub(crate) file: &'file XcoffFile<'data, Xcoff, R>, + pub(super) symbols: &'file SymbolTable<'data, Xcoff, R>, + pub(super) index: SymbolIndex, + pub(super) symbol: &'data Xcoff::Symbol, +} + +impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> read::private::Sealed + for XcoffSymbol<'data, 'file, Xcoff, R> +{ +} + +impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> ObjectSymbol<'data> + for XcoffSymbol<'data, 'file, Xcoff, R> +{ + #[inline] + fn index(&self) -> SymbolIndex { + self.index + } + + fn name_bytes(&self) -> Result<&'data [u8]> { + self.symbol.name(self.symbols.strings) + } + + fn name(&self) -> Result<&'data str> { + let name = self.name_bytes()?; + str::from_utf8(name) + .ok() + .read_error("Non UTF-8 XCOFF symbol name") + } + + #[inline] + fn address(&self) -> u64 { + match self.symbol.n_sclass() { + // Relocatable address. + xcoff::C_EXT + | xcoff::C_WEAKEXT + | xcoff::C_HIDEXT + | xcoff::C_FCN + | xcoff::C_BLOCK + | xcoff::C_STAT => self.symbol.n_value().into(), + _ => 0, + } + } + + #[inline] + fn size(&self) -> u64 { + if self.symbol.has_aux_csect() { + // XCOFF32 must have the csect auxiliary entry as the last auxiliary entry. + // XCOFF64 doesn't require this, but conventionally does. + if let Ok(aux_csect) = self + .file + .symbols + .aux_csect(self.index.0, self.symbol.n_numaux() as usize) + { + let sym_type = aux_csect.sym_type() & 0x07; + if sym_type == xcoff::XTY_SD || sym_type == xcoff::XTY_CM { + aux_csect.x_scnlen() + } else { + 0 + } + } else { + 0 + } + } else { + 0 + } + } + + fn kind(&self) -> SymbolKind { + match self.symbol.n_sclass() { + xcoff::C_FILE => SymbolKind::File, + xcoff::C_NULL => SymbolKind::Null, + _ => self + .file + .section_by_index(SectionIndex((self.symbol.n_scnum() - 1) as usize)) + .map(|section| match section.kind() { + SectionKind::Data | SectionKind::UninitializedData => SymbolKind::Data, + SectionKind::UninitializedTls | SectionKind::Tls => SymbolKind::Tls, + SectionKind::Text => SymbolKind::Text, + _ => SymbolKind::Unknown, + }) + .unwrap_or(SymbolKind::Unknown), + } + } + + fn section(&self) -> SymbolSection { + match self.symbol.n_scnum() { + xcoff::N_ABS => SymbolSection::Absolute, + xcoff::N_UNDEF => SymbolSection::Undefined, + xcoff::N_DEBUG => SymbolSection::None, + index if index > 0 => SymbolSection::Section(SectionIndex(index as usize)), + _ => SymbolSection::Unknown, + } + } + + #[inline] + fn is_undefined(&self) -> bool { + self.symbol.is_undefined() + } + + /// Return true if the symbol is a definition of a function or data object. + #[inline] + fn is_definition(&self) -> bool { + if self.symbol.has_aux_csect() { + if let Ok(aux_csect) = self + .symbols + .aux_csect(self.index.0, self.symbol.n_numaux() as usize) + { + let smclas = aux_csect.x_smclas(); + self.symbol.n_scnum() != xcoff::N_UNDEF + && (smclas == xcoff::XMC_PR + || smclas == xcoff::XMC_RW + || smclas == xcoff::XMC_RO) + } else { + false + } + } else { + false + } + } + + #[inline] + fn is_common(&self) -> bool { + self.symbol.n_sclass() == xcoff::C_EXT && self.symbol.n_scnum() == xcoff::N_UNDEF + } + + #[inline] + fn is_weak(&self) -> bool { + self.symbol.n_sclass() == xcoff::C_WEAKEXT + } + + fn scope(&self) -> SymbolScope { + if self.symbol.n_scnum() == xcoff::N_UNDEF { + SymbolScope::Unknown + } else { + match self.symbol.n_sclass() { + xcoff::C_EXT | xcoff::C_WEAKEXT | xcoff::C_HIDEXT => { + let visibility = self.symbol.n_type() & xcoff::SYM_V_MASK; + if visibility == xcoff::SYM_V_HIDDEN { + SymbolScope::Linkage + } else { + SymbolScope::Dynamic + } + } + _ => SymbolScope::Compilation, + } + } + } + + #[inline] + fn is_global(&self) -> bool { + match self.symbol.n_sclass() { + xcoff::C_EXT | xcoff::C_WEAKEXT => true, + _ => false, + } + } + + #[inline] + fn is_local(&self) -> bool { + !self.is_global() + } + + #[inline] + fn flags(&self) -> SymbolFlags { + SymbolFlags::None + } +} + +/// A trait for generic access to `Symbol32` and `Symbol64`. +#[allow(missing_docs)] +pub trait Symbol: Debug + Pod { + type Word: Into; + + fn n_value(&self) -> Self::Word; + fn n_scnum(&self) -> i16; + fn n_type(&self) -> u16; + fn n_sclass(&self) -> u8; + fn n_numaux(&self) -> u8; + + fn name<'data, R: ReadRef<'data>>( + &'data self, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]>; + + /// Return true if the symbol is undefined. + #[inline] + fn is_undefined(&self) -> bool { + let n_sclass = self.n_sclass(); + (n_sclass == xcoff::C_EXT || n_sclass == xcoff::C_WEAKEXT) + && self.n_scnum() == xcoff::N_UNDEF + } + + /// Return true if the symbol has file auxiliary entry. + fn has_aux_file(&self) -> bool { + self.n_numaux() > 0 && self.n_sclass() == xcoff::C_FILE + } + + /// Return true if the symbol has csect auxiliary entry. + /// + /// A csect auxiliary entry is required for each symbol table entry that has + /// a storage class value of C_EXT, C_WEAKEXT, or C_HIDEXT. + fn has_aux_csect(&self) -> bool { + let sclass = self.n_sclass(); + self.n_numaux() > 0 + && (sclass == xcoff::C_EXT || sclass == xcoff::C_WEAKEXT || sclass == xcoff::C_HIDEXT) + } +} + +impl Symbol for xcoff::Symbol64 { + type Word = u64; + + fn n_value(&self) -> Self::Word { + self.n_value.get(BE) + } + + fn n_scnum(&self) -> i16 { + self.n_scnum.get(BE) + } + + fn n_type(&self) -> u16 { + self.n_type.get(BE) + } + + fn n_sclass(&self) -> u8 { + self.n_sclass + } + + fn n_numaux(&self) -> u8 { + self.n_numaux + } + + /// Parse the symbol name for XCOFF64. + fn name<'data, R: ReadRef<'data>>( + &'data self, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + strings + .get(self.n_offset.get(BE)) + .read_error("Invalid XCOFF symbol name offset") + } +} + +impl Symbol for xcoff::Symbol32 { + type Word = u32; + + fn n_value(&self) -> Self::Word { + self.n_value.get(BE) + } + + fn n_scnum(&self) -> i16 { + self.n_scnum.get(BE) + } + + fn n_type(&self) -> u16 { + self.n_type.get(BE) + } + + fn n_sclass(&self) -> u8 { + self.n_sclass + } + + fn n_numaux(&self) -> u8 { + self.n_numaux + } + + /// Parse the symbol name for XCOFF32. + fn name<'data, R: ReadRef<'data>>( + &'data self, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + if self.n_name[0] == 0 { + // If the name starts with 0 then the last 4 bytes are a string table offset. + let offset = u32::from_be_bytes(self.n_name[4..8].try_into().unwrap()); + strings + .get(offset) + .read_error("Invalid XCOFF symbol name offset") + } else { + // The name is inline and padded with nulls. + Ok(match memchr::memchr(b'\0', &self.n_name) { + Some(end) => &self.n_name[..end], + None => &self.n_name, + }) + } + } +} + +/// A trait for generic access to `FileAux32` and `FileAux64`. +#[allow(missing_docs)] +pub trait FileAux: Debug + Pod { + fn x_fname(&self) -> &[u8; 8]; + fn x_ftype(&self) -> u8; + fn x_auxtype(&self) -> Option; +} + +impl FileAux for xcoff::FileAux64 { + fn x_fname(&self) -> &[u8; 8] { + &self.x_fname + } + + fn x_ftype(&self) -> u8 { + self.x_ftype + } + + fn x_auxtype(&self) -> Option { + Some(self.x_auxtype) + } +} + +impl FileAux for xcoff::FileAux32 { + fn x_fname(&self) -> &[u8; 8] { + &self.x_fname + } + + fn x_ftype(&self) -> u8 { + self.x_ftype + } + + fn x_auxtype(&self) -> Option { + None + } +} + +/// A trait for generic access to `CsectAux32` and `CsectAux64`. +#[allow(missing_docs)] +pub trait CsectAux: Debug + Pod { + fn x_scnlen(&self) -> u64; + fn x_parmhash(&self) -> u32; + fn x_snhash(&self) -> u16; + fn x_smtyp(&self) -> u8; + fn x_smclas(&self) -> u8; + fn x_auxtype(&self) -> Option; + + fn sym_type(&self) -> u8 { + self.x_smtyp() & 0x07 + } +} + +impl CsectAux for xcoff::CsectAux64 { + fn x_scnlen(&self) -> u64 { + self.x_scnlen_lo.get(BE) as u64 | ((self.x_scnlen_hi.get(BE) as u64) << 32) + } + + fn x_parmhash(&self) -> u32 { + self.x_parmhash.get(BE) + } + + fn x_snhash(&self) -> u16 { + self.x_snhash.get(BE) + } + + fn x_smtyp(&self) -> u8 { + self.x_smtyp + } + + fn x_smclas(&self) -> u8 { + self.x_smclas + } + + fn x_auxtype(&self) -> Option { + Some(self.x_auxtype) + } +} + +impl CsectAux for xcoff::CsectAux32 { + fn x_scnlen(&self) -> u64 { + self.x_scnlen.get(BE) as u64 + } + + fn x_parmhash(&self) -> u32 { + self.x_parmhash.get(BE) + } + + fn x_snhash(&self) -> u16 { + self.x_snhash.get(BE) + } + + fn x_smtyp(&self) -> u8 { + self.x_smtyp + } + + fn x_smclas(&self) -> u8 { + self.x_smclas + } + + fn x_auxtype(&self) -> Option { + None + } +} -- cgit v1.2.3