remove relocation entry logic

This commit is contained in:
Folkert 2022-10-30 00:46:58 +02:00
parent 5a67687bba
commit 21a2f6f103
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C

View file

@ -1246,178 +1246,7 @@ fn write_section_header(
data[section_header_start..][..header_array.len()].copy_from_slice(&header_array);
}
struct BaseRelocations {
new_size: u32,
delta: u32,
}
fn write_image_base_relocation(
mmap: &mut [u8],
reloc_section_start: usize,
new_block_va: u32,
relocations: &[u16],
) -> BaseRelocations {
// first, collect the blocks of relocations (each relocation block covers a 4K page)
// we will be moving stuff around, and have to work from the back to the front. However, the
// relocations are encoded in such a way that we can only decode them from front to back.
let mut next_block_start = reloc_section_start as u32;
let mut blocks = vec![];
let mut block_has_relocation = false;
loop {
let header =
load_struct_inplace_mut::<ImageBaseRelocation>(mmap, next_block_start as usize);
if header.virtual_address.get(LE) == 0 {
break;
}
if new_block_va == header.virtual_address.get(LE) {
block_has_relocation = true;
}
blocks.push((next_block_start, *header));
next_block_start += header.size_of_block.get(LE);
}
let old_size = next_block_start - reloc_section_start as u32;
// this is 8 in practice
const HEADER_WIDTH: usize = std::mem::size_of::<ImageBaseRelocation>();
const ENTRY_WIDTH: usize = std::mem::size_of::<u16>();
// extra space that we'll use for the new relocations
let shift_amount = relocations.len() * ENTRY_WIDTH;
if block_has_relocation {
// now, starting from the back, shift sections that need to be shifted and add the
// new relocations to the right block
while let Some((block_start, header)) = blocks.pop() {
let header_va = header.virtual_address.get(LE);
let header_size = header.size_of_block.get(LE);
let block_start = block_start as usize;
match header_va.cmp(&new_block_va) {
std::cmp::Ordering::Greater => {
// shift this block
mmap.copy_within(
block_start..block_start + header_size as usize,
block_start + shift_amount,
);
}
std::cmp::Ordering::Equal => {
// extend this block
let header = load_struct_inplace_mut::<ImageBaseRelocation>(mmap, block_start);
let new_size = header.size_of_block.get(LE) + shift_amount as u32;
header.size_of_block.set(LE, new_size);
let number_of_entries = (new_size as usize - HEADER_WIDTH) / ENTRY_WIDTH;
let entries = load_structs_inplace_mut::<u16>(
mmap,
block_start + HEADER_WIDTH,
number_of_entries,
);
entries[number_of_entries - relocations.len()..].copy_from_slice(relocations);
// sort by VA. Upper 4 bits store the relocation type
entries.sort_unstable_by_key(|x| x & 0b0000_1111_1111_1111);
}
std::cmp::Ordering::Less => {
// done
break;
}
}
}
BaseRelocations {
new_size: old_size + shift_amount as u32,
delta: shift_amount as u32,
}
} else {
let header =
load_struct_inplace_mut::<ImageBaseRelocation>(mmap, next_block_start as usize);
let size_of_block = HEADER_WIDTH + relocations.len() * ENTRY_WIDTH;
header.virtual_address.set(LE, new_block_va);
header.size_of_block.set(LE, size_of_block as u32);
let number_of_entries = relocations.len();
let entries = load_structs_inplace_mut::<u16>(
mmap,
next_block_start as usize + HEADER_WIDTH,
number_of_entries,
);
entries[number_of_entries - relocations.len()..].copy_from_slice(relocations);
// sort by VA. Upper 4 bits store the relocation type
entries.sort_unstable_by_key(|x| x & 0b0000_1111_1111_1111);
BaseRelocations {
new_size: old_size + size_of_block as u32,
delta: size_of_block as u32,
}
}
}
/// the roc app functions are called from the host with an indirect call: the code will look
/// in a table to find the actual address of the app function. This table must be relocated,
/// because it contains absolute addresses to jump to.
fn relocate_dummy_dll_entries(executable: &mut [u8], md: &PeMetadata) {
let thunks_start_va =
md.dummy_dll_thunk_section_virtual_address + md.thunks_start_offset_in_section as u32;
// relocations are defined per 4kb page
const BLOCK_SIZE: u32 = 4096;
let thunks_offset_in_block = thunks_start_va % BLOCK_SIZE;
let thunks_relocation_block_va = thunks_start_va - thunks_offset_in_block;
let relocations = vec![];
// let relocations: Vec<_> = (0..md.dynamic_relocations.name_by_virtual_address.len())
// .map(|i| (thunks_offset_in_block as usize + 2 * i) as u16)
// .collect();
let base_relocations = write_image_base_relocation(
executable,
md.reloc_offset_in_file,
thunks_relocation_block_va,
&relocations,
);
// the reloc section got bigger, and we need to update the header with the new size
let reloc_section_header_start = md.dynamic_relocations.section_headers_offset_in_file as usize
+ md.reloc_section_index * std::mem::size_of::<ImageSectionHeader>();
let next_section = load_struct_inplace::<ImageSectionHeader>(
executable,
reloc_section_header_start + std::mem::size_of::<ImageSectionHeader>(),
);
let next_section_pointer_to_raw_data = next_section.pointer_to_raw_data.get(LE);
let reloc_section =
load_struct_inplace_mut::<ImageSectionHeader>(executable, reloc_section_header_start);
let old_section_size = reloc_section.virtual_size.get(LE);
let new_virtual_size = old_section_size + base_relocations.delta;
let new_virtual_size = next_multiple_of(new_virtual_size as usize, 8) as u32;
reloc_section.virtual_size.set(LE, new_virtual_size);
assert!(
reloc_section.pointer_to_raw_data.get(LE)
+ reloc_section.virtual_size.get(LE)
+ base_relocations.delta
< next_section_pointer_to_raw_data,
"new .reloc section is too big, and runs into the next section!",
);
// in the data directories, update the length of the base relocations
let dir = load_struct_inplace_mut::<pe::ImageDataDirectory>(
executable,
@ -1426,9 +1255,9 @@ fn relocate_dummy_dll_entries(executable: &mut [u8], md: &PeMetadata) {
* std::mem::size_of::<pe::ImageDataDirectory>(),
);
// it is crucial that the directory size is rounded up to a multiple of 8!
// the value is already rounded, so to be correct we can't rely on `dir.size.get(LE)`
let new_reloc_directory_size = next_multiple_of(base_relocations.new_size as usize, 8);
// for unclear reasons, we must bump the image directory size here.
let new_reloc_directory_size =
next_multiple_of(dir.size.get(LE) as usize, md.file_alignment as usize);
dir.size.set(LE, new_reloc_directory_size as u32);
}