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}
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_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(&section.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(&section_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(&section_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(&section.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 &section.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}