linera_wasmer_compiler/engine/
link.rs

1//! Linking for Universal-compiled code.
2
3use crate::get_libcall_trampoline;
4use crate::FunctionExtent;
5use std::collections::HashMap;
6use std::ptr::{read_unaligned, write_unaligned};
7use wasmer_types::entity::PrimaryMap;
8use wasmer_types::RelocationLike;
9use wasmer_types::{LocalFunctionIndex, ModuleInfo};
10use wasmer_types::{RelocationKind, RelocationTarget, SectionIndex};
11use wasmer_vm::libcalls::function_pointer;
12use wasmer_vm::SectionBodyPtr;
13
14fn apply_relocation(
15    body: usize,
16    r: &impl RelocationLike,
17    allocated_functions: &PrimaryMap<LocalFunctionIndex, FunctionExtent>,
18    allocated_sections: &PrimaryMap<SectionIndex, SectionBodyPtr>,
19    libcall_trampolines: SectionIndex,
20    libcall_trampoline_len: usize,
21    riscv_pcrel_hi20s: &mut HashMap<usize, u32>,
22) {
23    let target_func_address: usize = match r.reloc_target() {
24        RelocationTarget::LocalFunc(index) => *allocated_functions[index].ptr as usize,
25        RelocationTarget::LibCall(libcall) => {
26            // Use the direct target of the libcall if the relocation supports
27            // a full 64-bit address. Otherwise use a trampoline.
28            if r.kind() == RelocationKind::Abs8 || r.kind() == RelocationKind::X86PCRel8 {
29                function_pointer(libcall)
30            } else {
31                get_libcall_trampoline(
32                    libcall,
33                    allocated_sections[libcall_trampolines].0 as usize,
34                    libcall_trampoline_len,
35                )
36            }
37        }
38        RelocationTarget::CustomSection(custom_section) => {
39            *allocated_sections[custom_section] as usize
40        }
41    };
42
43    match r.kind() {
44        RelocationKind::Abs8 => unsafe {
45            let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
46            write_unaligned(reloc_address as *mut u64, reloc_delta);
47        },
48        RelocationKind::X86PCRel4 => unsafe {
49            let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
50            write_unaligned(reloc_address as *mut u32, reloc_delta as _);
51        },
52        RelocationKind::X86PCRel8 => unsafe {
53            let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
54            write_unaligned(reloc_address as *mut u64, reloc_delta);
55        },
56        RelocationKind::X86CallPCRel4 => unsafe {
57            let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
58            write_unaligned(reloc_address as *mut u32, reloc_delta as _);
59        },
60        RelocationKind::Arm64Call => unsafe {
61            let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
62            if (reloc_delta as i64).abs() >= 0x1000_0000 {
63                panic!(
64                    "Relocation to big for {:?} for {:?} with {:x}, current val {:x}",
65                    r.kind(),
66                    r.reloc_target(),
67                    reloc_delta,
68                    read_unaligned(reloc_address as *mut u32)
69                )
70            }
71            let reloc_delta = (((reloc_delta / 4) as u32) & 0x3ff_ffff)
72                | (read_unaligned(reloc_address as *mut u32) & 0xfc00_0000);
73            write_unaligned(reloc_address as *mut u32, reloc_delta);
74        },
75        RelocationKind::Arm64Movw0 => unsafe {
76            let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
77            let reloc_delta =
78                (((reloc_delta & 0xffff) as u32) << 5) | read_unaligned(reloc_address as *mut u32);
79            write_unaligned(reloc_address as *mut u32, reloc_delta);
80        },
81        RelocationKind::Arm64Movw1 => unsafe {
82            let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
83            let reloc_delta = ((((reloc_delta >> 16) & 0xffff) as u32) << 5)
84                | read_unaligned(reloc_address as *mut u32);
85            write_unaligned(reloc_address as *mut u32, reloc_delta);
86        },
87        RelocationKind::Arm64Movw2 => unsafe {
88            let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
89            let reloc_delta = ((((reloc_delta >> 32) & 0xffff) as u32) << 5)
90                | read_unaligned(reloc_address as *mut u32);
91            write_unaligned(reloc_address as *mut u32, reloc_delta);
92        },
93        RelocationKind::Arm64Movw3 => unsafe {
94            let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
95            let reloc_delta = ((((reloc_delta >> 48) & 0xffff) as u32) << 5)
96                | read_unaligned(reloc_address as *mut u32);
97            write_unaligned(reloc_address as *mut u32, reloc_delta);
98        },
99        RelocationKind::RiscvPCRelHi20 => unsafe {
100            let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
101
102            // save for later reference with RiscvPCRelLo12I
103            riscv_pcrel_hi20s.insert(reloc_address, reloc_delta as u32);
104
105            let reloc_delta = ((reloc_delta.wrapping_add(0x800) & 0xfffff000) as u32)
106                | read_unaligned(reloc_address as *mut u32);
107            write_unaligned(reloc_address as *mut u32, reloc_delta);
108        },
109        RelocationKind::RiscvPCRelLo12I => unsafe {
110            let (reloc_address, reloc_abs) = r.for_address(body, target_func_address as u64);
111            let reloc_delta = ((riscv_pcrel_hi20s.get(&(reloc_abs as usize)).expect(
112                "R_RISCV_PCREL_LO12_I relocation target must be a symbol with R_RISCV_PCREL_HI20",
113            ) & 0xfff)
114                << 20)
115                | read_unaligned(reloc_address as *mut u32);
116            write_unaligned(reloc_address as *mut u32, reloc_delta);
117        },
118        RelocationKind::RiscvCall => unsafe {
119            let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
120            let reloc_delta = ((reloc_delta & 0xfff) << 52)
121                | (reloc_delta.wrapping_add(0x800) & 0xfffff000)
122                | read_unaligned(reloc_address as *mut u64);
123            write_unaligned(reloc_address as *mut u64, reloc_delta);
124        },
125        kind => panic!(
126            "Relocation kind unsupported in the current architecture {}",
127            kind
128        ),
129    }
130}
131
132/// Links a module, patching the allocated functions with the
133/// required relocations and jump tables.
134pub fn link_module<'a>(
135    _module: &ModuleInfo,
136    allocated_functions: &PrimaryMap<LocalFunctionIndex, FunctionExtent>,
137    function_relocations: impl Iterator<
138        Item = (
139            LocalFunctionIndex,
140            impl Iterator<Item = &'a (impl RelocationLike + 'a)>,
141        ),
142    >,
143    allocated_sections: &PrimaryMap<SectionIndex, SectionBodyPtr>,
144    section_relocations: impl Iterator<
145        Item = (
146            SectionIndex,
147            impl Iterator<Item = &'a (impl RelocationLike + 'a)>,
148        ),
149    >,
150    libcall_trampolines: SectionIndex,
151    trampoline_len: usize,
152) {
153    let mut riscv_pcrel_hi20s: HashMap<usize, u32> = HashMap::new();
154
155    for (i, section_relocs) in section_relocations {
156        let body = *allocated_sections[i] as usize;
157        for r in section_relocs {
158            apply_relocation(
159                body,
160                r,
161                allocated_functions,
162                allocated_sections,
163                libcall_trampolines,
164                trampoline_len,
165                &mut riscv_pcrel_hi20s,
166            );
167        }
168    }
169    for (i, function_relocs) in function_relocations {
170        let body = *allocated_functions[i].ptr as usize;
171        for r in function_relocs {
172            apply_relocation(
173                body,
174                r,
175                allocated_functions,
176                allocated_sections,
177                libcall_trampolines,
178                trampoline_len,
179                &mut riscv_pcrel_hi20s,
180            );
181        }
182    }
183}