1 use crc32fast;
2 use scroll::ctx::SizeWith;
3 use scroll::IOwrite;
4 use std::iter;
5 use std::string::String;
6 
7 use crate::alloc::vec::Vec;
8 use crate::write::string::*;
9 use crate::write::util::*;
10 use crate::write::*;
11 
12 mod coff {
13     pub use goblin::pe::characteristic::*;
14     pub use goblin::pe::header::*;
15     pub use goblin::pe::relocation::*;
16     pub use goblin::pe::section_table::*;
17     pub use goblin::pe::symbol::*;
18 }
19 
20 #[derive(Default, Clone, Copy)]
21 struct SectionOffsets {
22     offset: usize,
23     str_id: Option<StringId>,
24     reloc_offset: usize,
25 }
26 
27 #[derive(Default, Clone, Copy)]
28 struct SymbolOffsets {
29     index: usize,
30     str_id: Option<StringId>,
31     aux_count: u8,
32 }
33 
34 impl Object {
coff_section_info( &self, section: StandardSection, ) -> (&'static [u8], &'static [u8], SectionKind)35     pub(crate) fn coff_section_info(
36         &self,
37         section: StandardSection,
38     ) -> (&'static [u8], &'static [u8], SectionKind) {
39         match section {
40             StandardSection::Text => (&[], &b".text"[..], SectionKind::Text),
41             StandardSection::Data => (&[], &b".data"[..], SectionKind::Data),
42             StandardSection::ReadOnlyData
43             | StandardSection::ReadOnlyDataWithRel
44             | StandardSection::ReadOnlyString => (&[], &b".rdata"[..], SectionKind::ReadOnlyData),
45             StandardSection::UninitializedData => {
46                 (&[], &b".bss"[..], SectionKind::UninitializedData)
47             }
48             StandardSection::Tls => (&[], &b".tls$"[..], SectionKind::Tls),
49             StandardSection::UninitializedTls => {
50                 // Unsupported section.
51                 (&[], &[], SectionKind::UninitializedTls)
52             }
53             StandardSection::TlsVariables => {
54                 // Unsupported section.
55                 (&[], &[], SectionKind::TlsVariables)
56             }
57         }
58     }
59 
coff_subsection_name(&self, section: &[u8], value: &[u8]) -> Vec<u8>60     pub(crate) fn coff_subsection_name(&self, section: &[u8], value: &[u8]) -> Vec<u8> {
61         let mut name = section.to_vec();
62         name.push(b'$');
63         name.extend(value);
64         name
65     }
66 
coff_fixup_relocation(&mut self, mut relocation: &mut Relocation) -> i6467     pub(crate) fn coff_fixup_relocation(&mut self, mut relocation: &mut Relocation) -> i64 {
68         if relocation.kind == RelocationKind::GotRelative {
69             // Use a stub symbol for the relocation instead.
70             // This isn't really a GOT, but it's a similar purpose.
71             // TODO: need to handle DLL imports differently?
72             relocation.kind = RelocationKind::Relative;
73             relocation.symbol = self.coff_add_stub_symbol(relocation.symbol);
74         } else if relocation.kind == RelocationKind::PltRelative {
75             // Windows doesn't need a separate relocation type for
76             // references to functions in import libraries.
77             // For convenience, treat this the same as Relative.
78             relocation.kind = RelocationKind::Relative;
79         }
80 
81         let constant = match self.architecture {
82             Architecture::I386 => match relocation.kind {
83                 RelocationKind::Relative => {
84                     // IMAGE_REL_I386_REL32
85                     relocation.addend + 4
86                 }
87                 _ => relocation.addend,
88             },
89             Architecture::X86_64 => match relocation.kind {
90                 RelocationKind::Relative => {
91                     // IMAGE_REL_AMD64_REL32 through to IMAGE_REL_AMD64_REL32_5
92                     if relocation.addend >= -4 && relocation.addend <= -9 {
93                         0
94                     } else {
95                         relocation.addend + 4
96                     }
97                 }
98                 _ => relocation.addend,
99             },
100             _ => unimplemented!(),
101         };
102         relocation.addend -= constant;
103         constant
104     }
105 
coff_add_stub_symbol(&mut self, symbol_id: SymbolId) -> SymbolId106     fn coff_add_stub_symbol(&mut self, symbol_id: SymbolId) -> SymbolId {
107         if let Some(stub_id) = self.stub_symbols.get(&symbol_id) {
108             return *stub_id;
109         }
110         let stub_size = self.architecture.pointer_width().unwrap().bytes();
111 
112         let mut name = b".rdata$.refptr.".to_vec();
113         name.extend(&self.symbols[symbol_id.0].name);
114         let section_id = self.add_section(Vec::new(), name, SectionKind::ReadOnlyData);
115         let section = self.section_mut(section_id);
116         section.set_data(vec![0; stub_size as usize], u64::from(stub_size));
117         section.relocations = vec![Relocation {
118             offset: 0,
119             size: stub_size * 8,
120             kind: RelocationKind::Absolute,
121             encoding: RelocationEncoding::Generic,
122             symbol: symbol_id,
123             addend: 0,
124         }];
125 
126         let mut name = b".refptr.".to_vec();
127         name.extend(&self.symbol(symbol_id).name);
128         let stub_id = self.add_raw_symbol(Symbol {
129             name,
130             value: 0,
131             size: u64::from(stub_size),
132             kind: SymbolKind::Data,
133             scope: SymbolScope::Compilation,
134             weak: false,
135             section: Some(section_id),
136         });
137         self.stub_symbols.insert(symbol_id, stub_id);
138 
139         stub_id
140     }
141 
coff_write(&self) -> Result<Vec<u8>, String>142     pub(crate) fn coff_write(&self) -> Result<Vec<u8>, String> {
143         // Calculate offsets of everything, and build strtab.
144         let mut offset = 0;
145         let mut strtab = StringTable::default();
146 
147         // COFF header.
148         let ctx = scroll::LE;
149         offset += coff::CoffHeader::size_with(&ctx);
150 
151         // Section headers.
152         offset += self.sections.len() * coff::SectionTable::size_with(&ctx);
153 
154         // Calculate size of section data and add section strings to strtab.
155         let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()];
156         for (index, section) in self.sections.iter().enumerate() {
157             if section.name.len() > 8 {
158                 section_offsets[index].str_id = Some(strtab.add(&section.name));
159             }
160 
161             let len = section.data.len();
162             if len != 0 {
163                 // TODO: not sure what alignment is required here, but this seems to match LLVM
164                 offset = align(offset, 4);
165                 section_offsets[index].offset = offset;
166                 offset += len;
167             } else {
168                 section_offsets[index].offset = offset;
169             }
170 
171             // Calculate size of relocations.
172             let count = section.relocations.len();
173             if count != 0 {
174                 section_offsets[index].reloc_offset = offset;
175                 offset += count * coff::Relocation::size_with(&ctx);
176             }
177         }
178 
179         // Calculate size of symbols and add symbol strings to strtab.
180         let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()];
181         let mut symtab_count = 0;
182         for (index, symbol) in self.symbols.iter().enumerate() {
183             symbol_offsets[index].index = symtab_count;
184             symtab_count += 1;
185             match symbol.kind {
186                 SymbolKind::File => {
187                     // Name goes in auxilary symbol records.
188                     let aux_count =
189                         (symbol.name.len() + coff::COFF_SYMBOL_SIZE - 1) / coff::COFF_SYMBOL_SIZE;
190                     symbol_offsets[index].aux_count = aux_count as u8;
191                     symtab_count += aux_count;
192                     // Don't add name to strtab.
193                     continue;
194                 }
195                 SymbolKind::Section => {
196                     symbol_offsets[index].aux_count = 1;
197                     symtab_count += 1;
198                 }
199                 _ => {}
200             }
201             if symbol.name.len() > 8 {
202                 symbol_offsets[index].str_id = Some(strtab.add(&symbol.name));
203             }
204         }
205 
206         // Calculate size of symtab.
207         let symtab_offset = offset;
208         let symtab_len = symtab_count * coff::COFF_SYMBOL_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         let mut buffer = Vec::with_capacity(offset);
221 
222         // Write file header.
223         let header = coff::CoffHeader {
224             machine: match self.architecture {
225                 Architecture::I386 => coff::COFF_MACHINE_X86,
226                 Architecture::X86_64 => coff::COFF_MACHINE_X86_64,
227                 _ => {
228                     return Err(format!(
229                         "unimplemented architecture {:?}",
230                         self.architecture
231                     ))
232                 }
233             },
234             number_of_sections: self.sections.len() as u16,
235             time_date_stamp: 0,
236             pointer_to_symbol_table: symtab_offset as u32,
237             number_of_symbol_table: symtab_count as u32,
238             size_of_optional_header: 0,
239             characteristics: 0,
240         };
241         buffer.iowrite_with(header, ctx).unwrap();
242 
243         // Write section headers.
244         for (index, section) in self.sections.iter().enumerate() {
245             // TODO: IMAGE_SCN_LNK_COMDAT
246             let characteristics = match section.kind {
247                 SectionKind::Text => {
248                     coff::IMAGE_SCN_CNT_CODE
249                         | coff::IMAGE_SCN_MEM_EXECUTE
250                         | coff::IMAGE_SCN_MEM_READ
251                 }
252                 SectionKind::Data => {
253                     coff::IMAGE_SCN_CNT_INITIALIZED_DATA
254                         | coff::IMAGE_SCN_MEM_READ
255                         | coff::IMAGE_SCN_MEM_WRITE
256                 }
257                 SectionKind::UninitializedData => {
258                     coff::IMAGE_SCN_CNT_UNINITIALIZED_DATA
259                         | coff::IMAGE_SCN_MEM_READ
260                         | coff::IMAGE_SCN_MEM_WRITE
261                 }
262                 SectionKind::ReadOnlyData | SectionKind::ReadOnlyString => {
263                     coff::IMAGE_SCN_CNT_INITIALIZED_DATA | coff::IMAGE_SCN_MEM_READ
264                 }
265                 SectionKind::Debug | SectionKind::Other | SectionKind::OtherString => {
266                     coff::IMAGE_SCN_CNT_INITIALIZED_DATA
267                         | coff::IMAGE_SCN_MEM_READ
268                         | coff::IMAGE_SCN_MEM_DISCARDABLE
269                 }
270                 SectionKind::Linker => coff::IMAGE_SCN_LNK_INFO | coff::IMAGE_SCN_LNK_REMOVE,
271                 SectionKind::Tls
272                 | SectionKind::UninitializedTls
273                 | SectionKind::TlsVariables
274                 | SectionKind::Unknown
275                 | SectionKind::Metadata => {
276                     return Err(format!("unimplemented section {:?}", section.kind))
277                 }
278             };
279             let align = match section.align {
280                 1 => coff::IMAGE_SCN_ALIGN_1BYTES,
281                 2 => coff::IMAGE_SCN_ALIGN_2BYTES,
282                 4 => coff::IMAGE_SCN_ALIGN_4BYTES,
283                 8 => coff::IMAGE_SCN_ALIGN_8BYTES,
284                 16 => coff::IMAGE_SCN_ALIGN_16BYTES,
285                 32 => coff::IMAGE_SCN_ALIGN_32BYTES,
286                 64 => coff::IMAGE_SCN_ALIGN_64BYTES,
287                 128 => coff::IMAGE_SCN_ALIGN_128BYTES,
288                 256 => coff::IMAGE_SCN_ALIGN_256BYTES,
289                 512 => coff::IMAGE_SCN_ALIGN_512BYTES,
290                 1024 => coff::IMAGE_SCN_ALIGN_1024BYTES,
291                 2048 => coff::IMAGE_SCN_ALIGN_2048BYTES,
292                 4096 => coff::IMAGE_SCN_ALIGN_4096BYTES,
293                 8192 => coff::IMAGE_SCN_ALIGN_8192BYTES,
294                 _ => return Err(format!("unimplemented section align {}", section.align)),
295             };
296             let mut coff_section = coff::SectionTable {
297                 name: [0; 8],
298                 real_name: None,
299                 virtual_size: if section.data.is_empty() {
300                     section.size as u32
301                 } else {
302                     0
303                 },
304                 virtual_address: 0,
305                 size_of_raw_data: section.data.len() as u32,
306                 pointer_to_raw_data: if section.data.is_empty() {
307                     0
308                 } else {
309                     section_offsets[index].offset as u32
310                 },
311                 pointer_to_relocations: section_offsets[index].reloc_offset as u32,
312                 pointer_to_linenumbers: 0,
313                 number_of_relocations: section.relocations.len() as u16,
314                 number_of_linenumbers: 0,
315                 characteristics: characteristics | align,
316             };
317             if section.name.len() <= 8 {
318                 coff_section.name[..section.name.len()].copy_from_slice(&section.name);
319             } else {
320                 let str_offset = strtab.get_offset(section_offsets[index].str_id.unwrap());
321                 coff_section.set_name_offset(str_offset).unwrap();
322             }
323             buffer.iowrite_with(coff_section, ctx).unwrap();
324         }
325 
326         // Write section data and relocations.
327         for (index, section) in self.sections.iter().enumerate() {
328             let len = section.data.len();
329             if len != 0 {
330                 write_align(&mut buffer, 4);
331                 debug_assert_eq!(section_offsets[index].offset, buffer.len());
332                 buffer.extend(&section.data);
333             }
334 
335             if !section.relocations.is_empty() {
336                 debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len());
337                 for reloc in &section.relocations {
338                     //assert!(reloc.implicit_addend);
339                     let typ = match self.architecture {
340                         Architecture::I386 => match (reloc.kind, reloc.size, reloc.addend) {
341                             (RelocationKind::Absolute, 16, 0) => coff::IMAGE_REL_I386_DIR16,
342                             (RelocationKind::Relative, 16, 0) => coff::IMAGE_REL_I386_REL16,
343                             (RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_I386_DIR32,
344                             (RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_I386_DIR32NB,
345                             (RelocationKind::SectionIndex, 16, 0) => coff::IMAGE_REL_I386_SECTION,
346                             (RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_I386_SECREL,
347                             (RelocationKind::SectionOffset, 7, 0) => coff::IMAGE_REL_I386_SECREL7,
348                             (RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_I386_REL32,
349                             (RelocationKind::Coff(x), _, _) => x,
350                             _ => return Err(format!("unimplemented relocation {:?}", reloc)),
351                         },
352                         Architecture::X86_64 => match (reloc.kind, reloc.size, reloc.addend) {
353                             (RelocationKind::Absolute, 64, 0) => coff::IMAGE_REL_AMD64_ADDR64,
354                             (RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_AMD64_ADDR32,
355                             (RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_AMD64_ADDR32NB,
356                             (RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_AMD64_REL32,
357                             (RelocationKind::Relative, 32, -5) => coff::IMAGE_REL_AMD64_REL32_1,
358                             (RelocationKind::Relative, 32, -6) => coff::IMAGE_REL_AMD64_REL32_2,
359                             (RelocationKind::Relative, 32, -7) => coff::IMAGE_REL_AMD64_REL32_3,
360                             (RelocationKind::Relative, 32, -8) => coff::IMAGE_REL_AMD64_REL32_4,
361                             (RelocationKind::Relative, 32, -9) => coff::IMAGE_REL_AMD64_REL32_5,
362                             (RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_AMD64_SECREL,
363                             (RelocationKind::SectionOffset, 7, 0) => coff::IMAGE_REL_AMD64_SECREL7,
364                             (RelocationKind::Coff(x), _, _) => x,
365                             _ => return Err(format!("unimplemented relocation {:?}", reloc)),
366                         },
367                         _ => {
368                             return Err(format!(
369                                 "unimplemented architecture {:?}",
370                                 self.architecture
371                             ))
372                         }
373                     };
374                     buffer
375                         .iowrite_with(
376                             coff::Relocation {
377                                 virtual_address: reloc.offset as u32,
378                                 symbol_table_index: symbol_offsets[reloc.symbol.0].index as u32,
379                                 typ,
380                             },
381                             ctx,
382                         )
383                         .unwrap();
384                 }
385             }
386         }
387 
388         // Write symbols.
389         debug_assert_eq!(symtab_offset, buffer.len());
390         for (index, symbol) in self.symbols.iter().enumerate() {
391             let mut name = &symbol.name[..];
392             let mut section_number = symbol.section.map(|x| x.0 + 1).unwrap_or(0) as i16;
393             let typ = if symbol.kind == SymbolKind::Text {
394                 coff::IMAGE_SYM_DTYPE_FUNCTION << coff::IMAGE_SYM_DTYPE_SHIFT
395             } else {
396                 coff::IMAGE_SYM_TYPE_NULL
397             };
398             let storage_class = match symbol.kind {
399                 SymbolKind::File => {
400                     // Name goes in auxilary symbol records.
401                     name = b".file";
402                     section_number = coff::IMAGE_SYM_DEBUG;
403                     coff::IMAGE_SYM_CLASS_FILE
404                 }
405                 SymbolKind::Section => coff::IMAGE_SYM_CLASS_STATIC,
406                 SymbolKind::Label => coff::IMAGE_SYM_CLASS_LABEL,
407                 SymbolKind::Text | SymbolKind::Data => {
408                     match symbol.scope {
409                         _ if symbol.is_undefined() => coff::IMAGE_SYM_CLASS_EXTERNAL,
410                         // TODO: does this need aux symbol records too?
411                         _ if symbol.weak => coff::IMAGE_SYM_CLASS_WEAK_EXTERNAL,
412                         SymbolScope::Unknown => {
413                             return Err(format!("unimplemented symbol scope {:?}", symbol))
414                         }
415                         SymbolScope::Compilation => coff::IMAGE_SYM_CLASS_STATIC,
416                         SymbolScope::Linkage | SymbolScope::Dynamic => {
417                             coff::IMAGE_SYM_CLASS_EXTERNAL
418                         }
419                     }
420                 }
421                 _ => return Err(format!("unimplemented symbol {:?}", symbol.kind)),
422             };
423             let number_of_aux_symbols = symbol_offsets[index].aux_count;
424             let mut coff_symbol = coff::Symbol {
425                 name: [0; 8],
426                 value: symbol.value as u32,
427                 section_number,
428                 typ,
429                 storage_class,
430                 number_of_aux_symbols,
431             };
432             if name.len() <= 8 {
433                 coff_symbol.name[..name.len()].copy_from_slice(name);
434             } else {
435                 let str_offset = strtab.get_offset(symbol_offsets[index].str_id.unwrap());
436                 coff_symbol.set_name_offset(str_offset as u32);
437             }
438             buffer.iowrite_with(coff_symbol, ctx).unwrap();
439 
440             match symbol.kind {
441                 SymbolKind::File => {
442                     let aux_len = number_of_aux_symbols as usize * coff::COFF_SYMBOL_SIZE;
443                     debug_assert!(aux_len >= symbol.name.len());
444                     buffer.extend(&symbol.name);
445                     buffer.extend(iter::repeat(0).take(aux_len - symbol.name.len()));
446                 }
447                 SymbolKind::Section => {
448                     debug_assert_eq!(number_of_aux_symbols, 1);
449                     let section = &self.sections[symbol.section.unwrap().0];
450                     buffer
451                         .iowrite_with(
452                             coff::AuxSectionDefinition {
453                                 length: section.data.len() as u32,
454                                 number_of_relocations: section.relocations.len() as u16,
455                                 number_of_line_numbers: 0,
456                                 checksum: checksum(&section.data),
457                                 number: section_number as u16,
458                                 // TODO: COMDAT
459                                 selection: 0,
460                                 unused: [0; 3],
461                             },
462                             ctx,
463                         )
464                         .unwrap();
465                 }
466                 _ => {
467                     debug_assert_eq!(number_of_aux_symbols, 0);
468                 }
469             }
470         }
471 
472         // Write strtab section.
473         debug_assert_eq!(strtab_offset, buffer.len());
474         buffer.iowrite_with(strtab_len as u32, ctx).unwrap();
475         buffer.extend(&strtab_data);
476 
477         Ok(buffer)
478     }
479 }
480 
481 // JamCRC
checksum(data: &[u8]) -> u32482 fn checksum(data: &[u8]) -> u32 {
483     let mut hasher = crc32fast::Hasher::new_with_initial(0xffff_ffff);
484     hasher.update(data);
485     !hasher.finalize()
486 }
487