///|
/// Base relocations (data-directory slot 5).
///
/// A relocation block names a 4 KiB page and lists the offsets inside it that
/// the loader must patch when the image is not loaded at its preferred base.
/// Counting them is enough for the questions this library targets: does the
/// image need relocation at all, and is it mostly 32-bit or 64-bit fixups.
///|
pub(all) struct RelocationBlock {
page_rva : Int32
entries : Int32
absolute : Int32
high : Int32
low : Int32
highlow : Int32
dir64 : Int32
other : Int32
} derive(@debug.Debug)
///|
pub(all) struct RelocationSummary {
blocks : Array[RelocationBlock]
total_entries : Int32
absolute : Int32
high : Int32
low : Int32
highlow : Int32
dir64 : Int32
other : Int32
} derive(@debug.Debug)
///|
/// The documented name of a relocation type.
pub fn relocation_type_name(kind : Int32) -> String {
match kind {
0 => "ABSOLUTE"
1 => "HIGH"
2 => "LOW"
3 => "HIGHLOW"
4 => "HIGHADJ"
5 => "MIPS_JMPADDR"
7 => "THUMB_MOV32"
9 => "MIPS_JMPADDR16"
10 => "DIR64"
_ => "UNKNOWN(" + kind.to_string() + ")"
}
}
///|
/// Read and summarise the relocation directory.
pub fn Pe::relocations(self : Pe) -> RelocationSummary raise PeError {
let blocks : Array[RelocationBlock] = []
let mut total = 0
let mut total_absolute = 0
let mut total_high = 0
let mut total_low = 0
let mut total_highlow = 0
let mut total_dir64 = 0
let mut total_other = 0
let directory = match self.directory(5) {
None =>
return {
blocks,
total_entries: 0,
absolute: 0,
high: 0,
low: 0,
highlow: 0,
dir64: 0,
other: 0,
}
Some(directory) => directory
}
if directory.virtual_address == 0 || directory.size == 0 {
return {
blocks,
total_entries: 0,
absolute: 0,
high: 0,
low: 0,
highlow: 0,
dir64: 0,
other: 0,
}
}
let reader = match self.reader_at_rva(directory.virtual_address) {
Some(reader) => reader
None =>
raise PeError::Malformed(
"relocation directory RVA " +
hex(directory.virtual_address) +
" is not backed by file bytes",
0,
)
}
let mut block_count = 0
while reader.remaining() >= 8 && block_count < 65536 {
let page_rva = reader.u32()
let block_size = reader.u32()
if page_rva == 0 && block_size == 0 {
break
}
if block_size < 8 || block_size > reader.remaining() + 8 {
raise PeError::Malformed(
"relocation block at 0x" +
hex(page_rva) +
" declares an impossible size",
reader.position(),
)
}
let entries = (block_size - 8) / 2
let mut absolute = 0
let mut high = 0
let mut low = 0
let mut highlow = 0
let mut dir64 = 0
let mut other = 0
let mut i = 0
while i < entries {
let entry = reader.u16()
let kind = entry >> 12
if kind == 0 {
absolute = absolute + 1
} else if kind == 1 {
high = high + 1
} else if kind == 2 {
low = low + 1
} else if kind == 3 {
highlow = highlow + 1
} else if kind == 10 {
dir64 = dir64 + 1
} else {
other = other + 1
}
i = i + 1
}
blocks.push({
page_rva,
entries,
absolute,
high,
low,
highlow,
dir64,
other,
})
total = total + entries
total_absolute = total_absolute + absolute
total_high = total_high + high
total_low = total_low + low
total_highlow = total_highlow + highlow
total_dir64 = total_dir64 + dir64
total_other = total_other + other
block_count = block_count + 1
}
{
blocks,
total_entries: total,
absolute: total_absolute,
high: total_high,
low: total_low,
highlow: total_highlow,
dir64: total_dir64,
other: total_other,
}
}