1 | use core::mem; |
2 | |
3 | use crate::endian::{BigEndian as BE, I16, U16, U32}; |
4 | use crate::write::string::*; |
5 | use crate::write::util::*; |
6 | use crate::write::*; |
7 | |
8 | use crate::{xcoff, AddressSize}; |
9 | |
10 | #[derive (Default, Clone, Copy)] |
11 | struct SectionOffsets { |
12 | address: u64, |
13 | data_offset: usize, |
14 | reloc_offset: usize, |
15 | } |
16 | |
17 | #[derive (Default, Clone, Copy)] |
18 | struct SymbolOffsets { |
19 | index: usize, |
20 | str_id: Option<StringId>, |
21 | aux_count: u8, |
22 | storage_class: u8, |
23 | } |
24 | |
25 | impl<'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(§ion.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(§ion_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(§ion_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(§ion.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 §ion.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 | |