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