1//! Support for reading short import files.
2//!
3//! These are used by some Windows linkers as a more compact way to describe
4//! dynamically imported symbols.
5
6use crate::read::{Architecture, Error, ReadError, ReadRef, Result};
7use crate::{pe, ByteString, Bytes, LittleEndian as LE, SubArchitecture};
8
9/// A Windows short form description of a symbol to import.
10///
11/// Used in Windows import libraries to provide a mapping from
12/// a symbol name to a DLL export. This is not an object file.
13///
14/// This is a file that starts with [`pe::ImportObjectHeader`], and corresponds
15/// to [`crate::FileKind::CoffImport`].
16#[derive(Debug, Clone)]
17pub struct ImportFile<'data> {
18 header: &'data pe::ImportObjectHeader,
19 kind: ImportType,
20 dll: ByteString<'data>,
21 symbol: ByteString<'data>,
22 import: Option<ByteString<'data>>,
23}
24
25impl<'data> ImportFile<'data> {
26 /// Parse it.
27 pub fn parse<R: ReadRef<'data>>(data: R) -> Result<Self> {
28 let mut offset = 0;
29 let header = pe::ImportObjectHeader::parse(data, &mut offset)?;
30 let data = header.parse_data(data, &mut offset)?;
31
32 // Unmangles a name by removing a `?`, `@` or `_` prefix.
33 fn strip_prefix(s: &[u8]) -> &[u8] {
34 match s.split_first() {
35 Some((b, rest)) if [b'?', b'@', b'_'].contains(b) => rest,
36 _ => s,
37 }
38 }
39 Ok(Self {
40 header,
41 dll: data.dll,
42 symbol: data.symbol,
43 kind: match header.import_type() {
44 pe::IMPORT_OBJECT_CODE => ImportType::Code,
45 pe::IMPORT_OBJECT_DATA => ImportType::Data,
46 pe::IMPORT_OBJECT_CONST => ImportType::Const,
47 _ => return Err(Error("Invalid COFF import library import type")),
48 },
49 import: match header.name_type() {
50 pe::IMPORT_OBJECT_ORDINAL => None,
51 pe::IMPORT_OBJECT_NAME => Some(data.symbol()),
52 pe::IMPORT_OBJECT_NAME_NO_PREFIX => Some(strip_prefix(data.symbol())),
53 pe::IMPORT_OBJECT_NAME_UNDECORATE => Some(
54 strip_prefix(data.symbol())
55 .split(|&b| b == b'@')
56 .next()
57 .unwrap(),
58 ),
59 pe::IMPORT_OBJECT_NAME_EXPORTAS => data.export(),
60 _ => return Err(Error("Unknown COFF import library name type")),
61 }
62 .map(ByteString),
63 })
64 }
65
66 /// Get the machine type.
67 pub fn architecture(&self) -> Architecture {
68 match self.header.machine.get(LE) {
69 pe::IMAGE_FILE_MACHINE_ARMNT => Architecture::Arm,
70 pe::IMAGE_FILE_MACHINE_ARM64 | pe::IMAGE_FILE_MACHINE_ARM64EC => Architecture::Aarch64,
71 pe::IMAGE_FILE_MACHINE_I386 => Architecture::I386,
72 pe::IMAGE_FILE_MACHINE_AMD64 => Architecture::X86_64,
73 _ => Architecture::Unknown,
74 }
75 }
76
77 /// Get the sub machine type, if available.
78 pub fn sub_architecture(&self) -> Option<SubArchitecture> {
79 match self.header.machine.get(LE) {
80 pe::IMAGE_FILE_MACHINE_ARM64EC => Some(SubArchitecture::Arm64EC),
81 _ => None,
82 }
83 }
84
85 /// The public symbol name.
86 pub fn symbol(&self) -> &'data [u8] {
87 self.symbol.0
88 }
89
90 /// The name of the DLL to import the symbol from.
91 pub fn dll(&self) -> &'data [u8] {
92 self.dll.0
93 }
94
95 /// The name exported from the DLL.
96 pub fn import(&self) -> ImportName<'data> {
97 match self.import {
98 Some(name) => ImportName::Name(name.0),
99 None => ImportName::Ordinal(self.header.ordinal_or_hint.get(LE)),
100 }
101 }
102
103 /// The type of import. Usually either a function or data.
104 pub fn import_type(&self) -> ImportType {
105 self.kind
106 }
107}
108
109/// The name or ordinal to import from a DLL.
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub enum ImportName<'data> {
112 /// Import by ordinal. Ordinarily this is a 1-based index.
113 Ordinal(u16),
114 /// Import by name.
115 Name(&'data [u8]),
116}
117
118/// The kind of import symbol.
119#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
120pub enum ImportType {
121 /// An executable code symbol.
122 Code,
123 /// A data symbol.
124 Data,
125 /// A constant value.
126 Const,
127}
128
129impl pe::ImportObjectHeader {
130 /// Read the short import header.
131 ///
132 /// Also checks that the signature and version are valid.
133 /// Directly following this header will be the string data.
134 pub fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> Result<&'data Self> {
135 let header = data
136 .read::<pe::ImportObjectHeader>(offset)
137 .read_error("Invalid COFF import library header size")?;
138 if header.sig1.get(LE) != 0 || header.sig2.get(LE) != pe::IMPORT_OBJECT_HDR_SIG2 {
139 Err(Error("Invalid COFF import library header"))
140 } else if header.version.get(LE) != 0 {
141 Err(Error("Unknown COFF import library header version"))
142 } else {
143 Ok(header)
144 }
145 }
146
147 /// Parse the data following the header.
148 pub fn parse_data<'data, R: ReadRef<'data>>(
149 &self,
150 data: R,
151 offset: &mut u64,
152 ) -> Result<ImportObjectData<'data>> {
153 let mut data = Bytes(
154 data.read_bytes(offset, u64::from(self.size_of_data.get(LE)))
155 .read_error("Invalid COFF import library data size")?,
156 );
157 let symbol = data
158 .read_string()
159 .map(ByteString)
160 .read_error("Could not read COFF import library symbol name")?;
161 let dll = data
162 .read_string()
163 .map(ByteString)
164 .read_error("Could not read COFF import library DLL name")?;
165 let export = if self.name_type() == pe::IMPORT_OBJECT_NAME_EXPORTAS {
166 data.read_string()
167 .map(ByteString)
168 .map(Some)
169 .read_error("Could not read COFF import library export name")?
170 } else {
171 None
172 };
173 Ok(ImportObjectData {
174 symbol,
175 dll,
176 export,
177 })
178 }
179
180 /// The type of import.
181 ///
182 /// This is one of the `IMPORT_OBJECT_*` constants.
183 pub fn import_type(&self) -> u16 {
184 self.name_type.get(LE) & pe::IMPORT_OBJECT_TYPE_MASK
185 }
186
187 /// The type of import name.
188 ///
189 /// This is one of the `IMPORT_OBJECT_*` constants.
190 pub fn name_type(&self) -> u16 {
191 (self.name_type.get(LE) >> pe::IMPORT_OBJECT_NAME_SHIFT) & pe::IMPORT_OBJECT_NAME_MASK
192 }
193}
194
195/// The data following [`pe::ImportObjectHeader`].
196#[derive(Debug, Clone)]
197pub struct ImportObjectData<'data> {
198 symbol: ByteString<'data>,
199 dll: ByteString<'data>,
200 export: Option<ByteString<'data>>,
201}
202
203impl<'data> ImportObjectData<'data> {
204 /// The public symbol name.
205 pub fn symbol(&self) -> &'data [u8] {
206 self.symbol.0
207 }
208
209 /// The name of the DLL to import the symbol from.
210 pub fn dll(&self) -> &'data [u8] {
211 self.dll.0
212 }
213
214 /// The name exported from the DLL.
215 ///
216 /// This is only set if the name is not derived from the symbol name.
217 pub fn export(&self) -> Option<&'data [u8]> {
218 self.export.map(|export: ByteString<'_>| export.0)
219 }
220}
221