///|
/// Delay-loaded imports (data-directory slot 13).
///
/// A delay-load entry is a real dependency: the DLL is named in the image and
/// will be loaded the first time one of its functions is called. Dependency
/// inspection that only reads slot 1 therefore misses a whole class of
/// libraries, which is why this module exists.
///
/// The descriptor comes in two flavours, distinguished by bit 0 of its first
/// field (`dlattrRva`):
///
/// * bit set: every address field is an RVA;
/// * bit clear: every address field is a virtual address and must be rebased
/// against the image base.
///|
pub(all) struct DelayImportLibrary {
name : String
entries : Array[ImportEntry]
// True when the descriptor used RVA-based address fields.
rva_based : Bool
} derive(@debug.Debug)
///|
/// Read the delay-load import directory.
pub fn Pe::delay_imports(self : Pe) -> Array[DelayImportLibrary] raise PeError {
let out : Array[DelayImportLibrary] = []
let directory = match self.directory(13) {
None => return out
Some(directory) => directory
}
if directory.virtual_address == 0 || directory.size == 0 {
return out
}
let reader = match self.reader_at_rva(directory.virtual_address) {
Some(reader) => reader
None =>
raise PeError::Malformed(
"delay-import directory RVA " +
hex(directory.virtual_address) +
" is not backed by file bytes",
0,
)
}
let mut count = 0
let mut finished = false
while !finished && count < 4096 {
let attributes = reader.u32_raw()
let name = reader.u32()
let module_handle = reader.u32()
let iat = reader.u32()
let int_table = reader.u32()
let bound_iat = reader.u32()
let unload_iat = reader.u32()
let _timestamp = reader.u32()
if attributes == 0 &&
name == 0 &&
module_handle == 0 &&
iat == 0 &&
int_table == 0 &&
bound_iat == 0 &&
unload_iat == 0 {
finished = true
} else {
let rva_based = (attributes.to_int64() & 1) != 0
let name_rva = self.delay_rva(name, rva_based)
let table = if int_table != 0 {
self.delay_rva(int_table, rva_based)
} else {
self.delay_rva(iat, rva_based)
}
let library_name = self.string_at_rva(name_rva, 4096)
let entries = self.read_thunks(table)
out.push({ name: library_name, entries, rva_based, })
count = count + 1
}
}
out
}
///|
/// Convert a delay-import address field to an RVA.
fn Pe::delay_rva(
self : Pe,
value : Int32,
rva_based : Bool,
) -> Int32 raise PeError {
if value == 0 {
return 0
}
if rva_based {
return value
}
let base = self.optional.image_base
let address = value.to_int64()
if address < base {
raise PeError::Malformed(
"delay-import address 0x" + hex(value) + " lies below the image base",
0,
)
}
let delta = address - base
if delta > 2147483647 {
raise PeError::Malformed(
"delay-import RVA exceeds the supported 2 GiB range", 0,
)
}
delta.to_int()
}