linera_wasmer_compiler/artifact_builders/
trampoline.rs

1//! Trampolines for libcalls.
2//!
3//! This is needed because the target of libcall relocations are not reachable
4//! through normal branch instructions.
5
6use enum_iterator::IntoEnumIterator;
7use wasmer_types::{
8    Architecture, CustomSection, CustomSectionProtection, LibCall, Relocation, RelocationKind,
9    RelocationTarget, SectionBody, Target,
10};
11
12// SystemV says that both x16 and x17 are available as intra-procedural scratch
13// registers but Apple's ABI restricts us to use x17.
14// LDR x17, [PC, #8]  51 00 00 58
15// BR x17             20 02 1f d6
16// JMPADDR            00 00 00 00 00 00 00 00
17const AARCH64_TRAMPOLINE: [u8; 16] = [
18    0x51, 0x00, 0x00, 0x58, 0x20, 0x02, 0x1f, 0xd6, 0, 0, 0, 0, 0, 0, 0, 0,
19];
20
21// 2 padding bytes are used to preserve alignment.
22// JMP [RIP + 2]   FF 25 02 00 00 00 [00 00]
23// 64-bit ADDR     00 00 00 00 00 00 00 00
24const X86_64_TRAMPOLINE: [u8; 16] = [
25    0xff, 0x25, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
26];
27
28// can it be shorter than this?
29// 4 padding bytes are used to preserve alignment.
30// AUIPC t1,0     17 03 00 00
31// LD t1, 16(t1)  03 33 03 01
32// JR t1          67 00 03 00 [00 00 00 00]
33// JMPADDR        00 00 00 00 00 00 00 00
34const RISCV64_TRAMPOLINE: [u8; 24] = [
35    0x17, 0x03, 0x00, 0x00, 0x03, 0x33, 0x03, 0x01, 0x67, 0x00, 0x03, 0x00, 0, 0, 0, 0, 0, 0, 0, 0,
36    0, 0, 0, 0,
37];
38
39fn make_trampoline(
40    target: &Target,
41    libcall: LibCall,
42    code: &mut Vec<u8>,
43    relocations: &mut Vec<Relocation>,
44) {
45    match target.triple().architecture {
46        Architecture::Aarch64(_) => {
47            code.extend(AARCH64_TRAMPOLINE);
48            relocations.push(Relocation {
49                kind: RelocationKind::Abs8,
50                reloc_target: RelocationTarget::LibCall(libcall),
51                offset: code.len() as u32 - 8,
52                addend: 0,
53            });
54        }
55        Architecture::X86_64 => {
56            code.extend(X86_64_TRAMPOLINE);
57            relocations.push(Relocation {
58                kind: RelocationKind::Abs8,
59                reloc_target: RelocationTarget::LibCall(libcall),
60                offset: code.len() as u32 - 8,
61                addend: 0,
62            });
63        }
64        Architecture::Riscv64(_) => {
65            code.extend(RISCV64_TRAMPOLINE);
66            relocations.push(Relocation {
67                kind: RelocationKind::Abs8,
68                reloc_target: RelocationTarget::LibCall(libcall),
69                offset: code.len() as u32 - 8,
70                addend: 0,
71            });
72        }
73        arch => panic!("Unsupported architecture: {}", arch),
74    };
75}
76
77/// Returns the length of a libcall trampoline.
78pub fn libcall_trampoline_len(target: &Target) -> usize {
79    match target.triple().architecture {
80        Architecture::Aarch64(_) => AARCH64_TRAMPOLINE.len(),
81        Architecture::X86_64 => X86_64_TRAMPOLINE.len(),
82        Architecture::Riscv64(_) => RISCV64_TRAMPOLINE.len(),
83        arch => panic!("Unsupported architecture: {}", arch),
84    }
85}
86
87/// Creates a custom section containing the libcall trampolines.
88pub fn make_libcall_trampolines(target: &Target) -> CustomSection {
89    let mut code = vec![];
90    let mut relocations = vec![];
91    for libcall in LibCall::into_enum_iter() {
92        make_trampoline(target, libcall, &mut code, &mut relocations);
93    }
94    CustomSection {
95        protection: CustomSectionProtection::ReadExecute,
96        bytes: SectionBody::new_with_vec(code),
97        relocations,
98    }
99}
100
101/// Returns the address of a trampoline in the libcall trampolines section.
102pub fn get_libcall_trampoline(
103    libcall: LibCall,
104    libcall_trampolines: usize,
105    libcall_trampoline_len: usize,
106) -> usize {
107    libcall_trampolines + libcall as usize * libcall_trampoline_len
108}