use enum_iterator::IntoEnumIterator;
use object::{
elf, macho,
write::{Object, Relocation, SectionId, StandardSection, Symbol, SymbolId, SymbolSection},
BinaryFormat, RelocationEncoding, RelocationKind, SymbolFlags, SymbolKind, SymbolScope,
};
use wasmer_compiler::{Architecture, Target};
use wasmer_vm::libcalls::{function_pointer, LibCall};
pub const WASMER_TRAMPOLINES_SYMBOL: &[u8] = b"WASMER_TRAMPOLINES";
const AARCH64_TRAMPOLINE: [u8; 12] = [
0x11, 0x00, 0x00, 0x90, 0x31, 0x02, 0x40, 0xf9, 0x20, 0x02, 0x1f, 0xd6,
];
const X86_64_TRAMPOLINE: [u8; 6] = [0xff, 0x25, 0x00, 0x00, 0x00, 0x00];
fn emit_trampoline(
obj: &mut Object,
text: SectionId,
trampoline_table_symbols: &[SymbolId],
libcall: LibCall,
target: &Target,
) {
let function_name = libcall.to_function_name();
let libcall_symbol = obj.add_symbol(Symbol {
name: function_name.as_bytes().to_vec(),
value: 0,
size: 0,
kind: SymbolKind::Text,
scope: SymbolScope::Linkage,
weak: false,
section: SymbolSection::Section(text),
flags: SymbolFlags::None,
});
match target.triple().architecture {
Architecture::Aarch64(_) => {
let (reloc1, reloc2) = match obj.format() {
BinaryFormat::Elf => (
RelocationKind::Elf(elf::R_AARCH64_ADR_PREL_PG_HI21),
RelocationKind::Elf(elf::R_AARCH64_LDST64_ABS_LO12_NC),
),
BinaryFormat::MachO => (
RelocationKind::MachO {
value: macho::ARM64_RELOC_PAGE21,
relative: true,
},
RelocationKind::MachO {
value: macho::ARM64_RELOC_PAGEOFF12,
relative: false,
},
),
_ => panic!("Unsupported binary format on AArch64"),
};
let offset = obj.add_symbol_data(libcall_symbol, text, &AARCH64_TRAMPOLINE, 4);
obj.add_relocation(
text,
Relocation {
offset,
size: 32,
kind: reloc1,
encoding: RelocationEncoding::Generic,
symbol: trampoline_table_symbols[libcall as usize],
addend: 0,
},
)
.unwrap();
obj.add_relocation(
text,
Relocation {
offset: offset + 4,
size: 32,
kind: reloc2,
encoding: RelocationEncoding::Generic,
symbol: trampoline_table_symbols[libcall as usize],
addend: 0,
},
)
.unwrap();
}
Architecture::X86_64 => {
let offset = obj.add_symbol_data(libcall_symbol, text, &X86_64_TRAMPOLINE, 1);
obj.add_relocation(
text,
Relocation {
offset: offset + 2,
size: 32,
kind: RelocationKind::Relative,
encoding: RelocationEncoding::Generic,
symbol: trampoline_table_symbols[libcall as usize],
addend: -4,
},
)
.unwrap();
}
arch => panic!("Unsupported architecture: {}", arch),
};
}
pub fn emit_trampolines(obj: &mut Object, target: &Target) {
let text = obj.section_id(StandardSection::Text);
let bss = obj.section_id(StandardSection::UninitializedData);
let trampoline_table = obj.add_symbol(Symbol {
name: WASMER_TRAMPOLINES_SYMBOL.to_vec(),
value: 0,
size: 0,
kind: SymbolKind::Data,
scope: SymbolScope::Dynamic,
weak: false,
section: SymbolSection::Section(bss),
flags: SymbolFlags::None,
});
let table_offset =
obj.add_symbol_bss(trampoline_table, bss, LibCall::VARIANT_COUNT as u64 * 8, 8);
let mut trampoline_table_symbols = vec![];
for libcall in LibCall::into_enum_iter() {
trampoline_table_symbols.push(obj.add_symbol(Symbol {
name: format!("__WASMER_TRAMPOLINE{}", libcall as usize).into_bytes(),
value: table_offset + libcall as u64 * 8,
size: 0,
kind: SymbolKind::Data,
scope: SymbolScope::Compilation,
weak: false,
section: SymbolSection::Section(bss),
flags: SymbolFlags::None,
}));
}
for libcall in LibCall::into_enum_iter() {
emit_trampoline(obj, text, &trampoline_table_symbols, libcall, target);
}
}
pub unsafe fn fill_trampoline_table(table: *mut usize) {
for libcall in LibCall::into_enum_iter() {
*table.add(libcall as usize) = function_pointer(libcall);
}
}