mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 13:59:08 +00:00
do inter-app relocations (PE)
This commit is contained in:
parent
b725b36697
commit
ba704d731f
1 changed files with 213 additions and 33 deletions
|
@ -16,11 +16,12 @@ use object::{
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use roc_collections::MutMap;
|
use roc_collections::{MutMap, VecMap};
|
||||||
use roc_error_macros::internal_error;
|
use roc_error_macros::internal_error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
generate_dylib::APP_DLL, load_struct_inplace, load_struct_inplace_mut, open_mmap, open_mmap_mut,
|
dbg_hex, generate_dylib::APP_DLL, load_struct_inplace, load_struct_inplace_mut, open_mmap,
|
||||||
|
open_mmap_mut,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The metadata stores information about/from the host .exe because
|
/// The metadata stores information about/from the host .exe because
|
||||||
|
@ -194,7 +195,7 @@ pub(crate) fn surgery_pe(executable_path: &Path, metadata_path: &Path, roc_app_b
|
||||||
let md = PeMetadata::read_from_file(metadata_path);
|
let md = PeMetadata::read_from_file(metadata_path);
|
||||||
|
|
||||||
let app_obj_sections = AppSections::from_data(roc_app_bytes);
|
let app_obj_sections = AppSections::from_data(roc_app_bytes);
|
||||||
let mut symbols = app_obj_sections.symbols;
|
let mut symbols = app_obj_sections.roc_symbols;
|
||||||
|
|
||||||
let image_base: u64 = md.image_base;
|
let image_base: u64 = md.image_base;
|
||||||
let file_alignment = md.file_alignment as usize;
|
let file_alignment = md.file_alignment as usize;
|
||||||
|
@ -225,6 +226,15 @@ pub(crate) fn surgery_pe(executable_path: &Path, metadata_path: &Path, roc_app_b
|
||||||
let mut data_bytes_added = 0;
|
let mut data_bytes_added = 0;
|
||||||
let mut file_bytes_added = 0;
|
let mut file_bytes_added = 0;
|
||||||
|
|
||||||
|
// relocations between the sections of the roc application
|
||||||
|
// (as opposed to relocations for symbols the app imports from the host)
|
||||||
|
let inter_app_relocations = process_internal_relocations(
|
||||||
|
&app_obj_sections.sections,
|
||||||
|
&app_obj_sections.other_symbols,
|
||||||
|
(app_code_section_va - image_base) as u32,
|
||||||
|
section_alignment,
|
||||||
|
);
|
||||||
|
|
||||||
for kind in [SectionKind::Text, SectionKind::ReadOnlyData] {
|
for kind in [SectionKind::Text, SectionKind::ReadOnlyData] {
|
||||||
let length: usize = app_obj_sections
|
let length: usize = app_obj_sections
|
||||||
.sections
|
.sections
|
||||||
|
@ -287,16 +297,14 @@ pub(crate) fn surgery_pe(executable_path: &Path, metadata_path: &Path, roc_app_b
|
||||||
address,
|
address,
|
||||||
} = app_relocation;
|
} = app_relocation;
|
||||||
|
|
||||||
match md.exports.get(name) {
|
if let Some(destination) = md.exports.get(name) {
|
||||||
Some(destination) => {
|
|
||||||
match relocation.kind() {
|
match relocation.kind() {
|
||||||
object::RelocationKind::Relative => {
|
object::RelocationKind::Relative => {
|
||||||
// we implicitly only do 32-bit relocations
|
// we implicitly only do 32-bit relocations
|
||||||
debug_assert_eq!(relocation.size(), 32);
|
debug_assert_eq!(relocation.size(), 32);
|
||||||
|
|
||||||
let delta = destination
|
let delta =
|
||||||
- virtual_address as i64
|
destination - virtual_address as i64 - *offset_in_section as i64
|
||||||
- *offset_in_section as i64
|
|
||||||
+ relocation.addend();
|
+ relocation.addend();
|
||||||
|
|
||||||
executable[offset + *offset_in_section as usize..][..4]
|
executable[offset + *offset_in_section as usize..][..4]
|
||||||
|
@ -304,15 +312,23 @@ pub(crate) fn surgery_pe(executable_path: &Path, metadata_path: &Path, roc_app_b
|
||||||
}
|
}
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
} else if let Some(destination) = inter_app_relocations.get(name) {
|
||||||
None => {
|
// we implicitly only do 32-bit relocations
|
||||||
|
debug_assert_eq!(relocation.size(), 32);
|
||||||
|
|
||||||
|
let delta = destination - virtual_address as i64 - *offset_in_section as i64
|
||||||
|
+ relocation.addend();
|
||||||
|
|
||||||
|
executable[offset + *offset_in_section as usize..][..4]
|
||||||
|
.copy_from_slice(&(delta as i32).to_le_bytes());
|
||||||
|
} else {
|
||||||
match relocation.kind() {
|
match relocation.kind() {
|
||||||
object::RelocationKind::Relative => {
|
object::RelocationKind::Relative => {
|
||||||
// we implicitly only do 32-bit relocations
|
// we implicitly only do 32-bit relocations
|
||||||
debug_assert_eq!(relocation.size(), 32);
|
debug_assert_eq!(relocation.size(), 32);
|
||||||
|
|
||||||
let delta = *address as i64 - *offset_in_section as i64
|
let delta =
|
||||||
+ relocation.addend();
|
*address as i64 - *offset_in_section as i64 + relocation.addend();
|
||||||
|
|
||||||
executable[offset + *offset_in_section as usize..][..4]
|
executable[offset + *offset_in_section as usize..][..4]
|
||||||
.copy_from_slice(&(delta as i32).to_le_bytes());
|
.copy_from_slice(&(delta as i32).to_le_bytes());
|
||||||
|
@ -321,7 +337,6 @@ pub(crate) fn surgery_pe(executable_path: &Path, metadata_path: &Path, roc_app_b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
offset += slice.len();
|
offset += slice.len();
|
||||||
}
|
}
|
||||||
|
@ -767,6 +782,7 @@ fn redirect_dummy_dll_functions(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
#[repr(u8)]
|
||||||
enum SectionKind {
|
enum SectionKind {
|
||||||
Text,
|
Text,
|
||||||
// Data,
|
// Data,
|
||||||
|
@ -786,6 +802,7 @@ struct Section {
|
||||||
file_range: Range<usize>,
|
file_range: Range<usize>,
|
||||||
kind: SectionKind,
|
kind: SectionKind,
|
||||||
relocations: MutMap<String, AppRelocation>,
|
relocations: MutMap<String, AppRelocation>,
|
||||||
|
app_section_index: SectionIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -798,7 +815,44 @@ struct AppSymbol {
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct AppSections {
|
struct AppSections {
|
||||||
sections: Vec<Section>,
|
sections: Vec<Section>,
|
||||||
symbols: Vec<AppSymbol>,
|
roc_symbols: Vec<AppSymbol>,
|
||||||
|
other_symbols: Vec<(SectionIndex, AppSymbol)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process relocations between two places within the app. This a bit different from doing a
|
||||||
|
/// relocation of a symbol that will be "imported" from the host
|
||||||
|
fn process_internal_relocations(
|
||||||
|
sections: &[Section],
|
||||||
|
other_symbols: &[(SectionIndex, AppSymbol)],
|
||||||
|
first_host_section_virtual_address: u32,
|
||||||
|
section_alignment: usize,
|
||||||
|
) -> VecMap<String, i64> {
|
||||||
|
let mut result = VecMap::default();
|
||||||
|
let mut host_section_virtual_address = first_host_section_virtual_address;
|
||||||
|
|
||||||
|
for kind in [SectionKind::Text, SectionKind::ReadOnlyData] {
|
||||||
|
let it = sections.iter().filter(|s| s.kind == kind);
|
||||||
|
for section in it {
|
||||||
|
for (s_index, app_symbol) in other_symbols.iter() {
|
||||||
|
if *s_index == section.app_section_index {
|
||||||
|
result.insert(
|
||||||
|
app_symbol.name.clone(),
|
||||||
|
app_symbol.offset_in_section as i64 + host_section_virtual_address as i64,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let length: usize = sections
|
||||||
|
.iter()
|
||||||
|
.filter(|s| s.kind == kind)
|
||||||
|
.map(|s| s.file_range.end - s.file_range.start)
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
host_section_virtual_address += next_multiple_of(length, section_alignment) as u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppSections {
|
impl AppSections {
|
||||||
|
@ -866,6 +920,7 @@ impl AppSections {
|
||||||
}
|
}
|
||||||
|
|
||||||
let section = Section {
|
let section = Section {
|
||||||
|
app_section_index: index,
|
||||||
file_range,
|
file_range,
|
||||||
kind,
|
kind,
|
||||||
relocations,
|
relocations,
|
||||||
|
@ -874,11 +929,14 @@ impl AppSections {
|
||||||
sections.push(section);
|
sections.push(section);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut symbols = Vec::new();
|
let mut roc_symbols = Vec::new();
|
||||||
|
let mut other_symbols = Vec::new();
|
||||||
|
|
||||||
for symbol in file.symbols() {
|
for symbol in file.symbols() {
|
||||||
use object::ObjectSymbol;
|
use object::ObjectSymbol;
|
||||||
|
|
||||||
|
let name = String::from_utf8_lossy(symbol.name_bytes().unwrap_or_default());
|
||||||
|
|
||||||
if symbol.name_bytes().unwrap_or_default().starts_with(b"roc") {
|
if symbol.name_bytes().unwrap_or_default().starts_with(b"roc") {
|
||||||
if let object::SymbolSection::Section(index) = symbol.section() {
|
if let object::SymbolSection::Section(index) = symbol.section() {
|
||||||
let (kind, offset_in_host_section) = section_starts[&index];
|
let (kind, offset_in_host_section) = section_starts[&index];
|
||||||
|
@ -889,12 +947,26 @@ impl AppSections {
|
||||||
offset_in_section: (offset_in_host_section + symbol.address()) as usize,
|
offset_in_section: (offset_in_host_section + symbol.address()) as usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
symbols.push(symbol);
|
roc_symbols.push(symbol);
|
||||||
|
}
|
||||||
|
} else if let object::SymbolSection::Section(index) = symbol.section() {
|
||||||
|
if let Some((kind, offset_in_host_section)) = section_starts.get(&index) {
|
||||||
|
let symbol = AppSymbol {
|
||||||
|
name: symbol.name().unwrap_or_default().to_string(),
|
||||||
|
section_kind: *kind,
|
||||||
|
offset_in_section: (offset_in_host_section + symbol.address()) as usize,
|
||||||
|
};
|
||||||
|
|
||||||
|
other_symbols.push((index, symbol));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AppSections { sections, symbols }
|
AppSections {
|
||||||
|
sections,
|
||||||
|
roc_symbols,
|
||||||
|
other_symbols,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1385,7 +1457,7 @@ mod test {
|
||||||
let roc_app = unsafe { memmap2::Mmap::map(&file) }.unwrap();
|
let roc_app = unsafe { memmap2::Mmap::map(&file) }.unwrap();
|
||||||
|
|
||||||
let roc_app_sections = AppSections::from_data(&*roc_app);
|
let roc_app_sections = AppSections::from_data(&*roc_app);
|
||||||
let mut symbols = roc_app_sections.symbols;
|
let mut symbols = roc_app_sections.roc_symbols;
|
||||||
|
|
||||||
// make the dummy dylib based on the app object
|
// make the dummy dylib based on the app object
|
||||||
let names: Vec<_> = symbols.iter().map(|s| s.name.clone()).collect();
|
let names: Vec<_> = symbols.iter().map(|s| s.name.clone()).collect();
|
||||||
|
@ -1617,13 +1689,121 @@ mod test {
|
||||||
assert_eq!("Hello, 234567 32 1 3!\n", output);
|
assert_eq!("Hello, 234567 32 1 3!\n", output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This zig code sample containts a static relocation
|
||||||
|
fn static_relocation_help(dir: &Path) {
|
||||||
|
let host_zig = indoc!(
|
||||||
|
r#"
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
extern fn roc_magic1(usize) callconv(.C) [*]const u8;
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
const stdout = std.io.getStdOut().writer();
|
||||||
|
try stdout.print("Hello {s}\n", .{roc_magic1(0)[0..3]});
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
let app_zig = indoc!(
|
||||||
|
r#"
|
||||||
|
const X = [_][]const u8 { "foo" };
|
||||||
|
|
||||||
|
export fn roc_magic1(index: usize) [*]const u8 {
|
||||||
|
return X[index].ptr;
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
let zig = std::env::var("ROC_ZIG").unwrap_or_else(|_| "zig".into());
|
||||||
|
|
||||||
|
std::fs::write(dir.join("host.zig"), host_zig.as_bytes()).unwrap();
|
||||||
|
std::fs::write(dir.join("app.zig"), app_zig.as_bytes()).unwrap();
|
||||||
|
|
||||||
|
// we need to compile the app first
|
||||||
|
let output = std::process::Command::new(&zig)
|
||||||
|
.current_dir(dir)
|
||||||
|
.args(&[
|
||||||
|
"build-obj",
|
||||||
|
"app.zig",
|
||||||
|
"-target",
|
||||||
|
"x86_64-windows-gnu",
|
||||||
|
"--strip",
|
||||||
|
"-rdynamic",
|
||||||
|
"-OReleaseFast",
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
std::io::stdout().write_all(&output.stdout).unwrap();
|
||||||
|
std::io::stderr().write_all(&output.stderr).unwrap();
|
||||||
|
|
||||||
|
panic!("zig build-obj failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// open our app object; we'll copy sections from it later
|
||||||
|
let file = std::fs::File::open(dir.join("app.obj")).unwrap();
|
||||||
|
let roc_app = unsafe { memmap2::Mmap::map(&file) }.unwrap();
|
||||||
|
|
||||||
|
let roc_app_sections = AppSections::from_data(&*roc_app);
|
||||||
|
let symbols = roc_app_sections.roc_symbols;
|
||||||
|
|
||||||
|
// make the dummy dylib based on the app object
|
||||||
|
let names: Vec<_> = symbols.iter().map(|s| s.name.clone()).collect();
|
||||||
|
let dylib_bytes = crate::generate_dylib::synthetic_dll(&names);
|
||||||
|
std::fs::write(dir.join("libapp.obj"), dylib_bytes).unwrap();
|
||||||
|
|
||||||
|
// now we can compile the host (it uses libapp.obj, hence the order here)
|
||||||
|
let output = std::process::Command::new(&zig)
|
||||||
|
.current_dir(dir)
|
||||||
|
.args(&[
|
||||||
|
"build-exe",
|
||||||
|
"libapp.obj",
|
||||||
|
"host.zig",
|
||||||
|
"-lc",
|
||||||
|
"-target",
|
||||||
|
"x86_64-windows-gnu",
|
||||||
|
"-rdynamic",
|
||||||
|
"--strip",
|
||||||
|
"-rdynamic",
|
||||||
|
"-OReleaseFast",
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
std::io::stdout().write_all(&output.stdout).unwrap();
|
||||||
|
std::io::stderr().write_all(&output.stderr).unwrap();
|
||||||
|
|
||||||
|
panic!("zig build-exe failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
preprocess_windows(
|
||||||
|
&dir.join("host.exe"),
|
||||||
|
&dir.join("metadata"),
|
||||||
|
&dir.join("preprocessedhost"),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
std::fs::copy(&dir.join("preprocessedhost"), &dir.join("app.exe")).unwrap();
|
||||||
|
|
||||||
|
surgery_pe(&dir.join("app.exe"), &dir.join("metadata"), &*roc_app);
|
||||||
|
}
|
||||||
|
|
||||||
#[ignore]
|
#[ignore]
|
||||||
#[test]
|
#[test]
|
||||||
fn link_zig_host_and_app_wine() {
|
fn link_zig_host_and_app_wine() {
|
||||||
let dir = tempfile::tempdir().unwrap();
|
let dir = tempfile::tempdir().unwrap();
|
||||||
let dir = dir.path();
|
let dir = dir.path();
|
||||||
|
let dir = Path::new("/tmp/roc");
|
||||||
|
|
||||||
link_zig_host_and_app_help(dir);
|
static_relocation_help(dir);
|
||||||
|
|
||||||
let output = std::process::Command::new("wine")
|
let output = std::process::Command::new("wine")
|
||||||
.current_dir(dir)
|
.current_dir(dir)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue