object/read/
archive.rs

1//! Support for archive files.
2//!
3//! ## Example
4//!  ```no_run
5//! use object::{Object, ObjectSection};
6//! use std::error::Error;
7//! use std::fs;
8//!
9//! /// Reads an archive and displays the name of each member.
10//! fn main() -> Result<(), Box<dyn Error>> {
11//! #   #[cfg(feature = "std")] {
12//!     let data = fs::read("path/to/binary")?;
13//!     let file = object::read::archive::ArchiveFile::parse(&*data)?;
14//!     for member in file.members() {
15//!         let member = member?;
16//!         println!("{}", String::from_utf8_lossy(member.name()));
17//!     }
18//! #   }
19//!     Ok(())
20//! }
21//! ```
22
23use core::convert::TryInto;
24use core::slice;
25
26use crate::archive;
27use crate::endian::{BigEndian as BE, LittleEndian as LE, U16Bytes, U32Bytes, U64Bytes};
28use crate::read::{self, Bytes, Error, ReadError, ReadRef};
29
30/// The kind of archive format.
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
32#[non_exhaustive]
33pub enum ArchiveKind {
34    /// There are no special files that indicate the archive format.
35    Unknown,
36    /// The GNU (or System V) archive format.
37    Gnu,
38    /// The GNU (or System V) archive format with 64-bit symbol table.
39    Gnu64,
40    /// The BSD archive format.
41    Bsd,
42    /// The BSD archive format with 64-bit symbol table.
43    ///
44    /// This is used for Darwin.
45    Bsd64,
46    /// The Windows COFF archive format.
47    Coff,
48    /// The AIX big archive format.
49    AixBig,
50}
51
52/// The list of members in the archive.
53#[derive(Debug, Clone, Copy)]
54enum Members<'data> {
55    Common {
56        offset: u64,
57        end_offset: u64,
58    },
59    AixBig {
60        index: &'data [archive::AixMemberOffset],
61    },
62}
63
64/// A partially parsed archive file.
65#[derive(Debug, Clone, Copy)]
66pub struct ArchiveFile<'data, R: ReadRef<'data> = &'data [u8]> {
67    data: R,
68    kind: ArchiveKind,
69    members: Members<'data>,
70    symbols: (u64, u64),
71    names: &'data [u8],
72    thin: bool,
73}
74
75impl<'data, R: ReadRef<'data>> ArchiveFile<'data, R> {
76    /// Parse the archive header and special members.
77    pub fn parse(data: R) -> read::Result<Self> {
78        let len = data.len().read_error("Unknown archive length")?;
79        let mut tail = 0;
80        let magic = data
81            .read_bytes(&mut tail, archive::MAGIC.len() as u64)
82            .read_error("Invalid archive size")?;
83
84        let thin = if magic == archive::AIX_BIG_MAGIC {
85            return Self::parse_aixbig(data);
86        } else if magic == archive::THIN_MAGIC {
87            true
88        } else if magic == archive::MAGIC {
89            false
90        } else {
91            return Err(Error("Unsupported archive identifier"));
92        };
93
94        let mut members_offset = tail;
95        let members_end_offset = len;
96
97        let mut file = ArchiveFile {
98            data,
99            kind: ArchiveKind::Unknown,
100            members: Members::Common {
101                offset: 0,
102                end_offset: 0,
103            },
104            symbols: (0, 0),
105            names: &[],
106            thin,
107        };
108
109        // The first few members may be special, so parse them.
110        // GNU has:
111        // - "/" or "/SYM64/": symbol table (optional)
112        // - "//": names table (optional)
113        // COFF has:
114        // - "/": first linker member
115        // - "/": second linker member
116        // - "//": names table
117        // BSD has:
118        // - "__.SYMDEF" or "__.SYMDEF SORTED": symbol table (optional)
119        // BSD 64-bit has:
120        // - "__.SYMDEF_64" or "__.SYMDEF_64 SORTED": symbol table (optional)
121        // BSD may use the extended name for the symbol table. This is handled
122        // by `ArchiveMember::parse`.
123        if tail < len {
124            let member = ArchiveMember::parse(data, &mut tail, &[], thin)?;
125            if member.name == b"/" {
126                // GNU symbol table (unless we later determine this is COFF).
127                file.kind = ArchiveKind::Gnu;
128                file.symbols = member.file_range();
129                members_offset = tail;
130
131                if tail < len {
132                    let member = ArchiveMember::parse(data, &mut tail, &[], thin)?;
133                    if member.name == b"/" {
134                        // COFF linker member.
135                        file.kind = ArchiveKind::Coff;
136                        file.symbols = member.file_range();
137                        members_offset = tail;
138
139                        if tail < len {
140                            let member = ArchiveMember::parse(data, &mut tail, &[], thin)?;
141                            if member.name == b"//" {
142                                // COFF names table.
143                                file.names = member.data(data)?;
144                                members_offset = tail;
145                            }
146                        }
147                        if tail < len {
148                            let member = ArchiveMember::parse(data, &mut tail, file.names, thin)?;
149                            if member.name == b"/<ECSYMBOLS>/" {
150                                // COFF EC Symbol Table.
151                                members_offset = tail;
152                            }
153                        }
154                    } else if member.name == b"//" {
155                        // GNU names table.
156                        file.names = member.data(data)?;
157                        members_offset = tail;
158                    }
159                }
160            } else if member.name == b"/SYM64/" {
161                // GNU 64-bit symbol table.
162                file.kind = ArchiveKind::Gnu64;
163                file.symbols = member.file_range();
164                members_offset = tail;
165
166                if tail < len {
167                    let member = ArchiveMember::parse(data, &mut tail, &[], thin)?;
168                    if member.name == b"//" {
169                        // GNU names table.
170                        file.names = member.data(data)?;
171                        members_offset = tail;
172                    }
173                }
174            } else if member.name == b"//" {
175                // GNU names table.
176                file.kind = ArchiveKind::Gnu;
177                file.names = member.data(data)?;
178                members_offset = tail;
179            } else if member.name == b"__.SYMDEF" || member.name == b"__.SYMDEF SORTED" {
180                // BSD symbol table.
181                file.kind = ArchiveKind::Bsd;
182                file.symbols = member.file_range();
183                members_offset = tail;
184            } else if member.name == b"__.SYMDEF_64" || member.name == b"__.SYMDEF_64 SORTED" {
185                // BSD 64-bit symbol table.
186                file.kind = ArchiveKind::Bsd64;
187                file.symbols = member.file_range();
188                members_offset = tail;
189            } else {
190                // TODO: This could still be a BSD file. We leave this as unknown for now.
191            }
192        }
193        file.members = Members::Common {
194            offset: members_offset,
195            end_offset: members_end_offset,
196        };
197        Ok(file)
198    }
199
200    fn parse_aixbig(data: R) -> read::Result<Self> {
201        let mut tail = 0;
202
203        let file_header = data
204            .read::<archive::AixFileHeader>(&mut tail)
205            .read_error("Invalid AIX big archive file header")?;
206        // Caller already validated this.
207        debug_assert_eq!(file_header.magic, archive::AIX_BIG_MAGIC);
208
209        let mut file = ArchiveFile {
210            data,
211            kind: ArchiveKind::AixBig,
212            members: Members::AixBig { index: &[] },
213            symbols: (0, 0),
214            names: &[],
215            thin: false,
216        };
217
218        // Read the span of symbol table.
219        // TODO: an archive may have both 32-bit and 64-bit symbol tables.
220        let symtbl64 = parse_u64_digits(&file_header.gst64off, 10)
221            .read_error("Invalid offset to 64-bit symbol table in AIX big archive")?;
222        if symtbl64 > 0 {
223            // The symbol table is also a file with header.
224            let member = ArchiveMember::parse_aixbig(data, symtbl64)?;
225            file.symbols = member.file_range();
226        } else {
227            let symtbl = parse_u64_digits(&file_header.gstoff, 10)
228                .read_error("Invalid offset to symbol table in AIX big archive")?;
229            if symtbl > 0 {
230                // The symbol table is also a file with header.
231                let member = ArchiveMember::parse_aixbig(data, symtbl)?;
232                file.symbols = member.file_range();
233            }
234        }
235
236        // Big archive member index table lists file entries with offsets and names.
237        // To avoid potential infinite loop (members are double-linked list), the
238        // iterator goes through the index instead of real members.
239        let member_table_offset = parse_u64_digits(&file_header.memoff, 10)
240            .read_error("Invalid offset for member table of AIX big archive")?;
241        if member_table_offset == 0 {
242            // The offset would be zero if archive contains no file.
243            return Ok(file);
244        }
245
246        // The member index table is also a file with header.
247        let member = ArchiveMember::parse_aixbig(data, member_table_offset)?;
248        let mut member_data = Bytes(member.data(data)?);
249
250        // Structure of member index table:
251        // Number of entries (20 bytes)
252        // Offsets of each entry (20*N bytes)
253        // Names string table (the rest of bytes to fill size defined in header)
254        let members_count_bytes = member_data
255            .read_slice::<u8>(20)
256            .read_error("Missing member count in AIX big archive")?;
257        let members_count = parse_u64_digits(members_count_bytes, 10)
258            .and_then(|size| size.try_into().ok())
259            .read_error("Invalid member count in AIX big archive")?;
260        let index = member_data
261            .read_slice::<archive::AixMemberOffset>(members_count)
262            .read_error("Member count overflow in AIX big archive")?;
263        file.members = Members::AixBig { index };
264
265        Ok(file)
266    }
267
268    /// Return the archive format.
269    #[inline]
270    pub fn kind(&self) -> ArchiveKind {
271        self.kind
272    }
273
274    /// Return true if the archive is a thin archive.
275    pub fn is_thin(&self) -> bool {
276        self.thin
277    }
278
279    /// Iterate over the members of the archive.
280    ///
281    /// This does not return special members.
282    #[inline]
283    pub fn members(&self) -> ArchiveMemberIterator<'data, R> {
284        ArchiveMemberIterator {
285            data: self.data,
286            members: self.members,
287            names: self.names,
288            thin: self.thin,
289        }
290    }
291
292    /// Return the member at the given offset.
293    pub fn member(&self, member: ArchiveOffset) -> read::Result<ArchiveMember<'data>> {
294        match self.members {
295            Members::Common { offset, end_offset } => {
296                if member.0 < offset || member.0 >= end_offset {
297                    return Err(Error("Invalid archive member offset"));
298                }
299                let mut offset = member.0;
300                ArchiveMember::parse(self.data, &mut offset, self.names, self.thin)
301            }
302            Members::AixBig { .. } => {
303                let offset = member.0;
304                ArchiveMember::parse_aixbig(self.data, offset)
305            }
306        }
307    }
308
309    /// Iterate over the symbols in the archive.
310    pub fn symbols(&self) -> read::Result<Option<ArchiveSymbolIterator<'data>>> {
311        if self.symbols == (0, 0) {
312            return Ok(None);
313        }
314        let (offset, size) = self.symbols;
315        ArchiveSymbolIterator::new(self.kind, self.data, offset, size)
316            .read_error("Invalid archive symbol table")
317            .map(Some)
318    }
319}
320
321/// An iterator over the members of an archive.
322#[derive(Debug)]
323pub struct ArchiveMemberIterator<'data, R: ReadRef<'data> = &'data [u8]> {
324    data: R,
325    members: Members<'data>,
326    names: &'data [u8],
327    thin: bool,
328}
329
330impl<'data, R: ReadRef<'data>> Iterator for ArchiveMemberIterator<'data, R> {
331    type Item = read::Result<ArchiveMember<'data>>;
332
333    fn next(&mut self) -> Option<Self::Item> {
334        match &mut self.members {
335            Members::Common {
336                ref mut offset,
337                ref mut end_offset,
338            } => {
339                if *offset >= *end_offset {
340                    return None;
341                }
342                let member = ArchiveMember::parse(self.data, offset, self.names, self.thin);
343                if member.is_err() {
344                    *offset = *end_offset;
345                }
346                Some(member)
347            }
348            Members::AixBig { ref mut index } => match **index {
349                [] => None,
350                [ref first, ref rest @ ..] => {
351                    *index = rest;
352                    let member = ArchiveMember::parse_aixbig_index(self.data, first);
353                    if member.is_err() {
354                        *index = &[];
355                    }
356                    Some(member)
357                }
358            },
359        }
360    }
361}
362
363/// An archive member header.
364#[derive(Debug, Clone, Copy)]
365enum MemberHeader<'data> {
366    /// Common header used by many formats.
367    Common(&'data archive::Header),
368    /// AIX big archive header
369    AixBig(&'data archive::AixHeader),
370}
371
372/// A partially parsed archive member.
373#[derive(Debug)]
374pub struct ArchiveMember<'data> {
375    header: MemberHeader<'data>,
376    name: &'data [u8],
377    // May be zero for thin members.
378    offset: u64,
379    size: u64,
380}
381
382impl<'data> ArchiveMember<'data> {
383    /// Parse the member header, name, and file data in an archive with the common format.
384    ///
385    /// This reads the extended name (if any) and adjusts the file size.
386    fn parse<R: ReadRef<'data>>(
387        data: R,
388        offset: &mut u64,
389        names: &'data [u8],
390        thin: bool,
391    ) -> read::Result<Self> {
392        let header = data
393            .read::<archive::Header>(offset)
394            .read_error("Invalid archive member header")?;
395        if header.terminator != archive::TERMINATOR {
396            return Err(Error("Invalid archive terminator"));
397        }
398
399        let header_file_size =
400            parse_u64_digits(&header.size, 10).read_error("Invalid archive member size")?;
401        let mut file_offset = *offset;
402        let mut file_size = header_file_size;
403
404        let name = if header.name[0] == b'/' && (header.name[1] as char).is_ascii_digit() {
405            // Read file name from the names table.
406            parse_sysv_extended_name(&header.name[1..], names)
407                .read_error("Invalid archive extended name offset")?
408        } else if &header.name[..3] == b"#1/" && (header.name[3] as char).is_ascii_digit() {
409            // Read file name from the start of the file data.
410            parse_bsd_extended_name(&header.name[3..], data, &mut file_offset, &mut file_size)
411                .read_error("Invalid archive extended name length")?
412        } else if header.name[0] == b'/' {
413            let name_len = memchr::memchr(b' ', &header.name).unwrap_or(header.name.len());
414            &header.name[..name_len]
415        } else {
416            // Name is terminated by slash or space.
417            // Slash allows embedding spaces in the name, so only look
418            // for space if there is no slash.
419            let name_len = memchr::memchr(b'/', &header.name)
420                .or_else(|| memchr::memchr(b' ', &header.name))
421                .unwrap_or(header.name.len());
422            &header.name[..name_len]
423        };
424
425        // Members in thin archives don't have data unless they are special members.
426        if thin && name != b"/" && name != b"//" && name != b"/SYM64/" {
427            return Ok(ArchiveMember {
428                header: MemberHeader::Common(header),
429                name,
430                offset: 0,
431                size: file_size,
432            });
433        }
434
435        // Skip the file data.
436        *offset = offset
437            .checked_add(header_file_size)
438            .read_error("Archive member size is too large")?;
439        // Entries are padded to an even number of bytes.
440        if (header_file_size & 1) != 0 {
441            *offset = offset.saturating_add(1);
442        }
443
444        Ok(ArchiveMember {
445            header: MemberHeader::Common(header),
446            name,
447            offset: file_offset,
448            size: file_size,
449        })
450    }
451
452    /// Parse a member index entry in an AIX big archive,
453    /// and then parse the member header, name, and file data.
454    fn parse_aixbig_index<R: ReadRef<'data>>(
455        data: R,
456        index: &archive::AixMemberOffset,
457    ) -> read::Result<Self> {
458        let offset = parse_u64_digits(&index.0, 10)
459            .read_error("Invalid AIX big archive file member offset")?;
460        Self::parse_aixbig(data, offset)
461    }
462
463    /// Parse the member header, name, and file data in an AIX big archive.
464    fn parse_aixbig<R: ReadRef<'data>>(data: R, mut offset: u64) -> read::Result<Self> {
465        // The format was described at
466        // https://www.ibm.com/docs/en/aix/7.3?topic=formats-ar-file-format-big
467        let header = data
468            .read::<archive::AixHeader>(&mut offset)
469            .read_error("Invalid AIX big archive member header")?;
470        let name_length = parse_u64_digits(&header.namlen, 10)
471            .read_error("Invalid AIX big archive member name length")?;
472        let name = data
473            .read_bytes(&mut offset, name_length)
474            .read_error("Invalid AIX big archive member name")?;
475
476        // The actual data for a file member begins at the first even-byte boundary beyond the
477        // member header and continues for the number of bytes specified by the ar_size field. The
478        // ar command inserts null bytes for padding where necessary.
479        if offset & 1 != 0 {
480            offset = offset.saturating_add(1);
481        }
482        // Because of the even-byte boundary, we have to read and check terminator after header.
483        let terminator = data
484            .read_bytes(&mut offset, 2)
485            .read_error("Invalid AIX big archive terminator")?;
486        if terminator != archive::TERMINATOR {
487            return Err(Error("Invalid AIX big archive terminator"));
488        }
489
490        let size = parse_u64_digits(&header.size, 10)
491            .read_error("Invalid archive member size in AIX big archive")?;
492        Ok(ArchiveMember {
493            header: MemberHeader::AixBig(header),
494            name,
495            offset,
496            size,
497        })
498    }
499
500    /// Return the raw header that is common to many archive formats.
501    ///
502    /// Returns `None` if this archive does not use the common header format.
503    #[inline]
504    pub fn header(&self) -> Option<&'data archive::Header> {
505        match self.header {
506            MemberHeader::Common(header) => Some(header),
507            _ => None,
508        }
509    }
510
511    /// Return the raw header for AIX big archives.
512    ///
513    /// Returns `None` if this is not an AIX big archive.
514    #[inline]
515    pub fn aix_header(&self) -> Option<&'data archive::AixHeader> {
516        match self.header {
517            MemberHeader::AixBig(header) => Some(header),
518            _ => None,
519        }
520    }
521
522    /// Return the parsed file name.
523    ///
524    /// This may be an extended file name.
525    #[inline]
526    pub fn name(&self) -> &'data [u8] {
527        self.name
528    }
529
530    /// Parse the file modification timestamp from the header.
531    #[inline]
532    pub fn date(&self) -> Option<u64> {
533        match &self.header {
534            MemberHeader::Common(header) => parse_u64_digits(&header.date, 10),
535            MemberHeader::AixBig(header) => parse_u64_digits(&header.date, 10),
536        }
537    }
538
539    /// Parse the user ID from the header.
540    #[inline]
541    pub fn uid(&self) -> Option<u64> {
542        match &self.header {
543            MemberHeader::Common(header) => parse_u64_digits(&header.uid, 10),
544            MemberHeader::AixBig(header) => parse_u64_digits(&header.uid, 10),
545        }
546    }
547
548    /// Parse the group ID from the header.
549    #[inline]
550    pub fn gid(&self) -> Option<u64> {
551        match &self.header {
552            MemberHeader::Common(header) => parse_u64_digits(&header.gid, 10),
553            MemberHeader::AixBig(header) => parse_u64_digits(&header.gid, 10),
554        }
555    }
556
557    /// Parse the file mode from the header.
558    #[inline]
559    pub fn mode(&self) -> Option<u64> {
560        match &self.header {
561            MemberHeader::Common(header) => parse_u64_digits(&header.mode, 8),
562            MemberHeader::AixBig(header) => parse_u64_digits(&header.mode, 8),
563        }
564    }
565
566    /// Return the size of the file data.
567    pub fn size(&self) -> u64 {
568        self.size
569    }
570
571    /// Return the offset and size of the file data.
572    pub fn file_range(&self) -> (u64, u64) {
573        (self.offset, self.size)
574    }
575
576    /// Return true if the member is a thin member.
577    ///
578    /// Thin members have no file data.
579    pub fn is_thin(&self) -> bool {
580        self.offset == 0
581    }
582
583    /// Return the file data.
584    ///
585    /// This is an empty slice for thin members.
586    #[inline]
587    pub fn data<R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [u8]> {
588        if self.is_thin() {
589            return Ok(&[]);
590        }
591        data.read_bytes_at(self.offset, self.size)
592            .read_error("Archive member size is too large")
593    }
594}
595
596/// An offset of a member in an archive.
597#[derive(Debug, Clone, Copy)]
598pub struct ArchiveOffset(pub u64);
599
600/// An iterator over the symbols in the archive symbol table.
601#[derive(Debug, Clone)]
602pub struct ArchiveSymbolIterator<'data>(SymbolIteratorInternal<'data>);
603
604#[derive(Debug, Clone)]
605enum SymbolIteratorInternal<'data> {
606    /// There is no symbol table.
607    None,
608    /// A GNU symbol table.
609    ///
610    /// Contains:
611    /// - the number of symbols as a 32-bit big-endian integer
612    /// - the offsets of the member headers as 32-bit big-endian integers
613    /// - the symbol names as null-terminated strings
614    Gnu {
615        offsets: slice::Iter<'data, U32Bytes<BE>>,
616        names: Bytes<'data>,
617    },
618    /// A GNU 64-bit symbol table
619    ///
620    /// Contains:
621    /// - the number of symbols as a 64-bit big-endian integer
622    /// - the offsets of the member headers as 64-bit big-endian integers
623    /// - the symbol names as null-terminated strings
624    Gnu64 {
625        offsets: slice::Iter<'data, U64Bytes<BE>>,
626        names: Bytes<'data>,
627    },
628    /// A BSD symbol table.
629    ///
630    /// Contains:
631    /// - the size in bytes of the offsets array as a 32-bit little-endian integer
632    /// - the offsets array, for which each entry is a pair of 32-bit little-endian integers
633    ///   for the offset of the member header and the offset of the symbol name
634    /// - the size in bytes of the symbol names as a 32-bit little-endian integer
635    /// - the symbol names as null-terminated strings
636    Bsd {
637        offsets: slice::Iter<'data, [U32Bytes<LE>; 2]>,
638        names: Bytes<'data>,
639    },
640    /// A BSD 64-bit symbol table.
641    ///
642    /// Contains:
643    /// - the size in bytes of the offsets array as a 64-bit little-endian integer
644    /// - the offsets array, for which each entry is a pair of 64-bit little-endian integers
645    ///   for the offset of the member header and the offset of the symbol name
646    /// - the size in bytes of the symbol names as a 64-bit little-endian integer
647    /// - the symbol names as null-terminated strings
648    Bsd64 {
649        offsets: slice::Iter<'data, [U64Bytes<LE>; 2]>,
650        names: Bytes<'data>,
651    },
652    /// A Windows COFF symbol table.
653    ///
654    /// Contains:
655    /// - the number of members as a 32-bit little-endian integer
656    /// - the offsets of the member headers as 32-bit little-endian integers
657    /// - the number of symbols as a 32-bit little-endian integer
658    /// - the member index for each symbol as a 16-bit little-endian integer
659    /// - the symbol names as null-terminated strings in lexical order
660    Coff {
661        members: &'data [U32Bytes<LE>],
662        indices: slice::Iter<'data, U16Bytes<LE>>,
663        names: Bytes<'data>,
664    },
665}
666
667impl<'data> ArchiveSymbolIterator<'data> {
668    fn new<R: ReadRef<'data>>(
669        kind: ArchiveKind,
670        data: R,
671        offset: u64,
672        size: u64,
673    ) -> Result<Self, ()> {
674        let mut data = data.read_bytes_at(offset, size).map(Bytes)?;
675        match kind {
676            ArchiveKind::Unknown => Ok(ArchiveSymbolIterator(SymbolIteratorInternal::None)),
677            ArchiveKind::Gnu => {
678                let offsets_count = data.read::<U32Bytes<BE>>()?.get(BE);
679                let offsets = data.read_slice::<U32Bytes<BE>>(offsets_count as usize)?;
680                Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Gnu {
681                    offsets: offsets.iter(),
682                    names: data,
683                }))
684            }
685            ArchiveKind::Gnu64 => {
686                let offsets_count = data.read::<U64Bytes<BE>>()?.get(BE);
687                let offsets = data.read_slice::<U64Bytes<BE>>(offsets_count as usize)?;
688                Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Gnu64 {
689                    offsets: offsets.iter(),
690                    names: data,
691                }))
692            }
693            ArchiveKind::Bsd => {
694                let offsets_size = data.read::<U32Bytes<LE>>()?.get(LE);
695                let offsets = data.read_slice::<[U32Bytes<LE>; 2]>(offsets_size as usize / 8)?;
696                let names_size = data.read::<U32Bytes<LE>>()?.get(LE);
697                let names = data.read_bytes(names_size as usize)?;
698                Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Bsd {
699                    offsets: offsets.iter(),
700                    names,
701                }))
702            }
703            ArchiveKind::Bsd64 => {
704                let offsets_size = data.read::<U64Bytes<LE>>()?.get(LE);
705                let offsets = data.read_slice::<[U64Bytes<LE>; 2]>(offsets_size as usize / 16)?;
706                let names_size = data.read::<U64Bytes<LE>>()?.get(LE);
707                let names = data.read_bytes(names_size as usize)?;
708                Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Bsd64 {
709                    offsets: offsets.iter(),
710                    names,
711                }))
712            }
713            ArchiveKind::Coff => {
714                let members_count = data.read::<U32Bytes<LE>>()?.get(LE);
715                let members = data.read_slice::<U32Bytes<LE>>(members_count as usize)?;
716                let indices_count = data.read::<U32Bytes<LE>>()?.get(LE);
717                let indices = data.read_slice::<U16Bytes<LE>>(indices_count as usize)?;
718                Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Coff {
719                    members,
720                    indices: indices.iter(),
721                    names: data,
722                }))
723            }
724            // TODO: Implement AIX big archive symbol table.
725            ArchiveKind::AixBig => Ok(ArchiveSymbolIterator(SymbolIteratorInternal::None)),
726        }
727    }
728}
729
730impl<'data> Iterator for ArchiveSymbolIterator<'data> {
731    type Item = read::Result<ArchiveSymbol<'data>>;
732
733    fn next(&mut self) -> Option<Self::Item> {
734        match &mut self.0 {
735            SymbolIteratorInternal::None => None,
736            SymbolIteratorInternal::Gnu { offsets, names } => {
737                let offset = offsets.next()?.get(BE);
738                Some(
739                    names
740                        .read_string()
741                        .read_error("Missing archive symbol name")
742                        .map(|name| ArchiveSymbol {
743                            name,
744                            offset: ArchiveOffset(offset.into()),
745                        }),
746                )
747            }
748            SymbolIteratorInternal::Gnu64 { offsets, names } => {
749                let offset = offsets.next()?.get(BE);
750                Some(
751                    names
752                        .read_string()
753                        .read_error("Missing archive symbol name")
754                        .map(|name| ArchiveSymbol {
755                            name,
756                            offset: ArchiveOffset(offset),
757                        }),
758                )
759            }
760            SymbolIteratorInternal::Bsd { offsets, names } => {
761                let entry = offsets.next()?;
762                Some(
763                    names
764                        .read_string_at(entry[0].get(LE) as usize)
765                        .read_error("Invalid archive symbol name offset")
766                        .map(|name| ArchiveSymbol {
767                            name,
768                            offset: ArchiveOffset(entry[1].get(LE).into()),
769                        }),
770                )
771            }
772            SymbolIteratorInternal::Bsd64 { offsets, names } => {
773                let entry = offsets.next()?;
774                Some(
775                    names
776                        .read_string_at(entry[0].get(LE) as usize)
777                        .read_error("Invalid archive symbol name offset")
778                        .map(|name| ArchiveSymbol {
779                            name,
780                            offset: ArchiveOffset(entry[1].get(LE)),
781                        }),
782                )
783            }
784            SymbolIteratorInternal::Coff {
785                members,
786                indices,
787                names,
788            } => {
789                let index = indices.next()?.get(LE).wrapping_sub(1);
790                let member = members
791                    .get(index as usize)
792                    .read_error("Invalid archive symbol member index");
793                let name = names
794                    .read_string()
795                    .read_error("Missing archive symbol name");
796                Some(member.and_then(|member| {
797                    name.map(|name| ArchiveSymbol {
798                        name,
799                        offset: ArchiveOffset(member.get(LE).into()),
800                    })
801                }))
802            }
803        }
804    }
805}
806
807/// A symbol in the archive symbol table.
808///
809/// This is used to find the member containing the symbol.
810#[derive(Debug, Clone, Copy)]
811pub struct ArchiveSymbol<'data> {
812    name: &'data [u8],
813    offset: ArchiveOffset,
814}
815
816impl<'data> ArchiveSymbol<'data> {
817    /// Return the symbol name.
818    #[inline]
819    pub fn name(&self) -> &'data [u8] {
820        self.name
821    }
822
823    /// Return the offset of the header for the member containing the symbol.
824    #[inline]
825    pub fn offset(&self) -> ArchiveOffset {
826        self.offset
827    }
828}
829
830// Ignores bytes starting from the first space.
831fn parse_u64_digits(digits: &[u8], radix: u32) -> Option<u64> {
832    if let [b' ', ..] = digits {
833        return None;
834    }
835    let mut result: u64 = 0;
836    for &c in digits {
837        if c == b' ' {
838            return Some(result);
839        } else {
840            let x = (c as char).to_digit(radix)?;
841            result = result
842                .checked_mul(u64::from(radix))?
843                .checked_add(u64::from(x))?;
844        }
845    }
846    Some(result)
847}
848
849/// Digits are a decimal offset into the extended name table.
850/// Name is terminated by "/\n" (for GNU) or a null byte (for COFF).
851fn parse_sysv_extended_name<'data>(digits: &[u8], names: &'data [u8]) -> Result<&'data [u8], ()> {
852    let offset = parse_u64_digits(digits, 10).ok_or(())?;
853    let offset = offset.try_into().map_err(|_| ())?;
854    let name_data = names.get(offset..).ok_or(())?;
855    let len = memchr::memchr2(b'\n', b'\0', name_data).ok_or(())?;
856    if name_data[len] == b'\n' {
857        if len < 1 || name_data[len - 1] != b'/' {
858            Err(())
859        } else {
860            Ok(&name_data[..len - 1])
861        }
862    } else {
863        Ok(&name_data[..len])
864    }
865}
866
867/// Digits are a decimal length of the extended name, which is contained
868/// in `data` at `offset`.
869/// Modifies `offset` and `size` to start after the extended name.
870fn parse_bsd_extended_name<'data, R: ReadRef<'data>>(
871    digits: &[u8],
872    data: R,
873    offset: &mut u64,
874    size: &mut u64,
875) -> Result<&'data [u8], ()> {
876    let len = parse_u64_digits(digits, 10).ok_or(())?;
877    *size = size.checked_sub(len).ok_or(())?;
878    let name_data = data.read_bytes(offset, len)?;
879    let name = match memchr::memchr(b'\0', name_data) {
880        Some(len) => &name_data[..len],
881        None => name_data,
882    };
883    Ok(name)
884}
885
886#[cfg(test)]
887mod tests {
888    use super::*;
889
890    #[test]
891    fn kind() {
892        let data = b"!<arch>\n";
893        let archive = ArchiveFile::parse(&data[..]).unwrap();
894        assert_eq!(archive.kind(), ArchiveKind::Unknown);
895
896        let data = b"\
897            !<arch>\n\
898            /                                               4         `\n\
899            0000";
900        let archive = ArchiveFile::parse(&data[..]).unwrap();
901        assert_eq!(archive.kind(), ArchiveKind::Gnu);
902
903        let data = b"\
904            !<arch>\n\
905            //                                              4         `\n\
906            0000";
907        let archive = ArchiveFile::parse(&data[..]).unwrap();
908        assert_eq!(archive.kind(), ArchiveKind::Gnu);
909
910        let data = b"\
911            !<arch>\n\
912            /                                               4         `\n\
913            0000\
914            //                                              4         `\n\
915            0000";
916        let archive = ArchiveFile::parse(&data[..]).unwrap();
917        assert_eq!(archive.kind(), ArchiveKind::Gnu);
918
919        let data = b"\
920            !<arch>\n\
921            /SYM64/                                         4         `\n\
922            0000";
923        let archive = ArchiveFile::parse(&data[..]).unwrap();
924        assert_eq!(archive.kind(), ArchiveKind::Gnu64);
925
926        let data = b"\
927            !<arch>\n\
928            /SYM64/                                         4         `\n\
929            0000\
930            //                                              4         `\n\
931            0000";
932        let archive = ArchiveFile::parse(&data[..]).unwrap();
933        assert_eq!(archive.kind(), ArchiveKind::Gnu64);
934
935        let data = b"\
936            !<arch>\n\
937            __.SYMDEF                                       4         `\n\
938            0000";
939        let archive = ArchiveFile::parse(&data[..]).unwrap();
940        assert_eq!(archive.kind(), ArchiveKind::Bsd);
941
942        let data = b"\
943            !<arch>\n\
944            #1/9                                            13        `\n\
945            __.SYMDEF0000";
946        let archive = ArchiveFile::parse(&data[..]).unwrap();
947        assert_eq!(archive.kind(), ArchiveKind::Bsd);
948
949        let data = b"\
950            !<arch>\n\
951            #1/16                                           20        `\n\
952            __.SYMDEF SORTED0000";
953        let archive = ArchiveFile::parse(&data[..]).unwrap();
954        assert_eq!(archive.kind(), ArchiveKind::Bsd);
955
956        let data = b"\
957            !<arch>\n\
958            __.SYMDEF_64                                    4         `\n\
959            0000";
960        let archive = ArchiveFile::parse(&data[..]).unwrap();
961        assert_eq!(archive.kind(), ArchiveKind::Bsd64);
962
963        let data = b"\
964            !<arch>\n\
965            #1/12                                           16        `\n\
966            __.SYMDEF_640000";
967        let archive = ArchiveFile::parse(&data[..]).unwrap();
968        assert_eq!(archive.kind(), ArchiveKind::Bsd64);
969
970        let data = b"\
971            !<arch>\n\
972            #1/19                                           23        `\n\
973            __.SYMDEF_64 SORTED0000";
974        let archive = ArchiveFile::parse(&data[..]).unwrap();
975        assert_eq!(archive.kind(), ArchiveKind::Bsd64);
976
977        let data = b"\
978            !<arch>\n\
979            /                                               4         `\n\
980            0000\
981            /                                               4         `\n\
982            0000\
983            //                                              4         `\n\
984            0000";
985        let archive = ArchiveFile::parse(&data[..]).unwrap();
986        assert_eq!(archive.kind(), ArchiveKind::Coff);
987
988        let data = b"\
989            <bigaf>\n\
990            0                   0                   \
991            0                   0                   \
992            0                   128                 \
993            6                   0                   \
994            0                   \0\0\0\0\0\0\0\0\0\0\0\0\
995            \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
996            \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
997            \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
998        let archive = ArchiveFile::parse(&data[..]).unwrap();
999        assert_eq!(archive.kind(), ArchiveKind::AixBig);
1000
1001        let data = b"\
1002            !<thin>\n\
1003            /                                               4         `\n\
1004            0000";
1005        let archive = ArchiveFile::parse(&data[..]).unwrap();
1006        assert_eq!(archive.kind(), ArchiveKind::Gnu);
1007        assert!(archive.is_thin());
1008    }
1009
1010    #[test]
1011    fn gnu_names() {
1012        let data = b"\
1013            !<arch>\n\
1014            //                                              18        `\n\
1015            0123456789abcdef/\n\
1016            s p a c e/      0           0     0     644     4         `\n\
1017            0000\
1018            0123456789abcde/0           0     0     644     3         `\n\
1019            odd\n\
1020            /0              0           0     0     644     4         `\n\
1021            even";
1022        let data = &data[..];
1023        let archive = ArchiveFile::parse(data).unwrap();
1024        assert_eq!(archive.kind(), ArchiveKind::Gnu);
1025        let mut members = archive.members();
1026
1027        let member = members.next().unwrap().unwrap();
1028        assert_eq!(member.name(), b"s p a c e");
1029        assert_eq!(member.data(data).unwrap(), &b"0000"[..]);
1030
1031        let member = members.next().unwrap().unwrap();
1032        assert_eq!(member.name(), b"0123456789abcde");
1033        assert_eq!(member.data(data).unwrap(), &b"odd"[..]);
1034
1035        let member = members.next().unwrap().unwrap();
1036        assert_eq!(member.name(), b"0123456789abcdef");
1037        assert_eq!(member.data(data).unwrap(), &b"even"[..]);
1038
1039        assert!(members.next().is_none());
1040    }
1041
1042    #[test]
1043    fn thin_gnu_names() {
1044        let data = b"\
1045            !<thin>\n\
1046            //                                              18        `\n\
1047            0123456789/abcde/\n\
1048            s p a c e/      0           0     0     644     4         `\n\
1049            0123456789abcde/0           0     0     644     3         `\n\
1050            /0              0           0     0     644     4         `\n\
1051            ";
1052        let data = &data[..];
1053        let archive = ArchiveFile::parse(data).unwrap();
1054        assert_eq!(archive.kind(), ArchiveKind::Gnu);
1055        let mut members = archive.members();
1056
1057        let member = members.next().unwrap().unwrap();
1058        assert_eq!(member.name(), b"s p a c e");
1059        assert!(member.is_thin());
1060        assert_eq!(member.size(), 4);
1061        assert_eq!(member.data(data).unwrap(), &[]);
1062
1063        let member = members.next().unwrap().unwrap();
1064        assert_eq!(member.name(), b"0123456789abcde");
1065        assert!(member.is_thin());
1066        assert_eq!(member.size(), 3);
1067        assert_eq!(member.data(data).unwrap(), &[]);
1068
1069        let member = members.next().unwrap().unwrap();
1070        assert_eq!(member.name(), b"0123456789/abcde");
1071        assert!(member.is_thin());
1072        assert_eq!(member.size(), 4);
1073        assert_eq!(member.data(data).unwrap(), &[]);
1074
1075        assert!(members.next().is_none());
1076    }
1077
1078    #[test]
1079    fn bsd_names() {
1080        let data = b"\
1081            !<arch>\n\
1082            0123456789abcde 0           0     0     644     3         `\n\
1083            odd\n\
1084            #1/16           0           0     0     644     20        `\n\
1085            0123456789abcdefeven";
1086        let data = &data[..];
1087        let archive = ArchiveFile::parse(data).unwrap();
1088        assert_eq!(archive.kind(), ArchiveKind::Unknown);
1089        let mut members = archive.members();
1090
1091        let member = members.next().unwrap().unwrap();
1092        assert_eq!(member.name(), b"0123456789abcde");
1093        assert_eq!(member.data(data).unwrap(), &b"odd"[..]);
1094
1095        let member = members.next().unwrap().unwrap();
1096        assert_eq!(member.name(), b"0123456789abcdef");
1097        assert_eq!(member.data(data).unwrap(), &b"even"[..]);
1098
1099        assert!(members.next().is_none());
1100    }
1101
1102    #[test]
1103    fn aix_names() {
1104        let data = b"\
1105            <bigaf>\n\
1106            396                 0                   0                   \
1107            128                 262                 0                   \
1108            4                   262                 0                   \
1109            1662610370  223         1           644         16  \
1110            0123456789abcdef`\nord\n\
1111            4                   396                 128                 \
1112            1662610374  223         1           644         16  \
1113            fedcba9876543210`\nrev\n\
1114            94                  0                   262                 \
1115            0           0           0           0           0   \
1116            `\n2                   128                 \
1117            262                 0123456789abcdef\0fedcba9876543210\0";
1118        let data = &data[..];
1119        let archive = ArchiveFile::parse(data).unwrap();
1120        assert_eq!(archive.kind(), ArchiveKind::AixBig);
1121        let mut members = archive.members();
1122
1123        let member = members.next().unwrap().unwrap();
1124        assert_eq!(member.name(), b"0123456789abcdef");
1125        assert_eq!(member.data(data).unwrap(), &b"ord\n"[..]);
1126
1127        let member = members.next().unwrap().unwrap();
1128        assert_eq!(member.name(), b"fedcba9876543210");
1129        assert_eq!(member.data(data).unwrap(), &b"rev\n"[..]);
1130
1131        assert!(members.next().is_none());
1132    }
1133}