object/write/
xcoff.rs

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                // Unsupported section.
59                (&[], &[], SectionKind::TlsVariables, SectionFlags::None)
60            }
61            StandardSection::Common => {
62                // Unsupported section.
63                (&[], &[], SectionKind::Common, SectionFlags::None)
64            }
65            StandardSection::GnuProperty => {
66                // Unsupported section.
67                (&[], &[], 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        // Calculate offsets and build strtab.
225        let mut offset = 0;
226        let mut strtab = StringTable::default();
227        // We place the shared address 0 immediately after the section header table.
228        let mut address = 0;
229
230        // XCOFF file header.
231        offset += hdr_size;
232        // Section headers.
233        offset += self.sections.len() * sechdr_size;
234
235        // Calculate size of section data.
236        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            // Section address should be 0 for all sections except the .text, .data, and .bss sections.
241            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                // Set the default section alignment as 4.
253                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        // Calculate size of relocations.
262        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        // Calculate size of symbols.
273        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                // TODO: support auxiliary entry for other types of symbol.
320                _ => {}
321            }
322        }
323        let symtab_offset = offset;
324        let symtab_len = symtab_count * sym_size;
325        offset += symtab_len;
326
327        // Calculate size of strtab.
328        let strtab_offset = offset;
329        let mut strtab_data = Vec::new();
330        // First 4 bytes of strtab are the length.
331        strtab.write(4, &mut strtab_data);
332        let strtab_len = strtab_data.len() + 4;
333        offset += strtab_len;
334
335        // Start writing.
336        buffer
337            .reserve(offset)
338            .map_err(|_| Error(String::from("Cannot allocate buffer")))?;
339
340        // Write file header.
341        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        // Write section headers.
372        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(&section.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                    // This field has the same value as the s_paddr field.
395                    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(&section_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                    // This field has the same value as the s_paddr field.
411                    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                    // TODO: If more than 65,534 relocation entries are required, the field
417                    // value will be 65535, and an STYP_OVRFLO section header will contain
418                    // the actual count of relocation entries in the s_paddr field.
419                    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(&section_header);
424            }
425        }
426
427        // Write section data.
428        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(&section.data);
434            }
435        }
436
437        // Write relocations.
438        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 &section.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        // Write symbols.
470        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            // Generate auxiliary entries.
533            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        // Write string table.
601        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}