implib/
gnu.rs

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, /* ldr  ip, [pc] */
15    0x00, 0xf0, 0x9c, 0xe5, /* ldr  pc, [ip] */
16    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/// GNU flavored Windows import library generator
32#[derive(Debug, Clone)]
33pub struct GnuImportLibrary {
34    def: ModuleDef,
35    machine: MachineType,
36}
37
38impl GnuImportLibrary {
39    /// Create new import library generator from `ModuleDef`
40    pub fn new(mut def: ModuleDef, machine: MachineType) -> Self {
41        // If ext_name is set (if the "ext_name = name" syntax was used), overwrite
42        // name with ext_name and clear ext_name. When only creating an import
43        // library and not linking, the internal name is irrelevant.
44        for export in &mut def.exports {
45            if let Some(ext_name) = export.ext_name.take() {
46                export.name = ext_name;
47            }
48        }
49        // Skipped i386 handling
50        // See https://github.com/llvm/llvm-project/blob/09c2b7c35af8c4bad39f03e9f60df8bd07323028/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L197-L212
51        GnuImportLibrary { def, machine }
52    }
53
54    /// Write out the import library
55    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        // All subsequent symbols should be added unmangled.
372        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            // Remove i386 mangling added by the def parser.
462            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}