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