//! Object file builder.1//!2//! Creates ELF image based on `Compilation` information. The ELF contains3//! functions and trampolines in the ".text" section. It also contains all4//! relocation records for the linking stage. If DWARF sections exist, their5//! content will be written as well.6//!7//! The object file has symbols for each function and trampoline, as well as8//! symbols that refer to libcalls.9//!10//! The function symbol names have format "_wasm_function_N", where N is11//! `FuncIndex`. The defined wasm function symbols refer to a JIT compiled12//! function body, the imported wasm function do not. The trampolines symbol13//! names have format "_trampoline_N", where N is `SignatureIndex`.1415use crate::CompiledFunction;16use anyhow::Result;17use cranelift_codegen::TextSectionBuilder;18use cranelift_codegen::isa::unwind::{UnwindInfo, systemv};19use cranelift_control::ControlPlane;20use gimli::RunTimeEndian;21use gimli::write::{Address, EhFrame, EndianVec, FrameTable, Writer};22use object::write::{Object, SectionId, StandardSegment, Symbol, SymbolId, SymbolSection};23use object::{Architecture, SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope};24use std::ops::Range;25use wasmtime_environ::{Compiler, TripleExt};26use wasmtime_environ::{FuncKey, obj};2728const TEXT_SECTION_NAME: &[u8] = b".text";2930fn text_align(compiler: &dyn Compiler) -> u64 {31// text pages will not be made executable with pulley, so the section32// doesn't need to be padded out to page alignment boundaries.33if compiler.triple().is_pulley() {340x135} else {36compiler.page_size_align()37}38}3940/// A helper structure used to assemble the final text section of an executable,41/// plus unwinding information and other related details.42///43/// This builder relies on Cranelift-specific internals but assembles into a44/// generic `Object` which will get further appended to in a compiler-agnostic45/// fashion later.46pub struct ModuleTextBuilder<'a> {47/// The target that we're compiling for, used to query target-specific48/// information as necessary.49compiler: &'a dyn Compiler,5051/// The object file that we're generating code into.52obj: &'a mut Object<'static>,5354/// The WebAssembly module we're generating code for.55text_section: SectionId,5657unwind_info: UnwindInfoBuilder<'a>,5859/// In-progress text section that we're using cranelift's `MachBuffer` to60/// build to resolve relocations (calls) between functions.61text: Box<dyn TextSectionBuilder>,6263ctrl_plane: ControlPlane,64}6566impl<'a> ModuleTextBuilder<'a> {67/// Creates a new builder for the text section of an executable.68///69/// The `.text` section will be appended to the specified `obj` along with70/// any unwinding or such information as necessary. The `num_funcs`71/// parameter indicates the number of times the `append_func` function will72/// be called. The `finish` function will panic if this contract is not met.73pub fn new(74obj: &'a mut Object<'static>,75compiler: &'a dyn Compiler,76text: Box<dyn TextSectionBuilder>,77) -> Self {78// Entire code (functions and trampolines) will be placed79// in the ".text" section.80let text_section = obj.add_section(81obj.segment_name(StandardSegment::Text).to_vec(),82TEXT_SECTION_NAME.to_vec(),83SectionKind::Text,84);8586// If this target is Pulley then flag the text section as not needing the87// executable bit in virtual memory which means that the runtime won't88// try to call `Mmap::make_executable`, which makes Pulley more89// portable.90if compiler.triple().is_pulley() {91let section = obj.section_mut(text_section);92assert!(matches!(section.flags, SectionFlags::None));93section.flags = SectionFlags::Elf {94sh_flags: obj::SH_WASMTIME_NOT_EXECUTED,95};96}9798Self {99compiler,100obj,101text_section,102unwind_info: Default::default(),103text,104ctrl_plane: ControlPlane::default(),105}106}107108/// Appends the `func` specified named `name` to this object.109///110/// The `resolve_reloc_target` closure is used to resolve a relocation111/// target to an adjacent function which has already been added or will be112/// added to this object. The argument is the relocation target specified113/// within `CompiledFunction` and the return value must be an index where114/// the target will be defined by the `n`th call to `append_func`.115///116/// Returns the symbol associated with the function as well as the range117/// that the function resides within the text section.118pub fn append_func(119&mut self,120name: &str,121compiled_func: &'a CompiledFunction,122resolve_reloc_target: impl Fn(wasmtime_environ::FuncKey) -> usize,123) -> (SymbolId, Range<u64>) {124let body = compiled_func.buffer.data();125let alignment = compiled_func.alignment;126let body_len = body.len() as u64;127let off = self128.text129.append(true, &body, alignment, &mut self.ctrl_plane);130131let symbol_id = self.obj.add_symbol(Symbol {132name: name.as_bytes().to_vec(),133value: off,134size: body_len,135kind: SymbolKind::Text,136scope: SymbolScope::Compilation,137weak: false,138section: SymbolSection::Section(self.text_section),139flags: SymbolFlags::None,140});141142if let Some(info) = compiled_func.unwind_info() {143self.unwind_info.push(off, body_len, info);144}145146for r in compiled_func.relocations() {147let reloc_offset = off + u64::from(r.offset);148149// This relocation is used to fill in which hostcall id is150// desired within the `call_indirect_host` opcode of Pulley151// itself. The relocation target is the start of the instruction152// and the goal is to insert the static signature number, `n`,153// into the instruction.154//155// At this time the instruction looks like:156//157// +------+------+------+------+158// | OP | OP_EXTENDED | N |159// +------+------+------+------+160//161// This 4-byte encoding has `OP` indicating this is an "extended162// opcode" where `OP_EXTENDED` is a 16-bit extended opcode.163// The `N` byte is the index of the signature being called and164// is what's b eing filled in.165//166// See the `test_call_indirect_host_width` in167// `pulley/tests/all.rs` for this guarantee as well.168if let FuncKey::PulleyHostCall(host_call) = r.reloc_target {169#[cfg(feature = "pulley")]170{171use pulley_interpreter::encode::Encode;172assert_eq!(pulley_interpreter::CallIndirectHost::WIDTH, 4);173}174let n = host_call.index();175let byte = u8::try_from(n).unwrap();176self.text.write(reloc_offset + 3, &[byte]);177continue;178}179180let target = resolve_reloc_target(r.reloc_target);181if self182.text183.resolve_reloc(reloc_offset, r.reloc, r.addend, target)184{185continue;186}187188panic!("failed to resolve relocation: {r:?} -> {target}");189}190191(symbol_id, off..off + body_len)192}193194/// Forces "veneers" to be used for inter-function calls in the text195/// section which means that in-bounds optimized addresses are never used.196///197/// This is only useful for debugging cranelift itself and typically this198/// option is disabled.199pub fn force_veneers(&mut self) {200self.text.force_veneers();201}202203/// Appends the specified amount of bytes of padding into the text section.204///205/// This is only useful when fuzzing and/or debugging cranelift itself and206/// for production scenarios `padding` is 0 and this function does nothing.207pub fn append_padding(&mut self, padding: usize) {208if padding == 0 {209return;210}211self.text212.append(false, &vec![0; padding], 1, &mut self.ctrl_plane);213}214215/// Indicates that the text section has been written completely and this216/// will finish appending it to the original object.217///218/// Note that this will also write out the unwind information sections if219/// necessary.220pub fn finish(mut self) {221// Finish up the text section now that we're done adding functions.222let text = self.text.finish(&mut self.ctrl_plane);223self.obj224.section_mut(self.text_section)225.set_data(text, text_align(self.compiler));226227// Append the unwind information for all our functions, if necessary.228self.unwind_info229.append_section(self.compiler, self.obj, self.text_section);230}231}232233/// Builder used to create unwind information for a set of functions added to a234/// text section.235#[derive(Default)]236struct UnwindInfoBuilder<'a> {237windows_xdata: Vec<u8>,238windows_pdata: Vec<RUNTIME_FUNCTION>,239systemv_unwind_info: Vec<(u64, &'a systemv::UnwindInfo)>,240}241242// This is a mirror of `RUNTIME_FUNCTION` in the Windows API, but defined here243// to ensure everything is always `u32` and to have it available on all244// platforms. Note that all of these specifiers here are relative to a "base245// address" which we define as the base of where the text section is eventually246// loaded.247#[expect(non_camel_case_types, reason = "matching Windows style, not Rust")]248struct RUNTIME_FUNCTION {249begin: u32,250end: u32,251unwind_address: u32,252}253254impl<'a> UnwindInfoBuilder<'a> {255/// Pushes the unwind information for a function into this builder.256///257/// The function being described must be located at `function_offset` within258/// the text section itself, and the function's size is specified by259/// `function_len`.260///261/// The `info` should come from Cranelift. and is handled here depending on262/// its flavor.263fn push(&mut self, function_offset: u64, function_len: u64, info: &'a UnwindInfo) {264match info {265// Windows unwind information is stored in two locations:266//267// * First is the actual unwinding information which is stored268// in the `.xdata` section. This is where `info`'s emitted269// information will go into.270// * Second are pointers to connect all this unwind information,271// stored in the `.pdata` section. The `.pdata` section is an272// array of `RUNTIME_FUNCTION` structures.273//274// Due to how these will be loaded at runtime the `.pdata` isn't275// actually assembled byte-wise here. Instead that's deferred to276// happen later during `write_windows_unwind_info` which will apply277// a further offset to `unwind_address`.278//279// FIXME: in theory we could "intern" the `unwind_info` value280// here within the `.xdata` section. Most of our unwind281// information for functions is probably pretty similar in which282// case the `.xdata` could be quite small and `.pdata` could283// have multiple functions point to the same unwinding284// information.285UnwindInfo::WindowsX64(info) => {286let unwind_size = info.emit_size();287let mut unwind_info = vec![0; unwind_size];288info.emit(&mut unwind_info);289290// `.xdata` entries are always 4-byte aligned291while self.windows_xdata.len() % 4 != 0 {292self.windows_xdata.push(0x00);293}294let unwind_address = self.windows_xdata.len();295self.windows_xdata.extend_from_slice(&unwind_info);296297// Record a `RUNTIME_FUNCTION` which this will point to.298self.windows_pdata.push(RUNTIME_FUNCTION {299begin: u32::try_from(function_offset).unwrap(),300end: u32::try_from(function_offset + function_len).unwrap(),301unwind_address: u32::try_from(unwind_address).unwrap(),302});303}304305// See https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling306UnwindInfo::WindowsArm64(info) => {307let code_words = info.code_words();308let mut unwind_codes = vec![0; (code_words * 4) as usize];309info.emit(&mut unwind_codes);310311// `.xdata` entries are always 4-byte aligned312while self.windows_xdata.len() % 4 != 0 {313self.windows_xdata.push(0x00);314}315316// First word:317// 0-17: Function Length318// 18-19: Version (must be 0)319// 20: X bit (is exception data present?)320// 21: E bit (has single packed epilogue?)321// 22-26: Epilogue count322// 27-31: Code words count323let requires_extended_counts = code_words > (1 << 5);324let encoded_function_len = function_len / 4;325assert!(encoded_function_len < (1 << 18), "function too large");326let mut word1 = u32::try_from(encoded_function_len).unwrap();327if !requires_extended_counts {328word1 |= u32::from(code_words) << 27;329}330let unwind_address = self.windows_xdata.len();331self.windows_xdata.extend_from_slice(&word1.to_le_bytes());332333if requires_extended_counts {334// Extended counts word:335// 0-15: Epilogue count336// 16-23: Code words count337let extended_counts_word = (code_words as u32) << 16;338self.windows_xdata339.extend_from_slice(&extended_counts_word.to_le_bytes());340}341342// Skip epilogue information: Per comment on [`UnwindInst`], we343// do not emit information about epilogues.344345// Emit the unwind codes.346self.windows_xdata.extend_from_slice(&unwind_codes);347348// Record a `RUNTIME_FUNCTION` which this will point to.349// NOTE: `end` is not used, so leave it as 0.350self.windows_pdata.push(RUNTIME_FUNCTION {351begin: u32::try_from(function_offset).unwrap(),352end: 0,353unwind_address: u32::try_from(unwind_address).unwrap(),354});355}356357// System-V is different enough that we just record the unwinding358// information to get processed at a later time.359UnwindInfo::SystemV(info) => {360self.systemv_unwind_info.push((function_offset, info));361}362363_ => panic!("some unwind info isn't handled here"),364}365}366367/// Appends the unwind information section, if any, to the `obj` specified.368///369/// This function must be called immediately after the text section was370/// added to a builder. The unwind information section must trail the text371/// section immediately.372///373/// The `text_section`'s section identifier is passed into this function.374fn append_section(375&self,376compiler: &dyn Compiler,377obj: &mut Object<'_>,378text_section: SectionId,379) {380// This write will align the text section to a page boundary and then381// return the offset at that point. This gives us the full size of the382// text section at that point, after alignment.383let text_section_size = obj.append_section_data(text_section, &[], text_align(compiler));384385if self.windows_xdata.len() > 0 {386assert!(self.systemv_unwind_info.len() == 0);387// The `.xdata` section must come first to be just-after the `.text`388// section for the reasons documented in `write_windows_unwind_info`389// below.390let segment = obj.segment_name(StandardSegment::Data).to_vec();391let xdata_id = obj.add_section(segment, b".xdata".to_vec(), SectionKind::ReadOnlyData);392let segment = obj.segment_name(StandardSegment::Data).to_vec();393let pdata_id = obj.add_section(segment, b".pdata".to_vec(), SectionKind::ReadOnlyData);394self.write_windows_unwind_info(obj, xdata_id, pdata_id, text_section_size);395}396397if self.systemv_unwind_info.len() > 0 {398let segment = obj.segment_name(StandardSegment::Data).to_vec();399let section_id =400obj.add_section(segment, b".eh_frame".to_vec(), SectionKind::ReadOnlyData);401self.write_systemv_unwind_info(compiler, obj, section_id, text_section_size)402}403}404405/// This function appends a nonstandard section to the object which is only406/// used during `CodeMemory::publish`.407///408/// This custom section effectively stores a `[RUNTIME_FUNCTION; N]` into409/// the object file itself. This way registration of unwind info can simply410/// pass this slice to the OS itself and there's no need to recalculate411/// anything on the other end of loading a module from a precompiled object.412///413/// Support for reading this is in `crates/jit/src/unwind/winx64.rs`.414fn write_windows_unwind_info(415&self,416obj: &mut Object<'_>,417xdata_id: SectionId,418pdata_id: SectionId,419text_section_size: u64,420) {421// Append the `.xdata` section, or the actual unwinding information422// codes and such which were built as we found unwind information for423// functions.424obj.append_section_data(xdata_id, &self.windows_xdata, 4);425426// Next append the `.pdata` section, or the array of `RUNTIME_FUNCTION`427// structures stored in the binary.428//429// This memory will be passed at runtime to `RtlAddFunctionTable` which430// takes a "base address" and the entries within `RUNTIME_FUNCTION` are431// all relative to this base address. The base address we pass is the432// address of the text section itself so all the pointers here must be433// text-section-relative. The `begin` and `end` fields for the function434// it describes are already text-section-relative, but the435// `unwind_address` field needs to be updated here since the value436// stored right now is `xdata`-section-relative. We know that the437// `xdata` section follows the `.text` section so the438// `text_section_size` is added in to calculate the final439// `.text`-section-relative address of the unwind information.440let xdata_rva = |address| {441let address = u64::from(address);442let address = address + text_section_size;443u32::try_from(address).unwrap()444};445let pdata = match obj.architecture() {446Architecture::X86_64 => {447let mut pdata = Vec::with_capacity(self.windows_pdata.len() * 3 * 4);448for info in self.windows_pdata.iter() {449pdata.extend_from_slice(&info.begin.to_le_bytes());450pdata.extend_from_slice(&info.end.to_le_bytes());451pdata.extend_from_slice(&xdata_rva(info.unwind_address).to_le_bytes());452}453pdata454}455456Architecture::Aarch64 => {457// Windows Arm64 .pdata also supports packed unwind data, but458// we're not currently using that.459let mut pdata = Vec::with_capacity(self.windows_pdata.len() * 2 * 4);460for info in self.windows_pdata.iter() {461pdata.extend_from_slice(&info.begin.to_le_bytes());462pdata.extend_from_slice(&xdata_rva(info.unwind_address).to_le_bytes());463}464pdata465}466467_ => unimplemented!("unsupported architecture for windows unwind info"),468};469obj.append_section_data(pdata_id, &pdata, 4);470}471472/// This function appends a nonstandard section to the object which is only473/// used during `CodeMemory::publish`.474///475/// This will generate a `.eh_frame` section, but not one that can be476/// naively loaded. The goal of this section is that we can create the477/// section once here and never again does it need to change. To describe478/// dynamically loaded functions though each individual FDE needs to talk479/// about the function's absolute address that it's referencing. Naturally480/// we don't actually know the function's absolute address when we're481/// creating an object here.482///483/// To solve this problem the FDE address encoding mode is set to484/// `DW_EH_PE_pcrel`. This means that the actual effective address that the485/// FDE describes is a relative to the address of the FDE itself. By486/// leveraging this relative-ness we can assume that the relative distance487/// between the FDE and the function it describes is constant, which should488/// allow us to generate an FDE ahead-of-time here.489///490/// For now this assumes that all the code of functions will start at a491/// page-aligned address when loaded into memory. The eh_frame encoded here492/// then assumes that the text section is itself page aligned to its size493/// and the eh_frame will follow just after the text section. This means494/// that the relative offsets we're using here is the FDE going backwards495/// into the text section itself.496///497/// Note that the library we're using to create the FDEs, `gimli`, doesn't498/// actually encode addresses relative to the FDE itself. Instead the499/// addresses are encoded relative to the start of the `.eh_frame` section.500/// This makes it much easier for us where we provide the relative offset501/// from the start of `.eh_frame` to the function in the text section, which502/// given our layout basically means the offset of the function in the text503/// section from the end of the text section.504///505/// A final note is that the reason we page-align the text section's size is506/// so the .eh_frame lives on a separate page from the text section itself.507/// This allows `.eh_frame` to have different virtual memory permissions,508/// such as being purely read-only instead of read/execute like the code509/// bits.510fn write_systemv_unwind_info(511&self,512compiler: &dyn Compiler,513obj: &mut Object<'_>,514section_id: SectionId,515text_section_size: u64,516) {517let mut cie = match compiler.create_systemv_cie() {518Some(cie) => cie,519None => return,520};521let mut table = FrameTable::default();522cie.fde_address_encoding = gimli::constants::DW_EH_PE_pcrel;523let cie_id = table.add_cie(cie);524525for (text_section_off, unwind_info) in self.systemv_unwind_info.iter() {526let backwards_off = text_section_size - text_section_off;527let actual_offset = -i64::try_from(backwards_off).unwrap();528// Note that gimli wants an unsigned 64-bit integer here, but529// unwinders just use this constant for a relative addition with the530// address of the FDE, which means that the sign doesn't actually531// matter.532let fde = unwind_info.to_fde(Address::Constant(actual_offset.cast_unsigned()));533table.add_fde(cie_id, fde);534}535let endian = match compiler.triple().endianness().unwrap() {536target_lexicon::Endianness::Little => RunTimeEndian::Little,537target_lexicon::Endianness::Big => RunTimeEndian::Big,538};539let mut eh_frame = EhFrame(MyVec(EndianVec::new(endian)));540table.write_eh_frame(&mut eh_frame).unwrap();541542// Some unwinding implementations expect a terminating "empty" length so543// a 0 is written at the end of the table for those implementations.544let mut endian_vec = (eh_frame.0).0;545endian_vec.write_u32(0).unwrap();546obj.append_section_data(section_id, endian_vec.slice(), 1);547548use gimli::constants;549use gimli::write::Error;550551struct MyVec(EndianVec<RunTimeEndian>);552553impl Writer for MyVec {554type Endian = RunTimeEndian;555556fn endian(&self) -> RunTimeEndian {557self.0.endian()558}559560fn len(&self) -> usize {561self.0.len()562}563564fn write(&mut self, buf: &[u8]) -> Result<(), Error> {565self.0.write(buf)566}567568fn write_at(&mut self, pos: usize, buf: &[u8]) -> Result<(), Error> {569self.0.write_at(pos, buf)570}571572// FIXME(gimli-rs/gimli#576) this is the definition we want for573// `write_eh_pointer` but the default implementation, at the time574// of this writing, uses `offset - val` instead of `val - offset`.575// A PR has been merged to fix this but until that's published we576// can't use it.577fn write_eh_pointer(578&mut self,579address: Address,580eh_pe: constants::DwEhPe,581size: u8,582) -> Result<(), Error> {583let val = match address {584Address::Constant(val) => val,585Address::Symbol { .. } => unreachable!(),586};587assert_eq!(eh_pe.application(), constants::DW_EH_PE_pcrel);588let offset = self.len() as u64;589let val = val.wrapping_sub(offset);590self.write_eh_pointer_data(val, eh_pe.format(), size)591}592}593}594}595596597