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