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;
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 (&[], &[], SectionKind::TlsVariables, SectionFlags::None)
57 }
58 StandardSection::Common => {
59 (&[], &[], SectionKind::Common, SectionFlags::None)
61 }
62 StandardSection::GnuProperty => {
63 (&[], &[], 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 let mut offset = 0;
139 let mut strtab = StringTable::default();
140 let mut address = 0;
142
143 offset += hdr_size;
145 offset += self.sections.len() * sechdr_size;
147
148 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 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 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 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 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 _ => {}
241 }
242 }
243 let symtab_offset = offset;
244 let symtab_len = symtab_count * sym_size;
245 offset += symtab_len;
246
247 let strtab_offset = offset;
249 let mut strtab_data = Vec::new();
250 strtab.write(4, &mut strtab_data);
252 let strtab_len = strtab_data.len() + 4;
253 offset += strtab_len;
254
255 buffer
257 .reserve(offset)
258 .map_err(|_| Error(String::from("Cannot allocate buffer")))?;
259
260 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 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 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 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 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 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 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 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 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 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}