1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
use crate::object::utils::try_parse_func_name;
use object::read::{Object, ObjectSection, Relocation, RelocationTarget};
use object::{elf, File, ObjectSymbol, RelocationEncoding, RelocationKind};
use std::ptr::{read_unaligned, write_unaligned};
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::wasm::DefinedFuncIndex;
use wasmtime_environ::Module;
use wasmtime_runtime::libcalls;
use wasmtime_runtime::VMFunctionBody;
pub fn link_module(
obj: &File,
module: &Module,
code_range: &mut [u8],
finished_functions: &PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
) {
let text_section = obj.section_by_name(".text").unwrap();
let body = code_range.as_ptr() as *const VMFunctionBody;
for (offset, r) in text_section.relocations() {
apply_reloc(module, obj, finished_functions, body, offset, r);
}
}
fn apply_reloc(
module: &Module,
obj: &File,
finished_functions: &PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
body: *const VMFunctionBody,
offset: u64,
r: Relocation,
) {
let target_func_address: usize = match r.target() {
RelocationTarget::Symbol(i) => {
let sym = obj.symbol_by_index(i).unwrap();
match sym.name() {
Ok(name) => {
if let Some(index) = try_parse_func_name(name) {
match module.defined_func_index(index) {
Some(f) => {
let fatptr: *const [VMFunctionBody] = finished_functions[f];
fatptr as *const VMFunctionBody as usize
}
None => panic!("direct call to import"),
}
} else if let Some(addr) = to_libcall_address(name) {
addr
} else {
panic!("unknown function to link: {}", name);
}
}
Err(_) => panic!("unexpected relocation target: not a symbol"),
}
}
_ => panic!("unexpected relocation target"),
};
match (r.kind(), r.encoding(), r.size()) {
#[cfg(target_pointer_width = "64")]
(RelocationKind::Absolute, RelocationEncoding::Generic, 64) => unsafe {
let reloc_address = body.add(offset as usize) as usize;
let reloc_addend = r.addend() as isize;
let reloc_abs = (target_func_address as u64)
.checked_add(reloc_addend as u64)
.unwrap();
write_unaligned(reloc_address as *mut u64, reloc_abs);
},
#[cfg(target_pointer_width = "32")]
(RelocationKind::Relative, RelocationEncoding::Generic, 32) => unsafe {
let reloc_address = body.add(offset as usize) as usize;
let reloc_addend = r.addend() as isize;
let reloc_delta_u32 = (target_func_address as u32)
.wrapping_sub(reloc_address as u32)
.checked_add(reloc_addend as u32)
.unwrap();
write_unaligned(reloc_address as *mut u32, reloc_delta_u32);
},
#[cfg(target_pointer_width = "32")]
(RelocationKind::Relative, RelocationEncoding::X86Branch, 32) => unsafe {
let reloc_address = body.add(offset as usize) as usize;
let reloc_addend = r.addend() as isize;
let reloc_delta_u32 = (target_func_address as u32)
.wrapping_sub(reloc_address as u32)
.wrapping_add(reloc_addend as u32);
write_unaligned(reloc_address as *mut u32, reloc_delta_u32);
},
#[cfg(target_pointer_width = "64")]
(RelocationKind::Relative, RelocationEncoding::Generic, 32) => unsafe {
let reloc_address = body.add(offset as usize) as usize;
let reloc_addend = r.addend() as isize;
let reloc_delta_u64 = (target_func_address as u64)
.wrapping_sub(reloc_address as u64)
.wrapping_add(reloc_addend as u64);
assert!(
reloc_delta_u64 as isize <= i32::max_value() as isize,
"relocation too large to fit in i32"
);
write_unaligned(reloc_address as *mut u32, reloc_delta_u64 as u32);
},
(RelocationKind::Elf(elf::R_AARCH64_CALL26), RelocationEncoding::Generic, 32) => unsafe {
let reloc_address = body.add(offset as usize) as usize;
let reloc_addend = r.addend() as isize;
let reloc_delta = (target_func_address as u64).wrapping_sub(reloc_address as u64);
assert!((reloc_delta as i64) < (1 << 27));
assert!((reloc_delta as i64) >= -(1 << 27));
let reloc_delta = reloc_delta as u32;
let reloc_delta = reloc_delta.wrapping_add(reloc_addend as u32);
let delta_bits = reloc_delta >> 2;
let insn = read_unaligned(reloc_address as *const u32);
let new_insn = (insn & 0xfc00_0000) | (delta_bits & 0x03ff_ffff);
write_unaligned(reloc_address as *mut u32, new_insn);
},
other => panic!("unsupported reloc kind: {:?}", other),
}
}
fn to_libcall_address(name: &str) -> Option<usize> {
use self::libcalls::*;
use wasmtime_environ::for_each_libcall;
macro_rules! add_libcall_symbol {
[$(($libcall:ident, $export:ident)),*] => {
Some(match name {
$(
stringify!($export) => $export as usize,
)+
_ => {
return None;
}
})
};
}
for_each_libcall!(add_libcall_symbol)
}