Coverage Report

Created: 2026-03-26 07:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/object-0.37.3/src/read/archive.rs
Line
Count
Source
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
23
use core::convert::TryInto;
24
use core::slice;
25
26
use crate::archive;
27
use crate::endian::{BigEndian as BE, LittleEndian as LE, U16Bytes, U32Bytes, U64Bytes};
28
use 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]
33
pub 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)]
54
enum 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)]
66
pub 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
75
impl<'data, R: ReadRef<'data>> ArchiveFile<'data, R> {
76
    /// Parse the archive header and special members.
77
0
    pub fn parse(data: R) -> read::Result<Self> {
78
0
        let len = data.len().read_error("Unknown archive length")?;
79
0
        let mut tail = 0;
80
0
        let magic = data
81
0
            .read_bytes(&mut tail, archive::MAGIC.len() as u64)
82
0
            .read_error("Invalid archive size")?;
83
84
0
        let thin = if magic == archive::AIX_BIG_MAGIC {
85
0
            return Self::parse_aixbig(data);
86
0
        } else if magic == archive::THIN_MAGIC {
87
0
            true
88
0
        } else if magic == archive::MAGIC {
89
0
            false
90
        } else {
91
0
            return Err(Error("Unsupported archive identifier"));
92
        };
93
94
0
        let mut members_offset = tail;
95
0
        let members_end_offset = len;
96
97
0
        let mut file = ArchiveFile {
98
0
            data,
99
0
            kind: ArchiveKind::Unknown,
100
0
            members: Members::Common {
101
0
                offset: 0,
102
0
                end_offset: 0,
103
0
            },
104
0
            symbols: (0, 0),
105
0
            names: &[],
106
0
            thin,
107
0
        };
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
0
        if tail < len {
124
0
            let member = ArchiveMember::parse(data, &mut tail, &[], thin)?;
125
0
            if member.name == b"/" {
126
                // GNU symbol table (unless we later determine this is COFF).
127
0
                file.kind = ArchiveKind::Gnu;
128
0
                file.symbols = member.file_range();
129
0
                members_offset = tail;
130
131
0
                if tail < len {
132
0
                    let member = ArchiveMember::parse(data, &mut tail, &[], thin)?;
133
0
                    if member.name == b"/" {
134
                        // COFF linker member.
135
0
                        file.kind = ArchiveKind::Coff;
136
0
                        file.symbols = member.file_range();
137
0
                        members_offset = tail;
138
139
0
                        if tail < len {
140
0
                            let member = ArchiveMember::parse(data, &mut tail, &[], thin)?;
141
0
                            if member.name == b"//" {
142
                                // COFF names table.
143
0
                                file.names = member.data(data)?;
144
0
                                members_offset = tail;
145
0
                            }
146
0
                        }
147
0
                        if tail < len {
148
0
                            let member = ArchiveMember::parse(data, &mut tail, file.names, thin)?;
149
0
                            if member.name == b"/<ECSYMBOLS>/" {
150
0
                                // COFF EC Symbol Table.
151
0
                                members_offset = tail;
152
0
                            }
153
0
                        }
154
0
                    } else if member.name == b"//" {
155
                        // GNU names table.
156
0
                        file.names = member.data(data)?;
157
0
                        members_offset = tail;
158
0
                    }
159
0
                }
160
0
            } else if member.name == b"/SYM64/" {
161
                // GNU 64-bit symbol table.
162
0
                file.kind = ArchiveKind::Gnu64;
163
0
                file.symbols = member.file_range();
164
0
                members_offset = tail;
165
166
0
                if tail < len {
167
0
                    let member = ArchiveMember::parse(data, &mut tail, &[], thin)?;
168
0
                    if member.name == b"//" {
169
                        // GNU names table.
170
0
                        file.names = member.data(data)?;
171
0
                        members_offset = tail;
172
0
                    }
173
0
                }
174
0
            } else if member.name == b"//" {
175
                // GNU names table.
176
0
                file.kind = ArchiveKind::Gnu;
177
0
                file.names = member.data(data)?;
178
0
                members_offset = tail;
179
0
            } else if member.name == b"__.SYMDEF" || member.name == b"__.SYMDEF SORTED" {
180
0
                // BSD symbol table.
181
0
                file.kind = ArchiveKind::Bsd;
182
0
                file.symbols = member.file_range();
183
0
                members_offset = tail;
184
0
            } else if member.name == b"__.SYMDEF_64" || member.name == b"__.SYMDEF_64 SORTED" {
185
0
                // BSD 64-bit symbol table.
186
0
                file.kind = ArchiveKind::Bsd64;
187
0
                file.symbols = member.file_range();
188
0
                members_offset = tail;
189
0
            } else {
190
0
                // TODO: This could still be a BSD file. We leave this as unknown for now.
191
0
            }
192
0
        }
193
0
        file.members = Members::Common {
194
0
            offset: members_offset,
195
0
            end_offset: members_end_offset,
196
0
        };
197
0
        Ok(file)
198
0
    }
199
200
0
    fn parse_aixbig(data: R) -> read::Result<Self> {
201
0
        let mut tail = 0;
202
203
0
        let file_header = data
204
0
            .read::<archive::AixFileHeader>(&mut tail)
205
0
            .read_error("Invalid AIX big archive file header")?;
206
        // Caller already validated this.
207
0
        debug_assert_eq!(file_header.magic, archive::AIX_BIG_MAGIC);
208
209
0
        let mut file = ArchiveFile {
210
0
            data,
211
0
            kind: ArchiveKind::AixBig,
212
0
            members: Members::AixBig { index: &[] },
213
0
            symbols: (0, 0),
214
0
            names: &[],
215
0
            thin: false,
216
0
        };
217
218
        // Read the span of symbol table.
219
        // TODO: an archive may have both 32-bit and 64-bit symbol tables.
220
0
        let symtbl64 = parse_u64_digits(&file_header.gst64off, 10)
221
0
            .read_error("Invalid offset to 64-bit symbol table in AIX big archive")?;
222
0
        if symtbl64 > 0 {
223
            // The symbol table is also a file with header.
224
0
            let member = ArchiveMember::parse_aixbig(data, symtbl64)?;
225
0
            file.symbols = member.file_range();
226
        } else {
227
0
            let symtbl = parse_u64_digits(&file_header.gstoff, 10)
228
0
                .read_error("Invalid offset to symbol table in AIX big archive")?;
229
0
            if symtbl > 0 {
230
                // The symbol table is also a file with header.
231
0
                let member = ArchiveMember::parse_aixbig(data, symtbl)?;
232
0
                file.symbols = member.file_range();
233
0
            }
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
0
        let member_table_offset = parse_u64_digits(&file_header.memoff, 10)
240
0
            .read_error("Invalid offset for member table of AIX big archive")?;
241
0
        if member_table_offset == 0 {
242
            // The offset would be zero if archive contains no file.
243
0
            return Ok(file);
244
0
        }
245
246
        // The member index table is also a file with header.
247
0
        let member = ArchiveMember::parse_aixbig(data, member_table_offset)?;
248
0
        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
0
        let members_count_bytes = member_data
255
0
            .read_slice::<u8>(20)
256
0
            .read_error("Missing member count in AIX big archive")?;
257
0
        let members_count = parse_u64_digits(members_count_bytes, 10)
258
0
            .and_then(|size| size.try_into().ok())
259
0
            .read_error("Invalid member count in AIX big archive")?;
260
0
        let index = member_data
261
0
            .read_slice::<archive::AixMemberOffset>(members_count)
262
0
            .read_error("Member count overflow in AIX big archive")?;
263
0
        file.members = Members::AixBig { index };
264
265
0
        Ok(file)
266
0
    }
267
268
    /// Return the archive format.
269
    #[inline]
270
0
    pub fn kind(&self) -> ArchiveKind {
271
0
        self.kind
272
0
    }
273
274
    /// Return true if the archive is a thin archive.
275
0
    pub fn is_thin(&self) -> bool {
276
0
        self.thin
277
0
    }
278
279
    /// Iterate over the members of the archive.
280
    ///
281
    /// This does not return special members.
282
    #[inline]
283
0
    pub fn members(&self) -> ArchiveMemberIterator<'data, R> {
284
0
        ArchiveMemberIterator {
285
0
            data: self.data,
286
0
            members: self.members,
287
0
            names: self.names,
288
0
            thin: self.thin,
289
0
        }
290
0
    }
291
292
    /// Return the member at the given offset.
293
0
    pub fn member(&self, member: ArchiveOffset) -> read::Result<ArchiveMember<'data>> {
294
0
        match self.members {
295
0
            Members::Common { offset, end_offset } => {
296
0
                if member.0 < offset || member.0 >= end_offset {
297
0
                    return Err(Error("Invalid archive member offset"));
298
0
                }
299
0
                let mut offset = member.0;
300
0
                ArchiveMember::parse(self.data, &mut offset, self.names, self.thin)
301
            }
302
            Members::AixBig { .. } => {
303
0
                let offset = member.0;
304
0
                ArchiveMember::parse_aixbig(self.data, offset)
305
            }
306
        }
307
0
    }
308
309
    /// Iterate over the symbols in the archive.
310
0
    pub fn symbols(&self) -> read::Result<Option<ArchiveSymbolIterator<'data>>> {
311
0
        if self.symbols == (0, 0) {
312
0
            return Ok(None);
313
0
        }
314
0
        let (offset, size) = self.symbols;
315
0
        ArchiveSymbolIterator::new(self.kind, self.data, offset, size)
316
0
            .read_error("Invalid archive symbol table")
317
0
            .map(Some)
318
0
    }
319
}
320
321
/// An iterator over the members of an archive.
322
#[derive(Debug)]
323
pub struct ArchiveMemberIterator<'data, R: ReadRef<'data> = &'data [u8]> {
324
    data: R,
325
    members: Members<'data>,
326
    names: &'data [u8],
327
    thin: bool,
328
}
329
330
impl<'data, R: ReadRef<'data>> Iterator for ArchiveMemberIterator<'data, R> {
331
    type Item = read::Result<ArchiveMember<'data>>;
332
333
0
    fn next(&mut self) -> Option<Self::Item> {
334
0
        match &mut self.members {
335
            Members::Common {
336
0
                ref mut offset,
337
0
                ref mut end_offset,
338
            } => {
339
0
                if *offset >= *end_offset {
340
0
                    return None;
341
0
                }
342
0
                let member = ArchiveMember::parse(self.data, offset, self.names, self.thin);
343
0
                if member.is_err() {
344
0
                    *offset = *end_offset;
345
0
                }
346
0
                Some(member)
347
            }
348
0
            Members::AixBig { ref mut index } => match **index {
349
0
                [] => None,
350
0
                [ref first, ref rest @ ..] => {
351
0
                    *index = rest;
352
0
                    let member = ArchiveMember::parse_aixbig_index(self.data, first);
353
0
                    if member.is_err() {
354
0
                        *index = &[];
355
0
                    }
356
0
                    Some(member)
357
                }
358
            },
359
        }
360
0
    }
361
}
362
363
/// An archive member header.
364
#[derive(Debug, Clone, Copy)]
365
enum 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)]
374
pub 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
382
impl<'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
0
    fn parse<R: ReadRef<'data>>(
387
0
        data: R,
388
0
        offset: &mut u64,
389
0
        names: &'data [u8],
390
0
        thin: bool,
391
0
    ) -> read::Result<Self> {
392
0
        let header = data
393
0
            .read::<archive::Header>(offset)
394
0
            .read_error("Invalid archive member header")?;
395
0
        if header.terminator != archive::TERMINATOR {
396
0
            return Err(Error("Invalid archive terminator"));
397
0
        }
398
399
0
        let header_file_size =
400
0
            parse_u64_digits(&header.size, 10).read_error("Invalid archive member size")?;
401
0
        let mut file_offset = *offset;
402
0
        let mut file_size = header_file_size;
403
404
0
        let name = if header.name[0] == b'/' && (header.name[1] as char).is_ascii_digit() {
405
            // Read file name from the names table.
406
0
            parse_sysv_extended_name(&header.name[1..], names)
407
0
                .read_error("Invalid archive extended name offset")?
408
0
        } 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
0
            parse_bsd_extended_name(&header.name[3..], data, &mut file_offset, &mut file_size)
411
0
                .read_error("Invalid archive extended name length")?
412
0
        } else if header.name[0] == b'/' {
413
0
            let name_len = memchr::memchr(b' ', &header.name).unwrap_or(header.name.len());
414
0
            &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
0
            let name_len = memchr::memchr(b'/', &header.name)
420
0
                .or_else(|| memchr::memchr(b' ', &header.name))
421
0
                .unwrap_or(header.name.len());
422
0
            &header.name[..name_len]
423
        };
424
425
        // Members in thin archives don't have data unless they are special members.
426
0
        if thin && name != b"/" && name != b"//" && name != b"/SYM64/" {
427
0
            return Ok(ArchiveMember {
428
0
                header: MemberHeader::Common(header),
429
0
                name,
430
0
                offset: 0,
431
0
                size: file_size,
432
0
            });
433
0
        }
434
435
        // Skip the file data.
436
0
        *offset = offset
437
0
            .checked_add(header_file_size)
438
0
            .read_error("Archive member size is too large")?;
439
        // Entries are padded to an even number of bytes.
440
0
        if (header_file_size & 1) != 0 {
441
0
            *offset = offset.saturating_add(1);
442
0
        }
443
444
0
        Ok(ArchiveMember {
445
0
            header: MemberHeader::Common(header),
446
0
            name,
447
0
            offset: file_offset,
448
0
            size: file_size,
449
0
        })
450
0
    }
451
452
    /// Parse a member index entry in an AIX big archive,
453
    /// and then parse the member header, name, and file data.
454
0
    fn parse_aixbig_index<R: ReadRef<'data>>(
455
0
        data: R,
456
0
        index: &archive::AixMemberOffset,
457
0
    ) -> read::Result<Self> {
458
0
        let offset = parse_u64_digits(&index.0, 10)
459
0
            .read_error("Invalid AIX big archive file member offset")?;
460
0
        Self::parse_aixbig(data, offset)
461
0
    }
462
463
    /// Parse the member header, name, and file data in an AIX big archive.
464
0
    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
0
        let header = data
468
0
            .read::<archive::AixHeader>(&mut offset)
469
0
            .read_error("Invalid AIX big archive member header")?;
470
0
        let name_length = parse_u64_digits(&header.namlen, 10)
471
0
            .read_error("Invalid AIX big archive member name length")?;
472
0
        let name = data
473
0
            .read_bytes(&mut offset, name_length)
474
0
            .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
0
        if offset & 1 != 0 {
480
0
            offset = offset.saturating_add(1);
481
0
        }
482
        // Because of the even-byte boundary, we have to read and check terminator after header.
483
0
        let terminator = data
484
0
            .read_bytes(&mut offset, 2)
485
0
            .read_error("Invalid AIX big archive terminator")?;
486
0
        if terminator != archive::TERMINATOR {
487
0
            return Err(Error("Invalid AIX big archive terminator"));
488
0
        }
489
490
0
        let size = parse_u64_digits(&header.size, 10)
491
0
            .read_error("Invalid archive member size in AIX big archive")?;
492
0
        Ok(ArchiveMember {
493
0
            header: MemberHeader::AixBig(header),
494
0
            name,
495
0
            offset,
496
0
            size,
497
0
        })
498
0
    }
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
0
    pub fn header(&self) -> Option<&'data archive::Header> {
505
0
        match self.header {
506
0
            MemberHeader::Common(header) => Some(header),
507
0
            _ => None,
508
        }
509
0
    }
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
0
    pub fn aix_header(&self) -> Option<&'data archive::AixHeader> {
516
0
        match self.header {
517
0
            MemberHeader::AixBig(header) => Some(header),
518
0
            _ => None,
519
        }
520
0
    }
521
522
    /// Return the parsed file name.
523
    ///
524
    /// This may be an extended file name.
525
    #[inline]
526
0
    pub fn name(&self) -> &'data [u8] {
527
0
        self.name
528
0
    }
529
530
    /// Parse the file modification timestamp from the header.
531
    #[inline]
532
0
    pub fn date(&self) -> Option<u64> {
533
0
        match &self.header {
534
0
            MemberHeader::Common(header) => parse_u64_digits(&header.date, 10),
535
0
            MemberHeader::AixBig(header) => parse_u64_digits(&header.date, 10),
536
        }
537
0
    }
538
539
    /// Parse the user ID from the header.
540
    #[inline]
541
0
    pub fn uid(&self) -> Option<u64> {
542
0
        match &self.header {
543
0
            MemberHeader::Common(header) => parse_u64_digits(&header.uid, 10),
544
0
            MemberHeader::AixBig(header) => parse_u64_digits(&header.uid, 10),
545
        }
546
0
    }
547
548
    /// Parse the group ID from the header.
549
    #[inline]
550
0
    pub fn gid(&self) -> Option<u64> {
551
0
        match &self.header {
552
0
            MemberHeader::Common(header) => parse_u64_digits(&header.gid, 10),
553
0
            MemberHeader::AixBig(header) => parse_u64_digits(&header.gid, 10),
554
        }
555
0
    }
556
557
    /// Parse the file mode from the header.
558
    #[inline]
559
0
    pub fn mode(&self) -> Option<u64> {
560
0
        match &self.header {
561
0
            MemberHeader::Common(header) => parse_u64_digits(&header.mode, 8),
562
0
            MemberHeader::AixBig(header) => parse_u64_digits(&header.mode, 8),
563
        }
564
0
    }
565
566
    /// Return the size of the file data.
567
0
    pub fn size(&self) -> u64 {
568
0
        self.size
569
0
    }
570
571
    /// Return the offset and size of the file data.
572
0
    pub fn file_range(&self) -> (u64, u64) {
573
0
        (self.offset, self.size)
574
0
    }
575
576
    /// Return true if the member is a thin member.
577
    ///
578
    /// Thin members have no file data.
579
0
    pub fn is_thin(&self) -> bool {
580
0
        self.offset == 0
581
0
    }
582
583
    /// Return the file data.
584
    ///
585
    /// This is an empty slice for thin members.
586
    #[inline]
587
0
    pub fn data<R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [u8]> {
588
0
        if self.is_thin() {
589
0
            return Ok(&[]);
590
0
        }
591
0
        data.read_bytes_at(self.offset, self.size)
592
0
            .read_error("Archive member size is too large")
593
0
    }
594
}
595
596
/// An offset of a member in an archive.
597
#[derive(Debug, Clone, Copy)]
598
pub struct ArchiveOffset(pub u64);
599
600
/// An iterator over the symbols in the archive symbol table.
601
#[derive(Debug, Clone)]
602
pub struct ArchiveSymbolIterator<'data>(SymbolIteratorInternal<'data>);
603
604
#[derive(Debug, Clone)]
605
enum 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
667
impl<'data> ArchiveSymbolIterator<'data> {
668
0
    fn new<R: ReadRef<'data>>(
669
0
        kind: ArchiveKind,
670
0
        data: R,
671
0
        offset: u64,
672
0
        size: u64,
673
0
    ) -> Result<Self, ()> {
674
0
        let mut data = data.read_bytes_at(offset, size).map(Bytes)?;
675
0
        match kind {
676
0
            ArchiveKind::Unknown => Ok(ArchiveSymbolIterator(SymbolIteratorInternal::None)),
677
            ArchiveKind::Gnu => {
678
0
                let offsets_count = data.read::<U32Bytes<BE>>()?.get(BE);
679
0
                let offsets = data.read_slice::<U32Bytes<BE>>(offsets_count as usize)?;
680
0
                Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Gnu {
681
0
                    offsets: offsets.iter(),
682
0
                    names: data,
683
0
                }))
684
            }
685
            ArchiveKind::Gnu64 => {
686
0
                let offsets_count = data.read::<U64Bytes<BE>>()?.get(BE);
687
0
                let offsets = data.read_slice::<U64Bytes<BE>>(offsets_count as usize)?;
688
0
                Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Gnu64 {
689
0
                    offsets: offsets.iter(),
690
0
                    names: data,
691
0
                }))
692
            }
693
            ArchiveKind::Bsd => {
694
0
                let offsets_size = data.read::<U32Bytes<LE>>()?.get(LE);
695
0
                let offsets = data.read_slice::<[U32Bytes<LE>; 2]>(offsets_size as usize / 8)?;
696
0
                let names_size = data.read::<U32Bytes<LE>>()?.get(LE);
697
0
                let names = data.read_bytes(names_size as usize)?;
698
0
                Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Bsd {
699
0
                    offsets: offsets.iter(),
700
0
                    names,
701
0
                }))
702
            }
703
            ArchiveKind::Bsd64 => {
704
0
                let offsets_size = data.read::<U64Bytes<LE>>()?.get(LE);
705
0
                let offsets = data.read_slice::<[U64Bytes<LE>; 2]>(offsets_size as usize / 16)?;
706
0
                let names_size = data.read::<U64Bytes<LE>>()?.get(LE);
707
0
                let names = data.read_bytes(names_size as usize)?;
708
0
                Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Bsd64 {
709
0
                    offsets: offsets.iter(),
710
0
                    names,
711
0
                }))
712
            }
713
            ArchiveKind::Coff => {
714
0
                let members_count = data.read::<U32Bytes<LE>>()?.get(LE);
715
0
                let members = data.read_slice::<U32Bytes<LE>>(members_count as usize)?;
716
0
                let indices_count = data.read::<U32Bytes<LE>>()?.get(LE);
717
0
                let indices = data.read_slice::<U16Bytes<LE>>(indices_count as usize)?;
718
0
                Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Coff {
719
0
                    members,
720
0
                    indices: indices.iter(),
721
0
                    names: data,
722
0
                }))
723
            }
724
            // TODO: Implement AIX big archive symbol table.
725
0
            ArchiveKind::AixBig => Ok(ArchiveSymbolIterator(SymbolIteratorInternal::None)),
726
        }
727
0
    }
728
}
729
730
impl<'data> Iterator for ArchiveSymbolIterator<'data> {
731
    type Item = read::Result<ArchiveSymbol<'data>>;
732
733
0
    fn next(&mut self) -> Option<Self::Item> {
734
0
        match &mut self.0 {
735
0
            SymbolIteratorInternal::None => None,
736
0
            SymbolIteratorInternal::Gnu { offsets, names } => {
737
0
                let offset = offsets.next()?.get(BE);
738
                Some(
739
0
                    names
740
0
                        .read_string()
741
0
                        .read_error("Missing archive symbol name")
742
0
                        .map(|name| ArchiveSymbol {
743
0
                            name,
744
0
                            offset: ArchiveOffset(offset.into()),
745
0
                        }),
746
                )
747
            }
748
0
            SymbolIteratorInternal::Gnu64 { offsets, names } => {
749
0
                let offset = offsets.next()?.get(BE);
750
                Some(
751
0
                    names
752
0
                        .read_string()
753
0
                        .read_error("Missing archive symbol name")
754
0
                        .map(|name| ArchiveSymbol {
755
0
                            name,
756
0
                            offset: ArchiveOffset(offset),
757
0
                        }),
758
                )
759
            }
760
0
            SymbolIteratorInternal::Bsd { offsets, names } => {
761
0
                let entry = offsets.next()?;
762
                Some(
763
0
                    names
764
0
                        .read_string_at(entry[0].get(LE) as usize)
765
0
                        .read_error("Invalid archive symbol name offset")
766
0
                        .map(|name| ArchiveSymbol {
767
0
                            name,
768
0
                            offset: ArchiveOffset(entry[1].get(LE).into()),
769
0
                        }),
770
                )
771
            }
772
0
            SymbolIteratorInternal::Bsd64 { offsets, names } => {
773
0
                let entry = offsets.next()?;
774
                Some(
775
0
                    names
776
0
                        .read_string_at(entry[0].get(LE) as usize)
777
0
                        .read_error("Invalid archive symbol name offset")
778
0
                        .map(|name| ArchiveSymbol {
779
0
                            name,
780
0
                            offset: ArchiveOffset(entry[1].get(LE)),
781
0
                        }),
782
                )
783
            }
784
            SymbolIteratorInternal::Coff {
785
0
                members,
786
0
                indices,
787
0
                names,
788
            } => {
789
0
                let index = indices.next()?.get(LE).wrapping_sub(1);
790
0
                let member = members
791
0
                    .get(index as usize)
792
0
                    .read_error("Invalid archive symbol member index");
793
0
                let name = names
794
0
                    .read_string()
795
0
                    .read_error("Missing archive symbol name");
796
0
                Some(member.and_then(|member| {
797
0
                    name.map(|name| ArchiveSymbol {
798
0
                        name,
799
0
                        offset: ArchiveOffset(member.get(LE).into()),
800
0
                    })
801
0
                }))
802
            }
803
        }
804
0
    }
805
806
0
    fn size_hint(&self) -> (usize, Option<usize>) {
807
0
        match &self.0 {
808
0
            SymbolIteratorInternal::None => (0, None),
809
0
            SymbolIteratorInternal::Gnu { offsets, .. } => offsets.size_hint(),
810
0
            SymbolIteratorInternal::Gnu64 { offsets, .. } => offsets.size_hint(),
811
0
            SymbolIteratorInternal::Bsd { offsets, .. } => offsets.size_hint(),
812
0
            SymbolIteratorInternal::Bsd64 { offsets, .. } => offsets.size_hint(),
813
0
            SymbolIteratorInternal::Coff { indices, .. } => {
814
                // The `slice::Iter` is in the indices field for this variant
815
0
                indices.size_hint()
816
            }
817
        }
818
0
    }
819
}
820
821
/// A symbol in the archive symbol table.
822
///
823
/// This is used to find the member containing the symbol.
824
#[derive(Debug, Clone, Copy)]
825
pub struct ArchiveSymbol<'data> {
826
    name: &'data [u8],
827
    offset: ArchiveOffset,
828
}
829
830
impl<'data> ArchiveSymbol<'data> {
831
    /// Return the symbol name.
832
    #[inline]
833
0
    pub fn name(&self) -> &'data [u8] {
834
0
        self.name
835
0
    }
836
837
    /// Return the offset of the header for the member containing the symbol.
838
    #[inline]
839
0
    pub fn offset(&self) -> ArchiveOffset {
840
0
        self.offset
841
0
    }
842
}
843
844
// Ignores bytes starting from the first space.
845
0
fn parse_u64_digits(digits: &[u8], radix: u32) -> Option<u64> {
846
0
    if let [b' ', ..] = digits {
847
0
        return None;
848
0
    }
849
0
    let mut result: u64 = 0;
850
0
    for &c in digits {
851
0
        if c == b' ' {
852
0
            return Some(result);
853
        } else {
854
0
            let x = (c as char).to_digit(radix)?;
855
0
            result = result
856
0
                .checked_mul(u64::from(radix))?
857
0
                .checked_add(u64::from(x))?;
858
        }
859
    }
860
0
    Some(result)
861
0
}
862
863
/// Digits are a decimal offset into the extended name table.
864
/// Name is terminated by "/\n" (for GNU) or a null byte (for COFF).
865
0
fn parse_sysv_extended_name<'data>(digits: &[u8], names: &'data [u8]) -> Result<&'data [u8], ()> {
866
0
    let offset = parse_u64_digits(digits, 10).ok_or(())?;
867
0
    let offset = offset.try_into().map_err(|_| ())?;
868
0
    let name_data = names.get(offset..).ok_or(())?;
869
0
    let len = memchr::memchr2(b'\n', b'\0', name_data).ok_or(())?;
870
0
    if name_data[len] == b'\n' {
871
0
        if len < 1 || name_data[len - 1] != b'/' {
872
0
            Err(())
873
        } else {
874
0
            Ok(&name_data[..len - 1])
875
        }
876
    } else {
877
0
        Ok(&name_data[..len])
878
    }
879
0
}
880
881
/// Digits are a decimal length of the extended name, which is contained
882
/// in `data` at `offset`.
883
/// Modifies `offset` and `size` to start after the extended name.
884
0
fn parse_bsd_extended_name<'data, R: ReadRef<'data>>(
885
0
    digits: &[u8],
886
0
    data: R,
887
0
    offset: &mut u64,
888
0
    size: &mut u64,
889
0
) -> Result<&'data [u8], ()> {
890
0
    let len = parse_u64_digits(digits, 10).ok_or(())?;
891
0
    *size = size.checked_sub(len).ok_or(())?;
892
0
    let name_data = data.read_bytes(offset, len)?;
893
0
    let name = match memchr::memchr(b'\0', name_data) {
894
0
        Some(len) => &name_data[..len],
895
0
        None => name_data,
896
    };
897
0
    Ok(name)
898
0
}
899
900
#[cfg(test)]
901
mod tests {
902
    use super::*;
903
904
    #[test]
905
    fn kind() {
906
        let data = b"!<arch>\n";
907
        let archive = ArchiveFile::parse(&data[..]).unwrap();
908
        assert_eq!(archive.kind(), ArchiveKind::Unknown);
909
910
        let data = b"\
911
            !<arch>\n\
912
            /                                               4         `\n\
913
            0000";
914
        let archive = ArchiveFile::parse(&data[..]).unwrap();
915
        assert_eq!(archive.kind(), ArchiveKind::Gnu);
916
917
        let data = b"\
918
            !<arch>\n\
919
            //                                              4         `\n\
920
            0000";
921
        let archive = ArchiveFile::parse(&data[..]).unwrap();
922
        assert_eq!(archive.kind(), ArchiveKind::Gnu);
923
924
        let data = b"\
925
            !<arch>\n\
926
            /                                               4         `\n\
927
            0000\
928
            //                                              4         `\n\
929
            0000";
930
        let archive = ArchiveFile::parse(&data[..]).unwrap();
931
        assert_eq!(archive.kind(), ArchiveKind::Gnu);
932
933
        let data = b"\
934
            !<arch>\n\
935
            /SYM64/                                         4         `\n\
936
            0000";
937
        let archive = ArchiveFile::parse(&data[..]).unwrap();
938
        assert_eq!(archive.kind(), ArchiveKind::Gnu64);
939
940
        let data = b"\
941
            !<arch>\n\
942
            /SYM64/                                         4         `\n\
943
            0000\
944
            //                                              4         `\n\
945
            0000";
946
        let archive = ArchiveFile::parse(&data[..]).unwrap();
947
        assert_eq!(archive.kind(), ArchiveKind::Gnu64);
948
949
        let data = b"\
950
            !<arch>\n\
951
            __.SYMDEF                                       4         `\n\
952
            0000";
953
        let archive = ArchiveFile::parse(&data[..]).unwrap();
954
        assert_eq!(archive.kind(), ArchiveKind::Bsd);
955
956
        let data = b"\
957
            !<arch>\n\
958
            #1/9                                            13        `\n\
959
            __.SYMDEF0000";
960
        let archive = ArchiveFile::parse(&data[..]).unwrap();
961
        assert_eq!(archive.kind(), ArchiveKind::Bsd);
962
963
        let data = b"\
964
            !<arch>\n\
965
            #1/16                                           20        `\n\
966
            __.SYMDEF SORTED0000";
967
        let archive = ArchiveFile::parse(&data[..]).unwrap();
968
        assert_eq!(archive.kind(), ArchiveKind::Bsd);
969
970
        let data = b"\
971
            !<arch>\n\
972
            __.SYMDEF_64                                    4         `\n\
973
            0000";
974
        let archive = ArchiveFile::parse(&data[..]).unwrap();
975
        assert_eq!(archive.kind(), ArchiveKind::Bsd64);
976
977
        let data = b"\
978
            !<arch>\n\
979
            #1/12                                           16        `\n\
980
            __.SYMDEF_640000";
981
        let archive = ArchiveFile::parse(&data[..]).unwrap();
982
        assert_eq!(archive.kind(), ArchiveKind::Bsd64);
983
984
        let data = b"\
985
            !<arch>\n\
986
            #1/19                                           23        `\n\
987
            __.SYMDEF_64 SORTED0000";
988
        let archive = ArchiveFile::parse(&data[..]).unwrap();
989
        assert_eq!(archive.kind(), ArchiveKind::Bsd64);
990
991
        let data = b"\
992
            !<arch>\n\
993
            /                                               4         `\n\
994
            0000\
995
            /                                               4         `\n\
996
            0000\
997
            //                                              4         `\n\
998
            0000";
999
        let archive = ArchiveFile::parse(&data[..]).unwrap();
1000
        assert_eq!(archive.kind(), ArchiveKind::Coff);
1001
1002
        let data = b"\
1003
            <bigaf>\n\
1004
            0                   0                   \
1005
            0                   0                   \
1006
            0                   128                 \
1007
            6                   0                   \
1008
            0                   \0\0\0\0\0\0\0\0\0\0\0\0\
1009
            \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
1010
            \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
1011
            \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
1012
        let archive = ArchiveFile::parse(&data[..]).unwrap();
1013
        assert_eq!(archive.kind(), ArchiveKind::AixBig);
1014
1015
        let data = b"\
1016
            !<thin>\n\
1017
            /                                               4         `\n\
1018
            0000";
1019
        let archive = ArchiveFile::parse(&data[..]).unwrap();
1020
        assert_eq!(archive.kind(), ArchiveKind::Gnu);
1021
        assert!(archive.is_thin());
1022
    }
1023
1024
    #[test]
1025
    fn gnu_names() {
1026
        let data = b"\
1027
            !<arch>\n\
1028
            //                                              18        `\n\
1029
            0123456789abcdef/\n\
1030
            s p a c e/      0           0     0     644     4         `\n\
1031
            0000\
1032
            0123456789abcde/0           0     0     644     3         `\n\
1033
            odd\n\
1034
            /0              0           0     0     644     4         `\n\
1035
            even";
1036
        let data = &data[..];
1037
        let archive = ArchiveFile::parse(data).unwrap();
1038
        assert_eq!(archive.kind(), ArchiveKind::Gnu);
1039
        let mut members = archive.members();
1040
1041
        let member = members.next().unwrap().unwrap();
1042
        assert_eq!(member.name(), b"s p a c e");
1043
        assert_eq!(member.data(data).unwrap(), &b"0000"[..]);
1044
1045
        let member = members.next().unwrap().unwrap();
1046
        assert_eq!(member.name(), b"0123456789abcde");
1047
        assert_eq!(member.data(data).unwrap(), &b"odd"[..]);
1048
1049
        let member = members.next().unwrap().unwrap();
1050
        assert_eq!(member.name(), b"0123456789abcdef");
1051
        assert_eq!(member.data(data).unwrap(), &b"even"[..]);
1052
1053
        assert!(members.next().is_none());
1054
    }
1055
1056
    #[test]
1057
    fn thin_gnu_names() {
1058
        let data = b"\
1059
            !<thin>\n\
1060
            //                                              18        `\n\
1061
            0123456789/abcde/\n\
1062
            s p a c e/      0           0     0     644     4         `\n\
1063
            0123456789abcde/0           0     0     644     3         `\n\
1064
            /0              0           0     0     644     4         `\n\
1065
            ";
1066
        let data = &data[..];
1067
        let archive = ArchiveFile::parse(data).unwrap();
1068
        assert_eq!(archive.kind(), ArchiveKind::Gnu);
1069
        let mut members = archive.members();
1070
1071
        let member = members.next().unwrap().unwrap();
1072
        assert_eq!(member.name(), b"s p a c e");
1073
        assert!(member.is_thin());
1074
        assert_eq!(member.size(), 4);
1075
        assert_eq!(member.data(data).unwrap(), &[]);
1076
1077
        let member = members.next().unwrap().unwrap();
1078
        assert_eq!(member.name(), b"0123456789abcde");
1079
        assert!(member.is_thin());
1080
        assert_eq!(member.size(), 3);
1081
        assert_eq!(member.data(data).unwrap(), &[]);
1082
1083
        let member = members.next().unwrap().unwrap();
1084
        assert_eq!(member.name(), b"0123456789/abcde");
1085
        assert!(member.is_thin());
1086
        assert_eq!(member.size(), 4);
1087
        assert_eq!(member.data(data).unwrap(), &[]);
1088
1089
        assert!(members.next().is_none());
1090
    }
1091
1092
    #[test]
1093
    fn bsd_names() {
1094
        let data = b"\
1095
            !<arch>\n\
1096
            0123456789abcde 0           0     0     644     3         `\n\
1097
            odd\n\
1098
            #1/16           0           0     0     644     20        `\n\
1099
            0123456789abcdefeven";
1100
        let data = &data[..];
1101
        let archive = ArchiveFile::parse(data).unwrap();
1102
        assert_eq!(archive.kind(), ArchiveKind::Unknown);
1103
        let mut members = archive.members();
1104
1105
        let member = members.next().unwrap().unwrap();
1106
        assert_eq!(member.name(), b"0123456789abcde");
1107
        assert_eq!(member.data(data).unwrap(), &b"odd"[..]);
1108
1109
        let member = members.next().unwrap().unwrap();
1110
        assert_eq!(member.name(), b"0123456789abcdef");
1111
        assert_eq!(member.data(data).unwrap(), &b"even"[..]);
1112
1113
        assert!(members.next().is_none());
1114
    }
1115
1116
    #[test]
1117
    fn aix_names() {
1118
        let data = b"\
1119
            <bigaf>\n\
1120
            396                 0                   0                   \
1121
            128                 262                 0                   \
1122
            4                   262                 0                   \
1123
            1662610370  223         1           644         16  \
1124
            0123456789abcdef`\nord\n\
1125
            4                   396                 128                 \
1126
            1662610374  223         1           644         16  \
1127
            fedcba9876543210`\nrev\n\
1128
            94                  0                   262                 \
1129
            0           0           0           0           0   \
1130
            `\n2                   128                 \
1131
            262                 0123456789abcdef\0fedcba9876543210\0";
1132
        let data = &data[..];
1133
        let archive = ArchiveFile::parse(data).unwrap();
1134
        assert_eq!(archive.kind(), ArchiveKind::AixBig);
1135
        let mut members = archive.members();
1136
1137
        let member = members.next().unwrap().unwrap();
1138
        assert_eq!(member.name(), b"0123456789abcdef");
1139
        assert_eq!(member.data(data).unwrap(), &b"ord\n"[..]);
1140
1141
        let member = members.next().unwrap().unwrap();
1142
        assert_eq!(member.name(), b"fedcba9876543210");
1143
        assert_eq!(member.data(data).unwrap(), &b"rev\n"[..]);
1144
1145
        assert!(members.next().is_none());
1146
    }
1147
}