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(§ion.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(§ion.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(§ion.data);
324 }
325
326 if !section.relocations.is_empty() {
327 debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len());
328 for reloc in §ion.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(§ion.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