implib/
lib.rs

1#[cfg(any(not(feature = "msvc"), not(feature = "gnu")))]
2use std::io::ErrorKind;
3use std::io::{Error, Seek, Write};
4
5use object::pe::*;
6
7/// Unix archiver writer
8#[cfg(any(feature = "msvc", feature = "gnu"))]
9mod ar;
10/// Parse .DEF file
11pub mod def;
12/// GNU binutils flavored import library
13#[cfg(feature = "gnu")]
14mod gnu;
15/// MSVC flavored import library
16#[cfg(feature = "msvc")]
17mod msvc;
18
19#[cfg(feature = "gnu")]
20use self::gnu::GnuImportLibrary;
21#[cfg(feature = "msvc")]
22use self::msvc::MsvcImportLibrary;
23use crate::def::ModuleDef;
24
25/// Machine types
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27#[repr(u16)]
28pub enum MachineType {
29    /// Intel 386
30    I386 = IMAGE_FILE_MACHINE_I386,
31    /// ARM Thumb-2 Little-Endian
32    ARMNT = IMAGE_FILE_MACHINE_ARMNT,
33    /// AMD64 (K8)
34    AMD64 = IMAGE_FILE_MACHINE_AMD64,
35    ARM64 = IMAGE_FILE_MACHINE_ARM64,
36}
37
38impl MachineType {
39    fn img_rel_relocation(&self) -> u16 {
40        match self {
41            Self::AMD64 => IMAGE_REL_AMD64_ADDR32NB,
42            Self::ARMNT => IMAGE_REL_ARM_ADDR32NB,
43            Self::ARM64 => IMAGE_REL_ARM64_ADDR32NB,
44            Self::I386 => IMAGE_REL_I386_DIR32NB,
45        }
46    }
47}
48
49#[derive(Debug)]
50struct ArchiveMember {
51    name: String,
52    data: Vec<u8>,
53    symbols: Vec<String>,
54}
55
56impl ArchiveMember {
57    #[cfg(any(feature = "msvc", feature = "gnu"))]
58    fn create_archive_entry(self) -> (ar::Header, ArchiveMember) {
59        let mut header =
60            ar::Header::new(self.name.to_string().into_bytes(), self.data.len() as u64);
61        header.set_mode(0o644);
62        (header, self)
63    }
64}
65
66/// Import library flavor
67#[derive(Debug, Clone, Copy)]
68pub enum Flavor {
69    /// MSVC short import library
70    Msvc,
71    /// GNU(MinGW) import library
72    Gnu,
73}
74
75/// Windows import library generator
76#[derive(Debug, Clone)]
77pub struct ImportLibrary {
78    def: ModuleDef,
79    machine: MachineType,
80    flavor: Flavor,
81}
82
83impl ImportLibrary {
84    /// Create new import library generator from module definition text content
85    pub fn new(def: &str, machine: MachineType, flavor: Flavor) -> Result<Self, Error> {
86        let def = ModuleDef::parse(def, machine)?;
87        Ok(Self::from_def(def, machine, flavor))
88    }
89
90    /// Create new import library generator from `ModuleDef`
91    pub fn from_def(mut def: ModuleDef, machine: MachineType, flavor: Flavor) -> Self {
92        // If ext_name is set (if the "ext_name = name" syntax was used), overwrite
93        // name with ext_name and clear ext_name. When only creating an import
94        // library and not linking, the internal name is irrelevant.
95        for export in &mut def.exports {
96            if let Some(ext_name) = export.ext_name.take() {
97                export.name = ext_name;
98            }
99        }
100        // Skipped i386 handling
101        // See https://github.com/llvm/llvm-project/blob/09c2b7c35af8c4bad39f03e9f60df8bd07323028/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L197-L212
102        ImportLibrary {
103            def,
104            machine,
105            flavor,
106        }
107    }
108
109    /// Get import library name
110    pub fn import_name(&self) -> &str {
111        &self.def.import_name
112    }
113
114    /// Write out the import library
115    pub fn write_to<W: Write + Seek>(self, writer: &mut W) -> Result<(), Error> {
116        match self.flavor {
117            #[cfg(feature = "msvc")]
118            Flavor::Msvc => MsvcImportLibrary::new(self.def, self.machine).write_to(writer),
119            #[cfg(not(feature = "msvc"))]
120            Flavor::Msvc => Err(Error::new(
121                ErrorKind::Unsupported,
122                "MSVC import library unsupported, enable 'msvc' feature to use it",
123            )),
124            #[cfg(feature = "gnu")]
125            Flavor::Gnu => GnuImportLibrary::new(self.def, self.machine).write_to(writer),
126            #[cfg(not(feature = "gnu"))]
127            Flavor::Gnu => Err(Error::new(
128                ErrorKind::Unsupported,
129                "GNU import library unsupported, enable 'gnu' feature to use it",
130            )),
131        }
132    }
133}