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