Removes duplicate code in src/obj.rs, crates/obj and crates/jit/object.rs (#1993)
Changes: - Moves object creation code from crates/jit/object.rs to the creates/obj (as ObjectBuilder) - Removes legacy crates/obj/function.rs - Removes write_debugsections
This commit is contained in:
3
Cargo.lock
generated
3
Cargo.lock
generated
@@ -2503,6 +2503,7 @@ dependencies = [
|
|||||||
"wasmparser 0.58.0",
|
"wasmparser 0.58.0",
|
||||||
"wasmtime-debug",
|
"wasmtime-debug",
|
||||||
"wasmtime-environ",
|
"wasmtime-environ",
|
||||||
|
"wasmtime-obj",
|
||||||
"wasmtime-profiling",
|
"wasmtime-profiling",
|
||||||
"wasmtime-runtime",
|
"wasmtime-runtime",
|
||||||
"winapi",
|
"winapi",
|
||||||
@@ -2515,6 +2516,8 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"more-asserts",
|
"more-asserts",
|
||||||
"object",
|
"object",
|
||||||
|
"target-lexicon",
|
||||||
|
"wasmtime-debug",
|
||||||
"wasmtime-environ",
|
"wasmtime-environ",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
#![allow(clippy::cast_ptr_alignment)]
|
#![allow(clippy::cast_ptr_alignment)]
|
||||||
|
|
||||||
use anyhow::{bail, ensure, Error};
|
use anyhow::{bail, ensure, Error};
|
||||||
use object::write::{Object, Relocation, StandardSegment};
|
use object::{RelocationEncoding, RelocationKind};
|
||||||
use object::{RelocationEncoding, RelocationKind, SectionKind};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub use crate::read_debuginfo::{read_debuginfo, DebugInfoData, WasmFileInfo};
|
pub use crate::read_debuginfo::{read_debuginfo, DebugInfoData, WasmFileInfo};
|
||||||
@@ -15,46 +14,6 @@ mod read_debuginfo;
|
|||||||
mod transform;
|
mod transform;
|
||||||
mod write_debuginfo;
|
mod write_debuginfo;
|
||||||
|
|
||||||
pub fn write_debugsections(obj: &mut Object, sections: Vec<DwarfSection>) -> 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(
|
pub fn create_gdbjit_image(
|
||||||
mut bytes: Vec<u8>,
|
mut bytes: Vec<u8>,
|
||||||
code_region: (*const u8, usize),
|
code_region: (*const u8, usize),
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ pub use crate::read_debuginfo::{read_debuginfo, DebugInfoData, WasmFileInfo};
|
|||||||
pub use crate::transform::transform_dwarf;
|
pub use crate::transform::transform_dwarf;
|
||||||
use gimli::write::{Address, Dwarf, EndianVec, FrameTable, Result, Sections, Writer};
|
use gimli::write::{Address, Dwarf, EndianVec, FrameTable, Result, Sections, Writer};
|
||||||
use gimli::{RunTimeEndian, SectionId};
|
use gimli::{RunTimeEndian, SectionId};
|
||||||
|
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
|
||||||
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
|
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)]
|
#[derive(Clone)]
|
||||||
pub enum DwarfSectionRelocTarget {
|
pub enum DwarfSectionRelocTarget {
|
||||||
@@ -130,18 +132,18 @@ impl Writer for WriterRelocate {
|
|||||||
|
|
||||||
fn create_frame_table<'a>(
|
fn create_frame_table<'a>(
|
||||||
isa: &dyn TargetIsa,
|
isa: &dyn TargetIsa,
|
||||||
infos: impl Iterator<Item = &'a Option<UnwindInfo>>,
|
infos: &PrimaryMap<DefinedFuncIndex, &Option<UnwindInfo>>,
|
||||||
) -> Option<FrameTable> {
|
) -> Option<FrameTable> {
|
||||||
let mut table = FrameTable::default();
|
let mut table = FrameTable::default();
|
||||||
|
|
||||||
let cie_id = table.add_cie(isa.create_systemv_cie()?);
|
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 {
|
if let Some(UnwindInfo::SystemV(info)) = info {
|
||||||
table.add_fde(
|
table.add_fde(
|
||||||
cie_id,
|
cie_id,
|
||||||
info.to_fde(Address::Symbol {
|
info.to_fde(Address::Symbol {
|
||||||
symbol: i,
|
symbol: i.index(),
|
||||||
addend: 0,
|
addend: 0,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -151,16 +153,16 @@ fn create_frame_table<'a>(
|
|||||||
Some(table)
|
Some(table)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emit_dwarf(
|
pub fn emit_dwarf<'a>(
|
||||||
isa: &dyn TargetIsa,
|
isa: &dyn TargetIsa,
|
||||||
debuginfo_data: &DebugInfoData,
|
debuginfo_data: &DebugInfoData,
|
||||||
at: &ModuleAddressMap,
|
at: &ModuleAddressMap,
|
||||||
vmctx_info: &ModuleVmctxInfo,
|
vmctx_info: &ModuleVmctxInfo,
|
||||||
ranges: &ValueLabelsRanges,
|
ranges: &ValueLabelsRanges,
|
||||||
compilation: &Compilation,
|
unwind_info: &PrimaryMap<DefinedFuncIndex, &Option<UnwindInfo>>,
|
||||||
) -> anyhow::Result<Vec<DwarfSection>> {
|
) -> anyhow::Result<Vec<DwarfSection>> {
|
||||||
let dwarf = transform_dwarf(isa, debuginfo_data, at, vmctx_info, ranges)?;
|
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)?;
|
let sections = emit_dwarf_sections(dwarf, frame_table)?;
|
||||||
Ok(sections)
|
Ok(sections)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,14 @@ impl Compilation {
|
|||||||
self.functions.is_empty()
|
self.functions.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns unwind info for all defined functions.
|
||||||
|
pub fn unwind_info(&self) -> PrimaryMap<DefinedFuncIndex, &Option<UnwindInfo>> {
|
||||||
|
self.functions
|
||||||
|
.iter()
|
||||||
|
.map(|(_, func)| &func.unwind_info)
|
||||||
|
.collect::<PrimaryMap<DefinedFuncIndex, _>>()
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets functions jump table offsets.
|
/// Gets functions jump table offsets.
|
||||||
pub fn get_jt_offsets(&self) -> PrimaryMap<DefinedFuncIndex, ir::JumpTableOffsets> {
|
pub fn get_jt_offsets(&self) -> PrimaryMap<DefinedFuncIndex, ir::JumpTableOffsets> {
|
||||||
self.functions
|
self.functions
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#![doc(hidden)]
|
#![doc(hidden)]
|
||||||
|
|
||||||
pub mod ir {
|
pub mod ir {
|
||||||
pub use cranelift_codegen::binemit::Stackmap;
|
pub use cranelift_codegen::binemit::{Reloc, Stackmap};
|
||||||
pub use cranelift_codegen::ir::{
|
pub use cranelift_codegen::ir::{
|
||||||
types, AbiParam, ArgumentPurpose, Signature, SourceLoc, StackSlots, TrapCode, Type,
|
types, AbiParam, ArgumentPurpose, JumpTableOffsets, LibCall, Signature, SourceLoc,
|
||||||
ValueLabel, ValueLoc,
|
StackSlots, TrapCode, Type, ValueLabel, ValueLoc,
|
||||||
};
|
};
|
||||||
pub use cranelift_codegen::{ValueLabelsRanges, ValueLocRange};
|
pub use cranelift_codegen::{ValueLabelsRanges, ValueLocRange};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,3 +87,28 @@ pub(crate) fn reference_type(
|
|||||||
_ => panic!("unsupported Wasm 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)
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ wasmtime-environ = { path = "../environ", version = "0.18.0" }
|
|||||||
wasmtime-runtime = { path = "../runtime", version = "0.18.0" }
|
wasmtime-runtime = { path = "../runtime", version = "0.18.0" }
|
||||||
wasmtime-debug = { path = "../debug", version = "0.18.0" }
|
wasmtime-debug = { path = "../debug", version = "0.18.0" }
|
||||||
wasmtime-profiling = { path = "../profiling", version = "0.18.0" }
|
wasmtime-profiling = { path = "../profiling", version = "0.18.0" }
|
||||||
|
wasmtime-obj = { path = "../obj", version = "0.18.0" }
|
||||||
region = "2.1.0"
|
region = "2.1.0"
|
||||||
thiserror = "1.0.4"
|
thiserror = "1.0.4"
|
||||||
target-lexicon = { version = "0.10.0", default-features = false }
|
target-lexicon = { version = "0.10.0", default-features = false }
|
||||||
|
|||||||
@@ -3,15 +3,15 @@
|
|||||||
use crate::instantiate::SetupError;
|
use crate::instantiate::SetupError;
|
||||||
use crate::object::{build_object, ObjectUnwindInfo};
|
use crate::object::{build_object, ObjectUnwindInfo};
|
||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
|
use object::write::Object;
|
||||||
use wasmtime_debug::{emit_dwarf, DebugInfoData, DwarfSection};
|
use wasmtime_debug::{emit_dwarf, DebugInfoData, DwarfSection};
|
||||||
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
|
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::wasm::{DefinedFuncIndex, DefinedMemoryIndex, MemoryIndex};
|
||||||
use wasmtime_environ::{
|
use wasmtime_environ::{
|
||||||
CacheConfig, Compiler as _C, Module, ModuleAddressMap, ModuleMemoryOffset, ModuleTranslation,
|
CacheConfig, Compiler as _C, Module, ModuleAddressMap, ModuleMemoryOffset, ModuleTranslation,
|
||||||
ModuleVmctxInfo, StackMaps, Traps, Tunables, VMOffsets, ValueLabelsRanges,
|
ModuleVmctxInfo, StackMaps, Traps, Tunables, VMOffsets, ValueLabelsRanges,
|
||||||
};
|
};
|
||||||
use wasmtime_runtime::InstantiationError;
|
|
||||||
|
|
||||||
/// Select which kind of compilation to use.
|
/// Select which kind of compilation to use.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
@@ -67,11 +67,11 @@ fn _assert_compiler_send_sync() {
|
|||||||
fn transform_dwarf_data(
|
fn transform_dwarf_data(
|
||||||
isa: &dyn TargetIsa,
|
isa: &dyn TargetIsa,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
debug_data: &DebugInfoData,
|
debug_data: DebugInfoData,
|
||||||
address_transform: &ModuleAddressMap,
|
address_transform: &ModuleAddressMap,
|
||||||
value_ranges: &ValueLabelsRanges,
|
value_ranges: &ValueLabelsRanges,
|
||||||
stack_slots: PrimaryMap<DefinedFuncIndex, ir::StackSlots>,
|
stack_slots: PrimaryMap<DefinedFuncIndex, ir::StackSlots>,
|
||||||
compilation: &wasmtime_environ::Compilation,
|
unwind_info: PrimaryMap<DefinedFuncIndex, &Option<UnwindInfo>>,
|
||||||
) -> Result<Vec<DwarfSection>, SetupError> {
|
) -> Result<Vec<DwarfSection>, SetupError> {
|
||||||
let target_config = isa.frontend_config();
|
let target_config = isa.frontend_config();
|
||||||
let ofs = VMOffsets::new(target_config.pointer_bytes(), &module.local);
|
let ofs = VMOffsets::new(target_config.pointer_bytes(), &module.local);
|
||||||
@@ -92,18 +92,18 @@ fn transform_dwarf_data(
|
|||||||
};
|
};
|
||||||
emit_dwarf(
|
emit_dwarf(
|
||||||
isa,
|
isa,
|
||||||
debug_data,
|
&debug_data,
|
||||||
&address_transform,
|
&address_transform,
|
||||||
&module_vmctx_info,
|
&module_vmctx_info,
|
||||||
&value_ranges,
|
&value_ranges,
|
||||||
&compilation,
|
&unwind_info,
|
||||||
)
|
)
|
||||||
.map_err(SetupError::DebugInfo)
|
.map_err(SetupError::DebugInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub struct Compilation {
|
pub struct Compilation {
|
||||||
pub obj: Vec<u8>,
|
pub obj: Object,
|
||||||
pub unwind_info: Vec<ObjectUnwindInfo>,
|
pub unwind_info: Vec<ObjectUnwindInfo>,
|
||||||
pub traps: Traps,
|
pub traps: Traps,
|
||||||
pub stack_maps: StackMaps,
|
pub stack_maps: StackMaps,
|
||||||
@@ -162,14 +162,15 @@ impl Compiler {
|
|||||||
.map_err(SetupError::Compile)?;
|
.map_err(SetupError::Compile)?;
|
||||||
|
|
||||||
let dwarf_sections = if debug_data.is_some() && !compilation.is_empty() {
|
let dwarf_sections = if debug_data.is_some() && !compilation.is_empty() {
|
||||||
|
let unwind_info = compilation.unwind_info();
|
||||||
transform_dwarf_data(
|
transform_dwarf_data(
|
||||||
&*self.isa,
|
&*self.isa,
|
||||||
&translation.module,
|
&translation.module,
|
||||||
debug_data.as_ref().unwrap(),
|
debug_data.unwrap(),
|
||||||
&address_transform,
|
&address_transform,
|
||||||
&value_ranges,
|
&value_ranges,
|
||||||
stack_slots,
|
stack_slots,
|
||||||
&compilation,
|
unwind_info,
|
||||||
)?
|
)?
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
@@ -177,16 +178,11 @@ impl Compiler {
|
|||||||
|
|
||||||
let (obj, unwind_info) = build_object(
|
let (obj, unwind_info) = build_object(
|
||||||
&*self.isa,
|
&*self.isa,
|
||||||
&compilation,
|
|
||||||
&relocations,
|
|
||||||
&translation.module,
|
&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 {
|
Ok(Compilation {
|
||||||
obj,
|
obj,
|
||||||
|
|||||||
@@ -96,6 +96,12 @@ impl CompilationArtifacts {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.into_boxed_slice();
|
.into_boxed_slice();
|
||||||
|
|
||||||
|
let obj = obj.write().map_err(|_| {
|
||||||
|
SetupError::Instantiate(InstantiationError::Resource(
|
||||||
|
"failed to create image memory".to_string(),
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
module,
|
module,
|
||||||
obj: obj.into_boxed_slice(),
|
obj: obj.into_boxed_slice(),
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ fn apply_reloc(
|
|||||||
|
|
||||||
fn to_libcall_address(name: &str) -> Option<usize> {
|
fn to_libcall_address(name: &str) -> Option<usize> {
|
||||||
use self::libcalls::*;
|
use self::libcalls::*;
|
||||||
use crate::for_each_libcall;
|
use wasmtime_environ::for_each_libcall;
|
||||||
macro_rules! add_libcall_symbol {
|
macro_rules! add_libcall_symbol {
|
||||||
[$(($libcall:ident, $export:ident)),*] => {
|
[$(($libcall:ident, $export:ident)),*] => {
|
||||||
Some(match name {
|
Some(match name {
|
||||||
|
|||||||
@@ -1,113 +1,16 @@
|
|||||||
//! Object file generation.
|
//! 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 super::trampoline::build_trampoline;
|
||||||
use cranelift_codegen::binemit::Reloc;
|
|
||||||
use cranelift_codegen::ir::{JumpTableOffsets, LibCall};
|
|
||||||
use cranelift_frontend::FunctionBuilderContext;
|
use cranelift_frontend::FunctionBuilderContext;
|
||||||
use object::write::{
|
use object::write::Object;
|
||||||
Object, Relocation as ObjectRelocation, SectionId, StandardSegment, Symbol, SymbolId,
|
use wasmtime_debug::DwarfSection;
|
||||||
SymbolSection,
|
|
||||||
};
|
|
||||||
use object::{
|
|
||||||
elf, Architecture, BinaryFormat, Endianness, RelocationEncoding, RelocationKind, SectionKind,
|
|
||||||
SymbolFlags, SymbolKind, SymbolScope,
|
|
||||||
};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use wasmtime_debug::{DwarfSection, DwarfSectionRelocTarget};
|
|
||||||
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
|
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
|
||||||
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
|
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
|
||||||
use wasmtime_environ::wasm::{DefinedFuncIndex, FuncIndex, SignatureIndex};
|
use wasmtime_environ::wasm::{FuncIndex, SignatureIndex};
|
||||||
use wasmtime_environ::{Compilation, Module, Relocation, RelocationTarget, Relocations};
|
use wasmtime_environ::{Compilation, Module, Relocations};
|
||||||
|
use wasmtime_obj::{ObjectBuilder, ObjectBuilderTarget};
|
||||||
|
|
||||||
fn to_object_relocations<'a>(
|
pub use wasmtime_obj::utils;
|
||||||
it: impl Iterator<Item = &'a Relocation> + 'a,
|
|
||||||
off: u64,
|
|
||||||
module: &'a Module,
|
|
||||||
funcs: &'a PrimaryMap<FuncIndex, SymbolId>,
|
|
||||||
libcalls: &'a HashMap<LibCall, SymbolId>,
|
|
||||||
jt_offsets: &'a PrimaryMap<DefinedFuncIndex, JumpTableOffsets>,
|
|
||||||
) -> impl Iterator<Item = ObjectRelocation> + '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<Architecture, anyhow::Error> {
|
|
||||||
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";
|
|
||||||
|
|
||||||
/// Unwind information for object files functions (including trampolines).
|
/// Unwind information for object files functions (including trampolines).
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
@@ -116,23 +19,13 @@ pub enum ObjectUnwindInfo {
|
|||||||
Trampoline(SignatureIndex, UnwindInfo),
|
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`.
|
// Builds ELF image from the module `Compilation`.
|
||||||
pub(crate) fn build_object(
|
pub(crate) fn build_object(
|
||||||
isa: &dyn TargetIsa,
|
isa: &dyn TargetIsa,
|
||||||
compilation: &Compilation,
|
|
||||||
relocations: &Relocations,
|
|
||||||
module: &Module,
|
module: &Module,
|
||||||
dwarf_sections: &[DwarfSection],
|
compilation: Compilation,
|
||||||
|
relocations: Relocations,
|
||||||
|
dwarf_sections: Vec<DwarfSection>,
|
||||||
) -> Result<(Object, Vec<ObjectUnwindInfo>), anyhow::Error> {
|
) -> Result<(Object, Vec<ObjectUnwindInfo>), anyhow::Error> {
|
||||||
const CODE_SECTION_ALIGNMENT: u64 = 0x1000;
|
const CODE_SECTION_ALIGNMENT: u64 = 0x1000;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -140,247 +33,44 @@ pub(crate) fn build_object(
|
|||||||
Ok(target_lexicon::Endianness::Little)
|
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();
|
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.
|
// Preserve function unwind info.
|
||||||
if let Some(info) = &func.unwind_info {
|
unwind_info.extend(
|
||||||
process_unwind_info(info, &mut obj, section_id);
|
compilation
|
||||||
unwind_info.push(ObjectUnwindInfo::Func(
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(index, func)| {
|
||||||
|
func.unwind_info.as_ref().map(|info| {
|
||||||
|
ObjectUnwindInfo::Func(
|
||||||
FuncIndex::new(module.local.num_imported_funcs + index),
|
FuncIndex::new(module.local.num_imported_funcs + index),
|
||||||
info.clone(),
|
info.clone(),
|
||||||
))
|
)
|
||||||
}
|
})
|
||||||
}
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
let mut trampoline_relocs = HashMap::new();
|
let mut trampolines = PrimaryMap::with_capacity(module.local.signatures.len());
|
||||||
let mut cx = FunctionBuilderContext::new();
|
let mut cx = FunctionBuilderContext::new();
|
||||||
// Build trampolines for every signature.
|
// Build trampolines for every signature.
|
||||||
for (i, (_, native_sig)) in module.local.signatures.iter() {
|
for (i, (_, native_sig)) in module.local.signatures.iter() {
|
||||||
let (func, relocs) =
|
let (func, relocs) =
|
||||||
build_trampoline(isa, &mut cx, native_sig, std::mem::size_of::<u128>())?;
|
build_trampoline(isa, &mut cx, native_sig, std::mem::size_of::<u128>())?;
|
||||||
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.
|
// Preserve trampoline function unwind info.
|
||||||
if let Some(info) = &func.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()))
|
unwind_info.push(ObjectUnwindInfo::Trampoline(i, info.clone()))
|
||||||
}
|
}
|
||||||
|
trampolines.push((func, relocs));
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.append_section_data(section_id, &[], CODE_SECTION_ALIGNMENT);
|
let target = ObjectBuilderTarget::new(isa.triple().architecture)?;
|
||||||
|
let mut builder = ObjectBuilder::new(target, module);
|
||||||
// If we have DWARF data, write it in the object file.
|
builder
|
||||||
let (debug_bodies, debug_relocs) = dwarf_sections
|
.set_code_alignment(CODE_SECTION_ALIGNMENT)
|
||||||
.into_iter()
|
.set_compilation(compilation, relocations)
|
||||||
.map(|s| ((s.name, &s.body), (s.name, &s.relocs)))
|
.set_trampolines(trampolines)
|
||||||
.unzip::<_, _, Vec<_>, Vec<_>>();
|
.set_dwarf_sections(dwarf_sections);
|
||||||
let mut dwarf_sections_ids = HashMap::new();
|
let obj = builder.build()?;
|
||||||
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),
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((obj, unwind_info))
|
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<LibCall, SymbolId> {
|
|
||||||
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<FuncIndex> {
|
|
||||||
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<SignatureIndex> {
|
|
||||||
if !name.starts_with(TRAMPOLINE_PREFIX) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
name[TRAMPOLINE_PREFIX.len()..]
|
|
||||||
.parse()
|
|
||||||
.ok()
|
|
||||||
.map(SignatureIndex::new)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ anyhow = "1.0"
|
|||||||
wasmtime-environ = { path = "../environ", version = "0.18.0" }
|
wasmtime-environ = { path = "../environ", version = "0.18.0" }
|
||||||
object = { version = "0.20", default-features = false, features = ["write"] }
|
object = { version = "0.20", default-features = false, features = ["write"] }
|
||||||
more-asserts = "0.2.1"
|
more-asserts = "0.2.1"
|
||||||
|
target-lexicon = { version = "0.10.0", default-features = false }
|
||||||
|
wasmtime-debug = { path = "../debug", version = "0.18.0" }
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
maintenance = { status = "experimental" }
|
maintenance = { status = "experimental" }
|
||||||
|
|||||||
472
crates/obj/src/builder.rs
Normal file
472
crates/obj/src/builder.rs
Normal file
@@ -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<Item = &'a Relocation> + 'a,
|
||||||
|
off: u64,
|
||||||
|
module: &'a Module,
|
||||||
|
funcs: &'a PrimaryMap<FuncIndex, SymbolId>,
|
||||||
|
libcalls: &'a HashMap<LibCall, SymbolId>,
|
||||||
|
jt_offsets: &'a PrimaryMap<DefinedFuncIndex, JumpTableOffsets>,
|
||||||
|
) -> impl Iterator<Item = ObjectRelocation> + '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<Architecture, anyhow::Error> {
|
||||||
|
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<LibCall, SymbolId> {
|
||||||
|
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<FuncIndex> {
|
||||||
|
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<SignatureIndex> {
|
||||||
|
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<Self, anyhow::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
binary_format: BinaryFormat::Elf,
|
||||||
|
architecture: to_object_architecture(arch)?,
|
||||||
|
endianness: Endianness::Little,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_triple(triple: &Triple) -> Result<Self, anyhow::Error> {
|
||||||
|
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<SignatureIndex, (CompiledFunction, Vec<Relocation>)>,
|
||||||
|
dwarf_sections: Vec<DwarfSection>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<SignatureIndex, (CompiledFunction, Vec<Relocation>)>,
|
||||||
|
) -> &mut Self {
|
||||||
|
self.trampolines = trampolines;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_dwarf_sections(&mut self, dwarf_sections: Vec<DwarfSection>) -> &mut Self {
|
||||||
|
self.dwarf_sections = dwarf_sections;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Result<Object, anyhow::Error> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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(())
|
|
||||||
}
|
|
||||||
@@ -23,12 +23,13 @@
|
|||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
|
|
||||||
|
mod builder;
|
||||||
mod context;
|
mod context;
|
||||||
mod data_segment;
|
mod data_segment;
|
||||||
mod function;
|
|
||||||
mod module;
|
mod module;
|
||||||
mod table;
|
mod table;
|
||||||
|
|
||||||
|
pub use crate::builder::{utils, ObjectBuilder, ObjectBuilderTarget};
|
||||||
pub use crate::module::emit_module;
|
pub use crate::module::emit_module;
|
||||||
|
|
||||||
/// Version number of this crate.
|
/// Version number of this crate.
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
|
use crate::builder::{ObjectBuilder, ObjectBuilderTarget};
|
||||||
use crate::context::layout_vmcontext;
|
use crate::context::layout_vmcontext;
|
||||||
use crate::data_segment::{declare_data_segment, emit_data_segment};
|
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 crate::table::{declare_table, emit_table};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use object::write::{Object, Relocation, StandardSection, Symbol, SymbolSection};
|
use object::write::{Object, Relocation, StandardSection, Symbol, SymbolSection};
|
||||||
use object::{RelocationEncoding, RelocationKind, SymbolFlags, SymbolKind, SymbolScope};
|
use object::{RelocationEncoding, RelocationKind, SymbolFlags, SymbolKind, SymbolScope};
|
||||||
|
use wasmtime_debug::DwarfSection;
|
||||||
use wasmtime_environ::isa::TargetFrontendConfig;
|
use wasmtime_environ::isa::TargetFrontendConfig;
|
||||||
use wasmtime_environ::{Compilation, DataInitializer, Module, Relocations};
|
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
|
/// Emits a module that has been emitted with the `wasmtime-environ` environment
|
||||||
/// implementation to a native object file.
|
/// implementation to a native object file.
|
||||||
pub fn emit_module(
|
pub fn emit_module(
|
||||||
obj: &mut Object,
|
target: ObjectBuilderTarget,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
compilation: &Compilation,
|
|
||||||
relocations: &Relocations,
|
|
||||||
data_initializers: &[DataInitializer],
|
|
||||||
target_config: &TargetFrontendConfig,
|
target_config: &TargetFrontendConfig,
|
||||||
) -> Result<()> {
|
compilation: Compilation,
|
||||||
declare_functions(obj, module, relocations)?;
|
relocations: Relocations,
|
||||||
|
dwarf_sections: Vec<DwarfSection>,
|
||||||
|
data_initializers: &[DataInitializer],
|
||||||
|
) -> Result<Object> {
|
||||||
|
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() {
|
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() {
|
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() {
|
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() {
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
80
src/obj.rs
80
src/obj.rs
@@ -2,7 +2,7 @@ use anyhow::{anyhow, bail, Context as _, Result};
|
|||||||
use object::write::Object;
|
use object::write::Object;
|
||||||
use target_lexicon::Triple;
|
use target_lexicon::Triple;
|
||||||
use wasmtime::Strategy;
|
use wasmtime::Strategy;
|
||||||
use wasmtime_debug::{emit_dwarf, read_debuginfo, write_debugsections};
|
use wasmtime_debug::{emit_dwarf, read_debuginfo};
|
||||||
#[cfg(feature = "lightbeam")]
|
#[cfg(feature = "lightbeam")]
|
||||||
use wasmtime_environ::Lightbeam;
|
use wasmtime_environ::Lightbeam;
|
||||||
use wasmtime_environ::{
|
use wasmtime_environ::{
|
||||||
@@ -11,43 +11,7 @@ use wasmtime_environ::{
|
|||||||
ModuleVmctxInfo, Tunables, VMOffsets,
|
ModuleVmctxInfo, Tunables, VMOffsets,
|
||||||
};
|
};
|
||||||
use wasmtime_jit::native;
|
use wasmtime_jit::native;
|
||||||
use wasmtime_obj::emit_module;
|
use wasmtime_obj::{emit_module, ObjectBuilderTarget};
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates object file from binary wasm data.
|
/// Creates object file from binary wasm data.
|
||||||
pub fn compile_to_obj(
|
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 isa = isa_builder.finish(settings::Flags::new(flag_builder));
|
||||||
|
|
||||||
let (obj_format, obj_arch, obj_endian) = to_obj_format(isa.triple())?;
|
let target = ObjectBuilderTarget::from_triple(isa.triple())?;
|
||||||
let mut obj = Object::new(obj_format, obj_arch, obj_endian);
|
|
||||||
|
|
||||||
// TODO: Expose the tunables as command-line flags.
|
// TODO: Expose the tunables as command-line flags.
|
||||||
let mut tunables = Tunables::default();
|
let mut tunables = Tunables::default();
|
||||||
@@ -143,29 +106,30 @@ pub fn compile_to_obj(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
emit_module(
|
let dwarf_sections = if debug_info {
|
||||||
&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 debug_data = read_debuginfo(wasm).context("failed to emit DWARF")?;
|
let debug_data = read_debuginfo(wasm).context("failed to emit DWARF")?;
|
||||||
let sections = emit_dwarf(
|
emit_dwarf(
|
||||||
&*isa,
|
&*isa,
|
||||||
&debug_data,
|
&debug_data,
|
||||||
&address_transform,
|
&address_transform,
|
||||||
&module_vmctx_info,
|
&module_vmctx_info,
|
||||||
&value_ranges,
|
&value_ranges,
|
||||||
&compilation,
|
&compilation.unwind_info(),
|
||||||
)
|
)
|
||||||
.context("failed to emit debug sections")?;
|
.context("failed to emit debug sections")?
|
||||||
write_debugsections(&mut obj, sections).context("failed to emit debug sections")?;
|
} else {
|
||||||
}
|
vec![]
|
||||||
Ok(obj)
|
};
|
||||||
|
|
||||||
|
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")?)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user