1use scroll::{Pread, Pwrite, SizeWith};
10
11use crate::error::{Error, Result};
12use crate::strtab;
13
14use alloc::collections::btree_map::BTreeMap;
15use alloc::vec::Vec;
16use core::usize;
17
18pub const SIZEOF_MAGIC: usize = 8;
19pub const MAGIC: &[u8; SIZEOF_MAGIC] = b"!<arch>\x0A";
21
22const SIZEOF_FILE_IDENTIFER: usize = 16;
23const SIZEOF_FILE_SIZE: usize = 10;
24
25#[repr(C)]
26#[derive(Debug, Clone, PartialEq, Pread, Pwrite, SizeWith)]
27pub struct MemberHeader {
45 pub identifier: [u8; 16],
47 pub timestamp: [u8; 12],
49 pub owner_id: [u8; 6],
51 pub group_id: [u8; 6],
53 pub mode: [u8; 8],
55 pub file_size: [u8; 10],
57 pub terminator: [u8; 2],
59}
60
61#[derive(Debug, Clone, Copy, PartialEq)]
62pub struct Header<'a> {
63 pub name: &'a str,
64 pub size: usize,
65}
66
67pub const SIZEOF_HEADER: usize = SIZEOF_FILE_IDENTIFER + 12 + 6 + 6 + 8 + SIZEOF_FILE_SIZE + 2;
68
69impl MemberHeader {
70 pub fn name(&self) -> Result<&str> {
71 Ok(self
72 .identifier
73 .pread_with::<&str>(0, ::scroll::ctx::StrCtx::Length(SIZEOF_FILE_IDENTIFER))?)
74 }
75 pub fn size(&self) -> Result<usize> {
76 match usize::from_str_radix(
77 self.file_size
78 .pread_with::<&str>(0, ::scroll::ctx::StrCtx::Length(self.file_size.len()))?
79 .trim_end(),
80 10,
81 ) {
82 Ok(file_size) => Ok(file_size),
83 Err(err) => Err(Error::Malformed(format!(
84 "{:?} Bad file_size in header: {:?}",
85 err, self
86 ))),
87 }
88 }
89}
90
91#[derive(Debug, Clone, PartialEq)]
92pub struct Member<'a> {
94 pub header: Header<'a>,
96 pub header_offset: u64,
98 pub offset: u64,
100 bsd_name: Option<&'a str>,
102 sysv_name: Option<&'a str>,
104}
105
106impl<'a> Member<'a> {
107 pub fn parse(buffer: &'a [u8], offset: &mut usize) -> Result<Member<'a>> {
111 let header_offset = *offset;
112 let name = buffer.pread_with::<&str>(
113 *offset,
114 ::scroll::ctx::StrCtx::Length(SIZEOF_FILE_IDENTIFER),
115 )?;
116 let archive_header = buffer.gread::<MemberHeader>(offset)?;
117 let mut header = Header {
118 name,
119 size: archive_header.size()?,
120 };
121
122 if *offset & 1 == 1 {
124 *offset += 1;
125 }
126
127 let bsd_name = if let Some(len) = Self::bsd_filename_length(name) {
128 let name = buffer.pread_with::<&str>(
130 header_offset + SIZEOF_HEADER,
131 ::scroll::ctx::StrCtx::Length(len),
132 )?;
133
134 if header.size > len {
136 *offset = header_offset + SIZEOF_HEADER + len;
137 header.size -= len;
138
139 Some(name.trim_end_matches('\0'))
141 } else {
142 None
143 }
144 } else {
145 None
146 };
147
148 Ok(Member {
149 header,
150 header_offset: header_offset as u64,
151 offset: *offset as u64,
152 bsd_name,
153 sysv_name: None,
154 })
155 }
156
157 pub fn size(&self) -> usize {
160 self.header.size
161 }
162
163 fn bsd_filename_length(name: &str) -> Option<usize> {
165 use core::str::FromStr;
166
167 if let Some(name) = name.strip_prefix("#1/") {
168 let trimmed_name = name.trim_end_matches(' ');
169 if let Ok(len) = usize::from_str(trimmed_name) {
170 Some(len)
171 } else {
172 None
173 }
174 } else {
175 None
176 }
177 }
178
179 pub fn extended_name(&self) -> &'a str {
181 if let Some(bsd_name) = self.bsd_name {
182 bsd_name
183 } else if let Some(ref sysv_name) = self.sysv_name {
184 sysv_name
185 } else {
186 self.header.name.trim_end_matches(' ').trim_end_matches('/')
187 }
188 }
189
190 pub fn raw_name(&self) -> &'a str {
193 self.header.name
194 }
195}
196
197#[derive(Debug, Default)]
198pub struct Index<'a> {
202 pub size: usize,
204 pub symbol_indexes: Vec<u32>,
206 pub strtab: Vec<&'a str>,
208}
209
210const INDEX_NAME: &str = "/ ";
212const NAME_INDEX_NAME: &str = "// ";
214const BSD_SYMDEF_NAME: &str = "__.SYMDEF";
216const BSD_SYMDEF_SORTED_NAME: &str = "__.SYMDEF SORTED";
217
218impl<'a> Index<'a> {
219 pub fn parse_sysv_index(buffer: &'a [u8]) -> Result<Self> {
221 let offset = &mut 0;
222 let sizeof_table = buffer.gread_with::<u32>(offset, scroll::BE)? as usize;
223
224 if sizeof_table > buffer.len() / 4 {
225 return Err(Error::BufferTooShort(sizeof_table, "indices"));
226 }
227
228 let mut indexes = Vec::with_capacity(sizeof_table);
229 for _ in 0..sizeof_table {
230 indexes.push(buffer.gread_with::<u32>(offset, scroll::BE)?);
231 }
232 let sizeof_strtab = buffer.len() - ((sizeof_table * 4) + 4);
233 let strtab = strtab::Strtab::parse(buffer, *offset, sizeof_strtab, 0x0)?;
234 Ok(Index {
235 size: sizeof_table,
236 symbol_indexes: indexes,
237 strtab: strtab.to_vec()?, })
239 }
240
241 pub fn parse_bsd_symdef(buffer: &'a [u8]) -> Result<Self> {
243 let entries_bytes = buffer.pread_with::<u32>(0, scroll::LE)? as usize;
275 let entries = entries_bytes / 8;
276
277 let strtab_bytes = buffer.pread_with::<u32>(entries_bytes + 4, scroll::LE)? as usize;
280 let strtab = strtab::Strtab::parse(buffer, entries_bytes + 8, strtab_bytes, 0x0)?;
281
282 if entries_bytes > buffer.len() {
283 return Err(Error::BufferTooShort(entries, "entries"));
284 }
285
286 let mut indexes = Vec::with_capacity(entries);
288 let mut strings = Vec::with_capacity(entries);
289 for i in 0..entries {
290 let string_offset: u32 = buffer.pread_with(i * 8 + 4, scroll::LE)?;
296 let archive_member: u32 = buffer.pread_with(i * 8 + 8, scroll::LE)?;
297
298 let string = match strtab.get_at(string_offset as usize) {
299 Some(result) => Ok(result),
300 None => Err(Error::Malformed(format!(
301 "{} entry {} has string offset {}, which is out of bounds",
302 BSD_SYMDEF_NAME, i, string_offset
303 ))),
304 }?;
305
306 indexes.push(archive_member);
307 strings.push(string);
308 }
309
310 Ok(Index {
311 size: entries,
312 symbol_indexes: indexes,
313 strtab: strings,
314 })
315 }
316
317 pub fn parse_windows_linker_member(buffer: &'a [u8]) -> Result<Self> {
325 let offset = &mut 0;
326 let members = buffer.gread_with::<u32>(offset, scroll::LE)? as usize;
327
328 if members > buffer.len() / 4 {
329 return Err(Error::BufferTooShort(members, "members"));
330 }
331
332 let mut member_offsets = Vec::with_capacity(members);
333 for _ in 0..members {
334 member_offsets.push(buffer.gread_with::<u32>(offset, scroll::LE)?);
335 }
336
337 let symbols = buffer.gread_with::<u32>(offset, scroll::LE)? as usize;
338
339 if symbols > buffer.len() / 2 {
340 return Err(Error::BufferTooShort(symbols, "symbols"));
341 }
342
343 let mut symbol_offsets = Vec::with_capacity(symbols);
344 for _ in 0..symbols {
345 let index = buffer.gread_with::<u16>(offset, scroll::LE)?;
346 if index <= 0 {
348 return Err(Error::BufferTooShort(members, "members"));
349 }
350 if let Some(symbol_offset) = member_offsets.get(index as usize - 1) {
351 symbol_offsets.push(*symbol_offset);
352 } else {
353 return Err(Error::BufferTooShort(members, "members"));
354 }
355 }
356 let strtab = strtab::Strtab::parse(buffer, *offset, buffer.len() - *offset, 0x0)?;
357 Ok(Index {
358 size: symbols,
359 symbol_indexes: symbol_offsets,
360 strtab: strtab.to_vec()?,
361 })
362 }
363}
364
365#[derive(Debug, Default)]
369struct NameIndex<'a> {
370 strtab: strtab::Strtab<'a>,
371}
372
373impl<'a> NameIndex<'a> {
374 pub fn parse(buffer: &'a [u8], offset: &mut usize, size: usize) -> Result<NameIndex<'a>> {
375 let hacked_size = size + 1;
378 if hacked_size < 2 {
379 return Err(Error::Malformed(format!(
380 "Size ({hacked_size:#x}) too small"
381 )));
382 }
383 let strtab = strtab::Strtab::parse(buffer, *offset - 1, hacked_size, b'\n')?;
384 *offset += hacked_size - 2;
386 Ok(NameIndex { strtab })
387 }
388
389 pub fn get(&self, name: &str) -> Result<&'a str> {
390 let idx = name.trim_start_matches('/').trim_end();
391 match usize::from_str_radix(idx, 10) {
392 Ok(idx) => {
393 let name = match self.strtab.get_at(idx + 1) {
394 Some(result) => Ok(result),
395 None => Err(Error::Malformed(format!(
396 "Name {} is out of range in archive NameIndex",
397 name
398 ))),
399 }?;
400
401 if name != "" {
402 Ok(name.trim_end_matches('/'))
403 } else {
404 Err(Error::Malformed(format!(
405 "Could not find {:?} in index",
406 name
407 )))
408 }
409 }
410 Err(_) => Err(Error::Malformed(format!(
411 "Bad name index {:?} in index",
412 name
413 ))),
414 }
415 }
416}
417
418#[derive(Debug, PartialEq)]
419pub enum IndexType {
422 None,
424 SysV,
426 Windows,
429 BSD,
431}
432
433#[derive(Debug)]
436pub struct Archive<'a> {
438 member_array: Vec<Member<'a>>,
441 members: BTreeMap<&'a str, usize>,
443 symbol_index: BTreeMap<&'a str, usize>,
445}
446
447impl<'a> Archive<'a> {
448 pub fn parse(buffer: &'a [u8]) -> Result<Archive<'a>> {
449 let mut magic = [0u8; SIZEOF_MAGIC];
450 let offset = &mut 0usize;
451 buffer.gread_inout(offset, &mut magic)?;
452 if &magic != MAGIC {
453 return Err(Error::BadMagic(magic.pread(0)?));
454 }
455 let mut member_array = Vec::new();
456 let mut index = Index::default();
457 let mut index_type = IndexType::None;
458 let mut sysv_name_index = NameIndex::default();
459 while *offset + 1 < buffer.len() {
460 if *offset & 1 == 1 {
462 *offset += 1;
463 }
464
465 let member = Member::parse(buffer, offset)?;
466
467 *offset = member.offset as usize + member.size() as usize;
469
470 let name = member.raw_name();
471 if name == INDEX_NAME {
472 let data: &[u8] = buffer.pread_with(member.offset as usize, member.size())?;
473 index = match index_type {
474 IndexType::None => {
475 index_type = IndexType::SysV;
476 Index::parse_sysv_index(data)?
477 }
478 IndexType::SysV => {
479 index_type = IndexType::Windows;
480 Index::parse_windows_linker_member(data)?
482 }
483 IndexType::BSD => {
484 return Err(Error::Malformed("SysV index occurs after BSD index".into()));
485 }
486 IndexType::Windows => {
487 return Err(Error::Malformed(
488 "More than two Windows Linker members".into(),
489 ));
490 }
491 }
492 } else if member.bsd_name == Some(BSD_SYMDEF_NAME)
493 || member.bsd_name == Some(BSD_SYMDEF_SORTED_NAME)
494 {
495 if index_type != IndexType::None {
496 return Err(Error::Malformed("BSD index occurs after SysV index".into()));
497 }
498 index_type = IndexType::BSD;
499 let data: &[u8] = buffer.pread_with(member.offset as usize, member.size())?;
500 index = Index::parse_bsd_symdef(data)?;
501 } else if name == NAME_INDEX_NAME {
502 let mut name_index_offset: usize = member.offset as usize;
503 sysv_name_index = NameIndex::parse(buffer, &mut name_index_offset, member.size())?;
504 } else {
505 member_array.push(member);
507 }
508 }
509
510 let mut members = BTreeMap::new();
512 let mut member_index_by_offset: BTreeMap<u32, usize> = BTreeMap::new();
513 for (i, member) in member_array.iter_mut().enumerate() {
514 if let Ok(sysv_name) = sysv_name_index.get(member.raw_name()) {
516 member.sysv_name = Some(sysv_name);
517 }
518
519 let key = member.extended_name();
521 members.insert(key, i);
522
523 member_index_by_offset.insert(member.header_offset as u32, i);
525 }
526
527 let mut symbol_index: BTreeMap<&str, usize> = BTreeMap::new();
529 for (member_offset, name) in index.symbol_indexes.iter().zip(index.strtab.iter()) {
530 let member_index = *member_index_by_offset.get(member_offset).ok_or_else(|| {
531 Error::Malformed(format!(
532 "Could not get member {:?} at offset: {}",
533 name, member_offset
534 ))
535 })?;
536 symbol_index.insert(&name, member_index);
537 }
538
539 Ok(Archive {
540 member_array,
541 members,
542 symbol_index,
543 })
544 }
545
546 pub fn get(&self, member: &str) -> Option<&Member<'_>> {
550 if let Some(idx) = self.members.get(member) {
551 Some(&self.member_array[*idx])
552 } else {
553 None
554 }
555 }
556
557 pub fn get_at(&self, index: usize) -> Option<&Member<'_>> {
559 self.member_array.get(index)
560 }
561
562 pub fn len(&self) -> usize {
564 self.member_array.len()
565 }
566
567 pub fn extract<'b>(&self, member: &str, buffer: &'b [u8]) -> Result<&'b [u8]> {
569 if let Some(member) = self.get(member) {
570 let bytes = buffer.pread_with(member.offset as usize, member.size())?;
571 Ok(bytes)
572 } else {
573 Err(Error::Malformed(format!(
574 "Cannot extract member {:?}",
575 member
576 )))
577 }
578 }
579
580 pub fn summarize(&self) -> Vec<(&str, &Member<'_>, Vec<&'a str>)> {
582 let mut result = self
584 .member_array
585 .iter()
586 .map(|ref member| (member.extended_name(), *member, Vec::new()))
587 .collect::<Vec<_>>();
588
589 for (symbol_name, member_index) in self.symbol_index.iter() {
591 result[*member_index].2.push(*symbol_name);
592 }
593
594 result
595 }
596
597 pub fn members(&self) -> Vec<&'a str> {
604 self.members.keys().cloned().collect()
605 }
606
607 pub fn member_of_symbol(&self, symbol: &str) -> Option<&'a str> {
609 if let Some(idx) = self.symbol_index.get(symbol) {
610 Some(self.member_array[*idx].extended_name())
611 } else {
612 None
613 }
614 }
615}
616
617#[cfg(test)]
618mod tests {
619 use super::*;
620 use crate::error;
621
622 #[test]
623 fn test_member_bsd_filename_length() {
624 assert_eq!(Member::bsd_filename_length(""), None);
626 assert_eq!(Member::bsd_filename_length("123"), None);
627 assert_eq!(Member::bsd_filename_length("#1"), None);
628 assert_eq!(Member::bsd_filename_length("#1/"), None);
629 assert_eq!(Member::bsd_filename_length("#2/1"), None);
630 assert_eq!(Member::bsd_filename_length(INDEX_NAME), None);
631 assert_eq!(Member::bsd_filename_length(NAME_INDEX_NAME), None);
632 assert_eq!(Member::bsd_filename_length("👺"), None);
633
634 assert_eq!(Member::bsd_filename_length("#1/1"), Some(1));
636 assert_eq!(Member::bsd_filename_length("#1/22"), Some(22));
637 assert_eq!(Member::bsd_filename_length("#1/333"), Some(333));
638 assert_eq!(Member::bsd_filename_length("#1/1 "), Some(1));
639 assert_eq!(Member::bsd_filename_length("#1/22 "), Some(22));
640 assert_eq!(Member::bsd_filename_length("#1/333 "), Some(333));
641
642 assert_eq!(Member::bsd_filename_length("#1/1A"), None);
644 assert_eq!(Member::bsd_filename_length("#1/1 A"), None);
645 }
646
647 const MALFORMED_ARCHIVE_INDEX_TOO_SMALL: [u8; 132] = [
649 0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E, 0x0A, 0x55, 0x52, 0x09, 0x5C, 0x09, 0x09, 0x10,
650 0x27, 0x2B, 0x09, 0x0A, 0x53, 0x54, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
651 0x09, 0x09, 0x09, 0x09, 0x2A, 0x29, 0x2A, 0x09, 0xF7, 0x08, 0x09, 0x09, 0x00, 0x01, 0x01,
652 0x01, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x01, 0x00, 0x31, 0x20, 0x20, 0x20,
653 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x08, 0x2F, 0x2F, 0x20, 0x20, 0x20,
654 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x09,
655 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x23, 0x42, 0x21, 0x09, 0xF7, 0x08, 0x20, 0x20, 0x00,
656 0x3C, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x09, 0x09, 0x01, 0x01, 0x30, 0x0D,
657 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x00, 0x00, 0x27, 0x55,
658 ];
659
660 const MALFORMED_ARCHIVE: [u8; 212] = [
662 0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E, 0x0A, 0x2F, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
663 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0F, 0x01, 0x00, 0x00, 0x31, 0x31,
664 0x31, 0x0E, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x03, 0x30, 0x34, 0x30, 0x30, 0x30, 0x71,
665 0x30, 0x30, 0x17, 0xE8, 0x33, 0x5A, 0x31, 0x30, 0x30, 0x30, 0x30, 0x35, 0x31, 0x20, 0x20,
666 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
667 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
668 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
669 0x38, 0x38, 0x37, 0x37, 0x37, 0x35, 0x37, 0x21, 0x3E, 0x61, 0x6D, 0x66, 0x76, 0x3B, 0x0A,
670 0x2F, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
671 0x20, 0x0F, 0x01, 0x00, 0x00, 0x31, 0x31, 0x31, 0x00, 0x0E, 0x30, 0x30, 0x30, 0x30, 0x30,
672 0x30, 0x03, 0x30, 0x38, 0x30, 0x30, 0x30, 0x72, 0x30, 0x30, 0x17, 0xE8, 0x36, 0x4E, 0x31,
673 0x30, 0x30, 0x30, 0x30, 0x33, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
674 0x01, 0x00, 0x00, 0x00, 0x01, 0x34, 0x37, 0x35, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36,
675 0x37, 0x33, 0x37, 0x34, 0x33, 0x32, 0x38, 0x35, 0x32, 0x34, 0x33, 0x38, 0x32, 0x35, 0x33,
676 0x14, 0x34,
677 ];
678
679 #[test]
680 fn parse_name_index_too_small() {
681 let res = Archive::parse(&MALFORMED_ARCHIVE_INDEX_TOO_SMALL);
682 assert_eq!(res.is_err(), true);
683 if let Err(Error::Malformed(msg)) = res {
684 assert_eq!(msg, "Size (0x1) too small");
685 } else {
686 panic!("Expected a Malformed error but got {:?}", res);
687 }
688 }
689
690 #[test]
691 fn parse_malformed_archive() {
692 let res = Archive::parse(&MALFORMED_ARCHIVE);
693 assert_eq!(res.is_err(), true);
694 match res {
695 Err(error::Error::BufferTooShort(num_member, msg)) => {
696 assert_eq!(num_member, 1);
697 assert_eq!(msg, "members");
698 }
699 _ => panic!("Expected a BufferTooShort error but got {:?}", res),
700 }
701 }
702}