Fix rust host with surgical linking

This commit is contained in:
Brendan Hansknecht 2021-09-24 21:37:07 -07:00
parent eae8a2ea37
commit 411ed58eec
16 changed files with 920 additions and 67 deletions

View file

@ -28,6 +28,7 @@ use target_lexicon::Triple;
use tempfile::Builder;
mod metadata;
use metadata::VirtualOffset;
pub const CMD_PREPROCESS: &str = "preprocess";
pub const CMD_SURGERY: &str = "surgery";
@ -196,7 +197,6 @@ fn generate_dynamic_lib(
let text_section = out_object.section_id(write::StandardSection::Text);
for sym in exposed_to_host {
// TODO properly generate this list.
for name in &[
format!("roc__{}_1_exposed", sym),
format!("roc__{}_1_exposed_generic", sym),
@ -317,7 +317,9 @@ fn preprocess_impl(
for sym in exec_obj.symbols().filter(|sym| {
sym.is_definition() && sym.name().is_ok() && sym.name().unwrap().starts_with("roc_")
}) {
let name = sym.name().unwrap().to_string();
// remove potentially trailing "@version".
let name = sym.name().unwrap().split("@").next().unwrap().to_string();
// special exceptions for memcpy and memset.
if &name == "roc_memcpy" {
md.roc_symbol_vaddresses
@ -368,9 +370,6 @@ fn preprocess_impl(
println!("PLT File Offset: {:+x}", plt_offset);
}
// TODO: it looks like we may need to support global data host relocations.
// Rust host look to be using them by default instead of the plt.
// I think this is due to first linking into a static lib and then linking to the c wrapper.
let plt_relocs = (match exec_obj.dynamic_relocations() {
Some(relocs) => relocs,
None => {
@ -380,7 +379,7 @@ fn preprocess_impl(
}
})
.map(|(_, reloc)| reloc)
.filter(|reloc| reloc.kind() == RelocationKind::Elf(7));
.filter(|reloc| matches!(reloc.kind(), RelocationKind::Elf(7)));
let app_syms: Vec<Symbol> = exec_obj
.dynamic_symbols()
@ -388,6 +387,28 @@ fn preprocess_impl(
sym.is_undefined() && sym.name().is_ok() && sym.name().unwrap().starts_with("roc_")
})
.collect();
let got_app_syms: Vec<(String, usize)> = (match exec_obj.dynamic_relocations() {
Some(relocs) => relocs,
None => {
println!("Executable never calls any application functions.");
println!("No work to do. Probably an invalid input.");
return Ok(-1);
}
})
.map(|(_, reloc)| reloc)
.filter(|reloc| matches!(reloc.kind(), RelocationKind::Elf(6)))
.map(|reloc| {
for symbol in app_syms.iter() {
if reloc.target() == RelocationTarget::Symbol(symbol.index()) {
return Some((symbol.name().unwrap().to_string(), symbol.index().0));
}
}
None
})
.filter_map(|x| x)
.collect();
for sym in app_syms.iter() {
let name = sym.name().unwrap().to_string();
md.app_functions.push(name.clone());
@ -408,6 +429,7 @@ fn preprocess_impl(
if reloc.target() == RelocationTarget::Symbol(symbol.index()) {
let func_address = (i as u64 + 1) * PLT_ADDRESS_OFFSET + plt_address;
let func_offset = (i as u64 + 1) * PLT_ADDRESS_OFFSET + plt_offset;
println!("{}", symbol.name().unwrap().to_string());
app_func_addresses.insert(func_address, symbol.name().unwrap());
md.plt_addresses.insert(
symbol.name().unwrap().to_string(),
@ -537,7 +559,7 @@ fn preprocess_impl(
.unwrap()
.push(metadata::SurgeryEntry {
file_offset: offset,
virtual_offset: inst.next_ip(),
virtual_offset: VirtualOffset::Relative(inst.next_ip()),
size: op_size,
});
}
@ -879,7 +901,7 @@ fn preprocess_impl(
sec_offset as usize + md.added_byte_count as usize,
sec_size as usize / mem::size_of::<elf::Rela64<LittleEndian>>(),
);
for rel in relocations.iter_mut() {
for (i, rel) in relocations.iter_mut().enumerate() {
let r_offset = rel.r_offset.get(NativeEndian);
if virtual_shift_start <= r_offset {
rel.r_offset = endian::U64::new(LittleEndian, r_offset + md.added_byte_count);
@ -891,6 +913,28 @@ fn preprocess_impl(
.set(LittleEndian, r_addend + md.added_byte_count as i64);
}
}
// If the relocation goes to a roc function, we need to surgically link it and change it to relative.
let r_type = rel.r_type(NativeEndian, false);
if r_type == elf::R_X86_64_GLOB_DAT {
let r_sym = rel.r_sym(NativeEndian, false);
for (name, index) in got_app_syms.iter() {
if *index as u32 == r_sym {
rel.set_r_info(LittleEndian, false, 0, elf::R_X86_64_RELATIVE);
let addend_addr = sec_offset as usize
+ i * mem::size_of::<elf::Rela64<LittleEndian>>()
// This 16 skips the first 2 fields and gets to the addend field.
+ 16;
md.surgeries
.get_mut(name)
.unwrap()
.push(metadata::SurgeryEntry {
file_offset: addend_addr as u64,
virtual_offset: VirtualOffset::Absolute,
size: 8,
});
}
}
}
}
}
@ -1462,7 +1506,7 @@ fn surgery_impl(
let dynsym_offset = md.dynamic_symbol_table_section_offset + md.added_byte_count;
for func_name in md.app_functions {
let virt_offset = match app_func_vaddr_map.get(&func_name) {
let func_virt_offset = match app_func_vaddr_map.get(&func_name) {
Some(offset) => *offset as u64,
None => {
println!("Function, {}, was not defined by the app", &func_name);
@ -1472,7 +1516,7 @@ fn surgery_impl(
if verbose {
println!(
"Updating calls to {} to the address: {:+x}",
&func_name, virt_offset
&func_name, func_virt_offset
);
}
@ -1480,11 +1524,13 @@ fn surgery_impl(
if verbose {
println!("\tPerforming surgery: {:+x?}", s);
}
let surgery_virt_offset = match s.virtual_offset {
VirtualOffset::Relative(vs) => (vs + md.added_byte_count) as i64,
VirtualOffset::Absolute => 0,
};
match s.size {
4 => {
let target = (virt_offset as i64
- (s.virtual_offset + md.added_byte_count) as i64)
as i32;
let target = (func_virt_offset as i64 - surgery_virt_offset) as i32;
if verbose {
println!("\tTarget Jump: {:+x}", target);
}
@ -1493,6 +1539,16 @@ fn surgery_impl(
..(s.file_offset + md.added_byte_count) as usize + 4]
.copy_from_slice(&data);
}
8 => {
let target = func_virt_offset as i64 - surgery_virt_offset;
if verbose {
println!("\tTarget Jump: {:+x}", target);
}
let data = target.to_le_bytes();
exec_mmap[(s.file_offset + md.added_byte_count) as usize
..(s.file_offset + md.added_byte_count) as usize + 8]
.copy_from_slice(&data);
}
x => {
println!("Surgery size not yet supported: {}", x);
return Ok(-1);
@ -1506,7 +1562,8 @@ fn surgery_impl(
let plt_off = (*plt_off + md.added_byte_count) as usize;
let plt_vaddr = *plt_vaddr + md.added_byte_count;
let jmp_inst_len = 5;
let target = (virt_offset as i64 - (plt_vaddr as i64 + jmp_inst_len as i64)) as i32;
let target =
(func_virt_offset as i64 - (plt_vaddr as i64 + jmp_inst_len as i64)) as i32;
if verbose {
println!("\tPLT: {:+x}, {:+x}", plt_off, plt_vaddr);
println!("\tTarget Jump: {:+x}", target);
@ -1525,7 +1582,7 @@ fn surgery_impl(
dynsym_offset as usize + *i as usize * mem::size_of::<elf::Sym64<LittleEndian>>(),
);
sym.st_shndx = endian::U16::new(LittleEndian, new_text_section_index as u16);
sym.st_value = endian::U64::new(LittleEndian, virt_offset as u64);
sym.st_value = endian::U64::new(LittleEndian, func_virt_offset as u64);
sym.st_size = endian::U64::new(
LittleEndian,
match app_func_size_map.get(&func_name) {