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 | 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)] |
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 | 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)] |
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 | 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)] |
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 | 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 | |
730 | impl<'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)] |
811 | pub struct ArchiveSymbol<'data> { |
812 | name: &'data [u8], |
813 | offset: ArchiveOffset, |
814 | } |
815 | |
816 | impl<'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. |
831 | fn 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: u8 in digits { |
837 | if c == b' ' { |
838 | return Some(result); |
839 | } else { |
840 | let x: u32 = (c as char).to_digit(radix)?; |
841 | result = resultu64 |
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). |
851 | fn parse_sysv_extended_name<'data>(digits: &[u8], names: &'data [u8]) -> Result<&'data [u8], ()> { |
852 | let offset: u64 = parse_u64_digits(digits, 10).ok_or(())?; |
853 | let offset: usize = offset.try_into().map_err(|_| ())?; |
854 | let name_data: &[u8] = names.get(offset..).ok_or(())?; |
855 | let len: usize = 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. |
870 | fn 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: u64 = parse_u64_digits(digits, 10).ok_or(())?; |
877 | *size = size.checked_sub(len).ok_or(())?; |
878 | let name_data: &'data [u8] = data.read_bytes(offset, size:len)?; |
879 | let name: &[u8] = match memchr::memchr(needle:b' \0' , haystack:name_data) { |
880 | Some(len: usize) => &name_data[..len], |
881 | None => name_data, |
882 | }; |
883 | Ok(name) |
884 | } |
885 | |
886 | #[cfg (test)] |
887 | mod 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 | } |
1134 | |