Skip to main content

wasmer_compiler/
elf.rs

1//! Helpers shared by the object-based compiler backends (Cranelift and
2//! Singlepass) for emitting per-function relocatable ELF objects and linking
3//! them into the final module image.
4
5use crate::compiler::{CompiledObjects, emit_metadata_and_link};
6use crate::dwarf::{EhRelocation, EhTarget};
7use crate::misc::{CompiledFunctionExt, CompiledKind};
8use crate::object::get_object_for_target;
9use crate::types::function::{Compilation, FunctionBody};
10use crate::types::relocation::{Relocation, RelocationKind, RelocationTarget};
11use crate::types::section::CustomSection;
12use object::{
13    RelocationEncoding, RelocationFlags, RelocationKind as ObjectRelocationKind, SectionKind,
14    SymbolFlags, SymbolKind, SymbolScope, elf,
15    write::{
16        Object, Relocation as ObjectRelocation, SectionId, StandardSection, StandardSegment,
17        Symbol, SymbolId, SymbolSection,
18    },
19};
20use std::path::PathBuf;
21use wasmer_types::{
22    CompileError, LibCall, LocalFunctionIndex, TrapInformation, entity::PrimaryMap, target::Target,
23};
24use wasmer_types::{FunctionIndex, FunctionType};
25
26/// The result of compiling a single unit (function or trampoline): either an
27/// in-memory body for the classic artifact format, or a relocatable object
28/// buffer for the ELF artifact format (together with the maximum stack usage,
29/// when known).
30pub enum CompileOutput<T> {
31    /// The compiled body, kept in memory.
32    InMemory(T),
33    /// Serialized relocatable object and the unit's maximum stack usage, when known.
34    Object(Vec<u8>, Option<usize>),
35}
36
37impl<T: crate::compiler::CompiledFunction> crate::compiler::CompiledFunction for CompileOutput<T> {}
38
39/// Extract the object buffers from ELF-mode compile outputs.
40pub fn compile_output_objects<T>(outputs: Vec<CompileOutput<T>>) -> Vec<Vec<u8>> {
41    outputs
42        .into_iter()
43        .map(|output| match output {
44            CompileOutput::Object(object, _) => object,
45            CompileOutput::InMemory(_) => unreachable!(),
46        })
47        .collect()
48}
49
50/// Extract the in-memory bodies from classic-mode compile outputs.
51pub fn compile_output_in_memory<T>(outputs: Vec<CompileOutput<T>>) -> Vec<T> {
52    outputs
53        .into_iter()
54        .map(|output| match output {
55            CompileOutput::InMemory(body) => body,
56            CompileOutput::Object(..) => unreachable!(),
57        })
58        .collect()
59}
60
61/// Declare an undefined text symbol resolved when the objects are linked.
62pub fn add_undefined_symbol(object: &mut Object<'static>, name: String) -> SymbolId {
63    object.add_symbol(Symbol {
64        name: name.into_bytes(),
65        value: 0,
66        size: 0,
67        kind: SymbolKind::Text,
68        scope: SymbolScope::Linkage,
69        weak: false,
70        section: SymbolSection::Undefined,
71        flags: SymbolFlags::None,
72    })
73}
74
75/// Declare an undefined dynamic symbol for a libcall, resolved by the runtime
76/// loader through a dynamic relocation.
77pub fn add_libcall_symbol(object: &mut Object<'static>, libcall: LibCall) -> SymbolId {
78    object.add_symbol(Symbol {
79        name: libcall.to_function_name().to_string().into_bytes(),
80        value: 0,
81        size: 0,
82        kind: SymbolKind::Unknown,
83        scope: SymbolScope::Dynamic,
84        weak: false,
85        section: SymbolSection::Undefined,
86        flags: SymbolFlags::None,
87    })
88}
89
90/// Map a Wasmer relocation kind onto the corresponding object-file relocation
91/// flags.
92pub fn relocation_kind_to_flags(kind: RelocationKind) -> Result<RelocationFlags, CompileError> {
93    use ObjectRelocationKind as K;
94    Ok(match kind {
95        RelocationKind::Abs4 => RelocationFlags::Generic {
96            kind: K::Absolute,
97            encoding: RelocationEncoding::Generic,
98            size: 32,
99        },
100        RelocationKind::Abs8 => RelocationFlags::Generic {
101            kind: K::Absolute,
102            encoding: RelocationEncoding::Generic,
103            size: 64,
104        },
105        RelocationKind::PCRel4 => RelocationFlags::Generic {
106            kind: K::Relative,
107            encoding: RelocationEncoding::Generic,
108            size: 32,
109        },
110        RelocationKind::X86CallPCRel4 => RelocationFlags::Generic {
111            kind: K::Relative,
112            encoding: RelocationEncoding::X86Branch,
113            size: 32,
114        },
115        RelocationKind::X86CallPLTRel4 => RelocationFlags::Generic {
116            kind: K::PltRelative,
117            encoding: RelocationEncoding::X86Branch,
118            size: 32,
119        },
120        RelocationKind::X86GOTPCRel4 => RelocationFlags::Generic {
121            kind: K::GotRelative,
122            encoding: RelocationEncoding::Generic,
123            size: 32,
124        },
125        RelocationKind::Arm64Call => RelocationFlags::Elf {
126            r_type: elf::R_AARCH64_CALL26,
127        },
128        // For RISC-V relocations, please refer to:
129        // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/2484f950a551c653f1823f1bd11926bf5a57fae3/riscv-elf.adoc#relocations
130        RelocationKind::RiscvPCRelHi20 => RelocationFlags::Elf {
131            r_type: elf::R_RISCV_PCREL_HI20,
132        },
133        RelocationKind::RiscvPCRelLo12I => RelocationFlags::Elf {
134            r_type: elf::R_RISCV_PCREL_LO12_I,
135        },
136        RelocationKind::RiscvCall => RelocationFlags::Elf {
137            r_type: elf::R_RISCV_CALL_PLT,
138        },
139        kind => {
140            return Err(CompileError::Codegen(format!(
141                "unsupported ELF relocation kind: {kind:?}"
142            )));
143        }
144    })
145}
146
147/// Apply the compiled code's relocations to `section`, declaring the referenced symbols.
148pub fn add_relocations(
149    object: &mut Object<'static>,
150    section: SectionId,
151    relocations: &[Relocation],
152    local_symbol: Option<(LocalFunctionIndex, SymbolId)>,
153) -> Result<(), CompileError> {
154    for relocation in relocations {
155        let symbol = match relocation.reloc_target {
156            RelocationTarget::LocalFunc(index) => local_symbol
157                .filter(|(local_index, _)| *local_index == index)
158                .map_or_else(
159                    || {
160                        add_undefined_symbol(
161                            object,
162                            CompiledKind::Local(index, String::new()).linkage_name(),
163                        )
164                    },
165                    |(_, symbol)| symbol,
166                ),
167            RelocationTarget::CustomSection(index) => add_undefined_symbol(
168                object,
169                CompiledKind::ImportFunctionTrampoline(
170                    FunctionIndex::from_u32(index.as_u32()),
171                    FunctionType::default(),
172                )
173                .linkage_name(),
174            ),
175            RelocationTarget::LibCall(libcall) => add_libcall_symbol(object, libcall),
176            RelocationTarget::DynamicTrampoline(index) => add_undefined_symbol(
177                object,
178                CompiledKind::DynamicFunctionTrampoline(index, FunctionType::default())
179                    .linkage_name(),
180            ),
181        };
182        let flags = relocation_kind_to_flags(relocation.kind)?;
183        object
184            .add_relocation(
185                section,
186                ObjectRelocation {
187                    offset: relocation.offset as u64,
188                    flags,
189                    symbol,
190                    addend: relocation.addend,
191                },
192            )
193            .map_err(|e| CompileError::Codegen(format!("failed to add ELF relocation: {e}")))?;
194    }
195    Ok(())
196}
197
198/// Emit the per-function trap table into a `.w.traps` section, under a weak
199/// data symbol so the metadata object can reference it per function.
200pub fn emit_trap_section(
201    object: &mut Object<'static>,
202    kind: &CompiledKind,
203    traps: &[TrapInformation],
204) {
205    let mut trap_data = Vec::with_capacity(traps.len() * 8 + size_of::<u32>());
206    trap_data.extend_from_slice(&(traps.len() as u32).to_le_bytes());
207    for trap in traps {
208        trap_data.extend_from_slice(&trap.code_offset.to_le_bytes());
209        trap_data.extend_from_slice(&(trap.trap_code as u32).to_le_bytes());
210    }
211    let traps_section = object.add_section(
212        object.segment_name(StandardSegment::Data).to_vec(),
213        crate::WASMER_TRAPS_SECTION_NAME.to_vec(),
214        SectionKind::Other,
215    );
216    let traps_symbol = object.add_symbol(Symbol {
217        name: kind.traps_name().into_bytes(),
218        value: 0,
219        size: trap_data.len() as u64,
220        kind: SymbolKind::Data,
221        scope: SymbolScope::Linkage,
222        weak: true,
223        section: SymbolSection::Section(traps_section),
224        flags: SymbolFlags::None,
225    });
226    object.add_symbol_data(traps_symbol, traps_section, &trap_data, 4);
227}
228
229/// Emit a serialized `.eh_frame` blob into its own section, resolving the
230/// recorded relocations against the function's text symbol, the exception
231/// personality routine and the function's LSDA section.
232pub fn emit_eh_frame_section(
233    object: &mut Object<'static>,
234    eh_frame_bytes: &[u8],
235    relocations: &[EhRelocation],
236    function_symbol: SymbolId,
237    lsda_section_symbol: Option<SymbolId>,
238) -> Result<(), CompileError> {
239    let section = object.add_section(
240        object.segment_name(StandardSegment::Debug).to_vec(),
241        crate::EH_FRAME_SECTION_NAME.to_vec(),
242        SectionKind::Other,
243    );
244    let data_offset = object.append_section_data(section, eh_frame_bytes, 4);
245
246    // Add the personality pointer lazily. CIEs reference this slot indirectly
247    // and PC-relative, while the slot itself has an absolute relocation to the
248    // runtime function. This mirrors the `DW.ref.*` convention used by native
249    // compilers and, unlike a GOT-relative data relocation, works across the
250    // 64-bit ELF architectures supported by Wasmer.
251    let mut personality_reference_symbol = None;
252    for relocation in relocations {
253        let symbol = match relocation.target {
254            EhTarget::Function => function_symbol,
255            EhTarget::Personality => {
256                if let Some(symbol) = personality_reference_symbol {
257                    symbol
258                } else {
259                    let personality_symbol = add_libcall_symbol(object, LibCall::EHPersonality);
260                    let personality_section =
261                        object.section_id(StandardSection::ReadOnlyDataWithRel);
262                    let reference_symbol = object.add_symbol(Symbol {
263                        name: b"DW.ref.wasmer_eh_personality".to_vec(),
264                        value: 0,
265                        size: 8,
266                        kind: SymbolKind::Data,
267                        scope: SymbolScope::Compilation,
268                        weak: false,
269                        section: SymbolSection::Undefined,
270                        flags: SymbolFlags::None,
271                    });
272                    let reference_offset =
273                        object.add_symbol_data(reference_symbol, personality_section, &[0; 8], 8);
274                    object
275                        .add_relocation(
276                            personality_section,
277                            ObjectRelocation {
278                                offset: reference_offset,
279                                flags: RelocationFlags::Generic {
280                                    kind: ObjectRelocationKind::Absolute,
281                                    encoding: RelocationEncoding::Generic,
282                                    size: 64,
283                                },
284                                symbol: personality_symbol,
285                                addend: 0,
286                            },
287                        )
288                        .map_err(|e| {
289                            CompileError::Codegen(format!(
290                                "failed to add personality reference relocation: {e}"
291                            ))
292                        })?;
293                    personality_reference_symbol = Some(reference_symbol);
294                    reference_symbol
295                }
296            }
297            EhTarget::Lsda => lsda_section_symbol.ok_or_else(|| {
298                CompileError::Codegen(
299                    ".eh_frame references an LSDA but none was emitted".to_string(),
300                )
301            })?,
302        };
303        object
304            .add_relocation(
305                section,
306                ObjectRelocation {
307                    offset: data_offset + relocation.offset,
308                    flags: RelocationFlags::Generic {
309                        kind: relocation.kind,
310                        encoding: RelocationEncoding::Generic,
311                        size: 8 * relocation.size,
312                    },
313                    symbol,
314                    addend: relocation.addend,
315                },
316            )
317            .map_err(|e| {
318                CompileError::Codegen(format!("failed to add .eh_frame relocation: {e}"))
319            })?;
320    }
321    Ok(())
322}
323
324/// Serialize a trampoline's body into its own relocatable object file.
325pub fn emit_function_body(
326    target: &Target,
327    kind: &CompiledKind,
328    body: &FunctionBody,
329) -> Result<Vec<u8>, CompileError> {
330    let mut object = get_object_for_target(target.triple())
331        .map_err(|e| CompileError::Codegen(format!("cannot create object: {e}")))?;
332    let symbol = object.add_symbol(Symbol {
333        name: kind.linkage_name().into_bytes(),
334        value: 0,
335        size: body.body.len() as u64,
336        kind: SymbolKind::Text,
337        scope: SymbolScope::Linkage,
338        weak: false,
339        section: SymbolSection::Undefined,
340        flags: SymbolFlags::None,
341    });
342    let text = object.section_id(StandardSection::Text);
343    object.add_symbol_data(symbol, text, &body.body, 4);
344    object
345        .write()
346        .map_err(|e| CompileError::Codegen(format!("failed to serialize object: {e}")))
347}
348
349/// Serialize an import trampoline's custom section into its own relocatable object file.
350pub fn emit_import_trampoline(
351    target: &Target,
352    kind: &CompiledKind,
353    section: &CustomSection,
354) -> Result<Vec<u8>, CompileError> {
355    let mut object = get_object_for_target(target.triple())
356        .map_err(|e| CompileError::Codegen(format!("cannot create object: {e}")))?;
357    let symbol = object.add_symbol(Symbol {
358        name: kind.linkage_name().into_bytes(),
359        value: 0,
360        size: section.bytes.len() as u64,
361        kind: SymbolKind::Text,
362        scope: SymbolScope::Linkage,
363        weak: false,
364        section: SymbolSection::Undefined,
365        flags: SymbolFlags::None,
366    });
367    let text = object.section_id(StandardSection::Text);
368    object.add_symbol_data(symbol, text, section.bytes.as_slice(), 4);
369    object
370        .write()
371        .map_err(|e| CompileError::Codegen(format!("failed to serialize object: {e}")))
372}
373/// Link all the per-function and trampoline objects (plus the Wasmer metadata
374/// object) into the final shared-object module image.
375#[allow(clippy::too_many_arguments)]
376pub fn link_module(
377    pool: &rayon::ThreadPool,
378    target: &Target,
379    compile_info_blob: &[u8],
380    object_files: Vec<Vec<u8>>,
381    import_trampoline_objects: Vec<Vec<u8>>,
382    trampoline_objects: Vec<Vec<u8>>,
383    dynamic_trampoline_objects: Vec<Vec<u8>>,
384    debug_dir: Option<PathBuf>,
385    module_hash: Option<String>,
386    function_max_stack_usage: PrimaryMap<LocalFunctionIndex, Option<usize>>,
387) -> Result<Compilation, CompileError> {
388    let elf = emit_metadata_and_link(
389        pool,
390        target,
391        compile_info_blob,
392        CompiledObjects {
393            object_files,
394            import_trampoline_object_files: import_trampoline_objects,
395            trampoline_object_files: trampoline_objects,
396            dynamic_trampoline_object_files: dynamic_trampoline_objects,
397        },
398        debug_dir,
399        module_hash,
400    )?;
401    Ok(Compilation::Elf {
402        data: elf,
403        function_max_stack_usage,
404    })
405}