diff --git a/Cargo.lock b/Cargo.lock index 91cc09f5d6..a981255b6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2503,6 +2503,7 @@ dependencies = [ "wasmparser 0.58.0", "wasmtime-debug", "wasmtime-environ", + "wasmtime-obj", "wasmtime-profiling", "wasmtime-runtime", "winapi", @@ -2515,6 +2516,8 @@ dependencies = [ "anyhow", "more-asserts", "object", + "target-lexicon", + "wasmtime-debug", "wasmtime-environ", ] diff --git a/crates/debug/src/lib.rs b/crates/debug/src/lib.rs index ea34793161..8b0a2b0bdb 100644 --- a/crates/debug/src/lib.rs +++ b/crates/debug/src/lib.rs @@ -3,8 +3,7 @@ #![allow(clippy::cast_ptr_alignment)] use anyhow::{bail, ensure, Error}; -use object::write::{Object, Relocation, StandardSegment}; -use object::{RelocationEncoding, RelocationKind, SectionKind}; +use object::{RelocationEncoding, RelocationKind}; use std::collections::HashMap; pub use crate::read_debuginfo::{read_debuginfo, DebugInfoData, WasmFileInfo}; @@ -15,46 +14,6 @@ mod read_debuginfo; mod transform; mod write_debuginfo; -pub fn write_debugsections(obj: &mut Object, sections: Vec) -> Result<(), Error> { - let (bodies, relocs) = sections - .into_iter() - .map(|s| ((s.name.clone(), s.body), (s.name, s.relocs))) - .unzip::<_, _, Vec<_>, Vec<_>>(); - let mut ids = HashMap::new(); - for (name, body) in bodies { - let segment = obj.segment_name(StandardSegment::Debug).to_vec(); - let section_id = obj.add_section(segment, name.as_bytes().to_vec(), SectionKind::Debug); - ids.insert(name, section_id); - obj.append_section_data(section_id, &body, 1); - } - for (name, relocs) in relocs { - let section_id = *ids.get(&name).unwrap(); - for reloc in relocs { - let target_symbol = match reloc.target { - DwarfSectionRelocTarget::Func(id) => obj - .symbol_id(format!("_wasm_function_{}", id).as_bytes()) - .unwrap(), - DwarfSectionRelocTarget::Section(name) => { - obj.section_symbol(*ids.get(name).unwrap()) - } - }; - obj.add_relocation( - section_id, - Relocation { - offset: u64::from(reloc.offset), - size: reloc.size << 3, - kind: RelocationKind::Absolute, - encoding: RelocationEncoding::Generic, - symbol: target_symbol, - addend: i64::from(reloc.addend), - }, - )?; - } - } - - Ok(()) -} - pub fn create_gdbjit_image( mut bytes: Vec, code_region: (*const u8, usize), diff --git a/crates/debug/src/write_debuginfo.rs b/crates/debug/src/write_debuginfo.rs index edb44cc236..68b9e9a004 100644 --- a/crates/debug/src/write_debuginfo.rs +++ b/crates/debug/src/write_debuginfo.rs @@ -2,8 +2,10 @@ pub use crate::read_debuginfo::{read_debuginfo, DebugInfoData, WasmFileInfo}; pub use crate::transform::transform_dwarf; use gimli::write::{Address, Dwarf, EndianVec, FrameTable, Result, Sections, Writer}; use gimli::{RunTimeEndian, SectionId}; +use wasmtime_environ::entity::{EntityRef, PrimaryMap}; use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa}; -use wasmtime_environ::{Compilation, ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges}; +use wasmtime_environ::wasm::DefinedFuncIndex; +use wasmtime_environ::{ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges}; #[derive(Clone)] pub enum DwarfSectionRelocTarget { @@ -130,18 +132,18 @@ impl Writer for WriterRelocate { fn create_frame_table<'a>( isa: &dyn TargetIsa, - infos: impl Iterator>, + infos: &PrimaryMap>, ) -> Option { let mut table = FrameTable::default(); let cie_id = table.add_cie(isa.create_systemv_cie()?); - for (i, info) in infos.enumerate() { + for (i, info) in infos { if let Some(UnwindInfo::SystemV(info)) = info { table.add_fde( cie_id, info.to_fde(Address::Symbol { - symbol: i, + symbol: i.index(), addend: 0, }), ); @@ -151,16 +153,16 @@ fn create_frame_table<'a>( Some(table) } -pub fn emit_dwarf( +pub fn emit_dwarf<'a>( isa: &dyn TargetIsa, debuginfo_data: &DebugInfoData, at: &ModuleAddressMap, vmctx_info: &ModuleVmctxInfo, ranges: &ValueLabelsRanges, - compilation: &Compilation, + unwind_info: &PrimaryMap>, ) -> anyhow::Result> { let dwarf = transform_dwarf(isa, debuginfo_data, at, vmctx_info, ranges)?; - let frame_table = create_frame_table(isa, compilation.into_iter().map(|f| &f.unwind_info)); + let frame_table = create_frame_table(isa, unwind_info); let sections = emit_dwarf_sections(dwarf, frame_table)?; Ok(sections) } diff --git a/crates/environ/src/compilation.rs b/crates/environ/src/compilation.rs index b08e82fd43..0aa13cd200 100644 --- a/crates/environ/src/compilation.rs +++ b/crates/environ/src/compilation.rs @@ -71,6 +71,14 @@ impl Compilation { self.functions.is_empty() } + /// Returns unwind info for all defined functions. + pub fn unwind_info(&self) -> PrimaryMap> { + self.functions + .iter() + .map(|(_, func)| &func.unwind_info) + .collect::>() + } + /// Gets functions jump table offsets. pub fn get_jt_offsets(&self) -> PrimaryMap { self.functions diff --git a/crates/environ/src/data_structures.rs b/crates/environ/src/data_structures.rs index 60ea62c2e3..550549c780 100644 --- a/crates/environ/src/data_structures.rs +++ b/crates/environ/src/data_structures.rs @@ -1,10 +1,10 @@ #![doc(hidden)] pub mod ir { - pub use cranelift_codegen::binemit::Stackmap; + pub use cranelift_codegen::binemit::{Reloc, Stackmap}; pub use cranelift_codegen::ir::{ - types, AbiParam, ArgumentPurpose, Signature, SourceLoc, StackSlots, TrapCode, Type, - ValueLabel, ValueLoc, + types, AbiParam, ArgumentPurpose, JumpTableOffsets, LibCall, Signature, SourceLoc, + StackSlots, TrapCode, Type, ValueLabel, ValueLoc, }; pub use cranelift_codegen::{ValueLabelsRanges, ValueLocRange}; } diff --git a/crates/environ/src/lib.rs b/crates/environ/src/lib.rs index 3de1ec08dd..a0ace205ab 100644 --- a/crates/environ/src/lib.rs +++ b/crates/environ/src/lib.rs @@ -87,3 +87,28 @@ pub(crate) fn reference_type( _ => panic!("unsupported Wasm reference type"), } } + +/// Iterates through all `LibCall` members and all runtime exported functions. +#[macro_export] +macro_rules! for_each_libcall { + ($op:ident) => { + $op![ + (UdivI64, wasmtime_i64_udiv), + (UdivI64, wasmtime_i64_udiv), + (SdivI64, wasmtime_i64_sdiv), + (UremI64, wasmtime_i64_urem), + (SremI64, wasmtime_i64_srem), + (IshlI64, wasmtime_i64_ishl), + (UshrI64, wasmtime_i64_ushr), + (SshrI64, wasmtime_i64_sshr), + (CeilF32, wasmtime_f32_ceil), + (FloorF32, wasmtime_f32_floor), + (TruncF32, wasmtime_f32_trunc), + (NearestF32, wasmtime_f32_nearest), + (CeilF64, wasmtime_f64_ceil), + (FloorF64, wasmtime_f64_floor), + (TruncF64, wasmtime_f64_trunc), + (NearestF64, wasmtime_f64_nearest) + ]; + }; +} diff --git a/crates/jit/Cargo.toml b/crates/jit/Cargo.toml index da7ac728a7..4e6509077d 100644 --- a/crates/jit/Cargo.toml +++ b/crates/jit/Cargo.toml @@ -21,6 +21,7 @@ wasmtime-environ = { path = "../environ", version = "0.18.0" } wasmtime-runtime = { path = "../runtime", version = "0.18.0" } wasmtime-debug = { path = "../debug", version = "0.18.0" } wasmtime-profiling = { path = "../profiling", version = "0.18.0" } +wasmtime-obj = { path = "../obj", version = "0.18.0" } region = "2.1.0" thiserror = "1.0.4" target-lexicon = { version = "0.10.0", default-features = false } diff --git a/crates/jit/src/compiler.rs b/crates/jit/src/compiler.rs index 46280c1204..534f963fd9 100644 --- a/crates/jit/src/compiler.rs +++ b/crates/jit/src/compiler.rs @@ -3,15 +3,15 @@ use crate::instantiate::SetupError; use crate::object::{build_object, ObjectUnwindInfo}; use cranelift_codegen::ir; +use object::write::Object; use wasmtime_debug::{emit_dwarf, DebugInfoData, DwarfSection}; use wasmtime_environ::entity::{EntityRef, PrimaryMap}; -use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa}; +use wasmtime_environ::isa::{unwind::UnwindInfo, TargetFrontendConfig, TargetIsa}; use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex, MemoryIndex}; use wasmtime_environ::{ CacheConfig, Compiler as _C, Module, ModuleAddressMap, ModuleMemoryOffset, ModuleTranslation, ModuleVmctxInfo, StackMaps, Traps, Tunables, VMOffsets, ValueLabelsRanges, }; -use wasmtime_runtime::InstantiationError; /// Select which kind of compilation to use. #[derive(Copy, Clone, Debug)] @@ -67,11 +67,11 @@ fn _assert_compiler_send_sync() { fn transform_dwarf_data( isa: &dyn TargetIsa, module: &Module, - debug_data: &DebugInfoData, + debug_data: DebugInfoData, address_transform: &ModuleAddressMap, value_ranges: &ValueLabelsRanges, stack_slots: PrimaryMap, - compilation: &wasmtime_environ::Compilation, + unwind_info: PrimaryMap>, ) -> Result, SetupError> { let target_config = isa.frontend_config(); let ofs = VMOffsets::new(target_config.pointer_bytes(), &module.local); @@ -92,18 +92,18 @@ fn transform_dwarf_data( }; emit_dwarf( isa, - debug_data, + &debug_data, &address_transform, &module_vmctx_info, &value_ranges, - &compilation, + &unwind_info, ) .map_err(SetupError::DebugInfo) } #[allow(missing_docs)] pub struct Compilation { - pub obj: Vec, + pub obj: Object, pub unwind_info: Vec, pub traps: Traps, pub stack_maps: StackMaps, @@ -162,14 +162,15 @@ impl Compiler { .map_err(SetupError::Compile)?; let dwarf_sections = if debug_data.is_some() && !compilation.is_empty() { + let unwind_info = compilation.unwind_info(); transform_dwarf_data( &*self.isa, &translation.module, - debug_data.as_ref().unwrap(), + debug_data.unwrap(), &address_transform, &value_ranges, stack_slots, - &compilation, + unwind_info, )? } else { vec![] @@ -177,16 +178,11 @@ impl Compiler { let (obj, unwind_info) = build_object( &*self.isa, - &compilation, - &relocations, &translation.module, - &dwarf_sections, + compilation, + relocations, + dwarf_sections, )?; - let obj = obj.write().map_err(|_| { - SetupError::Instantiate(InstantiationError::Resource( - "failed to create image memory".to_string(), - )) - })?; Ok(Compilation { obj, diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs index 039ae5e4c6..f7c9997598 100644 --- a/crates/jit/src/instantiate.rs +++ b/crates/jit/src/instantiate.rs @@ -96,6 +96,12 @@ impl CompilationArtifacts { .collect::>() .into_boxed_slice(); + let obj = obj.write().map_err(|_| { + SetupError::Instantiate(InstantiationError::Resource( + "failed to create image memory".to_string(), + )) + })?; + Ok(Self { module, obj: obj.into_boxed_slice(), diff --git a/crates/jit/src/link.rs b/crates/jit/src/link.rs index 3c746af1e7..68996f45ab 100644 --- a/crates/jit/src/link.rs +++ b/crates/jit/src/link.rs @@ -133,7 +133,7 @@ fn apply_reloc( fn to_libcall_address(name: &str) -> Option { use self::libcalls::*; - use crate::for_each_libcall; + use wasmtime_environ::for_each_libcall; macro_rules! add_libcall_symbol { [$(($libcall:ident, $export:ident)),*] => { Some(match name { diff --git a/crates/jit/src/object.rs b/crates/jit/src/object.rs index 6d2ab1d1fb..9e72ef6050 100644 --- a/crates/jit/src/object.rs +++ b/crates/jit/src/object.rs @@ -1,113 +1,16 @@ //! Object file generation. -//! -//! Creates ELF image based on `Compilation` information. The ELF contains -//! functions and trampolines in the ".text" section. It also contains all -//! relocation records for linking stage. If DWARF sections exist, their -//! content will be written as well. -//! -//! The object file has symbols for each function and trampoline, as well as -//! symbols that refer libcalls. -//! -//! The function symbol names have format "_wasm_function_N", where N is -//! `FuncIndex`. The defined wasm function symbols refer to a JIT compiled -//! function body, the imported wasm function do not. The trampolines symbol -//! names have format "_trampoline_N", where N is `SignatureIndex`. use super::trampoline::build_trampoline; -use cranelift_codegen::binemit::Reloc; -use cranelift_codegen::ir::{JumpTableOffsets, LibCall}; use cranelift_frontend::FunctionBuilderContext; -use object::write::{ - Object, Relocation as ObjectRelocation, SectionId, StandardSegment, Symbol, SymbolId, - SymbolSection, -}; -use object::{ - elf, Architecture, BinaryFormat, Endianness, RelocationEncoding, RelocationKind, SectionKind, - SymbolFlags, SymbolKind, SymbolScope, -}; -use std::collections::HashMap; -use wasmtime_debug::{DwarfSection, DwarfSectionRelocTarget}; +use object::write::Object; +use wasmtime_debug::DwarfSection; use wasmtime_environ::entity::{EntityRef, PrimaryMap}; use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa}; -use wasmtime_environ::wasm::{DefinedFuncIndex, FuncIndex, SignatureIndex}; -use wasmtime_environ::{Compilation, Module, Relocation, RelocationTarget, Relocations}; +use wasmtime_environ::wasm::{FuncIndex, SignatureIndex}; +use wasmtime_environ::{Compilation, Module, Relocations}; +use wasmtime_obj::{ObjectBuilder, ObjectBuilderTarget}; -fn to_object_relocations<'a>( - it: impl Iterator + 'a, - off: u64, - module: &'a Module, - funcs: &'a PrimaryMap, - libcalls: &'a HashMap, - jt_offsets: &'a PrimaryMap, -) -> impl Iterator + 'a { - it.filter_map(move |r| { - let (symbol, symbol_offset) = match r.reloc_target { - RelocationTarget::UserFunc(index) => (funcs[index], 0), - RelocationTarget::LibCall(call) => (libcalls[&call], 0), - RelocationTarget::JumpTable(f, jt) => { - let df = module.local.defined_func_index(f).unwrap(); - let offset = *jt_offsets - .get(df) - .and_then(|ofs| ofs.get(jt)) - .expect("func jump table"); - (funcs[f], offset) - } - }; - let (kind, encoding, size) = match r.reloc { - Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32), - Reloc::Abs8 => (RelocationKind::Absolute, RelocationEncoding::Generic, 64), - Reloc::X86PCRel4 => (RelocationKind::Relative, RelocationEncoding::Generic, 32), - Reloc::X86CallPCRel4 => (RelocationKind::Relative, RelocationEncoding::X86Branch, 32), - // TODO: Get Cranelift to tell us when we can use - // R_X86_64_GOTPCRELX/R_X86_64_REX_GOTPCRELX. - Reloc::X86CallPLTRel4 => ( - RelocationKind::PltRelative, - RelocationEncoding::X86Branch, - 32, - ), - Reloc::X86GOTPCRel4 => (RelocationKind::GotRelative, RelocationEncoding::Generic, 32), - Reloc::ElfX86_64TlsGd => ( - RelocationKind::Elf(elf::R_X86_64_TLSGD), - RelocationEncoding::Generic, - 32, - ), - Reloc::X86PCRelRodata4 => { - return None; - } - Reloc::Arm64Call => ( - RelocationKind::Elf(elf::R_AARCH64_CALL26), - RelocationEncoding::Generic, - 32, - ), - other => unimplemented!("Unimplemented relocation {:?}", other), - }; - Some(ObjectRelocation { - offset: off + r.offset as u64, - size, - kind, - encoding, - symbol, - addend: r.addend.wrapping_add(symbol_offset as i64), - }) - }) -} - -fn to_object_architecture( - arch: target_lexicon::Architecture, -) -> Result { - use target_lexicon::Architecture::*; - Ok(match arch { - I386 | I586 | I686 => Architecture::I386, - X86_64 => Architecture::X86_64, - Arm(_) => Architecture::Arm, - Aarch64(_) => Architecture::Aarch64, - architecture => { - anyhow::bail!("target architecture {:?} is unsupported", architecture,); - } - }) -} - -const TEXT_SECTION_NAME: &[u8] = b".text"; +pub use wasmtime_obj::utils; /// Unwind information for object files functions (including trampolines). #[derive(Debug, Clone, PartialEq, Eq)] @@ -116,23 +19,13 @@ pub enum ObjectUnwindInfo { Trampoline(SignatureIndex, UnwindInfo), } -fn process_unwind_info(info: &UnwindInfo, obj: &mut Object, code_section: SectionId) { - if let UnwindInfo::WindowsX64(info) = &info { - // Windows prefers Unwind info after the code -- writing it here. - let unwind_size = info.emit_size(); - let mut unwind_info = vec![0; unwind_size]; - info.emit(&mut unwind_info); - let _off = obj.append_section_data(code_section, &unwind_info, 4); - } -} - // Builds ELF image from the module `Compilation`. pub(crate) fn build_object( isa: &dyn TargetIsa, - compilation: &Compilation, - relocations: &Relocations, module: &Module, - dwarf_sections: &[DwarfSection], + compilation: Compilation, + relocations: Relocations, + dwarf_sections: Vec, ) -> Result<(Object, Vec), anyhow::Error> { const CODE_SECTION_ALIGNMENT: u64 = 0x1000; assert_eq!( @@ -140,247 +33,44 @@ pub(crate) fn build_object( Ok(target_lexicon::Endianness::Little) ); - let mut obj = Object::new( - BinaryFormat::Elf, - to_object_architecture(isa.triple().architecture)?, - Endianness::Little, - ); - // Entire code (functions and trampolines) will be placed - // in the ".text" section. - let section_id = obj.add_section( - obj.segment_name(StandardSegment::Text).to_vec(), - TEXT_SECTION_NAME.to_vec(), - SectionKind::Text, - ); - let mut unwind_info = Vec::new(); - // Create symbols for imports -- needed during linking. - let mut func_symbols = PrimaryMap::with_capacity(compilation.len()); - for index in 0..module.local.num_imported_funcs { - let symbol_id = obj.add_symbol(Symbol { - name: utils::func_symbol_name(FuncIndex::new(index)) - .as_bytes() - .to_vec(), - value: 0, - size: 0, - kind: SymbolKind::Text, - scope: SymbolScope::Linkage, - weak: false, - section: SymbolSection::Undefined, - flags: SymbolFlags::None, - }); - func_symbols.push(symbol_id); - } - // Create symbols and section data for the compiled functions. - for (index, func) in compilation.into_iter().enumerate() { - let off = obj.append_section_data(section_id, &func.body, 1); - let symbol_id = obj.add_symbol(Symbol { - name: utils::func_symbol_name(module.local.func_index(DefinedFuncIndex::new(index))) - .as_bytes() - .to_vec(), - value: off, - size: func.body.len() as u64, - kind: SymbolKind::Text, - scope: SymbolScope::Compilation, - weak: false, - section: SymbolSection::Section(section_id), - flags: SymbolFlags::None, - }); - func_symbols.push(symbol_id); - // Preserve function unwind info. - if let Some(info) = &func.unwind_info { - process_unwind_info(info, &mut obj, section_id); - unwind_info.push(ObjectUnwindInfo::Func( - FuncIndex::new(module.local.num_imported_funcs + index), - info.clone(), - )) - } - } + // Preserve function unwind info. + unwind_info.extend( + compilation + .into_iter() + .enumerate() + .filter_map(|(index, func)| { + func.unwind_info.as_ref().map(|info| { + ObjectUnwindInfo::Func( + FuncIndex::new(module.local.num_imported_funcs + index), + info.clone(), + ) + }) + }), + ); - let mut trampoline_relocs = HashMap::new(); + let mut trampolines = PrimaryMap::with_capacity(module.local.signatures.len()); let mut cx = FunctionBuilderContext::new(); // Build trampolines for every signature. for (i, (_, native_sig)) in module.local.signatures.iter() { let (func, relocs) = build_trampoline(isa, &mut cx, native_sig, std::mem::size_of::())?; - let off = obj.append_section_data(section_id, &func.body, 1); - let symbol_id = obj.add_symbol(Symbol { - name: utils::trampoline_symbol_name(i).as_bytes().to_vec(), - value: off, - size: func.body.len() as u64, - kind: SymbolKind::Text, - scope: SymbolScope::Compilation, - weak: false, - section: SymbolSection::Section(section_id), - flags: SymbolFlags::None, - }); - trampoline_relocs.insert(symbol_id, relocs); // Preserve trampoline function unwind info. if let Some(info) = &func.unwind_info { - process_unwind_info(info, &mut obj, section_id); unwind_info.push(ObjectUnwindInfo::Trampoline(i, info.clone())) } + trampolines.push((func, relocs)); } - obj.append_section_data(section_id, &[], CODE_SECTION_ALIGNMENT); - - // If we have DWARF data, write it in the object file. - let (debug_bodies, debug_relocs) = dwarf_sections - .into_iter() - .map(|s| ((s.name, &s.body), (s.name, &s.relocs))) - .unzip::<_, _, Vec<_>, Vec<_>>(); - let mut dwarf_sections_ids = HashMap::new(); - for (name, body) in debug_bodies { - let segment = obj.segment_name(StandardSegment::Debug).to_vec(); - let section_id = obj.add_section(segment, name.as_bytes().to_vec(), SectionKind::Debug); - dwarf_sections_ids.insert(name.to_string(), section_id); - obj.append_section_data(section_id, &body, 1); - } - - let libcalls = write_libcall_symbols(&mut obj); - - let jt_offsets = compilation.get_jt_offsets(); - - // Write all functions relocations. - for (index, relocs) in relocations.into_iter() { - let func_index = module.local.func_index(index); - let (_, off) = obj - .symbol_section_and_offset(func_symbols[func_index]) - .unwrap(); - for r in to_object_relocations( - relocs.iter(), - off, - module, - &func_symbols, - &libcalls, - &jt_offsets, - ) { - obj.add_relocation(section_id, r)?; - } - } - for (symbol, relocs) in trampoline_relocs { - let (_, off) = obj.symbol_section_and_offset(symbol).unwrap(); - for r in to_object_relocations( - relocs.iter(), - off, - module, - &func_symbols, - &libcalls, - &jt_offsets, - ) { - obj.add_relocation(section_id, r)?; - } - } - - // Write all debug data relocations. - for (name, relocs) in debug_relocs { - let section_id = *dwarf_sections_ids.get(name).unwrap(); - for reloc in relocs { - let target_symbol = match reloc.target { - DwarfSectionRelocTarget::Func(index) => func_symbols[FuncIndex::new(index)], - DwarfSectionRelocTarget::Section(name) => { - obj.section_symbol(*dwarf_sections_ids.get(name).unwrap()) - } - }; - obj.add_relocation( - section_id, - ObjectRelocation { - offset: u64::from(reloc.offset), - size: reloc.size << 3, - kind: RelocationKind::Absolute, - encoding: RelocationEncoding::Generic, - symbol: target_symbol, - addend: i64::from(reloc.addend), - }, - )?; - } - } + let target = ObjectBuilderTarget::new(isa.triple().architecture)?; + let mut builder = ObjectBuilder::new(target, module); + builder + .set_code_alignment(CODE_SECTION_ALIGNMENT) + .set_compilation(compilation, relocations) + .set_trampolines(trampolines) + .set_dwarf_sections(dwarf_sections); + let obj = builder.build()?; Ok((obj, unwind_info)) } - -/// Iterates through all `LibCall` members and all runtime exported functions. -#[macro_export] -macro_rules! for_each_libcall { - ($op:ident) => { - $op![ - (UdivI64, wasmtime_i64_udiv), - (UdivI64, wasmtime_i64_udiv), - (SdivI64, wasmtime_i64_sdiv), - (UremI64, wasmtime_i64_urem), - (SremI64, wasmtime_i64_srem), - (IshlI64, wasmtime_i64_ishl), - (UshrI64, wasmtime_i64_ushr), - (SshrI64, wasmtime_i64_sshr), - (CeilF32, wasmtime_f32_ceil), - (FloorF32, wasmtime_f32_floor), - (TruncF32, wasmtime_f32_trunc), - (NearestF32, wasmtime_f32_nearest), - (CeilF64, wasmtime_f64_ceil), - (FloorF64, wasmtime_f64_floor), - (TruncF64, wasmtime_f64_trunc), - (NearestF64, wasmtime_f64_nearest) - ]; - }; -} - -fn write_libcall_symbols(obj: &mut Object) -> HashMap { - let mut libcalls = HashMap::new(); - macro_rules! add_libcall_symbol { - [$(($libcall:ident, $export:ident)),*] => {{ - $( - let symbol_id = obj.add_symbol(Symbol { - name: stringify!($export).as_bytes().to_vec(), - value: 0, - size: 0, - kind: SymbolKind::Text, - scope: SymbolScope::Linkage, - weak: true, - section: SymbolSection::Undefined, - flags: SymbolFlags::None, - }); - libcalls.insert(LibCall::$libcall, symbol_id); - )+ - }}; - } - for_each_libcall!(add_libcall_symbol); - - libcalls -} - -pub(crate) mod utils { - use wasmtime_environ::entity::EntityRef; - use wasmtime_environ::wasm::{FuncIndex, SignatureIndex}; - - pub const FUNCTION_PREFIX: &str = "_wasm_function_"; - pub const TRAMPOLINE_PREFIX: &str = "_trampoline_"; - - pub fn func_symbol_name(index: FuncIndex) -> String { - format!("_wasm_function_{}", index.index()) - } - - pub fn try_parse_func_name(name: &str) -> Option { - if !name.starts_with(FUNCTION_PREFIX) { - return None; - } - name[FUNCTION_PREFIX.len()..] - .parse() - .ok() - .map(FuncIndex::new) - } - - pub fn trampoline_symbol_name(index: SignatureIndex) -> String { - format!("_trampoline_{}", index.index()) - } - - pub fn try_parse_trampoline_name(name: &str) -> Option { - if !name.starts_with(TRAMPOLINE_PREFIX) { - return None; - } - name[TRAMPOLINE_PREFIX.len()..] - .parse() - .ok() - .map(SignatureIndex::new) - } -} diff --git a/crates/obj/Cargo.toml b/crates/obj/Cargo.toml index 0615e2e32e..79cac969a5 100644 --- a/crates/obj/Cargo.toml +++ b/crates/obj/Cargo.toml @@ -15,6 +15,8 @@ anyhow = "1.0" wasmtime-environ = { path = "../environ", version = "0.18.0" } object = { version = "0.20", default-features = false, features = ["write"] } more-asserts = "0.2.1" +target-lexicon = { version = "0.10.0", default-features = false } +wasmtime-debug = { path = "../debug", version = "0.18.0" } [badges] maintenance = { status = "experimental" } diff --git a/crates/obj/src/builder.rs b/crates/obj/src/builder.rs new file mode 100644 index 0000000000..ef4f33e945 --- /dev/null +++ b/crates/obj/src/builder.rs @@ -0,0 +1,472 @@ +//! Object file builder. +//! +//! Creates ELF image based on `Compilation` information. The ELF contains +//! functions and trampolines in the ".text" section. It also contains all +//! relocation records for linking stage. If DWARF sections exist, their +//! content will be written as well. +//! +//! The object file has symbols for each function and trampoline, as well as +//! symbols that refer libcalls. +//! +//! The function symbol names have format "_wasm_function_N", where N is +//! `FuncIndex`. The defined wasm function symbols refer to a JIT compiled +//! function body, the imported wasm function do not. The trampolines symbol +//! names have format "_trampoline_N", where N is `SignatureIndex`. + +#![allow(missing_docs)] + +use anyhow::bail; +use object::write::{ + Object, Relocation as ObjectRelocation, SectionId, StandardSegment, Symbol, SymbolId, + SymbolSection, +}; +use object::{ + elf, Architecture, BinaryFormat, Endianness, RelocationEncoding, RelocationKind, SectionKind, + SymbolFlags, SymbolKind, SymbolScope, +}; +use std::collections::HashMap; +use target_lexicon::Triple; +use wasmtime_debug::{DwarfSection, DwarfSectionRelocTarget}; +use wasmtime_environ::entity::{EntityRef, PrimaryMap}; +use wasmtime_environ::ir::{JumpTableOffsets, LibCall, Reloc}; +use wasmtime_environ::isa::unwind::UnwindInfo; +use wasmtime_environ::wasm::{DefinedFuncIndex, FuncIndex, SignatureIndex}; +use wasmtime_environ::{ + Compilation, CompiledFunction, Module, Relocation, RelocationTarget, Relocations, +}; + +fn to_object_relocations<'a>( + it: impl Iterator + 'a, + off: u64, + module: &'a Module, + funcs: &'a PrimaryMap, + libcalls: &'a HashMap, + jt_offsets: &'a PrimaryMap, +) -> impl Iterator + 'a { + it.filter_map(move |r| { + let (symbol, symbol_offset) = match r.reloc_target { + RelocationTarget::UserFunc(index) => (funcs[index], 0), + RelocationTarget::LibCall(call) => (libcalls[&call], 0), + RelocationTarget::JumpTable(f, jt) => { + let df = module.local.defined_func_index(f).unwrap(); + let offset = *jt_offsets + .get(df) + .and_then(|ofs| ofs.get(jt)) + .expect("func jump table"); + (funcs[f], offset) + } + }; + let (kind, encoding, size) = match r.reloc { + Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32), + Reloc::Abs8 => (RelocationKind::Absolute, RelocationEncoding::Generic, 64), + Reloc::X86PCRel4 => (RelocationKind::Relative, RelocationEncoding::Generic, 32), + Reloc::X86CallPCRel4 => (RelocationKind::Relative, RelocationEncoding::X86Branch, 32), + // TODO: Get Cranelift to tell us when we can use + // R_X86_64_GOTPCRELX/R_X86_64_REX_GOTPCRELX. + Reloc::X86CallPLTRel4 => ( + RelocationKind::PltRelative, + RelocationEncoding::X86Branch, + 32, + ), + Reloc::X86GOTPCRel4 => (RelocationKind::GotRelative, RelocationEncoding::Generic, 32), + Reloc::ElfX86_64TlsGd => ( + RelocationKind::Elf(elf::R_X86_64_TLSGD), + RelocationEncoding::Generic, + 32, + ), + Reloc::X86PCRelRodata4 => { + return None; + } + Reloc::Arm64Call => ( + RelocationKind::Elf(elf::R_AARCH64_CALL26), + RelocationEncoding::Generic, + 32, + ), + other => unimplemented!("Unimplemented relocation {:?}", other), + }; + Some(ObjectRelocation { + offset: off + r.offset as u64, + size, + kind, + encoding, + symbol, + addend: r.addend.wrapping_add(symbol_offset as i64), + }) + }) +} + +fn to_object_architecture( + arch: target_lexicon::Architecture, +) -> Result { + use target_lexicon::Architecture::*; + Ok(match arch { + I386 | I586 | I686 => Architecture::I386, + X86_64 => Architecture::X86_64, + Arm(_) => Architecture::Arm, + Aarch64(_) => Architecture::Aarch64, + architecture => { + anyhow::bail!("target architecture {:?} is unsupported", architecture,); + } + }) +} + +const TEXT_SECTION_NAME: &[u8] = b".text"; + +fn process_unwind_info(info: &UnwindInfo, obj: &mut Object, code_section: SectionId) { + if let UnwindInfo::WindowsX64(info) = &info { + // Windows prefers Unwind info after the code -- writing it here. + let unwind_size = info.emit_size(); + let mut unwind_info = vec![0; unwind_size]; + info.emit(&mut unwind_info); + let _off = obj.append_section_data(code_section, &unwind_info, 4); + } +} + +/// Builds ELF image from the module `Compilation`. +// const CODE_SECTION_ALIGNMENT: u64 = 0x1000; +// assert_eq!( +// isa.triple().architecture.endianness(), +// Ok(target_lexicon::Endianness::Little) +// ); + +/// Iterates through all `LibCall` members and all runtime exported functions. +#[macro_export] +macro_rules! for_each_libcall { + ($op:ident) => { + $op![ + (UdivI64, wasmtime_i64_udiv), + (UdivI64, wasmtime_i64_udiv), + (SdivI64, wasmtime_i64_sdiv), + (UremI64, wasmtime_i64_urem), + (SremI64, wasmtime_i64_srem), + (IshlI64, wasmtime_i64_ishl), + (UshrI64, wasmtime_i64_ushr), + (SshrI64, wasmtime_i64_sshr), + (CeilF32, wasmtime_f32_ceil), + (FloorF32, wasmtime_f32_floor), + (TruncF32, wasmtime_f32_trunc), + (NearestF32, wasmtime_f32_nearest), + (CeilF64, wasmtime_f64_ceil), + (FloorF64, wasmtime_f64_floor), + (TruncF64, wasmtime_f64_trunc), + (NearestF64, wasmtime_f64_nearest) + ]; + }; +} + +fn write_libcall_symbols(obj: &mut Object) -> HashMap { + let mut libcalls = HashMap::new(); + macro_rules! add_libcall_symbol { + [$(($libcall:ident, $export:ident)),*] => {{ + $( + let symbol_id = obj.add_symbol(Symbol { + name: stringify!($export).as_bytes().to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Text, + scope: SymbolScope::Linkage, + weak: true, + section: SymbolSection::Undefined, + flags: SymbolFlags::None, + }); + libcalls.insert(LibCall::$libcall, symbol_id); + )+ + }}; + } + for_each_libcall!(add_libcall_symbol); + + libcalls +} + +pub mod utils { + use wasmtime_environ::entity::EntityRef; + use wasmtime_environ::wasm::{FuncIndex, SignatureIndex}; + + pub const FUNCTION_PREFIX: &str = "_wasm_function_"; + pub const TRAMPOLINE_PREFIX: &str = "_trampoline_"; + + pub fn func_symbol_name(index: FuncIndex) -> String { + format!("_wasm_function_{}", index.index()) + } + + pub fn try_parse_func_name(name: &str) -> Option { + if !name.starts_with(FUNCTION_PREFIX) { + return None; + } + name[FUNCTION_PREFIX.len()..] + .parse() + .ok() + .map(FuncIndex::new) + } + + pub fn trampoline_symbol_name(index: SignatureIndex) -> String { + format!("_trampoline_{}", index.index()) + } + + pub fn try_parse_trampoline_name(name: &str) -> Option { + if !name.starts_with(TRAMPOLINE_PREFIX) { + return None; + } + name[TRAMPOLINE_PREFIX.len()..] + .parse() + .ok() + .map(SignatureIndex::new) + } +} + +pub struct ObjectBuilderTarget { + pub(crate) binary_format: BinaryFormat, + pub(crate) architecture: Architecture, + pub(crate) endianness: Endianness, +} + +impl ObjectBuilderTarget { + pub fn new(arch: target_lexicon::Architecture) -> Result { + Ok(Self { + binary_format: BinaryFormat::Elf, + architecture: to_object_architecture(arch)?, + endianness: Endianness::Little, + }) + } + + pub fn from_triple(triple: &Triple) -> Result { + let binary_format = match triple.binary_format { + target_lexicon::BinaryFormat::Elf => object::BinaryFormat::Elf, + target_lexicon::BinaryFormat::Coff => object::BinaryFormat::Coff, + target_lexicon::BinaryFormat::Macho => object::BinaryFormat::MachO, + target_lexicon::BinaryFormat::Wasm => { + bail!("binary format wasm is unsupported"); + } + target_lexicon::BinaryFormat::Unknown => { + bail!("binary format is unknown"); + } + }; + let architecture = to_object_architecture(triple.architecture)?; + let endianness = match triple.endianness().unwrap() { + target_lexicon::Endianness::Little => object::Endianness::Little, + target_lexicon::Endianness::Big => object::Endianness::Big, + }; + Ok(Self { + binary_format, + architecture, + endianness, + }) + } +} + +pub struct ObjectBuilder<'a> { + target: ObjectBuilderTarget, + module: &'a Module, + code_alignment: u64, + compilation: Option<(Compilation, Relocations)>, + trampolines: PrimaryMap)>, + dwarf_sections: Vec, +} + +impl<'a> ObjectBuilder<'a> { + pub fn new(target: ObjectBuilderTarget, module: &'a Module) -> Self { + Self { + target, + module, + code_alignment: 1, + compilation: None, + trampolines: PrimaryMap::new(), + dwarf_sections: vec![], + } + } + + pub fn set_code_alignment(&mut self, code_alignment: u64) -> &mut Self { + self.code_alignment = code_alignment; + self + } + + pub fn set_compilation( + &mut self, + compilation: Compilation, + relocations: Relocations, + ) -> &mut Self { + self.compilation = Some((compilation, relocations)); + self + } + + pub fn set_trampolines( + &mut self, + trampolines: PrimaryMap)>, + ) -> &mut Self { + self.trampolines = trampolines; + self + } + + pub fn set_dwarf_sections(&mut self, dwarf_sections: Vec) -> &mut Self { + self.dwarf_sections = dwarf_sections; + self + } + + pub fn build(self) -> Result { + let mut obj = Object::new( + self.target.binary_format, + self.target.architecture, + self.target.endianness, + ); + + let module = self.module; + + // Entire code (functions and trampolines) will be placed + // in the ".text" section. + let section_id = obj.add_section( + obj.segment_name(StandardSegment::Text).to_vec(), + TEXT_SECTION_NAME.to_vec(), + SectionKind::Text, + ); + + let (compilation, jt_offsets, relocations) = self.compilation.map_or_else( + || (None, PrimaryMap::new(), PrimaryMap::new()), + |(c, relocations)| { + let jt_offsets = c.get_jt_offsets(); + (Some(c), jt_offsets, relocations) + }, + ); + + // Create symbols for imports -- needed during linking. + let mut func_symbols = + PrimaryMap::with_capacity(compilation.as_ref().map_or_else(|| 0, |c| c.len())); + for index in 0..module.local.num_imported_funcs { + let symbol_id = obj.add_symbol(Symbol { + name: utils::func_symbol_name(FuncIndex::new(index)) + .as_bytes() + .to_vec(), + value: 0, + size: 0, + kind: SymbolKind::Text, + scope: SymbolScope::Linkage, + weak: false, + section: SymbolSection::Undefined, + flags: SymbolFlags::None, + }); + func_symbols.push(symbol_id); + } + + if let Some(compilation) = compilation { + // Create symbols and section data for the compiled functions. + for (index, func) in compilation.into_iter().enumerate() { + let off = obj.append_section_data(section_id, &func.body, 1); + let symbol_id = obj.add_symbol(Symbol { + name: utils::func_symbol_name( + module.local.func_index(DefinedFuncIndex::new(index)), + ) + .as_bytes() + .to_vec(), + value: off, + size: func.body.len() as u64, + kind: SymbolKind::Text, + scope: SymbolScope::Compilation, + weak: false, + section: SymbolSection::Section(section_id), + flags: SymbolFlags::None, + }); + func_symbols.push(symbol_id); + // Preserve function unwind info. + if let Some(info) = &func.unwind_info { + process_unwind_info(info, &mut obj, section_id); + } + } + } + + // Create trampoline symbols for every signature. + let mut trampoline_relocs = HashMap::new(); + for (i, (func, relocs)) in self.trampolines.into_iter() { + let off = obj.append_section_data(section_id, &func.body, 1); + let symbol_id = obj.add_symbol(Symbol { + name: utils::trampoline_symbol_name(i).as_bytes().to_vec(), + value: off, + size: func.body.len() as u64, + kind: SymbolKind::Text, + scope: SymbolScope::Compilation, + weak: false, + section: SymbolSection::Section(section_id), + flags: SymbolFlags::None, + }); + trampoline_relocs.insert(symbol_id, relocs); + // Preserve trampoline function unwind info. + if let Some(info) = &func.unwind_info { + process_unwind_info(info, &mut obj, section_id); + } + } + + obj.append_section_data(section_id, &[], self.code_alignment); + + // If we have DWARF data, write it in the object file. + let (debug_bodies, debug_relocs) = self + .dwarf_sections + .into_iter() + .map(|s| ((s.name, s.body), (s.name, s.relocs))) + .unzip::<_, _, Vec<_>, Vec<_>>(); + let mut dwarf_sections_ids = HashMap::new(); + for (name, body) in debug_bodies { + let segment = obj.segment_name(StandardSegment::Debug).to_vec(); + let section_id = obj.add_section(segment, name.as_bytes().to_vec(), SectionKind::Debug); + dwarf_sections_ids.insert(name.to_string(), section_id); + obj.append_section_data(section_id, &body, 1); + } + + let libcalls = write_libcall_symbols(&mut obj); + + // Write all functions relocations. + for (index, relocs) in relocations.into_iter() { + let func_index = module.local.func_index(index); + let (_, off) = obj + .symbol_section_and_offset(func_symbols[func_index]) + .unwrap(); + for r in to_object_relocations( + relocs.iter(), + off, + module, + &func_symbols, + &libcalls, + &jt_offsets, + ) { + obj.add_relocation(section_id, r)?; + } + } + + for (symbol, relocs) in trampoline_relocs { + let (_, off) = obj.symbol_section_and_offset(symbol).unwrap(); + for r in to_object_relocations( + relocs.iter(), + off, + module, + &func_symbols, + &libcalls, + &jt_offsets, + ) { + obj.add_relocation(section_id, r)?; + } + } + + // Write all debug data relocations. + for (name, relocs) in debug_relocs { + let section_id = *dwarf_sections_ids.get(name).unwrap(); + for reloc in relocs { + let target_symbol = match reloc.target { + DwarfSectionRelocTarget::Func(index) => func_symbols[FuncIndex::new(index)], + DwarfSectionRelocTarget::Section(name) => { + obj.section_symbol(*dwarf_sections_ids.get(name).unwrap()) + } + }; + obj.add_relocation( + section_id, + ObjectRelocation { + offset: u64::from(reloc.offset), + size: reloc.size << 3, + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + symbol: target_symbol, + addend: i64::from(reloc.addend), + }, + )?; + } + } + + Ok(obj) + } +} diff --git a/crates/obj/src/function.rs b/crates/obj/src/function.rs deleted file mode 100644 index 6c3a09e6ab..0000000000 --- a/crates/obj/src/function.rs +++ /dev/null @@ -1,107 +0,0 @@ -use anyhow::Result; -use object::write::{Object, Relocation, StandardSection, Symbol, SymbolSection}; -use object::{RelocationEncoding, RelocationKind, SymbolFlags, SymbolKind, SymbolScope}; -use wasmtime_environ::entity::EntityRef; -use wasmtime_environ::settings; -use wasmtime_environ::settings::Configurable; -use wasmtime_environ::{Compilation, Module, RelocationTarget, Relocations}; - -/// Defines module functions -pub fn declare_functions( - obj: &mut Object, - module: &Module, - relocations: &Relocations, -) -> Result<()> { - for i in 0..module.local.num_imported_funcs { - let string_name = format!("_wasm_function_{}", i); - let _symbol_id = obj.add_symbol(Symbol { - name: string_name.as_bytes().to_vec(), - value: 0, - size: 0, - kind: SymbolKind::Text, - scope: SymbolScope::Unknown, - weak: false, - section: SymbolSection::Undefined, - flags: SymbolFlags::None, - }); - } - for (i, _function_relocs) in relocations.iter().rev() { - let func_index = module.local.func_index(i); - let string_name = format!("_wasm_function_{}", func_index.index()); - let _symbol_id = obj.add_symbol(Symbol { - name: string_name.as_bytes().to_vec(), - value: 0, - size: 0, - kind: SymbolKind::Text, - scope: SymbolScope::Linkage, - weak: false, - section: SymbolSection::Undefined, - flags: SymbolFlags::None, - }); - } - Ok(()) -} - -/// Emits module functions -pub fn emit_functions( - obj: &mut Object, - module: &Module, - compilation: &Compilation, - relocations: &Relocations, -) -> Result<()> { - debug_assert!( - module.start_func.is_none() - || module.start_func.unwrap().index() >= module.local.num_imported_funcs, - "imported start functions not supported yet" - ); - - let mut shared_builder = settings::builder(); - shared_builder - .enable("enable_verifier") - .expect("Missing enable_verifier setting"); - - for (i, _function_relocs) in relocations.iter() { - let body = &compilation.get(i).body; - let func_index = module.local.func_index(i); - let string_name = format!("_wasm_function_{}", func_index.index()); - - let symbol_id = obj.symbol_id(string_name.as_bytes()).unwrap(); - let section_id = obj.section_id(StandardSection::Text); - - obj.add_symbol_data(symbol_id, section_id, body, 1); - } - - for (i, function_relocs) in relocations.iter() { - let func_index = module.local.func_index(i); - let string_name = format!("_wasm_function_{}", func_index.index()); - let symbol_id = obj.symbol_id(string_name.as_bytes()).unwrap(); - let (_, section_offset) = obj.symbol_section_and_offset(symbol_id).unwrap(); - let section_id = obj.section_id(StandardSection::Text); - for r in function_relocs { - debug_assert_eq!(r.addend, 0); - match r.reloc_target { - RelocationTarget::UserFunc(target_index) => { - let target_name = format!("_wasm_function_{}", target_index.index()); - let target_symbol = obj.symbol_id(target_name.as_bytes()).unwrap(); - obj.add_relocation( - section_id, - Relocation { - offset: section_offset + r.offset as u64, - size: 64, // FIXME for all targets - kind: RelocationKind::Absolute, - encoding: RelocationEncoding::Generic, - symbol: target_symbol, - addend: 0, - }, - )?; - } - RelocationTarget::JumpTable(_, _) => { - // ignore relocations for jump tables - } - _ => panic!("relocations target not supported yet: {:?}", r.reloc_target), - }; - } - } - - Ok(()) -} diff --git a/crates/obj/src/lib.rs b/crates/obj/src/lib.rs index 197034e134..5db5bb071d 100644 --- a/crates/obj/src/lib.rs +++ b/crates/obj/src/lib.rs @@ -23,12 +23,13 @@ ) )] +mod builder; mod context; mod data_segment; -mod function; mod module; mod table; +pub use crate::builder::{utils, ObjectBuilder, ObjectBuilderTarget}; pub use crate::module::emit_module; /// Version number of this crate. diff --git a/crates/obj/src/module.rs b/crates/obj/src/module.rs index fdd5bf6e2b..652576209d 100644 --- a/crates/obj/src/module.rs +++ b/crates/obj/src/module.rs @@ -1,10 +1,11 @@ +use crate::builder::{ObjectBuilder, ObjectBuilderTarget}; use crate::context::layout_vmcontext; use crate::data_segment::{declare_data_segment, emit_data_segment}; -use crate::function::{declare_functions, emit_functions}; use crate::table::{declare_table, emit_table}; use anyhow::Result; use object::write::{Object, Relocation, StandardSection, Symbol, SymbolSection}; use object::{RelocationEncoding, RelocationKind, SymbolFlags, SymbolKind, SymbolScope}; +use wasmtime_debug::DwarfSection; use wasmtime_environ::isa::TargetFrontendConfig; use wasmtime_environ::{Compilation, DataInitializer, Module, Relocations}; @@ -48,34 +49,38 @@ fn emit_vmcontext_init( /// Emits a module that has been emitted with the `wasmtime-environ` environment /// implementation to a native object file. pub fn emit_module( - obj: &mut Object, + target: ObjectBuilderTarget, module: &Module, - compilation: &Compilation, - relocations: &Relocations, - data_initializers: &[DataInitializer], target_config: &TargetFrontendConfig, -) -> Result<()> { - declare_functions(obj, module, relocations)?; + compilation: Compilation, + relocations: Relocations, + dwarf_sections: Vec, + data_initializers: &[DataInitializer], +) -> Result { + let mut builder = ObjectBuilder::new(target, module); + builder.set_compilation(compilation, relocations); + builder.set_dwarf_sections(dwarf_sections); + let mut obj = builder.build()?; + + // Append data, table and vmcontext_init code to the object file. for (i, initializer) in data_initializers.iter().enumerate() { - declare_data_segment(obj, initializer, i)?; + declare_data_segment(&mut obj, initializer, i)?; } for i in 0..module.local.table_plans.len() { - declare_table(obj, i)?; + declare_table(&mut obj, i)?; } - emit_functions(obj, module, compilation, relocations)?; - for (i, initializer) in data_initializers.iter().enumerate() { - emit_data_segment(obj, initializer, i)?; + emit_data_segment(&mut obj, initializer, i)?; } for i in 0..module.local.table_plans.len() { - emit_table(obj, i)?; + emit_table(&mut obj, i)?; } - emit_vmcontext_init(obj, module, target_config)?; + emit_vmcontext_init(&mut obj, module, target_config)?; - Ok(()) + Ok(obj) } diff --git a/src/obj.rs b/src/obj.rs index 63929f619d..90fef7146e 100644 --- a/src/obj.rs +++ b/src/obj.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, bail, Context as _, Result}; use object::write::Object; use target_lexicon::Triple; use wasmtime::Strategy; -use wasmtime_debug::{emit_dwarf, read_debuginfo, write_debugsections}; +use wasmtime_debug::{emit_dwarf, read_debuginfo}; #[cfg(feature = "lightbeam")] use wasmtime_environ::Lightbeam; use wasmtime_environ::{ @@ -11,43 +11,7 @@ use wasmtime_environ::{ ModuleVmctxInfo, Tunables, VMOffsets, }; use wasmtime_jit::native; -use wasmtime_obj::emit_module; - -fn to_obj_format( - triple: &Triple, -) -> Result<( - object::BinaryFormat, - object::Architecture, - object::Endianness, -)> { - let binary_format = match triple.binary_format { - target_lexicon::BinaryFormat::Elf => object::BinaryFormat::Elf, - target_lexicon::BinaryFormat::Coff => object::BinaryFormat::Coff, - target_lexicon::BinaryFormat::Macho => object::BinaryFormat::MachO, - target_lexicon::BinaryFormat::Wasm => { - bail!("binary format wasm is unsupported"); - } - target_lexicon::BinaryFormat::Unknown => { - bail!("binary format is unknown"); - } - }; - let architecture = match triple.architecture { - target_lexicon::Architecture::I386 - | target_lexicon::Architecture::I586 - | target_lexicon::Architecture::I686 => object::Architecture::I386, - target_lexicon::Architecture::X86_64 => object::Architecture::X86_64, - target_lexicon::Architecture::Arm(_) => object::Architecture::Arm, - target_lexicon::Architecture::Aarch64(_) => object::Architecture::Aarch64, - architecture => { - bail!("target architecture {:?} is unsupported", architecture,); - } - }; - let endian = match triple.endianness().unwrap() { - target_lexicon::Endianness::Little => object::Endianness::Little, - target_lexicon::Endianness::Big => object::Endianness::Big, - }; - Ok((binary_format, architecture, endian)) -} +use wasmtime_obj::{emit_module, ObjectBuilderTarget}; /// Creates object file from binary wasm data. pub fn compile_to_obj( @@ -86,8 +50,7 @@ pub fn compile_to_obj( let isa = isa_builder.finish(settings::Flags::new(flag_builder)); - let (obj_format, obj_arch, obj_endian) = to_obj_format(isa.triple())?; - let mut obj = Object::new(obj_format, obj_arch, obj_endian); + let target = ObjectBuilderTarget::from_triple(isa.triple())?; // TODO: Expose the tunables as command-line flags. let mut tunables = Tunables::default(); @@ -143,29 +106,30 @@ pub fn compile_to_obj( } }; - emit_module( - &mut obj, - &translation.module, - &compilation, - &relocations, - &translation.data_initializers, - &translation.target_config, - ) - .map_err(|e| anyhow!(e)) - .context("failed to emit module")?; - - if debug_info { + let dwarf_sections = if debug_info { let debug_data = read_debuginfo(wasm).context("failed to emit DWARF")?; - let sections = emit_dwarf( + emit_dwarf( &*isa, &debug_data, &address_transform, &module_vmctx_info, &value_ranges, - &compilation, + &compilation.unwind_info(), ) - .context("failed to emit debug sections")?; - write_debugsections(&mut obj, sections).context("failed to emit debug sections")?; - } - Ok(obj) + .context("failed to emit debug sections")? + } else { + vec![] + }; + + Ok(emit_module( + target, + &translation.module, + &translation.target_config, + compilation, + relocations, + dwarf_sections, + &translation.data_initializers, + ) + .map_err(|e| anyhow!(e)) + .context("failed to emit module")?) }