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