1#![allow(rustc::default_hash_types, rustc::potential_query_instability)]
2
3// Derived from code in LLVM, which is:
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7
8// Derived from:
9// * https://github.com/llvm/llvm-project/blob/3d3ef9d073e1e27ea57480b371b7f5a9f5642ed2/llvm/include/llvm/Object/ArchiveWriter.h
10// * https://github.com/llvm/llvm-project/blob/3d3ef9d073e1e27ea57480b371b7f5a9f5642ed2/llvm/lib/Object/ArchiveWriter.cpp
11
12use std::collections::HashMap;
13use std::io::{self, Cursor, Seek, Write};
14
15use object::{Object, ObjectSymbol};
16
17use crate::alignment::*;
18use crate::archive::*;
19
20pub struct NewArchiveMember<'a> {
21 pub buf: Box<dyn AsRef<[u8]> + 'a>,
22 pub get_symbols: fn(buf: &[u8], f: &mut dyn FnMut(&[u8]) -> io::Result<()>) -> io::Result<bool>,
23 pub member_name: String,
24 pub mtime: u64,
25 pub uid: u32,
26 pub gid: u32,
27 pub perms: u32,
28}
29
30fn is_darwin(kind: ArchiveKind) -> bool {
31 matches!(kind, ArchiveKind::Darwin | ArchiveKind::Darwin64)
32}
33
34fn is_aix_big_archive(kind: ArchiveKind) -> bool {
35 kind == ArchiveKind::AixBig
36}
37
38fn is_bsd_like(kind: ArchiveKind) -> bool {
39 match kind {
40 ArchiveKind::Gnu | ArchiveKind::Gnu64 | ArchiveKind::AixBig => false,
41 ArchiveKind::Bsd | ArchiveKind::Darwin | ArchiveKind::Darwin64 => true,
42 ArchiveKind::Coff => panic!("not supported for writing"),
43 }
44}
45
46fn print_rest_of_member_header<W: Write>(
47 w: &mut W,
48 mtime: u64,
49 uid: u32,
50 gid: u32,
51 perms: u32,
52 size: u64,
53) -> io::Result<()> {
54 // The format has only 6 chars for uid and gid. Truncate if the provided
55 // values don't fit.
56 write!(
57 w,
58 "{:<12}{:<6}{:<6}{:<8o}{:<10}`\n",
59 mtime,
60 uid % 1000000,
61 gid % 1000000,
62 perms,
63 size
64 )
65}
66
67fn print_gnu_small_member_header<W: Write>(
68 w: &mut W,
69 name: String,
70 mtime: u64,
71 uid: u32,
72 gid: u32,
73 perms: u32,
74 size: u64,
75) -> io::Result<()> {
76 write!(w, "{:<16}", name + "/")?;
77 print_rest_of_member_header(w, mtime, uid, gid, perms, size)
78}
79
80fn print_bsd_member_header<W: Write>(
81 w: &mut W,
82 pos: u64,
83 name: &str,
84 mtime: u64,
85 uid: u32,
86 gid: u32,
87 perms: u32,
88 size: u64,
89) -> io::Result<()> {
90 let pos_after_header: u64 = pos + 60 + u64::try_from(name.len()).unwrap();
91 // Pad so that even 64 bit object files are aligned.
92 let pad: u64 = offset_to_alignment(value:pos_after_header, alignment:8);
93 let name_with_padding: u64 = u64::try_from(name.len()).unwrap() + pad;
94 write!(w, "#1/{:<13}", name_with_padding)?;
95 print_rest_of_member_header(w, mtime, uid, gid, perms, size:name_with_padding + size)?;
96 write!(w, "{}", name)?;
97 write!(
98 w,
99 "{nil:\0<pad$}",
100 nil = "",
101 pad = usize::try_from(pad).unwrap()
102 )
103}
104
105fn print_big_archive_member_header<W: Write>(
106 w: &mut W,
107 name: &str,
108 mtime: u64,
109 uid: u32,
110 gid: u32,
111 perms: u32,
112 size: u64,
113 prev_offset: u64,
114 next_offset: u64,
115) -> io::Result<()> {
116 write!(
117 w,
118 "{:<20}{:<20}{:<20}{:<12}{:<12}{:<12}{:<12o}{:<4}",
119 size,
120 next_offset,
121 prev_offset,
122 mtime,
123 u64::from(uid) % 1000000000000u64,
124 u64::from(gid) % 1000000000000u64,
125 perms,
126 name.len(),
127 )?;
128
129 if !name.is_empty() {
130 write!(w, "{}", name)?;
131
132 if name.len() % 2 != 0 {
133 write!(w, "\0")?;
134 }
135 }
136
137 write!(w, "`\n")?;
138
139 Ok(())
140}
141
142fn use_string_table(thin: bool, name: &str) -> bool {
143 thin || name.len() >= 16 || name.contains('/')
144}
145
146fn is_64bit_kind(kind: ArchiveKind) -> bool {
147 match kind {
148 ArchiveKind::Gnu | ArchiveKind::Bsd | ArchiveKind::Darwin | ArchiveKind::Coff => false,
149 ArchiveKind::AixBig | ArchiveKind::Darwin64 | ArchiveKind::Gnu64 => true,
150 }
151}
152
153fn print_member_header<'m, W: Write, T: Write + Seek>(
154 w: &mut W,
155 pos: u64,
156 string_table: &mut T,
157 member_names: &mut HashMap<&'m str, u64>,
158 kind: ArchiveKind,
159 thin: bool,
160 m: &'m NewArchiveMember<'m>,
161 mtime: u64,
162 size: u64,
163) -> io::Result<()> {
164 if is_bsd_like(kind) {
165 return print_bsd_member_header(w, pos, &m.member_name, mtime, m.uid, m.gid, m.perms, size);
166 }
167
168 if !use_string_table(thin, &m.member_name) {
169 return print_gnu_small_member_header(
170 w,
171 m.member_name.clone(),
172 mtime,
173 m.uid,
174 m.gid,
175 m.perms,
176 size,
177 );
178 }
179
180 write!(w, "/")?;
181 let name_pos;
182 if thin {
183 name_pos = string_table.stream_position()?;
184 write!(string_table, "{}/\n", m.member_name)?;
185 } else {
186 if let Some(&pos) = member_names.get(&*m.member_name) {
187 name_pos = pos;
188 } else {
189 name_pos = string_table.stream_position()?;
190 member_names.insert(&m.member_name, name_pos);
191 write!(string_table, "{}/\n", m.member_name)?;
192 }
193 }
194 write!(w, "{:<15}", name_pos)?;
195 print_rest_of_member_header(w, mtime, m.uid, m.gid, m.perms, size)
196}
197
198struct MemberData<'a> {
199 symbols: Vec<u64>,
200 header: Vec<u8>,
201 data: &'a [u8],
202 padding: &'static [u8],
203}
204
205fn compute_string_table(names: &[u8]) -> MemberData<'_> {
206 let size: u64 = u64::try_from(names.len()).unwrap();
207 let pad: u64 = offset_to_alignment(value:size, alignment:2);
208 let mut header: Vec = Vec::new();
209 write!(header, "{:<48}", "//").unwrap();
210 write!(header, "{:<10}", size + pad).unwrap();
211 write!(header, "`\n").unwrap();
212 MemberData {
213 symbols: vec![],
214 header,
215 data: names,
216 padding: if pad != 0 { b"\n" } else { b"" },
217 }
218}
219
220fn now(deterministic: bool) -> u64 {
221 if !deterministic {
222 todo!("non deterministic mode is not yet supported"); // FIXME
223 }
224 0
225}
226
227fn is_archive_symbol(sym: &object::read::Symbol<'_, '_>) -> bool {
228 // FIXME Use a better equivalent of LLVM's SymbolRef::SF_FormatSpecific
229 if sym.kind() == object::SymbolKind::Null
230 || sym.kind() == object::SymbolKind::File
231 || sym.kind() == object::SymbolKind::Section
232 {
233 return false;
234 }
235 if !sym.is_global() {
236 return false;
237 }
238 if sym.is_undefined() {
239 return false;
240 }
241 true
242}
243
244fn print_n_bits<W: Write>(w: &mut W, kind: ArchiveKind, val: u64) -> io::Result<()> {
245 if is_64bit_kind(kind) {
246 w.write_all(&if is_bsd_like(kind) {
247 u64::to_le_bytes(self:val)
248 } else {
249 u64::to_be_bytes(self:val)
250 })
251 } else {
252 w.write_all(&if is_bsd_like(kind) {
253 u32::to_le_bytes(self:u32::try_from(val).unwrap())
254 } else {
255 u32::to_be_bytes(self:u32::try_from(val).unwrap())
256 })
257 }
258}
259
260fn compute_symbol_table_size_and_pad(
261 kind: ArchiveKind,
262 num_syms: u64,
263 offset_size: u64,
264 string_table: &[u8],
265) -> (u64, u64) {
266 assert!(
267 offset_size == 4 || offset_size == 8,
268 "Unsupported offset_size"
269 );
270 let mut size = offset_size; // Number of entries
271 if is_bsd_like(kind) {
272 size += num_syms * offset_size * 2; // Table
273 } else {
274 size += num_syms * offset_size; // Table
275 }
276 if is_bsd_like(kind) {
277 size += offset_size; // byte count;
278 }
279 size += u64::try_from(string_table.len()).unwrap();
280 // ld64 expects the members to be 8-byte aligned for 64-bit content and at
281 // least 4-byte aligned for 32-bit content. Opt for the larger encoding
282 // uniformly.
283 // We do this for all bsd formats because it simplifies aligning members.
284 let pad = if is_aix_big_archive(kind) {
285 0
286 } else {
287 offset_to_alignment(size, if is_bsd_like(kind) { 8 } else { 2 })
288 };
289 size += pad;
290 (size, pad)
291}
292
293fn write_symbol_table_header<W: Write + Seek>(
294 w: &mut W,
295 kind: ArchiveKind,
296 deterministic: bool,
297 size: u64,
298 prev_member_offset: u64,
299) -> io::Result<()> {
300 if is_bsd_like(kind) {
301 let name = if is_64bit_kind(kind) {
302 "__.SYMDEF_64"
303 } else {
304 "__.SYMDEF"
305 };
306 let pos = w.stream_position()?;
307 print_bsd_member_header(w, pos, name, now(deterministic), 0, 0, 0, size)
308 } else if is_aix_big_archive(kind) {
309 print_big_archive_member_header(
310 w,
311 "",
312 now(deterministic),
313 0,
314 0,
315 0,
316 size,
317 prev_member_offset,
318 0,
319 )
320 } else {
321 let name = if is_64bit_kind(kind) { "/SYM64" } else { "" };
322 print_gnu_small_member_header(w, name.to_string(), now(deterministic), 0, 0, 0, size)
323 }
324}
325
326fn write_symbol_table<W: Write + Seek>(
327 w: &mut W,
328 kind: ArchiveKind,
329 deterministic: bool,
330 members: &[MemberData<'_>],
331 string_table: &[u8],
332 prev_member_offset: u64,
333) -> io::Result<()> {
334 // We don't write a symbol table on an archive with no members -- except on
335 // Darwin, where the linker will abort unless the archive has a symbol table.
336 if string_table.is_empty() && !is_darwin(kind) {
337 return Ok(());
338 }
339
340 let num_syms = u64::try_from(members.iter().map(|m| m.symbols.len()).sum::<usize>()).unwrap();
341
342 let offset_size = if is_64bit_kind(kind) { 8 } else { 4 };
343 let (size, pad) = compute_symbol_table_size_and_pad(kind, num_syms, offset_size, string_table);
344 write_symbol_table_header(w, kind, deterministic, size, prev_member_offset)?;
345
346 let mut pos = if is_aix_big_archive(kind) {
347 u64::try_from(std::mem::size_of::<big_archive::FixLenHdr>()).unwrap()
348 } else {
349 w.stream_position()? + size
350 };
351
352 if is_bsd_like(kind) {
353 print_n_bits(w, kind, num_syms * 2 * offset_size)?;
354 } else {
355 print_n_bits(w, kind, num_syms)?;
356 }
357
358 for m in members {
359 for &string_offset in &m.symbols {
360 if is_bsd_like(kind) {
361 print_n_bits(w, kind, string_offset)?;
362 }
363 print_n_bits(w, kind, pos)?; // member offset
364 }
365 pos += u64::try_from(m.header.len() + m.data.len() + m.padding.len()).unwrap();
366 }
367
368 if is_bsd_like(kind) {
369 // byte count of the string table
370 print_n_bits(w, kind, u64::try_from(string_table.len()).unwrap())?;
371 }
372
373 w.write_all(string_table)?;
374
375 write!(
376 w,
377 "{nil:\0<pad$}",
378 nil = "",
379 pad = usize::try_from(pad).unwrap()
380 )
381}
382
383pub fn get_native_object_symbols(
384 buf: &[u8],
385 f: &mut dyn FnMut(&[u8]) -> io::Result<()>,
386) -> io::Result<bool> {
387 // FIXME match what LLVM does
388
389 match object::File::parse(data:buf) {
390 Ok(file: File<'_>) => {
391 for sym: Symbol<'_, '_> in file.symbols() {
392 if !is_archive_symbol(&sym) {
393 continue;
394 }
395 f(sym.name_bytes().expect(msg:"FIXME"))?;
396 }
397 Ok(true)
398 }
399 Err(_) => Ok(false),
400 }
401}
402
403// NOTE: LLVM calls this getSymbols and has the get_native_symbols function inlined
404fn write_symbols(
405 buf: &[u8],
406 get_symbols: fn(buf: &[u8], f: &mut dyn FnMut(&[u8]) -> io::Result<()>) -> io::Result<bool>,
407 sym_names: &mut Cursor<Vec<u8>>,
408 has_object: &mut bool,
409) -> io::Result<Vec<u64>> {
410 let mut ret: Vec = vec![];
411 // We only set has_object if get_symbols determines it's looking at an
412 // object file. This is because if we're creating an rlib, the archive will
413 // always end in lib.rmeta, and cause has_object to always become false.
414 if get_symbols(buf, &mut |sym: &[u8]| {
415 ret.push(sym_names.stream_position()?);
416 sym_names.write_all(buf:sym)?;
417 sym_names.write_all(&[0])?;
418 Ok(())
419 })? {
420 *has_object = true;
421 }
422 Ok(ret)
423}
424
425fn compute_member_data<'a, S: Write + Seek>(
426 string_table: &mut S,
427 sym_names: &mut Cursor<Vec<u8>>,
428 kind: ArchiveKind,
429 thin: bool,
430 deterministic: bool,
431 need_symbols: bool,
432 new_members: &'a [NewArchiveMember<'a>],
433) -> io::Result<Vec<MemberData<'a>>> {
434 const PADDING_DATA: &[u8; 8] = &[b'\n'; 8];
435
436 // This ignores the symbol table, but we only need the value mod 8 and the
437 // symbol table is aligned to be a multiple of 8 bytes
438 let mut pos = if is_aix_big_archive(kind) {
439 u64::try_from(std::mem::size_of::<big_archive::FixLenHdr>()).unwrap()
440 } else {
441 0
442 };
443
444 let mut ret = vec![];
445 let mut has_object = false;
446
447 // Deduplicate long member names in the string table and reuse earlier name
448 // offsets. This especially saves space for COFF Import libraries where all
449 // members have the same name.
450 let mut member_names = HashMap::<&str, u64>::new();
451
452 // UniqueTimestamps is a special case to improve debugging on Darwin:
453 //
454 // The Darwin linker does not link debug info into the final
455 // binary. Instead, it emits entries of type N_OSO in in the output
456 // binary's symbol table, containing references to the linked-in
457 // object files. Using that reference, the debugger can read the
458 // debug data directly from the object files. Alternatively, an
459 // invocation of 'dsymutil' will link the debug data from the object
460 // files into a dSYM bundle, which can be loaded by the debugger,
461 // instead of the object files.
462 //
463 // For an object file, the N_OSO entries contain the absolute path
464 // path to the file, and the file's timestamp. For an object
465 // included in an archive, the path is formatted like
466 // "/absolute/path/to/archive.a(member.o)", and the timestamp is the
467 // archive member's timestamp, rather than the archive's timestamp.
468 //
469 // However, this doesn't always uniquely identify an object within
470 // an archive -- an archive file can have multiple entries with the
471 // same filename. (This will happen commonly if the original object
472 // files started in different directories.) The only way they get
473 // distinguished, then, is via the timestamp. But this process is
474 // unable to find the correct object file in the archive when there
475 // are two files of the same name and timestamp.
476 //
477 // Additionally, timestamp==0 is treated specially, and causes the
478 // timestamp to be ignored as a match criteria.
479 //
480 // That will "usually" work out okay when creating an archive not in
481 // deterministic timestamp mode, because the objects will probably
482 // have been created at different timestamps.
483 //
484 // To ameliorate this problem, in deterministic archive mode (which
485 // is the default), on Darwin we will emit a unique non-zero
486 // timestamp for each entry with a duplicated name. This is still
487 // deterministic: the only thing affecting that timestamp is the
488 // order of the files in the resultant archive.
489 //
490 // See also the functions that handle the lookup:
491 // in lldb: ObjectContainerBSDArchive::Archive::FindObject()
492 // in llvm/tools/dsymutil: BinaryHolder::GetArchiveMemberBuffers().
493 let unique_timestamps = deterministic && is_darwin(kind);
494 let mut filename_count = HashMap::new();
495 if unique_timestamps {
496 for m in new_members {
497 *filename_count.entry(&*m.member_name).or_insert(0) += 1;
498 }
499 for (_name, count) in filename_count.iter_mut() {
500 if *count > 1 {
501 *count = 1;
502 }
503 }
504 }
505
506 // The big archive format needs to know the offset of the previous member
507 // header.
508 let mut prev_offset = 0;
509 for m in new_members {
510 let mut header = Vec::new();
511
512 let data: &[u8] = if thin { &[][..] } else { (*m.buf).as_ref() };
513
514 // ld64 expects the members to be 8-byte aligned for 64-bit content and at
515 // least 4-byte aligned for 32-bit content. Opt for the larger encoding
516 // uniformly. This matches the behaviour with cctools and ensures that ld64
517 // is happy with archives that we generate.
518 let member_padding = if is_darwin(kind) {
519 offset_to_alignment(u64::try_from(data.len()).unwrap(), 8)
520 } else {
521 0
522 };
523 let tail_padding =
524 offset_to_alignment(u64::try_from(data.len()).unwrap() + member_padding, 2);
525 let padding = &PADDING_DATA[..usize::try_from(member_padding + tail_padding).unwrap()];
526
527 let mtime = if unique_timestamps {
528 // Increment timestamp for each file of a given name.
529 *filename_count.get_mut(&*m.member_name).unwrap() += 1;
530 filename_count[&*m.member_name] - 1
531 } else {
532 m.mtime
533 };
534
535 let size = u64::try_from(data.len()).unwrap() + member_padding;
536 if size > MAX_MEMBER_SIZE {
537 return Err(io::Error::new(
538 io::ErrorKind::Other,
539 format!("Archive member {} is too big", m.member_name),
540 ));
541 }
542
543 if is_aix_big_archive(kind) {
544 let next_offset = pos
545 + u64::try_from(std::mem::size_of::<big_archive::BigArMemHdrType>()).unwrap()
546 + align_to(u64::try_from(m.member_name.len()).unwrap(), 2)
547 + align_to(size, 2);
548 print_big_archive_member_header(
549 &mut header,
550 &m.member_name,
551 mtime,
552 m.uid,
553 m.gid,
554 m.perms,
555 size,
556 prev_offset,
557 next_offset,
558 )?;
559 prev_offset = pos;
560 } else {
561 print_member_header(
562 &mut header,
563 pos,
564 string_table,
565 &mut member_names,
566 kind,
567 thin,
568 m,
569 mtime,
570 size,
571 )?;
572 }
573
574 let symbols = if need_symbols {
575 write_symbols(data, m.get_symbols, sym_names, &mut has_object)?
576 } else {
577 vec![]
578 };
579
580 pos += u64::try_from(header.len() + data.len() + padding.len()).unwrap();
581 ret.push(MemberData {
582 symbols,
583 header,
584 data,
585 padding,
586 })
587 }
588
589 // If there are no symbols, emit an empty symbol table, to satisfy Solaris
590 // tools, older versions of which expect a symbol table in a non-empty
591 // archive, regardless of whether there are any symbols in it.
592 if has_object && sym_names.stream_position()? == 0 {
593 write!(sym_names, "\0\0\0")?;
594 }
595
596 Ok(ret)
597}
598
599pub fn write_archive_to_stream<W: Write + Seek>(
600 w: &mut W,
601 new_members: &[NewArchiveMember<'_>],
602 write_symtab: bool,
603 mut kind: ArchiveKind,
604 deterministic: bool,
605 thin: bool,
606) -> io::Result<()> {
607 assert!(
608 !thin || !is_bsd_like(kind),
609 "Only the gnu format has a thin mode"
610 );
611
612 let mut sym_names = Cursor::new(Vec::new());
613 let mut string_table = Cursor::new(Vec::new());
614
615 let mut data = compute_member_data(
616 &mut string_table,
617 &mut sym_names,
618 kind,
619 thin,
620 deterministic,
621 write_symtab,
622 new_members,
623 )?;
624
625 let sym_names = sym_names.into_inner();
626
627 let string_table = string_table.into_inner();
628 if !string_table.is_empty() && !is_aix_big_archive(kind) {
629 data.insert(0, compute_string_table(&string_table));
630 }
631
632 // We would like to detect if we need to switch to a 64-bit symbol table.
633 let mut last_member_end_offset = if is_aix_big_archive(kind) {
634 u64::try_from(std::mem::size_of::<big_archive::FixLenHdr>()).unwrap()
635 } else {
636 8
637 };
638 let mut last_member_header_offset = last_member_end_offset;
639 let mut num_syms = 0;
640 for m in &data {
641 // Record the start of the member's offset
642 last_member_header_offset = last_member_end_offset;
643 // Account for the size of each part associated with the member.
644 last_member_end_offset +=
645 u64::try_from(m.header.len() + m.data.len() + m.padding.len()).unwrap();
646 num_syms += u64::try_from(m.symbols.len()).unwrap();
647 }
648
649 // The symbol table is put at the end of the big archive file. The symbol
650 // table is at the start of the archive file for other archive formats.
651 if write_symtab && !is_aix_big_archive(kind) {
652 // We assume 32-bit offsets to see if 32-bit symbols are possible or not.
653 let (symtab_size, _pad) = compute_symbol_table_size_and_pad(kind, num_syms, 4, &sym_names);
654 last_member_header_offset += {
655 // FIXME avoid allocating memory here
656 let mut tmp = Cursor::new(vec![]);
657 write_symbol_table_header(&mut tmp, kind, deterministic, symtab_size, 0).unwrap();
658 u64::try_from(tmp.into_inner().len()).unwrap()
659 } + symtab_size;
660
661 // The SYM64 format is used when an archive's member offsets are larger than
662 // 32-bits can hold. The need for this shift in format is detected by
663 // writeArchive. To test this we need to generate a file with a member that
664 // has an offset larger than 32-bits but this demands a very slow test. To
665 // speed the test up we use this environment variable to pretend like the
666 // cutoff happens before 32-bits and instead happens at some much smaller
667 // value.
668 // FIXME allow lowering the threshold for tests
669 const SYM64_THRESHOLD: u64 = 1 << 32;
670
671 // If LastMemberHeaderOffset isn't going to fit in a 32-bit varible we need
672 // to switch to 64-bit. Note that the file can be larger than 4GB as long as
673 // the last member starts before the 4GB offset.
674 if last_member_header_offset >= SYM64_THRESHOLD {
675 if kind == ArchiveKind::Darwin {
676 kind = ArchiveKind::Darwin64;
677 } else {
678 kind = ArchiveKind::Gnu64;
679 }
680 }
681 }
682
683 if thin {
684 write!(w, "!<thin>\n")?;
685 } else if is_aix_big_archive(kind) {
686 write!(w, "<bigaf>\n")?;
687 } else {
688 write!(w, "!<arch>\n")?;
689 }
690
691 if !is_aix_big_archive(kind) {
692 if write_symtab {
693 write_symbol_table(w, kind, deterministic, &data, &sym_names, 0)?;
694 }
695
696 for m in data {
697 w.write_all(&m.header)?;
698 w.write_all(m.data)?;
699 w.write_all(m.padding)?;
700 }
701 } else {
702 // For the big archive (AIX) format, compute a table of member names and
703 // offsets, used in the member table.
704 let mut member_table_name_str_tbl_size = 0;
705 let mut member_offsets = vec![];
706 let mut member_names = vec![];
707
708 // Loop across object to find offset and names.
709 let mut member_end_offset =
710 u64::try_from(std::mem::size_of::<big_archive::FixLenHdr>()).unwrap();
711 for i in 0..new_members.len() {
712 let member = &new_members[i];
713 member_table_name_str_tbl_size += member.member_name.len() + 1;
714 member_offsets.push(member_end_offset);
715 member_names.push(&member.member_name);
716 // File member name ended with "`\n". The length is included in
717 // BigArMemHdrType.
718 member_end_offset += u64::try_from(std::mem::size_of::<big_archive::BigArMemHdrType>())
719 .unwrap()
720 + align_to(u64::try_from(data[i].data.len()).unwrap(), 2)
721 + align_to(u64::try_from(member.member_name.len()).unwrap(), 2);
722 }
723
724 // AIX member table size.
725 let member_table_size =
726 u64::try_from(20 + 20 * member_offsets.len() + member_table_name_str_tbl_size).unwrap();
727
728 let global_symbol_offset = if write_symtab && num_syms > 0 {
729 last_member_end_offset
730 + align_to(
731 u64::try_from(std::mem::size_of::<big_archive::BigArMemHdrType>()).unwrap()
732 + member_table_size,
733 2,
734 )
735 } else {
736 0
737 };
738
739 // Fixed Sized Header.
740 // Offset to member table
741 write!(
742 w,
743 "{:<20}",
744 if !new_members.is_empty() {
745 last_member_end_offset
746 } else {
747 0
748 }
749 )?;
750 // If there are no file members in the archive, there will be no global
751 // symbol table.
752 write!(
753 w,
754 "{:<20}",
755 if !new_members.is_empty() {
756 global_symbol_offset
757 } else {
758 0
759 }
760 )?;
761 // Offset to 64 bits global symbol table - Not supported yet
762 write!(w, "{:<20}", 0)?;
763 // Offset to first archive member
764 write!(
765 w,
766 "{:<20}",
767 if !new_members.is_empty() {
768 u64::try_from(std::mem::size_of::<big_archive::FixLenHdr>()).unwrap()
769 } else {
770 0
771 }
772 )?;
773 // Offset to last archive member
774 write!(
775 w,
776 "{:<20}",
777 if !new_members.is_empty() {
778 last_member_header_offset
779 } else {
780 0
781 }
782 )?;
783 // Offset to first member of free list - Not supported yet
784 write!(w, "{:<20}", 0)?;
785
786 for m in &data {
787 w.write_all(&m.header)?;
788 w.write_all(m.data)?;
789 if m.data.len() % 2 != 0 {
790 w.write_all(&[0])?;
791 }
792 }
793
794 if !new_members.is_empty() {
795 // Member table.
796 print_big_archive_member_header(
797 w,
798 "",
799 0,
800 0,
801 0,
802 0,
803 member_table_size,
804 last_member_header_offset,
805 global_symbol_offset,
806 )?;
807 write!(w, "{:<20}", member_offsets.len())?; // Number of members
808 for member_offset in member_offsets {
809 write!(w, "{:<20}", member_offset)?;
810 }
811 for member_name in member_names {
812 w.write_all(member_name.as_bytes())?;
813 w.write_all(&[0])?;
814 }
815
816 if member_table_name_str_tbl_size % 2 != 0 {
817 // Name table must be tail padded to an even number of
818 // bytes.
819 w.write_all(&[0])?;
820 }
821
822 if write_symtab && num_syms > 0 {
823 write_symbol_table(
824 w,
825 kind,
826 deterministic,
827 &data,
828 &sym_names,
829 last_member_end_offset,
830 )?;
831 }
832 }
833 }
834
835 w.flush()
836}
837