1 | use core::mem; |
2 | |
3 | use crate::endian::{BigEndian as BE, I16, U16, U32}; |
4 | use crate::write::string::*; |
5 | use crate::write::util::*; |
6 | use crate::write::*; |
7 | |
8 | use crate::xcoff; |
9 | |
10 | #[derive (Default, Clone, Copy)] |
11 | struct SectionOffsets { |
12 | address: u64, |
13 | data_offset: usize, |
14 | reloc_offset: usize, |
15 | } |
16 | |
17 | #[derive (Default, Clone, Copy)] |
18 | struct SymbolOffsets { |
19 | index: usize, |
20 | str_id: Option<StringId>, |
21 | aux_count: u8, |
22 | storage_class: u8, |
23 | } |
24 | |
25 | impl<'a> Object<'a> { |
26 | pub(crate) fn xcoff_section_info( |
27 | &self, |
28 | section: StandardSection, |
29 | ) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) { |
30 | match section { |
31 | StandardSection::Text => (&[], &b".text" [..], SectionKind::Text, SectionFlags::None), |
32 | StandardSection::Data => (&[], &b".data" [..], SectionKind::Data, SectionFlags::None), |
33 | StandardSection::ReadOnlyData |
34 | | StandardSection::ReadOnlyDataWithRel |
35 | | StandardSection::ReadOnlyString => ( |
36 | &[], |
37 | &b".rdata" [..], |
38 | SectionKind::ReadOnlyData, |
39 | SectionFlags::None, |
40 | ), |
41 | StandardSection::UninitializedData => ( |
42 | &[], |
43 | &b".bss" [..], |
44 | SectionKind::UninitializedData, |
45 | SectionFlags::None, |
46 | ), |
47 | StandardSection::Tls => (&[], &b".tdata" [..], SectionKind::Tls, SectionFlags::None), |
48 | StandardSection::UninitializedTls => ( |
49 | &[], |
50 | &b".tbss" [..], |
51 | SectionKind::UninitializedTls, |
52 | SectionFlags::None, |
53 | ), |
54 | StandardSection::TlsVariables => { |
55 | // Unsupported section. |
56 | (&[], &[], SectionKind::TlsVariables, SectionFlags::None) |
57 | } |
58 | StandardSection::Common => { |
59 | // Unsupported section. |
60 | (&[], &[], SectionKind::Common, SectionFlags::None) |
61 | } |
62 | StandardSection::GnuProperty => { |
63 | // Unsupported section. |
64 | (&[], &[], SectionKind::Note, SectionFlags::None) |
65 | } |
66 | } |
67 | } |
68 | |
69 | pub(crate) fn xcoff_translate_relocation(&mut self, reloc: &mut Relocation) -> Result<()> { |
70 | let (kind, _encoding, size) = if let RelocationFlags::Generic { |
71 | kind, |
72 | encoding, |
73 | size, |
74 | } = reloc.flags |
75 | { |
76 | (kind, encoding, size) |
77 | } else { |
78 | return Ok(()); |
79 | }; |
80 | |
81 | let r_rtype = match kind { |
82 | RelocationKind::Absolute => xcoff::R_POS, |
83 | RelocationKind::Relative => xcoff::R_REL, |
84 | RelocationKind::Got => xcoff::R_TOC, |
85 | _ => { |
86 | return Err(Error(format!("unimplemented relocation {:?}" , reloc))); |
87 | } |
88 | }; |
89 | let r_rsize = size - 1; |
90 | reloc.flags = RelocationFlags::Xcoff { r_rtype, r_rsize }; |
91 | Ok(()) |
92 | } |
93 | |
94 | pub(crate) fn xcoff_adjust_addend(&mut self, relocation: &mut Relocation) -> Result<bool> { |
95 | let r_rtype = if let RelocationFlags::Xcoff { r_rtype, .. } = relocation.flags { |
96 | r_rtype |
97 | } else { |
98 | return Err(Error(format!("invalid relocation flags {:?}" , relocation))); |
99 | }; |
100 | if r_rtype == xcoff::R_REL { |
101 | relocation.addend += 4; |
102 | } |
103 | Ok(true) |
104 | } |
105 | |
106 | pub(crate) fn xcoff_relocation_size(&self, reloc: &Relocation) -> Result<u8> { |
107 | let r_rsize = if let RelocationFlags::Xcoff { r_rsize, .. } = reloc.flags { |
108 | r_rsize |
109 | } else { |
110 | return Err(Error(format!("unexpected relocation {:?}" , reloc))); |
111 | }; |
112 | Ok(r_rsize + 1) |
113 | } |
114 | |
115 | pub(crate) fn xcoff_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> { |
116 | let is_64 = match self.architecture.address_size().unwrap() { |
117 | AddressSize::U8 | AddressSize::U16 | AddressSize::U32 => false, |
118 | AddressSize::U64 => true, |
119 | }; |
120 | |
121 | let (hdr_size, sechdr_size, rel_size, sym_size) = if is_64 { |
122 | ( |
123 | mem::size_of::<xcoff::FileHeader64>(), |
124 | mem::size_of::<xcoff::SectionHeader64>(), |
125 | mem::size_of::<xcoff::Rel64>(), |
126 | mem::size_of::<xcoff::Symbol64>(), |
127 | ) |
128 | } else { |
129 | ( |
130 | mem::size_of::<xcoff::FileHeader32>(), |
131 | mem::size_of::<xcoff::SectionHeader32>(), |
132 | mem::size_of::<xcoff::Rel32>(), |
133 | mem::size_of::<xcoff::Symbol32>(), |
134 | ) |
135 | }; |
136 | |
137 | // Calculate offsets and build strtab. |
138 | let mut offset = 0; |
139 | let mut strtab = StringTable::default(); |
140 | // We place the shared address 0 immediately after the section header table. |
141 | let mut address = 0; |
142 | |
143 | // XCOFF file header. |
144 | offset += hdr_size; |
145 | // Section headers. |
146 | offset += self.sections.len() * sechdr_size; |
147 | |
148 | // Calculate size of section data. |
149 | let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()]; |
150 | for (index, section) in self.sections.iter().enumerate() { |
151 | let len = section.data.len(); |
152 | let sectype = section.kind; |
153 | // Section address should be 0 for all sections except the .text, .data, and .bss sections. |
154 | if sectype == SectionKind::Data |
155 | || sectype == SectionKind::Text |
156 | || sectype == SectionKind::UninitializedData |
157 | { |
158 | section_offsets[index].address = address as u64; |
159 | address += len; |
160 | address = align(address, 4); |
161 | } else { |
162 | section_offsets[index].address = 0; |
163 | } |
164 | if len != 0 { |
165 | // Set the default section alignment as 4. |
166 | offset = align(offset, 4); |
167 | section_offsets[index].data_offset = offset; |
168 | offset += len; |
169 | } else { |
170 | section_offsets[index].data_offset = 0; |
171 | } |
172 | } |
173 | |
174 | // Calculate size of relocations. |
175 | for (index, section) in self.sections.iter().enumerate() { |
176 | let count = section.relocations.len(); |
177 | if count != 0 { |
178 | section_offsets[index].reloc_offset = offset; |
179 | offset += count * rel_size; |
180 | } else { |
181 | section_offsets[index].reloc_offset = 0; |
182 | } |
183 | } |
184 | |
185 | // Calculate size of symbols. |
186 | let mut file_str_id = None; |
187 | let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()]; |
188 | let mut symtab_count = 0; |
189 | for (index, symbol) in self.symbols.iter().enumerate() { |
190 | symbol_offsets[index].index = symtab_count; |
191 | symtab_count += 1; |
192 | |
193 | let storage_class = if let SymbolFlags::Xcoff { n_sclass, .. } = symbol.flags { |
194 | n_sclass |
195 | } else { |
196 | match symbol.kind { |
197 | SymbolKind::File => xcoff::C_FILE, |
198 | SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls => { |
199 | if symbol.is_local() { |
200 | xcoff::C_STAT |
201 | } else if symbol.weak { |
202 | xcoff::C_WEAKEXT |
203 | } else { |
204 | xcoff::C_EXT |
205 | } |
206 | } |
207 | SymbolKind::Section | SymbolKind::Label | SymbolKind::Unknown => { |
208 | return Err(Error(format!( |
209 | "unimplemented symbol ` {}` kind {:?}" , |
210 | symbol.name().unwrap_or("" ), |
211 | symbol.kind |
212 | ))); |
213 | } |
214 | } |
215 | }; |
216 | symbol_offsets[index].storage_class = storage_class; |
217 | |
218 | if storage_class == xcoff::C_FILE { |
219 | if is_64 && file_str_id.is_none() { |
220 | file_str_id = Some(strtab.add(b".file" )); |
221 | } |
222 | if symbol.name.len() > 8 { |
223 | symbol_offsets[index].str_id = Some(strtab.add(&symbol.name)); |
224 | } |
225 | } else if is_64 || symbol.name.len() > 8 { |
226 | symbol_offsets[index].str_id = Some(strtab.add(&symbol.name)); |
227 | } |
228 | |
229 | symbol_offsets[index].aux_count = 0; |
230 | match storage_class { |
231 | xcoff::C_FILE => { |
232 | symbol_offsets[index].aux_count = 1; |
233 | symtab_count += 1; |
234 | } |
235 | xcoff::C_EXT | xcoff::C_WEAKEXT | xcoff::C_HIDEXT => { |
236 | symbol_offsets[index].aux_count = 1; |
237 | symtab_count += 1; |
238 | } |
239 | // TODO: support auxiliary entry for other types of symbol. |
240 | _ => {} |
241 | } |
242 | } |
243 | let symtab_offset = offset; |
244 | let symtab_len = symtab_count * sym_size; |
245 | offset += symtab_len; |
246 | |
247 | // Calculate size of strtab. |
248 | let strtab_offset = offset; |
249 | let mut strtab_data = Vec::new(); |
250 | // First 4 bytes of strtab are the length. |
251 | strtab.write(4, &mut strtab_data); |
252 | let strtab_len = strtab_data.len() + 4; |
253 | offset += strtab_len; |
254 | |
255 | // Start writing. |
256 | buffer |
257 | .reserve(offset) |
258 | .map_err(|_| Error(String::from("Cannot allocate buffer" )))?; |
259 | |
260 | // Write file header. |
261 | if is_64 { |
262 | let header = xcoff::FileHeader64 { |
263 | f_magic: U16::new(BE, xcoff::MAGIC_64), |
264 | f_nscns: U16::new(BE, self.sections.len() as u16), |
265 | f_timdat: U32::new(BE, 0), |
266 | f_symptr: U64::new(BE, symtab_offset as u64), |
267 | f_nsyms: U32::new(BE, symtab_count as u32), |
268 | f_opthdr: U16::new(BE, 0), |
269 | f_flags: match self.flags { |
270 | FileFlags::Xcoff { f_flags } => U16::new(BE, f_flags), |
271 | _ => U16::default(), |
272 | }, |
273 | }; |
274 | buffer.write(&header); |
275 | } else { |
276 | let header = xcoff::FileHeader32 { |
277 | f_magic: U16::new(BE, xcoff::MAGIC_32), |
278 | f_nscns: U16::new(BE, self.sections.len() as u16), |
279 | f_timdat: U32::new(BE, 0), |
280 | f_symptr: U32::new(BE, symtab_offset as u32), |
281 | f_nsyms: U32::new(BE, symtab_count as u32), |
282 | f_opthdr: U16::new(BE, 0), |
283 | f_flags: match self.flags { |
284 | FileFlags::Xcoff { f_flags } => U16::new(BE, f_flags), |
285 | _ => U16::default(), |
286 | }, |
287 | }; |
288 | buffer.write(&header); |
289 | } |
290 | |
291 | // Write section headers. |
292 | for (index, section) in self.sections.iter().enumerate() { |
293 | let mut sectname = [0; 8]; |
294 | sectname |
295 | .get_mut(..section.name.len()) |
296 | .ok_or_else(|| { |
297 | Error(format!( |
298 | "section name ` {}` is too long" , |
299 | section.name().unwrap_or("" ), |
300 | )) |
301 | })? |
302 | .copy_from_slice(§ion.name); |
303 | let flags = if let SectionFlags::Xcoff { s_flags } = section.flags { |
304 | s_flags |
305 | } else { |
306 | match section.kind { |
307 | SectionKind::Text |
308 | | SectionKind::ReadOnlyData |
309 | | SectionKind::ReadOnlyString |
310 | | SectionKind::ReadOnlyDataWithRel => xcoff::STYP_TEXT, |
311 | SectionKind::Data => xcoff::STYP_DATA, |
312 | SectionKind::UninitializedData => xcoff::STYP_BSS, |
313 | SectionKind::Tls => xcoff::STYP_TDATA, |
314 | SectionKind::UninitializedTls => xcoff::STYP_TBSS, |
315 | SectionKind::OtherString => xcoff::STYP_INFO, |
316 | SectionKind::Debug | SectionKind::DebugString => xcoff::STYP_DEBUG, |
317 | SectionKind::Other | SectionKind::Metadata => 0, |
318 | SectionKind::Note |
319 | | SectionKind::Linker |
320 | | SectionKind::Common |
321 | | SectionKind::Unknown |
322 | | SectionKind::TlsVariables |
323 | | SectionKind::Elf(_) => { |
324 | return Err(Error(format!( |
325 | "unimplemented section ` {}` kind {:?}" , |
326 | section.name().unwrap_or("" ), |
327 | section.kind |
328 | ))); |
329 | } |
330 | } |
331 | .into() |
332 | }; |
333 | if is_64 { |
334 | let section_header = xcoff::SectionHeader64 { |
335 | s_name: sectname, |
336 | s_paddr: U64::new(BE, section_offsets[index].address), |
337 | // This field has the same value as the s_paddr field. |
338 | s_vaddr: U64::new(BE, section_offsets[index].address), |
339 | s_size: U64::new(BE, section.data.len() as u64), |
340 | s_scnptr: U64::new(BE, section_offsets[index].data_offset as u64), |
341 | s_relptr: U64::new(BE, section_offsets[index].reloc_offset as u64), |
342 | s_lnnoptr: U64::new(BE, 0), |
343 | s_nreloc: U32::new(BE, section.relocations.len() as u32), |
344 | s_nlnno: U32::new(BE, 0), |
345 | s_flags: U32::new(BE, flags), |
346 | s_reserve: U32::new(BE, 0), |
347 | }; |
348 | buffer.write(§ion_header); |
349 | } else { |
350 | let section_header = xcoff::SectionHeader32 { |
351 | s_name: sectname, |
352 | s_paddr: U32::new(BE, section_offsets[index].address as u32), |
353 | // This field has the same value as the s_paddr field. |
354 | s_vaddr: U32::new(BE, section_offsets[index].address as u32), |
355 | s_size: U32::new(BE, section.data.len() as u32), |
356 | s_scnptr: U32::new(BE, section_offsets[index].data_offset as u32), |
357 | s_relptr: U32::new(BE, section_offsets[index].reloc_offset as u32), |
358 | s_lnnoptr: U32::new(BE, 0), |
359 | // TODO: If more than 65,534 relocation entries are required, the field |
360 | // value will be 65535, and an STYP_OVRFLO section header will contain |
361 | // the actual count of relocation entries in the s_paddr field. |
362 | s_nreloc: U16::new(BE, section.relocations.len() as u16), |
363 | s_nlnno: U16::new(BE, 0), |
364 | s_flags: U32::new(BE, flags), |
365 | }; |
366 | buffer.write(§ion_header); |
367 | } |
368 | } |
369 | |
370 | // Write section data. |
371 | for (index, section) in self.sections.iter().enumerate() { |
372 | let len = section.data.len(); |
373 | if len != 0 { |
374 | write_align(buffer, 4); |
375 | debug_assert_eq!(section_offsets[index].data_offset, buffer.len()); |
376 | buffer.write_bytes(§ion.data); |
377 | } |
378 | } |
379 | |
380 | // Write relocations. |
381 | for (index, section) in self.sections.iter().enumerate() { |
382 | if !section.relocations.is_empty() { |
383 | debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len()); |
384 | for reloc in §ion.relocations { |
385 | let (r_rtype, r_rsize) = |
386 | if let RelocationFlags::Xcoff { r_rtype, r_rsize } = reloc.flags { |
387 | (r_rtype, r_rsize) |
388 | } else { |
389 | return Err(Error("invalid relocation flags" .into())); |
390 | }; |
391 | if is_64 { |
392 | let xcoff_rel = xcoff::Rel64 { |
393 | r_vaddr: U64::new(BE, reloc.offset), |
394 | r_symndx: U32::new(BE, symbol_offsets[reloc.symbol.0].index as u32), |
395 | r_rsize, |
396 | r_rtype, |
397 | }; |
398 | buffer.write(&xcoff_rel); |
399 | } else { |
400 | let xcoff_rel = xcoff::Rel32 { |
401 | r_vaddr: U32::new(BE, reloc.offset as u32), |
402 | r_symndx: U32::new(BE, symbol_offsets[reloc.symbol.0].index as u32), |
403 | r_rsize, |
404 | r_rtype, |
405 | }; |
406 | buffer.write(&xcoff_rel); |
407 | } |
408 | } |
409 | } |
410 | } |
411 | |
412 | // Write symbols. |
413 | debug_assert_eq!(symtab_offset, buffer.len()); |
414 | for (index, symbol) in self.symbols.iter().enumerate() { |
415 | let (n_value, section_kind) = if let SymbolSection::Section(id) = symbol.section { |
416 | ( |
417 | section_offsets[id.0].address + symbol.value, |
418 | self.sections[id.0].kind, |
419 | ) |
420 | } else { |
421 | (symbol.value, SectionKind::Unknown) |
422 | }; |
423 | let n_scnum = match symbol.section { |
424 | SymbolSection::None => { |
425 | debug_assert_eq!(symbol.kind, SymbolKind::File); |
426 | xcoff::N_DEBUG |
427 | } |
428 | SymbolSection::Undefined | SymbolSection::Common => xcoff::N_UNDEF, |
429 | SymbolSection::Absolute => xcoff::N_ABS, |
430 | SymbolSection::Section(id) => id.0 as i16 + 1, |
431 | }; |
432 | let n_sclass = symbol_offsets[index].storage_class; |
433 | let n_type = if (symbol.scope == SymbolScope::Linkage) |
434 | && (n_sclass == xcoff::C_EXT |
435 | || n_sclass == xcoff::C_WEAKEXT |
436 | || n_sclass == xcoff::C_HIDEXT) |
437 | { |
438 | xcoff::SYM_V_HIDDEN |
439 | } else { |
440 | 0 |
441 | }; |
442 | let n_numaux = symbol_offsets[index].aux_count; |
443 | if is_64 { |
444 | let str_id = if n_sclass == xcoff::C_FILE { |
445 | file_str_id.unwrap() |
446 | } else { |
447 | symbol_offsets[index].str_id.unwrap() |
448 | }; |
449 | let xcoff_sym = xcoff::Symbol64 { |
450 | n_value: U64::new(BE, n_value), |
451 | n_offset: U32::new(BE, strtab.get_offset(str_id) as u32), |
452 | n_scnum: I16::new(BE, n_scnum), |
453 | n_type: U16::new(BE, n_type), |
454 | n_sclass, |
455 | n_numaux, |
456 | }; |
457 | buffer.write(&xcoff_sym); |
458 | } else { |
459 | let mut sym_name = [0; 8]; |
460 | if n_sclass == xcoff::C_FILE { |
461 | sym_name[..5].copy_from_slice(b".file" ); |
462 | } else if symbol.name.len() <= 8 { |
463 | sym_name[..symbol.name.len()].copy_from_slice(&symbol.name[..]); |
464 | } else { |
465 | let str_offset = strtab.get_offset(symbol_offsets[index].str_id.unwrap()); |
466 | sym_name[4..8].copy_from_slice(&u32::to_be_bytes(str_offset as u32)); |
467 | } |
468 | let xcoff_sym = xcoff::Symbol32 { |
469 | n_name: sym_name, |
470 | n_value: U32::new(BE, n_value as u32), |
471 | n_scnum: I16::new(BE, n_scnum), |
472 | n_type: U16::new(BE, n_type), |
473 | n_sclass, |
474 | n_numaux, |
475 | }; |
476 | buffer.write(&xcoff_sym); |
477 | } |
478 | // Generate auxiliary entries. |
479 | if n_sclass == xcoff::C_FILE { |
480 | debug_assert_eq!(n_numaux, 1); |
481 | let mut x_fname = [0; 8]; |
482 | if symbol.name.len() <= 8 { |
483 | x_fname[..symbol.name.len()].copy_from_slice(&symbol.name[..]); |
484 | } else { |
485 | let str_offset = strtab.get_offset(symbol_offsets[index].str_id.unwrap()); |
486 | x_fname[4..8].copy_from_slice(&u32::to_be_bytes(str_offset as u32)); |
487 | } |
488 | if is_64 { |
489 | let file_aux = xcoff::FileAux64 { |
490 | x_fname, |
491 | x_fpad: Default::default(), |
492 | x_ftype: xcoff::XFT_FN, |
493 | x_freserve: Default::default(), |
494 | x_auxtype: xcoff::AUX_FILE, |
495 | }; |
496 | buffer.write(&file_aux); |
497 | } else { |
498 | let file_aux = xcoff::FileAux32 { |
499 | x_fname, |
500 | x_fpad: Default::default(), |
501 | x_ftype: xcoff::XFT_FN, |
502 | x_freserve: Default::default(), |
503 | }; |
504 | buffer.write(&file_aux); |
505 | } |
506 | } else if n_sclass == xcoff::C_EXT |
507 | || n_sclass == xcoff::C_WEAKEXT |
508 | || n_sclass == xcoff::C_HIDEXT |
509 | { |
510 | debug_assert_eq!(n_numaux, 1); |
511 | let (x_smtyp, x_smclas) = if let SymbolFlags::Xcoff { |
512 | x_smtyp, x_smclas, .. |
513 | } = symbol.flags |
514 | { |
515 | (x_smtyp, x_smclas) |
516 | } else { |
517 | match symbol.kind { |
518 | SymbolKind::Text => (xcoff::XTY_SD, xcoff::XMC_PR), |
519 | SymbolKind::Data => { |
520 | if section_kind == SectionKind::UninitializedData { |
521 | (xcoff::XTY_CM, xcoff::XMC_BS) |
522 | } else if section_kind == SectionKind::ReadOnlyData { |
523 | (xcoff::XTY_SD, xcoff::XMC_RO) |
524 | } else { |
525 | (xcoff::XTY_SD, xcoff::XMC_RW) |
526 | } |
527 | } |
528 | SymbolKind::Tls => { |
529 | if section_kind == SectionKind::UninitializedTls { |
530 | (xcoff::XTY_CM, xcoff::XMC_UL) |
531 | } else { |
532 | (xcoff::XTY_SD, xcoff::XMC_TL) |
533 | } |
534 | } |
535 | _ => { |
536 | return Err(Error(format!( |
537 | "unimplemented symbol ` {}` kind {:?}" , |
538 | symbol.name().unwrap_or("" ), |
539 | symbol.kind |
540 | ))); |
541 | } |
542 | } |
543 | }; |
544 | let scnlen = if let SymbolFlags::Xcoff { |
545 | containing_csect: Some(containing_csect), |
546 | .. |
547 | } = symbol.flags |
548 | { |
549 | symbol_offsets[containing_csect.0].index as u64 |
550 | } else { |
551 | symbol.size |
552 | }; |
553 | if is_64 { |
554 | let csect_aux = xcoff::CsectAux64 { |
555 | x_scnlen_lo: U32::new(BE, (scnlen & 0xFFFFFFFF) as u32), |
556 | x_scnlen_hi: U32::new(BE, ((scnlen >> 32) & 0xFFFFFFFF) as u32), |
557 | x_parmhash: U32::new(BE, 0), |
558 | x_snhash: U16::new(BE, 0), |
559 | x_smtyp, |
560 | x_smclas, |
561 | pad: 0, |
562 | x_auxtype: xcoff::AUX_CSECT, |
563 | }; |
564 | buffer.write(&csect_aux); |
565 | } else { |
566 | let csect_aux = xcoff::CsectAux32 { |
567 | x_scnlen: U32::new(BE, scnlen as u32), |
568 | x_parmhash: U32::new(BE, 0), |
569 | x_snhash: U16::new(BE, 0), |
570 | x_smtyp, |
571 | x_smclas, |
572 | x_stab: U32::new(BE, 0), |
573 | x_snstab: U16::new(BE, 0), |
574 | }; |
575 | buffer.write(&csect_aux); |
576 | } |
577 | } |
578 | } |
579 | |
580 | // Write string table. |
581 | debug_assert_eq!(strtab_offset, buffer.len()); |
582 | buffer.write_bytes(&u32::to_be_bytes(strtab_len as u32)); |
583 | buffer.write_bytes(&strtab_data); |
584 | |
585 | debug_assert_eq!(offset, buffer.len()); |
586 | Ok(()) |
587 | } |
588 | } |
589 | |