use crate::compiler::{CompiledObjects, emit_metadata_and_link};
use crate::dwarf::{EhRelocation, EhTarget};
use crate::misc::{CompiledFunctionExt, CompiledKind};
use crate::object::get_object_for_target;
use crate::types::function::{Compilation, FunctionBody};
use crate::types::relocation::{Relocation, RelocationKind, RelocationTarget};
use crate::types::section::CustomSection;
use object::{
RelocationEncoding, RelocationFlags, RelocationKind as ObjectRelocationKind, SectionKind,
SymbolFlags, SymbolKind, SymbolScope, elf,
write::{
Object, Relocation as ObjectRelocation, SectionId, StandardSection, StandardSegment,
Symbol, SymbolId, SymbolSection,
},
};
use std::path::PathBuf;
use wasmer_types::{
CompileError, LibCall, LocalFunctionIndex, TrapInformation, entity::PrimaryMap, target::Target,
};
use wasmer_types::{FunctionIndex, FunctionType};
pub enum CompileOutput<T> {
InMemory(T),
Object(Vec<u8>, Option<usize>),
}
impl<T: crate::compiler::CompiledFunction> crate::compiler::CompiledFunction for CompileOutput<T> {}
pub fn compile_output_objects<T>(outputs: Vec<CompileOutput<T>>) -> Vec<Vec<u8>> {
outputs
.into_iter()
.map(|output| match output {
CompileOutput::Object(object, _) => object,
CompileOutput::InMemory(_) => unreachable!(),
})
.collect()
}
pub fn compile_output_in_memory<T>(outputs: Vec<CompileOutput<T>>) -> Vec<T> {
outputs
.into_iter()
.map(|output| match output {
CompileOutput::InMemory(body) => body,
CompileOutput::Object(..) => unreachable!(),
})
.collect()
}
pub fn add_undefined_symbol(object: &mut Object<'static>, name: String) -> SymbolId {
object.add_symbol(Symbol {
name: name.into_bytes(),
value: 0,
size: 0,
kind: SymbolKind::Text,
scope: SymbolScope::Linkage,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
})
}
pub fn add_libcall_symbol(object: &mut Object<'static>, libcall: LibCall) -> SymbolId {
object.add_symbol(Symbol {
name: libcall.to_function_name().to_string().into_bytes(),
value: 0,
size: 0,
kind: SymbolKind::Unknown,
scope: SymbolScope::Dynamic,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
})
}
pub fn relocation_kind_to_flags(kind: RelocationKind) -> Result<RelocationFlags, CompileError> {
use ObjectRelocationKind as K;
Ok(match kind {
RelocationKind::Abs4 => RelocationFlags::Generic {
kind: K::Absolute,
encoding: RelocationEncoding::Generic,
size: 32,
},
RelocationKind::Abs8 => RelocationFlags::Generic {
kind: K::Absolute,
encoding: RelocationEncoding::Generic,
size: 64,
},
RelocationKind::PCRel4 => RelocationFlags::Generic {
kind: K::Relative,
encoding: RelocationEncoding::Generic,
size: 32,
},
RelocationKind::X86CallPCRel4 => RelocationFlags::Generic {
kind: K::Relative,
encoding: RelocationEncoding::X86Branch,
size: 32,
},
RelocationKind::X86CallPLTRel4 => RelocationFlags::Generic {
kind: K::PltRelative,
encoding: RelocationEncoding::X86Branch,
size: 32,
},
RelocationKind::X86GOTPCRel4 => RelocationFlags::Generic {
kind: K::GotRelative,
encoding: RelocationEncoding::Generic,
size: 32,
},
RelocationKind::Arm64Call => RelocationFlags::Elf {
r_type: elf::R_AARCH64_CALL26,
},
RelocationKind::RiscvPCRelHi20 => RelocationFlags::Elf {
r_type: elf::R_RISCV_PCREL_HI20,
},
RelocationKind::RiscvPCRelLo12I => RelocationFlags::Elf {
r_type: elf::R_RISCV_PCREL_LO12_I,
},
RelocationKind::RiscvCall => RelocationFlags::Elf {
r_type: elf::R_RISCV_CALL_PLT,
},
kind => {
return Err(CompileError::Codegen(format!(
"unsupported ELF relocation kind: {kind:?}"
)));
}
})
}
pub fn add_relocations(
object: &mut Object<'static>,
section: SectionId,
relocations: &[Relocation],
local_symbol: Option<(LocalFunctionIndex, SymbolId)>,
) -> Result<(), CompileError> {
for relocation in relocations {
let symbol = match relocation.reloc_target {
RelocationTarget::LocalFunc(index) => local_symbol
.filter(|(local_index, _)| *local_index == index)
.map_or_else(
|| {
add_undefined_symbol(
object,
CompiledKind::Local(index, String::new()).linkage_name(),
)
},
|(_, symbol)| symbol,
),
RelocationTarget::CustomSection(index) => add_undefined_symbol(
object,
CompiledKind::ImportFunctionTrampoline(
FunctionIndex::from_u32(index.as_u32()),
FunctionType::default(),
)
.linkage_name(),
),
RelocationTarget::LibCall(libcall) => add_libcall_symbol(object, libcall),
RelocationTarget::DynamicTrampoline(index) => add_undefined_symbol(
object,
CompiledKind::DynamicFunctionTrampoline(index, FunctionType::default())
.linkage_name(),
),
};
let flags = relocation_kind_to_flags(relocation.kind)?;
object
.add_relocation(
section,
ObjectRelocation {
offset: relocation.offset as u64,
flags,
symbol,
addend: relocation.addend,
},
)
.map_err(|e| CompileError::Codegen(format!("failed to add ELF relocation: {e}")))?;
}
Ok(())
}
pub fn emit_trap_section(
object: &mut Object<'static>,
kind: &CompiledKind,
traps: &[TrapInformation],
) {
let mut trap_data = Vec::with_capacity(traps.len() * 8 + size_of::<u32>());
trap_data.extend_from_slice(&(traps.len() as u32).to_le_bytes());
for trap in traps {
trap_data.extend_from_slice(&trap.code_offset.to_le_bytes());
trap_data.extend_from_slice(&(trap.trap_code as u32).to_le_bytes());
}
let traps_section = object.add_section(
object.segment_name(StandardSegment::Data).to_vec(),
crate::WASMER_TRAPS_SECTION_NAME.to_vec(),
SectionKind::Other,
);
let traps_symbol = object.add_symbol(Symbol {
name: kind.traps_name().into_bytes(),
value: 0,
size: trap_data.len() as u64,
kind: SymbolKind::Data,
scope: SymbolScope::Linkage,
weak: true,
section: SymbolSection::Section(traps_section),
flags: SymbolFlags::None,
});
object.add_symbol_data(traps_symbol, traps_section, &trap_data, 4);
}
pub fn emit_eh_frame_section(
object: &mut Object<'static>,
eh_frame_bytes: &[u8],
relocations: &[EhRelocation],
function_symbol: SymbolId,
lsda_section_symbol: Option<SymbolId>,
) -> Result<(), CompileError> {
let section = object.add_section(
object.segment_name(StandardSegment::Debug).to_vec(),
crate::EH_FRAME_SECTION_NAME.to_vec(),
SectionKind::Other,
);
let data_offset = object.append_section_data(section, eh_frame_bytes, 4);
let mut personality_reference_symbol = None;
for relocation in relocations {
let symbol = match relocation.target {
EhTarget::Function => function_symbol,
EhTarget::Personality => {
if let Some(symbol) = personality_reference_symbol {
symbol
} else {
let personality_symbol = add_libcall_symbol(object, LibCall::EHPersonality);
let personality_section =
object.section_id(StandardSection::ReadOnlyDataWithRel);
let reference_symbol = object.add_symbol(Symbol {
name: b"DW.ref.wasmer_eh_personality".to_vec(),
value: 0,
size: 8,
kind: SymbolKind::Data,
scope: SymbolScope::Compilation,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
let reference_offset =
object.add_symbol_data(reference_symbol, personality_section, &[0; 8], 8);
object
.add_relocation(
personality_section,
ObjectRelocation {
offset: reference_offset,
flags: RelocationFlags::Generic {
kind: ObjectRelocationKind::Absolute,
encoding: RelocationEncoding::Generic,
size: 64,
},
symbol: personality_symbol,
addend: 0,
},
)
.map_err(|e| {
CompileError::Codegen(format!(
"failed to add personality reference relocation: {e}"
))
})?;
personality_reference_symbol = Some(reference_symbol);
reference_symbol
}
}
EhTarget::Lsda => lsda_section_symbol.ok_or_else(|| {
CompileError::Codegen(
".eh_frame references an LSDA but none was emitted".to_string(),
)
})?,
};
object
.add_relocation(
section,
ObjectRelocation {
offset: data_offset + relocation.offset,
flags: RelocationFlags::Generic {
kind: relocation.kind,
encoding: RelocationEncoding::Generic,
size: 8 * relocation.size,
},
symbol,
addend: relocation.addend,
},
)
.map_err(|e| {
CompileError::Codegen(format!("failed to add .eh_frame relocation: {e}"))
})?;
}
Ok(())
}
pub fn emit_function_body(
target: &Target,
kind: &CompiledKind,
body: &FunctionBody,
) -> Result<Vec<u8>, CompileError> {
let mut object = get_object_for_target(target.triple())
.map_err(|e| CompileError::Codegen(format!("cannot create object: {e}")))?;
let symbol = object.add_symbol(Symbol {
name: kind.linkage_name().into_bytes(),
value: 0,
size: body.body.len() as u64,
kind: SymbolKind::Text,
scope: SymbolScope::Linkage,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
let text = object.section_id(StandardSection::Text);
object.add_symbol_data(symbol, text, &body.body, 4);
object
.write()
.map_err(|e| CompileError::Codegen(format!("failed to serialize object: {e}")))
}
pub fn emit_import_trampoline(
target: &Target,
kind: &CompiledKind,
section: &CustomSection,
) -> Result<Vec<u8>, CompileError> {
let mut object = get_object_for_target(target.triple())
.map_err(|e| CompileError::Codegen(format!("cannot create object: {e}")))?;
let symbol = object.add_symbol(Symbol {
name: kind.linkage_name().into_bytes(),
value: 0,
size: section.bytes.len() as u64,
kind: SymbolKind::Text,
scope: SymbolScope::Linkage,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
let text = object.section_id(StandardSection::Text);
object.add_symbol_data(symbol, text, section.bytes.as_slice(), 4);
object
.write()
.map_err(|e| CompileError::Codegen(format!("failed to serialize object: {e}")))
}
#[allow(clippy::too_many_arguments)]
pub fn link_module(
pool: &rayon::ThreadPool,
target: &Target,
compile_info_blob: &[u8],
object_files: Vec<Vec<u8>>,
import_trampoline_objects: Vec<Vec<u8>>,
trampoline_objects: Vec<Vec<u8>>,
dynamic_trampoline_objects: Vec<Vec<u8>>,
debug_dir: Option<PathBuf>,
module_hash: Option<String>,
function_max_stack_usage: PrimaryMap<LocalFunctionIndex, Option<usize>>,
) -> Result<Compilation, CompileError> {
let elf = emit_metadata_and_link(
pool,
target,
compile_info_blob,
CompiledObjects {
object_files,
import_trampoline_object_files: import_trampoline_objects,
trampoline_object_files: trampoline_objects,
dynamic_trampoline_object_files: dynamic_trampoline_objects,
},
debug_dir,
module_hash,
)?;
Ok(Compilation::Elf {
data: elf,
function_max_stack_usage,
})
}