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
6#![cfg_attr(not(feature = "compiler"), allow(dead_code))]
7
8use wasmer_types::LibCall;
9use wasmer_types::target::{Architecture, Target};
10
11use crate::types::{
12    relocation::{Relocation, RelocationKind, RelocationTarget},
13    section::{CustomSection, CustomSectionProtection, SectionBody},
14};
15
16// SystemV says that both x16 and x17 are available as intra-procedural scratch
17// registers but Apple's ABI restricts us to use x17.
18// LDR x17, [PC, #8]  51 00 00 58
19// BR x17             20 02 1f d6
20// JMPADDR            00 00 00 00 00 00 00 00
21const AARCH64_TRAMPOLINE: [u8; 16] = [
22    0x51, 0x00, 0x00, 0x58, 0x20, 0x02, 0x1f, 0xd6, 0, 0, 0, 0, 0, 0, 0, 0,
23];
24
25// 2 padding bytes are used to preserve alignment.
26// JMP [RIP + 2]   FF 25 02 00 00 00 [00 00]
27// 64-bit ADDR     00 00 00 00 00 00 00 00
28const X86_64_TRAMPOLINE: [u8; 16] = [
29    0xff, 0x25, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30];
31
32// can it be shorter than this?
33// 4 padding bytes are used to preserve alignment.
34// AUIPC t1,0     17 03 00 00
35// LD t1, 16(t1)  03 33 03 01
36// JR t1          67 00 03 00 [00 00 00 00]
37// JMPADDR        00 00 00 00 00 00 00 00
38const RISCV64_TRAMPOLINE: [u8; 24] = [
39    0x17, 0x03, 0x00, 0x00, 0x03, 0x33, 0x03, 0x01, 0x67, 0x00, 0x03, 0x00, 0, 0, 0, 0, 0, 0, 0, 0,
40    0, 0, 0, 0,
41];
42
43// PCADDI r12, 0      0c 00 00 18
44// LD.D r12, r12, 16  8c 41 c0 28
45// JR r12             80 01 00 4c [00 00 00 00]
46// JMPADDR            00 00 00 00 00 00 00 00
47const LOONGARCH64_TRAMPOLINE: [u8; 24] = [
48    0x0c, 0x00, 0x00, 0x18, 0x8c, 0x41, 0xc0, 0x28, 0x80, 0x01, 0x00, 0x4c, 0, 0, 0, 0, 0, 0, 0, 0,
49    0, 0, 0, 0,
50];
51
52fn make_trampoline(
53    target: &Target,
54    libcall: LibCall,
55    code: &mut Vec<u8>,
56    relocations: &mut Vec<Relocation>,
57) {
58    match target.triple().architecture {
59        Architecture::Aarch64(_) => {
60            code.extend(AARCH64_TRAMPOLINE);
61            relocations.push(Relocation {
62                kind: RelocationKind::Abs8,
63                reloc_target: RelocationTarget::LibCall(libcall),
64                offset: code.len() as u32 - 8,
65                addend: 0,
66            });
67        }
68        Architecture::X86_64 => {
69            code.extend(X86_64_TRAMPOLINE);
70            relocations.push(Relocation {
71                kind: RelocationKind::Abs8,
72                reloc_target: RelocationTarget::LibCall(libcall),
73                offset: code.len() as u32 - 8,
74                addend: 0,
75            });
76        }
77        Architecture::Riscv64(_) => {
78            code.extend(RISCV64_TRAMPOLINE);
79            relocations.push(Relocation {
80                kind: RelocationKind::Abs8,
81                reloc_target: RelocationTarget::LibCall(libcall),
82                offset: code.len() as u32 - 8,
83                addend: 0,
84            });
85        }
86        Architecture::LoongArch64 => {
87            code.extend(LOONGARCH64_TRAMPOLINE);
88            relocations.push(Relocation {
89                kind: RelocationKind::Abs8,
90                reloc_target: RelocationTarget::LibCall(libcall),
91                offset: code.len() as u32 - 8,
92                addend: 0,
93            });
94        }
95        arch => panic!("Unsupported architecture: {arch}"),
96    };
97}
98
99/// Returns the length of a libcall trampoline.
100pub fn libcall_trampoline_len(target: &Target) -> usize {
101    match target.triple().architecture {
102        Architecture::Aarch64(_) => AARCH64_TRAMPOLINE.len(),
103        Architecture::X86_64 => X86_64_TRAMPOLINE.len(),
104        Architecture::Riscv64(_) => RISCV64_TRAMPOLINE.len(),
105        Architecture::LoongArch64 => LOONGARCH64_TRAMPOLINE.len(),
106        arch => panic!("Unsupported architecture: {arch}"),
107    }
108}
109
110/// Creates a custom section containing the libcall trampolines.
111pub fn make_libcall_trampolines(target: &Target) -> CustomSection {
112    let mut code = vec![];
113    let mut relocations = vec![];
114    for libcall in enum_iterator::all::<LibCall>() {
115        make_trampoline(target, libcall, &mut code, &mut relocations);
116    }
117    CustomSection {
118        protection: CustomSectionProtection::ReadExecute,
119        alignment: None,
120        bytes: SectionBody::new_with_vec(code),
121        relocations,
122    }
123}
124
125/// Returns the address of a trampoline in the libcall trampolines section.
126pub fn get_libcall_trampoline(
127    libcall: LibCall,
128    libcall_trampolines: usize,
129    libcall_trampoline_len: usize,
130) -> usize {
131    libcall_trampolines + libcall as usize * libcall_trampoline_len
132}