1use std::io::{Error, ErrorKind, Seek, Write};
2
3use object::pe::*;
4use object::write::{Mangling, Object, Relocation, Symbol, SymbolId, SymbolSection};
5use object::{
6 BinaryFormat, Endianness, SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope,
7};
8
9use crate::def::{ModuleDef, ShortExport};
10use crate::{ar, ArchiveMember, MachineType};
11
12const JMP_IX86_BYTES: [u8; 8] = [0xff, 0x25, 0x00, 0x00, 0x00, 0x00, 0x90, 0x90];
13const JMP_ARM_BYTES: [u8; 12] = [
14 0x00, 0xc0, 0x9f, 0xe5, 0x00, 0xf0, 0x9c, 0xe5, 0, 0, 0, 0,
17];
18
19impl MachineType {
20 fn to_arch(self) -> object::Architecture {
21 use object::Architecture::*;
22 match self {
23 Self::AMD64 => X86_64,
24 Self::ARMNT => Arm,
25 Self::ARM64 => Aarch64,
26 Self::I386 => I386,
27 }
28 }
29}
30
31#[derive(Debug, Clone)]
33pub struct GnuImportLibrary {
34 def: ModuleDef,
35 machine: MachineType,
36}
37
38impl GnuImportLibrary {
39 pub fn new(mut def: ModuleDef, machine: MachineType) -> Self {
41 for export in &mut def.exports {
45 if let Some(ext_name) = export.ext_name.take() {
46 export.name = ext_name;
47 }
48 }
49 GnuImportLibrary { def, machine }
52 }
53
54 pub fn write_to<W: Write + Seek>(&self, writer: &mut W) -> Result<(), Error> {
56 let mut members = Vec::new();
57 let mut factory = ObjectFactory::new(&self.def.import_name, self.machine)?;
58 for export in &self.def.exports {
59 members.push(factory.make_one(export)?.create_archive_entry());
60 }
61 members.push(factory.make_head()?.create_archive_entry());
62 members.push(factory.make_tail()?.create_archive_entry());
63 members.reverse();
64
65 let identifiers = members
66 .iter()
67 .map(|(header, _)| header.identifier().to_vec())
68 .collect();
69 let symbol_table: Vec<Vec<Vec<u8>>> = members
70 .iter()
71 .map(|(_, member)| {
72 member
73 .symbols
74 .iter()
75 .map(|s| s.to_string().into_bytes())
76 .collect::<Vec<Vec<u8>>>()
77 })
78 .collect();
79 let mut archive =
80 ar::GnuBuilder::new_with_symbol_table(writer, true, identifiers, symbol_table)?;
81 for (header, member) in members {
82 archive.append(&header, &member.data[..])?;
83 }
84 Ok(())
85 }
86}
87
88#[derive(Debug)]
89struct ObjectFactory<'a> {
90 machine: MachineType,
91 import_name: &'a str,
92 output_name: String,
93 seq: usize,
94}
95
96impl<'a> ObjectFactory<'a> {
97 fn new(import_name: &'a str, machine: MachineType) -> Result<Self, Error> {
98 if import_name.contains('\0') {
99 return Err(Error::new(
100 ErrorKind::InvalidInput,
101 "import name contains null byte".to_string(),
102 ));
103 }
104 Ok(Self {
105 machine,
106 import_name,
107 output_name: format!("{}.a", import_name),
108 seq: 0,
109 })
110 }
111 fn make_relocation(
112 &self,
113 offset: u64,
114 symbol: SymbolId,
115 addend: i64,
116 rel_kind: u16,
117 ) -> Relocation {
118 Relocation {
119 offset,
120 symbol,
121 addend,
122 flags: object::RelocationFlags::Coff { typ: rel_kind },
123 }
124 }
125 fn make_head(&self) -> Result<ArchiveMember, Error> {
126 let mut obj = Object::new(
127 BinaryFormat::Coff,
128 self.machine.to_arch(),
129 Endianness::Little,
130 );
131 let text_sec = obj.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
132 obj.section_mut(text_sec).flags = SectionFlags::Coff {
133 characteristics: IMAGE_SCN_ALIGN_16BYTES
134 | IMAGE_SCN_CNT_CODE
135 | IMAGE_SCN_MEM_EXECUTE
136 | IMAGE_SCN_MEM_READ,
137 };
138
139 let data_sec = obj.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data);
140 obj.section_mut(data_sec).flags = SectionFlags::Coff {
141 characteristics: IMAGE_SCN_ALIGN_16BYTES
142 | IMAGE_SCN_CNT_INITIALIZED_DATA
143 | IMAGE_SCN_MEM_READ
144 | IMAGE_SCN_MEM_WRITE,
145 };
146
147 let bss_sec = obj.add_section(Vec::new(), b".bss".to_vec(), SectionKind::UninitializedData);
148 obj.section_mut(bss_sec).flags = SectionFlags::Coff {
149 characteristics: IMAGE_SCN_ALIGN_16BYTES
150 | IMAGE_SCN_CNT_UNINITIALIZED_DATA
151 | IMAGE_SCN_MEM_READ
152 | IMAGE_SCN_MEM_WRITE,
153 };
154
155 let id2 = obj.add_section(Vec::new(), b".idata$2".to_vec(), SectionKind::Data);
156 let id5 = obj.add_section(Vec::new(), b".idata$5".to_vec(), SectionKind::Data);
157 obj.section_mut(id5).flags = SectionFlags::Coff {
158 characteristics: IMAGE_SCN_ALIGN_4BYTES
159 | IMAGE_SCN_CNT_INITIALIZED_DATA
160 | IMAGE_SCN_MEM_READ
161 | IMAGE_SCN_MEM_WRITE,
162 };
163 let id4 = obj.add_section(Vec::new(), b".idata$4".to_vec(), SectionKind::Data);
164 obj.section_mut(id4).flags = SectionFlags::Coff {
165 characteristics: IMAGE_SCN_ALIGN_4BYTES
166 | IMAGE_SCN_CNT_INITIALIZED_DATA
167 | IMAGE_SCN_MEM_READ
168 | IMAGE_SCN_MEM_WRITE,
169 };
170
171 obj.add_file_symbol(b"fake".to_vec());
172 let id5_sym = obj.section_symbol(id5);
173 let id4_sym = obj.section_symbol(id4);
174 let img_rel = self.machine.img_rel_relocation();
175 obj.add_relocation(id2, self.make_relocation(0, id4_sym, 0, img_rel))
176 .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
177 obj.add_relocation(id2, self.make_relocation(16, id5_sym, 0, img_rel))
178 .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
179
180 let import_name = self.import_name.replace('.', "_");
181
182 let head_sym = Symbol {
183 name: format!("_head_{}", import_name).into_bytes(),
184 value: 0,
185 size: 0,
186 kind: SymbolKind::Data,
187 scope: SymbolScope::Dynamic,
188 weak: false,
189 section: SymbolSection::Section(id2),
190 flags: SymbolFlags::None,
191 };
192 let head_sym_id = obj.add_symbol(head_sym);
193 let head_sym_name = obj.symbol(head_sym_id).name.clone();
194
195 let iname_sym = Symbol {
196 name: format!("{}_iname", import_name).into_bytes(),
197 value: 0,
198 size: 0,
199 kind: SymbolKind::Data,
200 scope: SymbolScope::Dynamic,
201 weak: false,
202 section: SymbolSection::Undefined,
203 flags: SymbolFlags::None,
204 };
205 let iname_sym_id = obj.add_symbol(iname_sym);
206
207 obj.append_section_data(id2, &[0; 20], 4);
208 obj.add_relocation(id2, self.make_relocation(12, iname_sym_id, 0, img_rel))
209 .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
210 Ok(ArchiveMember {
211 name: format!("{}_h.o", self.output_name.replace('.', "_")),
212 data: obj
213 .write()
214 .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?,
215 symbols: vec![String::from_utf8(head_sym_name).unwrap()],
216 })
217 }
218
219 fn make_tail(&self) -> Result<ArchiveMember, Error> {
220 let mut obj = Object::new(
221 BinaryFormat::Coff,
222 self.machine.to_arch(),
223 Endianness::Little,
224 );
225 let text_sec = obj.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
226 obj.section_mut(text_sec).flags = SectionFlags::Coff {
227 characteristics: IMAGE_SCN_ALIGN_16BYTES
228 | IMAGE_SCN_CNT_CODE
229 | IMAGE_SCN_MEM_EXECUTE
230 | IMAGE_SCN_MEM_READ,
231 };
232
233 let data_sec = obj.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data);
234 obj.section_mut(data_sec).flags = SectionFlags::Coff {
235 characteristics: IMAGE_SCN_ALIGN_16BYTES
236 | IMAGE_SCN_CNT_INITIALIZED_DATA
237 | IMAGE_SCN_MEM_READ
238 | IMAGE_SCN_MEM_WRITE,
239 };
240
241 let bss_sec = obj.add_section(Vec::new(), b".bss".to_vec(), SectionKind::UninitializedData);
242 obj.section_mut(bss_sec).flags = SectionFlags::Coff {
243 characteristics: IMAGE_SCN_ALIGN_16BYTES
244 | IMAGE_SCN_CNT_UNINITIALIZED_DATA
245 | IMAGE_SCN_MEM_READ
246 | IMAGE_SCN_MEM_WRITE,
247 };
248
249 let id4 = obj.add_section(Vec::new(), b".idata$4".to_vec(), SectionKind::Data);
250 obj.section_mut(id4).flags = SectionFlags::Coff {
251 characteristics: IMAGE_SCN_ALIGN_4BYTES
252 | IMAGE_SCN_CNT_INITIALIZED_DATA
253 | IMAGE_SCN_MEM_READ
254 | IMAGE_SCN_MEM_WRITE,
255 };
256 let id5 = obj.add_section(Vec::new(), b".idata$5".to_vec(), SectionKind::Data);
257 obj.section_mut(id5).flags = SectionFlags::Coff {
258 characteristics: IMAGE_SCN_ALIGN_4BYTES
259 | IMAGE_SCN_CNT_INITIALIZED_DATA
260 | IMAGE_SCN_MEM_READ
261 | IMAGE_SCN_MEM_WRITE,
262 };
263 let id7 = obj.add_section(Vec::new(), b".idata$7".to_vec(), SectionKind::Data);
264 obj.section_mut(id4).flags = SectionFlags::Coff {
265 characteristics: IMAGE_SCN_ALIGN_4BYTES
266 | IMAGE_SCN_CNT_INITIALIZED_DATA
267 | IMAGE_SCN_MEM_READ
268 | IMAGE_SCN_MEM_WRITE,
269 };
270
271 obj.add_file_symbol(b"fake".to_vec());
272
273 let import_name = self.import_name.replace('.', "_");
274 let iname_sym = Symbol {
275 name: format!("{}_iname", import_name).into_bytes(),
276 value: 0,
277 size: 0,
278 kind: SymbolKind::Data,
279 scope: SymbolScope::Dynamic,
280 weak: false,
281 section: SymbolSection::Section(id7),
282 flags: SymbolFlags::None,
283 };
284 let iname_sym_id = obj.add_symbol(iname_sym);
285 let iname_sym_name = obj.symbol(iname_sym_id).name.clone();
286
287 obj.append_section_data(id4, &[0; 8], 4);
288 obj.append_section_data(id5, &[0; 8], 4);
289
290 let mut import_name_bytes = self.import_name.as_bytes().to_vec();
291 import_name_bytes.push(b'\0');
292 obj.append_section_data(id7, &import_name_bytes, 4);
293
294 Ok(ArchiveMember {
295 name: format!("{}_t.o", self.output_name.replace('.', "_")),
296 data: obj
297 .write()
298 .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?,
299 symbols: vec![String::from_utf8(iname_sym_name).unwrap()],
300 })
301 }
302
303 fn make_one(&mut self, export: &ShortExport) -> Result<ArchiveMember, Error> {
304 if export.name.contains('\0') {
305 return Err(Error::new(
306 ErrorKind::InvalidInput,
307 "export name contains null byte".to_string(),
308 ));
309 }
310
311 let mut obj = Object::new(
312 BinaryFormat::Coff,
313 self.machine.to_arch(),
314 Endianness::Little,
315 );
316
317 let text_sec = obj.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
318 obj.section_mut(text_sec).flags = SectionFlags::Coff {
319 characteristics: IMAGE_SCN_ALIGN_4BYTES
320 | IMAGE_SCN_CNT_CODE
321 | IMAGE_SCN_MEM_EXECUTE
322 | IMAGE_SCN_MEM_READ,
323 };
324
325 let data_sec = obj.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data);
326 obj.section_mut(data_sec).flags = SectionFlags::Coff {
327 characteristics: IMAGE_SCN_ALIGN_4BYTES
328 | IMAGE_SCN_CNT_INITIALIZED_DATA
329 | IMAGE_SCN_MEM_READ
330 | IMAGE_SCN_MEM_WRITE,
331 };
332
333 let bss_sec = obj.add_section(Vec::new(), b".bss".to_vec(), SectionKind::UninitializedData);
334 obj.section_mut(bss_sec).flags = SectionFlags::Coff {
335 characteristics: IMAGE_SCN_ALIGN_4BYTES
336 | IMAGE_SCN_CNT_UNINITIALIZED_DATA
337 | IMAGE_SCN_MEM_READ
338 | IMAGE_SCN_MEM_WRITE,
339 };
340
341 let id7 = obj.add_section(Vec::new(), b".idata$7".to_vec(), SectionKind::Data);
342 obj.section_mut(id7).flags = SectionFlags::Coff {
343 characteristics: IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE,
344 };
345 let id5 = obj.add_section(Vec::new(), b".idata$5".to_vec(), SectionKind::Data);
346 obj.section_mut(id5).flags = SectionFlags::Coff {
347 characteristics: IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE,
348 };
349 let id4 = obj.add_section(Vec::new(), b".idata$4".to_vec(), SectionKind::Data);
350 obj.section_mut(id4).flags = SectionFlags::Coff {
351 characteristics: IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE,
352 };
353 let id6 = obj.add_section(Vec::new(), b".idata$6".to_vec(), SectionKind::Data);
354 obj.section_mut(id6).flags = SectionFlags::Coff {
355 characteristics: IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE,
356 };
357
358 let import_name = self.import_name.replace('.', "_");
359 let head_sym = Symbol {
360 name: format!("_head_{}", import_name).into_bytes(),
361 value: 0,
362 size: 0,
363 kind: SymbolKind::Data,
364 scope: SymbolScope::Dynamic,
365 weak: false,
366 section: SymbolSection::Undefined,
367 flags: SymbolFlags::None,
368 };
369 let head_sym = obj.add_symbol(head_sym);
370
371 obj.mangling = Mangling::None;
373
374 let mut archive_symbols = Vec::new();
375 if !export.data {
376 let exp_sym = Symbol {
377 name: export.name.as_bytes().to_vec(),
378 value: 0,
379 size: 0,
380 kind: SymbolKind::Data,
381 scope: SymbolScope::Dynamic,
382 weak: false,
383 section: SymbolSection::Section(text_sec),
384 flags: SymbolFlags::None,
385 };
386 obj.add_symbol(exp_sym);
387 archive_symbols.push(export.name.to_string());
388 }
389 let exp_imp_sym = Symbol {
390 name: format!("__imp_{}", export.name).into_bytes(),
391 value: 0,
392 size: 0,
393 kind: SymbolKind::Data,
394 scope: SymbolScope::Dynamic,
395 weak: false,
396 section: SymbolSection::Section(id5),
397 flags: SymbolFlags::None,
398 };
399 let exp_imp_sym = obj.add_symbol(exp_imp_sym);
400 archive_symbols.push(format!("__imp_{}", export.name));
401
402 if !export.data {
403 let (jmp_stub, offset, rel_kind) = match self.machine {
404 MachineType::I386 => (&JMP_IX86_BYTES[..], 2, IMAGE_REL_I386_REL32),
405 MachineType::ARMNT => (&JMP_ARM_BYTES[..], 8, IMAGE_REL_ARM_REL32),
406 MachineType::AMD64 => (&JMP_IX86_BYTES[..], 2, IMAGE_REL_AMD64_REL32),
407 MachineType::ARM64 => (&JMP_ARM_BYTES[..], 8, IMAGE_REL_ARM64_REL32),
408 };
409 obj.append_section_data(text_sec, jmp_stub, 4);
410 obj.add_relocation(
411 text_sec,
412 self.make_relocation(offset, exp_imp_sym, -4, rel_kind),
413 )
414 .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
415 }
416
417 let img_rel = self.machine.img_rel_relocation();
418
419 obj.append_section_data(id7, &[0; 4], 4);
420 obj.add_relocation(id7, self.make_relocation(0, head_sym, 0, img_rel))
421 .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
422
423 let id6_sym = obj.section_symbol(id6);
424 let id5_data = if export.no_name {
425 [
426 export.ordinal as u8,
427 (export.ordinal >> 8) as u8,
428 0,
429 0,
430 0,
431 0,
432 0,
433 0x80,
434 ]
435 } else {
436 obj.add_relocation(id5, self.make_relocation(0, id6_sym, 0, img_rel))
437 .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
438 [0; 8]
439 };
440 obj.append_section_data(id5, &id5_data, 4);
441
442 let id4_data = if export.no_name {
443 [
444 export.ordinal as u8,
445 (export.ordinal >> 8) as u8,
446 0,
447 0,
448 0,
449 0,
450 0,
451 0x80,
452 ]
453 } else {
454 obj.add_relocation(id4, self.make_relocation(0, id6_sym, 0, img_rel))
455 .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
456 [0; 8]
457 };
458 obj.append_section_data(id4, &id4_data, 4);
459
460 if !export.no_name {
461 let export_name = match self.machine {
463 MachineType::I386 => export.name.strip_prefix("_").unwrap(),
464 _ => &export.name,
465 };
466 let len = 2 + export_name.len() + 1;
467 let mut id6_data = vec![0; len];
468 let ord = export.ordinal;
469 id6_data[0] = ord as u8;
470 id6_data[1] = (ord >> 8) as u8;
471 id6_data[2..len - 1].copy_from_slice(export_name.as_bytes());
472 obj.append_section_data(id6, &id6_data, 2);
473 }
474
475 let name = format!("{}_s{:05}.o", self.output_name.replace('.', "_"), self.seq);
476 self.seq += 1;
477
478 Ok(ArchiveMember {
479 name,
480 data: obj
481 .write()
482 .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?,
483 symbols: archive_symbols,
484 })
485 }
486}
487
488#[cfg(test)]
489mod test {
490 use super::*;
491 use std::io::Cursor;
492
493 #[test]
494 fn test_gnu_with_bad_input() {
495 let import_lib = GnuImportLibrary::new(
496 ModuleDef::parse("EXPORTS D\u{c}\0", MachineType::AMD64).unwrap(),
497 MachineType::AMD64,
498 );
499 import_lib
500 .write_to(&mut Cursor::new(Vec::new()))
501 .unwrap_err();
502 }
503
504 #[ignore]
505 #[test]
506 fn debug_head_tail_export() {
507 let mut factory = ObjectFactory::new("python39.dll", MachineType::AMD64).unwrap();
508 let head = factory.make_head().unwrap();
509 std::fs::write("head.o", head.data).unwrap();
510
511 let tail = factory.make_tail().unwrap();
512 std::fs::write("tail.o", tail.data).unwrap();
513
514 let export = ShortExport {
515 name: "PyAST_CompileEx".to_string(),
516 ext_name: None,
517 symbol_name: "".to_string(),
518 alias_target: "".to_string(),
519 ordinal: 0,
520 no_name: false,
521 data: false,
522 private: false,
523 constant: false,
524 };
525 let exp = factory.make_one(&export).unwrap();
526 std::fs::write("exp.o", exp.data).unwrap();
527 }
528}