wasmtime_obj/
builder.rs

1//! Object file builder.
2//!
3//! Creates ELF image based on `Compilation` information. The ELF contains
4//! functions and trampolines in the ".text" section. It also contains all
5//! relocation records for linking stage. If DWARF sections exist, their
6//! content will be written as well.
7//!
8//! The object file has symbols for each function and trampoline, as well as
9//! symbols that refer libcalls.
10//!
11//! The function symbol names have format "_wasm_function_N", where N is
12//! `FuncIndex`. The defined wasm function symbols refer to a JIT compiled
13//! function body, the imported wasm function do not. The trampolines symbol
14//! names have format "_trampoline_N", where N is `SignatureIndex`.
15
16#![allow(missing_docs)]
17
18use anyhow::bail;
19use object::write::{
20    Object, Relocation as ObjectRelocation, SectionId, StandardSegment, Symbol, SymbolId,
21    SymbolSection,
22};
23use object::{
24    elf, Architecture, BinaryFormat, Endianness, RelocationEncoding, RelocationKind, SectionKind,
25    SymbolFlags, SymbolKind, SymbolScope,
26};
27use std::collections::HashMap;
28use target_lexicon::Triple;
29use wasmtime_debug::{DwarfSection, DwarfSectionRelocTarget};
30use wasmtime_environ::entity::{EntityRef, PrimaryMap};
31use wasmtime_environ::ir::{LibCall, Reloc};
32use wasmtime_environ::isa::unwind::UnwindInfo;
33use wasmtime_environ::wasm::{DefinedFuncIndex, FuncIndex, SignatureIndex};
34use wasmtime_environ::{CompiledFunction, CompiledFunctions, Module, Relocation, RelocationTarget};
35
36fn to_object_relocations<'a>(
37    it: impl Iterator<Item = &'a Relocation> + 'a,
38    off: u64,
39    module: &'a Module,
40    funcs: &'a PrimaryMap<FuncIndex, SymbolId>,
41    libcalls: &'a HashMap<LibCall, SymbolId>,
42    compiled_funcs: &'a CompiledFunctions,
43) -> impl Iterator<Item = ObjectRelocation> + 'a {
44    it.filter_map(move |r| {
45        let (symbol, symbol_offset) = match r.reloc_target {
46            RelocationTarget::UserFunc(index) => (funcs[index], 0),
47            RelocationTarget::LibCall(call) => (libcalls[&call], 0),
48            RelocationTarget::JumpTable(f, jt) => {
49                let df = module.defined_func_index(f).unwrap();
50                let offset = *compiled_funcs
51                    .get(df)
52                    .and_then(|f| f.jt_offsets.get(jt))
53                    .expect("func jump table");
54                (funcs[f], offset)
55            }
56        };
57        let (kind, encoding, size) = match r.reloc {
58            Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32),
59            Reloc::Abs8 => (RelocationKind::Absolute, RelocationEncoding::Generic, 64),
60            Reloc::X86PCRel4 => (RelocationKind::Relative, RelocationEncoding::Generic, 32),
61            Reloc::X86CallPCRel4 => (RelocationKind::Relative, RelocationEncoding::X86Branch, 32),
62            // TODO: Get Cranelift to tell us when we can use
63            // R_X86_64_GOTPCRELX/R_X86_64_REX_GOTPCRELX.
64            Reloc::X86CallPLTRel4 => (
65                RelocationKind::PltRelative,
66                RelocationEncoding::X86Branch,
67                32,
68            ),
69            Reloc::X86GOTPCRel4 => (RelocationKind::GotRelative, RelocationEncoding::Generic, 32),
70            Reloc::ElfX86_64TlsGd => (
71                RelocationKind::Elf(elf::R_X86_64_TLSGD),
72                RelocationEncoding::Generic,
73                32,
74            ),
75            Reloc::X86PCRelRodata4 => {
76                return None;
77            }
78            Reloc::Arm64Call => (
79                RelocationKind::Elf(elf::R_AARCH64_CALL26),
80                RelocationEncoding::Generic,
81                32,
82            ),
83            Reloc::S390xPCRel32Dbl => (RelocationKind::Relative, RelocationEncoding::S390xDbl, 32),
84            other => unimplemented!("Unimplemented relocation {:?}", other),
85        };
86        Some(ObjectRelocation {
87            offset: off + r.offset as u64,
88            size,
89            kind,
90            encoding,
91            symbol,
92            addend: r.addend.wrapping_add(symbol_offset as i64),
93        })
94    })
95}
96
97fn to_object_architecture(
98    arch: target_lexicon::Architecture,
99) -> Result<Architecture, anyhow::Error> {
100    use target_lexicon::Architecture::*;
101    Ok(match arch {
102        X86_32(_) => Architecture::I386,
103        X86_64 => Architecture::X86_64,
104        Arm(_) => Architecture::Arm,
105        Aarch64(_) => Architecture::Aarch64,
106        S390x => Architecture::S390x,
107        architecture => {
108            anyhow::bail!("target architecture {:?} is unsupported", architecture,);
109        }
110    })
111}
112
113const TEXT_SECTION_NAME: &[u8] = b".text";
114
115fn process_unwind_info(info: &UnwindInfo, obj: &mut Object, code_section: SectionId) {
116    if let UnwindInfo::WindowsX64(info) = &info {
117        // Windows prefers Unwind info after the code -- writing it here.
118        let unwind_size = info.emit_size();
119        let mut unwind_info = vec![0; unwind_size];
120        info.emit(&mut unwind_info);
121        let _off = obj.append_section_data(code_section, &unwind_info, 4);
122    }
123}
124
125/// Builds ELF image from the module `Compilation`.
126// const CODE_SECTION_ALIGNMENT: u64 = 0x1000;
127
128/// Iterates through all `LibCall` members and all runtime exported functions.
129#[macro_export]
130macro_rules! for_each_libcall {
131    ($op:ident) => {
132        $op![
133            (UdivI64, wasmtime_i64_udiv),
134            (UdivI64, wasmtime_i64_udiv),
135            (SdivI64, wasmtime_i64_sdiv),
136            (UremI64, wasmtime_i64_urem),
137            (SremI64, wasmtime_i64_srem),
138            (IshlI64, wasmtime_i64_ishl),
139            (UshrI64, wasmtime_i64_ushr),
140            (SshrI64, wasmtime_i64_sshr),
141            (CeilF32, wasmtime_f32_ceil),
142            (FloorF32, wasmtime_f32_floor),
143            (TruncF32, wasmtime_f32_trunc),
144            (NearestF32, wasmtime_f32_nearest),
145            (CeilF64, wasmtime_f64_ceil),
146            (FloorF64, wasmtime_f64_floor),
147            (TruncF64, wasmtime_f64_trunc),
148            (NearestF64, wasmtime_f64_nearest)
149        ];
150    };
151}
152
153fn write_libcall_symbols(obj: &mut Object) -> HashMap<LibCall, SymbolId> {
154    let mut libcalls = HashMap::new();
155    macro_rules! add_libcall_symbol {
156        [$(($libcall:ident, $export:ident)),*] => {{
157            $(
158                let symbol_id = obj.add_symbol(Symbol {
159                    name: stringify!($export).as_bytes().to_vec(),
160                    value: 0,
161                    size: 0,
162                    kind: SymbolKind::Text,
163                    scope: SymbolScope::Linkage,
164                    weak: true,
165                    section: SymbolSection::Undefined,
166                    flags: SymbolFlags::None,
167                });
168                libcalls.insert(LibCall::$libcall, symbol_id);
169            )+
170        }};
171    }
172    for_each_libcall!(add_libcall_symbol);
173
174    libcalls
175}
176
177pub mod utils {
178    use wasmtime_environ::entity::EntityRef;
179    use wasmtime_environ::wasm::{FuncIndex, SignatureIndex};
180
181    pub const FUNCTION_PREFIX: &str = "_wasm_function_";
182    pub const TRAMPOLINE_PREFIX: &str = "_trampoline_";
183
184    pub fn func_symbol_name(index: FuncIndex) -> String {
185        format!("_wasm_function_{}", index.index())
186    }
187
188    pub fn try_parse_func_name(name: &str) -> Option<FuncIndex> {
189        if !name.starts_with(FUNCTION_PREFIX) {
190            return None;
191        }
192        name[FUNCTION_PREFIX.len()..]
193            .parse()
194            .ok()
195            .map(FuncIndex::new)
196    }
197
198    pub fn trampoline_symbol_name(index: SignatureIndex) -> String {
199        format!("_trampoline_{}", index.index())
200    }
201
202    pub fn try_parse_trampoline_name(name: &str) -> Option<SignatureIndex> {
203        if !name.starts_with(TRAMPOLINE_PREFIX) {
204            return None;
205        }
206        name[TRAMPOLINE_PREFIX.len()..]
207            .parse()
208            .ok()
209            .map(SignatureIndex::new)
210    }
211}
212
213pub struct ObjectBuilderTarget {
214    pub(crate) binary_format: BinaryFormat,
215    pub(crate) architecture: Architecture,
216    pub(crate) endianness: Endianness,
217}
218
219impl ObjectBuilderTarget {
220    pub fn new(arch: target_lexicon::Architecture) -> Result<Self, anyhow::Error> {
221        Ok(Self {
222            binary_format: BinaryFormat::Elf,
223            architecture: to_object_architecture(arch)?,
224            endianness: match arch.endianness().unwrap() {
225                target_lexicon::Endianness::Little => object::Endianness::Little,
226                target_lexicon::Endianness::Big => object::Endianness::Big,
227            },
228        })
229    }
230
231    pub fn from_triple(triple: &Triple) -> Result<Self, anyhow::Error> {
232        let binary_format = match triple.binary_format {
233            target_lexicon::BinaryFormat::Elf => object::BinaryFormat::Elf,
234            target_lexicon::BinaryFormat::Coff => object::BinaryFormat::Coff,
235            target_lexicon::BinaryFormat::Macho => object::BinaryFormat::MachO,
236            target_lexicon::BinaryFormat::Wasm => {
237                bail!("binary format wasm is unsupported");
238            }
239            target_lexicon::BinaryFormat::Unknown => {
240                bail!("binary format is unknown");
241            }
242            other => bail!("binary format {} is unsupported", other),
243        };
244        let architecture = to_object_architecture(triple.architecture)?;
245        let endianness = match triple.endianness().unwrap() {
246            target_lexicon::Endianness::Little => object::Endianness::Little,
247            target_lexicon::Endianness::Big => object::Endianness::Big,
248        };
249        Ok(Self {
250            binary_format,
251            architecture,
252            endianness,
253        })
254    }
255}
256
257pub struct ObjectBuilder<'a> {
258    target: ObjectBuilderTarget,
259    module: &'a Module,
260    code_alignment: u64,
261    compilation: &'a CompiledFunctions,
262    trampolines: Vec<(SignatureIndex, CompiledFunction)>,
263    dwarf_sections: Vec<DwarfSection>,
264}
265
266impl<'a> ObjectBuilder<'a> {
267    pub fn new(
268        target: ObjectBuilderTarget,
269        module: &'a Module,
270        compilation: &'a CompiledFunctions,
271    ) -> Self {
272        Self {
273            target,
274            module,
275            code_alignment: 1,
276            trampolines: Vec::new(),
277            dwarf_sections: vec![],
278            compilation,
279        }
280    }
281
282    pub fn set_code_alignment(&mut self, code_alignment: u64) -> &mut Self {
283        self.code_alignment = code_alignment;
284        self
285    }
286
287    pub fn set_trampolines(
288        &mut self,
289        trampolines: Vec<(SignatureIndex, CompiledFunction)>,
290    ) -> &mut Self {
291        self.trampolines = trampolines;
292        self
293    }
294
295    pub fn set_dwarf_sections(&mut self, dwarf_sections: Vec<DwarfSection>) -> &mut Self {
296        self.dwarf_sections = dwarf_sections;
297        self
298    }
299
300    pub fn build(self) -> Result<Object, anyhow::Error> {
301        let mut obj = Object::new(
302            self.target.binary_format,
303            self.target.architecture,
304            self.target.endianness,
305        );
306
307        let module = self.module;
308
309        // Entire code (functions and trampolines) will be placed
310        // in the ".text" section.
311        let section_id = obj.add_section(
312            obj.segment_name(StandardSegment::Text).to_vec(),
313            TEXT_SECTION_NAME.to_vec(),
314            SectionKind::Text,
315        );
316
317        // Create symbols for imports -- needed during linking.
318        let mut func_symbols = PrimaryMap::with_capacity(self.compilation.len());
319        for index in 0..module.num_imported_funcs {
320            let symbol_id = obj.add_symbol(Symbol {
321                name: utils::func_symbol_name(FuncIndex::new(index))
322                    .as_bytes()
323                    .to_vec(),
324                value: 0,
325                size: 0,
326                kind: SymbolKind::Text,
327                scope: SymbolScope::Linkage,
328                weak: false,
329                section: SymbolSection::Undefined,
330                flags: SymbolFlags::None,
331            });
332            func_symbols.push(symbol_id);
333        }
334
335        let mut append_func = |name: Vec<u8>, func: &CompiledFunction| {
336            let off = obj.append_section_data(section_id, &func.body, 1);
337            let symbol_id = obj.add_symbol(Symbol {
338                name,
339                value: off,
340                size: func.body.len() as u64,
341                kind: SymbolKind::Text,
342                scope: SymbolScope::Compilation,
343                weak: false,
344                section: SymbolSection::Section(section_id),
345                flags: SymbolFlags::None,
346            });
347            // Preserve function unwind info.
348            if let Some(info) = &func.unwind_info {
349                process_unwind_info(info, &mut obj, section_id);
350            }
351            symbol_id
352        };
353
354        // Create symbols and section data for the compiled functions.
355        for (index, func) in self.compilation.iter() {
356            let name = utils::func_symbol_name(module.func_index(index))
357                .as_bytes()
358                .to_vec();
359            let symbol_id = append_func(name, func);
360            func_symbols.push(symbol_id);
361        }
362        let mut trampolines = Vec::new();
363        for (i, func) in self.trampolines.iter() {
364            let name = utils::trampoline_symbol_name(*i).as_bytes().to_vec();
365            trampolines.push(append_func(name, func));
366        }
367
368        obj.append_section_data(section_id, &[], self.code_alignment);
369
370        // If we have DWARF data, write it in the object file.
371        let (debug_bodies, debug_relocs) = self
372            .dwarf_sections
373            .into_iter()
374            .map(|s| ((s.name, s.body), (s.name, s.relocs)))
375            .unzip::<_, _, Vec<_>, Vec<_>>();
376        let mut dwarf_sections_ids = HashMap::new();
377        for (name, body) in debug_bodies {
378            let segment = obj.segment_name(StandardSegment::Debug).to_vec();
379            let section_id = obj.add_section(segment, name.as_bytes().to_vec(), SectionKind::Debug);
380            dwarf_sections_ids.insert(name.to_string(), section_id);
381            obj.append_section_data(section_id, &body, 1);
382        }
383
384        let libcalls = write_libcall_symbols(&mut obj);
385
386        // Write all functions relocations.
387        for (index, func) in self.compilation.into_iter() {
388            let func_index = module.func_index(index);
389            let (_, off) = obj
390                .symbol_section_and_offset(func_symbols[func_index])
391                .unwrap();
392            for r in to_object_relocations(
393                func.relocations.iter(),
394                off,
395                module,
396                &func_symbols,
397                &libcalls,
398                &self.compilation,
399            ) {
400                obj.add_relocation(section_id, r)?;
401            }
402        }
403
404        for ((_, func), symbol) in self.trampolines.iter().zip(trampolines) {
405            let (_, off) = obj.symbol_section_and_offset(symbol).unwrap();
406            for r in to_object_relocations(
407                func.relocations.iter(),
408                off,
409                module,
410                &func_symbols,
411                &libcalls,
412                &self.compilation,
413            ) {
414                obj.add_relocation(section_id, r)?;
415            }
416        }
417
418        // Write all debug data relocations.
419        for (name, relocs) in debug_relocs {
420            let section_id = *dwarf_sections_ids.get(name).unwrap();
421            for reloc in relocs {
422                let target_symbol = match reloc.target {
423                    DwarfSectionRelocTarget::Func(index) => {
424                        func_symbols[module.func_index(DefinedFuncIndex::new(index))]
425                    }
426                    DwarfSectionRelocTarget::Section(name) => {
427                        obj.section_symbol(*dwarf_sections_ids.get(name).unwrap())
428                    }
429                };
430                obj.add_relocation(
431                    section_id,
432                    ObjectRelocation {
433                        offset: u64::from(reloc.offset),
434                        size: reloc.size << 3,
435                        kind: RelocationKind::Absolute,
436                        encoding: RelocationEncoding::Generic,
437                        symbol: target_symbol,
438                        addend: i64::from(reloc.addend),
439                    },
440                )?;
441            }
442        }
443
444        Ok(obj)
445    }
446}