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
//! Trampolines for libcalls.
//!
//! This is needed because the target of libcall relocations are not reachable
//! through normal branch instructions.

use enum_iterator::IntoEnumIterator;
use target_lexicon::Architecture;
use wasmer_types::LibCall;

use crate::types::{
    relocation::{Relocation, RelocationKind, RelocationTarget},
    section::{CustomSection, CustomSectionProtection, SectionBody},
    target::Target,
};

// SystemV says that both x16 and x17 are available as intra-procedural scratch
// registers but Apple's ABI restricts us to use x17.
// LDR x17, [PC, #8]  51 00 00 58
// BR x17             20 02 1f d6
// JMPADDR            00 00 00 00 00 00 00 00
const AARCH64_TRAMPOLINE: [u8; 16] = [
    0x51, 0x00, 0x00, 0x58, 0x20, 0x02, 0x1f, 0xd6, 0, 0, 0, 0, 0, 0, 0, 0,
];

// 2 padding bytes are used to preserve alignment.
// JMP [RIP + 2]   FF 25 02 00 00 00 [00 00]
// 64-bit ADDR     00 00 00 00 00 00 00 00
const X86_64_TRAMPOLINE: [u8; 16] = [
    0xff, 0x25, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];

// can it be shorter than this?
// 4 padding bytes are used to preserve alignment.
// AUIPC t1,0     17 03 00 00
// LD t1, 16(t1)  03 33 03 01
// JR t1          67 00 03 00 [00 00 00 00]
// JMPADDR        00 00 00 00 00 00 00 00
const RISCV64_TRAMPOLINE: [u8; 24] = [
    0x17, 0x03, 0x00, 0x00, 0x03, 0x33, 0x03, 0x01, 0x67, 0x00, 0x03, 0x00, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0,
];

// PCADDI r12, 0      0c 00 00 18
// LD.D r12, r12, 16  8c 41 c0 28
// JR r12             80 01 00 4c [00 00 00 00]
// JMPADDR            00 00 00 00 00 00 00 00
const LOONGARCH64_TRAMPOLINE: [u8; 24] = [
    0x0c, 0x00, 0x00, 0x0c, 0x8c, 0x41, 0xc0, 0x28, 0x80, 0x01, 0x00, 0x4c, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0,
];

fn make_trampoline(
    target: &Target,
    libcall: LibCall,
    code: &mut Vec<u8>,
    relocations: &mut Vec<Relocation>,
) {
    match target.triple().architecture {
        Architecture::Aarch64(_) => {
            code.extend(AARCH64_TRAMPOLINE);
            relocations.push(Relocation {
                kind: RelocationKind::Abs8,
                reloc_target: RelocationTarget::LibCall(libcall),
                offset: code.len() as u32 - 8,
                addend: 0,
            });
        }
        Architecture::X86_64 => {
            code.extend(X86_64_TRAMPOLINE);
            relocations.push(Relocation {
                kind: RelocationKind::Abs8,
                reloc_target: RelocationTarget::LibCall(libcall),
                offset: code.len() as u32 - 8,
                addend: 0,
            });
        }
        Architecture::Riscv64(_) => {
            code.extend(RISCV64_TRAMPOLINE);
            relocations.push(Relocation {
                kind: RelocationKind::Abs8,
                reloc_target: RelocationTarget::LibCall(libcall),
                offset: code.len() as u32 - 8,
                addend: 0,
            });
        }
        Architecture::LoongArch64 => {
            code.extend(LOONGARCH64_TRAMPOLINE);
            relocations.push(Relocation {
                kind: RelocationKind::Abs8,
                reloc_target: RelocationTarget::LibCall(libcall),
                offset: code.len() as u32 - 8,
                addend: 0,
            });
        }
        arch => panic!("Unsupported architecture: {}", arch),
    };
}

/// Returns the length of a libcall trampoline.
pub fn libcall_trampoline_len(target: &Target) -> usize {
    match target.triple().architecture {
        Architecture::Aarch64(_) => AARCH64_TRAMPOLINE.len(),
        Architecture::X86_64 => X86_64_TRAMPOLINE.len(),
        Architecture::Riscv64(_) => RISCV64_TRAMPOLINE.len(),
        Architecture::LoongArch64 => LOONGARCH64_TRAMPOLINE.len(),
        arch => panic!("Unsupported architecture: {}", arch),
    }
}

/// Creates a custom section containing the libcall trampolines.
pub fn make_libcall_trampolines(target: &Target) -> CustomSection {
    let mut code = vec![];
    let mut relocations = vec![];
    for libcall in LibCall::into_enum_iter() {
        make_trampoline(target, libcall, &mut code, &mut relocations);
    }
    CustomSection {
        protection: CustomSectionProtection::ReadExecute,
        bytes: SectionBody::new_with_vec(code),
        relocations,
    }
}

/// Returns the address of a trampoline in the libcall trampolines section.
pub fn get_libcall_trampoline(
    libcall: LibCall,
    libcall_trampolines: usize,
    libcall_trampoline_len: usize,
) -> usize {
    libcall_trampolines + libcall as usize * libcall_trampoline_len
}