Coverage Report

Created: 2026-08-28 08:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasmtime/crates/environ/src/compile/module_artifacts.rs
Line
Count
Source
1
//! Definitions of runtime structures and metadata which are serialized into ELF
2
//! with `postcard` as part of a module's compilation process.
3
4
use crate::WasmChecksum;
5
use crate::error::{Result, bail};
6
use crate::prelude::*;
7
use crate::{
8
    CompiledModuleInfo, DebugInfoData, FunctionName, Metadata, ModuleTranslation, Tunables, obj,
9
};
10
use object::SectionKind;
11
use object::write::{Object, SectionId, StandardSegment, WritableBuffer};
12
use std::ops::Range;
13
14
/// Helper structure to create an ELF file as a compilation artifact.
15
///
16
/// This structure exposes the process which Wasmtime will encode a core wasm
17
/// module into an ELF file, notably managing data sections and all that good
18
/// business going into the final file.
19
pub struct ObjectBuilder<'a> {
20
    /// The `object`-crate-defined ELF file write we're using.
21
    obj: Object<'a>,
22
23
    /// General compilation configuration.
24
    tunables: &'a Tunables,
25
26
    /// The section identifier for "rodata" which is where wasm data segments
27
    /// will go.
28
    data: SectionId,
29
30
    /// The section identifier for function name information, or otherwise where
31
    /// the `name` custom section of wasm is copied into.
32
    ///
33
    /// This is optional and lazily created on demand.
34
    names: Option<SectionId>,
35
36
    /// The section identifier for dwarf information copied from the original
37
    /// wasm files.
38
    ///
39
    /// This is optional and lazily created on demand.
40
    dwarf: Option<SectionId>,
41
}
42
43
impl<'a> ObjectBuilder<'a> {
44
    /// Creates a new builder for the `obj` specified.
45
99.9k
    pub fn new(mut obj: Object<'a>, tunables: &'a Tunables) -> ObjectBuilder<'a> {
46
99.9k
        let data = obj.add_section(
47
99.9k
            obj.segment_name(StandardSegment::Data).to_vec(),
48
99.9k
            obj::ELF_WASM_DATA.as_bytes().to_vec(),
49
99.9k
            SectionKind::ReadOnlyData,
50
        );
51
99.9k
        ObjectBuilder {
52
99.9k
            obj,
53
99.9k
            tunables,
54
99.9k
            data,
55
99.9k
            names: None,
56
99.9k
            dwarf: None,
57
99.9k
        }
58
99.9k
    }
59
60
    /// Insert the wasm raw wasm-based debuginfo into the output.
61
    /// Note that this is distinct from the native debuginfo
62
    /// possibly generated by the native compiler, hence these sections
63
    /// getting wasm-specific names.
64
0
    pub fn push_debuginfo(
65
0
        &mut self,
66
0
        dwarf: &mut Vec<(u8, Range<u64>)>,
67
0
        debuginfo: &DebugInfoData<'_>,
68
0
    ) {
69
0
        self.push_debug(dwarf, &debuginfo.dwarf.debug_abbrev);
70
0
        self.push_debug(dwarf, &debuginfo.dwarf.debug_addr);
71
0
        self.push_debug(dwarf, &debuginfo.dwarf.debug_aranges);
72
0
        self.push_debug(dwarf, &debuginfo.dwarf.debug_info);
73
0
        self.push_debug(dwarf, &debuginfo.dwarf.debug_line);
74
0
        self.push_debug(dwarf, &debuginfo.dwarf.debug_line_str);
75
0
        self.push_debug(dwarf, &debuginfo.dwarf.debug_str);
76
0
        self.push_debug(dwarf, &debuginfo.dwarf.debug_str_offsets);
77
0
        self.push_debug(dwarf, &debuginfo.debug_ranges);
78
0
        self.push_debug(dwarf, &debuginfo.debug_rnglists);
79
0
        self.push_debug(dwarf, &debuginfo.debug_cu_index);
80
81
        // Sort this for binary-search-lookup later in `symbolize_context`.
82
0
        dwarf.sort_by_key(|(id, _)| *id);
83
0
    }
84
85
    /// Completes compilation of the `translation` specified, inserting
86
    /// everything necessary into the `Object` being built.
87
    ///
88
    /// This function will consume the final results of compiling a wasm module
89
    /// and finish the ELF image in-progress as part of `self.obj` by appending
90
    /// any compiler-agnostic sections.
91
    ///
92
    /// The auxiliary `CompiledModuleInfo` structure returned here has also been
93
    /// serialized into the object returned, but if the caller will quickly
94
    /// turn-around and invoke `CompiledModule::from_artifacts` after this then
95
    /// the information can be passed to that method to avoid extra
96
    /// deserialization. This is done to avoid a serialize-then-deserialize for
97
    /// API calls like `Module::new` where the compiled module is immediately
98
    /// going to be used.
99
    ///
100
    /// The various arguments here are:
101
    ///
102
    /// * `translation` - the core wasm translation that's being completed.
103
    ///
104
    /// * `funcs` - compilation metadata about functions within the translation
105
    ///   as well as where the functions are located in the text section and any
106
    ///   associated trampolines.
107
    ///
108
    /// * `wasm_to_array_trampolines` - list of all trampolines necessary for
109
    ///   Wasm callers calling array callees (e.g. `Func::wrap`). One for each
110
    ///   function signature in the module. Must be sorted by `SignatureIndex`.
111
    ///
112
    /// Returns the `CompiledModuleInfo` corresponding to this core Wasm module
113
    /// as a result of this append operation. This is then serialized into the
114
    /// final artifact by the caller.
115
112k
    pub fn append(&mut self, translation: ModuleTranslation<'_>) -> Result<CompiledModuleInfo> {
116
        let ModuleTranslation {
117
112k
            mut module,
118
112k
            debuginfo,
119
112k
            has_unparsed_debuginfo,
120
112k
            data_align,
121
112k
            runtime_data,
122
112k
            wasm,
123
            ..
124
112k
        } = translation;
125
126
        // Place all data from the wasm module into a section which will the
127
        // source of the data later at runtime. This additionally keeps track of
128
        // the offset of
129
112k
        let data_offset = self
130
112k
            .obj
131
112k
            .append_section_data(self.data, &[], data_align.unwrap_or(1));
132
112k
        for (i, (_, data)) in runtime_data.iter().enumerate() {
133
            // The first data segment has its alignment specified as the
134
            // alignment for the entire section, but everything afterwards is
135
            // adjacent so it has alignment of 1.
136
94.1k
            let align = if i == 0 { data_align.unwrap_or(1) } else { 1 };
137
94.1k
            self.obj.append_section_data(self.data, data, align);
138
        }
139
140
        // If any names are present in the module then the `ELF_NAME_DATA` section
141
        // is create and appended.
142
112k
        let mut func_names = Vec::new();
143
112k
        if debuginfo.name_section.func_names.len() > 0 {
144
8.80k
            let name_id = *self.names.get_or_insert_with(|| {
145
4.83k
                self.obj.add_section(
146
4.83k
                    self.obj.segment_name(StandardSegment::Data).to_vec(),
147
4.83k
                    obj::ELF_NAME_DATA.as_bytes().to_vec(),
148
4.83k
                    SectionKind::ReadOnlyData,
149
                )
150
4.83k
            });
151
8.80k
            let mut sorted_names = debuginfo.name_section.func_names.iter().collect::<Vec<_>>();
152
8.80k
            sorted_names.sort_by_key(|(idx, _name)| *idx);
153
51.4k
            for (idx, name) in sorted_names {
154
51.4k
                let offset = self.obj.append_section_data(name_id, name.as_bytes(), 1);
155
51.4k
                let offset = match u32::try_from(offset) {
156
51.4k
                    Ok(offset) => offset,
157
0
                    Err(_) => bail!("name section too large (> 4gb)"),
158
                };
159
51.4k
                let len = u32::try_from(name.len()).unwrap();
160
51.4k
                func_names.push(FunctionName {
161
51.4k
                    idx: *idx,
162
51.4k
                    offset,
163
51.4k
                    len,
164
51.4k
                });
165
            }
166
103k
        }
167
168
        // Data offsets for passive data are relative to the start of
169
        // `translation.runtime_data` which was appended to the data segment
170
        // of this object, after active data in `translation.data`. Update the
171
        // offsets to account prior modules added in addition to active data.
172
112k
        let data_offset = u32::try_from(data_offset).unwrap();
173
112k
        for (_, range) in module.runtime_data.iter_mut() {
174
94.1k
            range.start = range.start.checked_add(data_offset).unwrap();
175
94.1k
            range.end = range.end.checked_add(data_offset).unwrap();
176
94.1k
        }
177
178
        // Insert the wasm raw wasm-based debuginfo into the output, if
179
        // requested. Note that this is distinct from the native debuginfo
180
        // possibly generated by the native compiler, hence these sections
181
        // getting wasm-specific names.
182
112k
        let mut dwarf = Vec::new();
183
112k
        if self.tunables.parse_wasm_debuginfo {
184
0
            self.push_debuginfo(&mut dwarf, &debuginfo);
185
112k
        }
186
187
112k
        Ok(CompiledModuleInfo {
188
112k
            module,
189
112k
            func_names,
190
112k
            meta: Metadata {
191
112k
                has_unparsed_debuginfo,
192
112k
                code_section_offset: debuginfo.wasm_file.code_section_offset,
193
112k
                has_wasm_debuginfo: self.tunables.parse_wasm_debuginfo,
194
112k
                dwarf,
195
112k
            },
196
112k
            checksum: WasmChecksum::from_binary(wasm, self.tunables.recording),
197
112k
        })
198
112k
    }
199
200
0
    fn push_debug<'b, T>(&mut self, dwarf: &mut Vec<(u8, Range<u64>)>, section: &T)
201
0
    where
202
0
        T: gimli::Section<gimli::EndianSlice<'b, gimli::LittleEndian>>,
203
    {
204
0
        let data = section.reader().slice();
205
0
        if data.is_empty() {
206
0
            return;
207
0
        }
208
0
        let section_id = *self.dwarf.get_or_insert_with(|| {
209
0
            self.obj.add_section(
210
0
                self.obj.segment_name(StandardSegment::Debug).to_vec(),
211
0
                obj::ELF_WASMTIME_DWARF.as_bytes().to_vec(),
212
0
                SectionKind::Debug,
213
            )
214
0
        });
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::str::DebugLineStr<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::{closure#0}
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::str::DebugStrOffsets<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::{closure#0}
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::str::DebugStr<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::{closure#0}
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::addr::DebugAddr<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::{closure#0}
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::line::DebugLine<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::{closure#0}
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::unit::DebugInfo<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::{closure#0}
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::index::DebugCuIndex<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::{closure#0}
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::abbrev::DebugAbbrev<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::{closure#0}
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::aranges::DebugAranges<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::{closure#0}
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::rnglists::DebugRanges<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::{closure#0}
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::rnglists::DebugRngLists<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::{closure#0}
215
0
        let offset = self.obj.append_section_data(section_id, data, 1);
216
0
        dwarf.push((T::id() as u8, offset..offset + data.len() as u64));
217
0
    }
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::str::DebugLineStr<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::str::DebugStrOffsets<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::str::DebugStr<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::addr::DebugAddr<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::line::DebugLine<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::unit::DebugInfo<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::index::DebugCuIndex<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::abbrev::DebugAbbrev<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::aranges::DebugAranges<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::rnglists::DebugRanges<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::push_debug::<gimli::read::rnglists::DebugRngLists<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>
218
219
    /// Appends the original Wasm bytecode for one or more core modules as a
220
    /// pair of new ELF sections.
221
    ///
222
    /// `modules` is an iterator of raw Wasm binary slices, one per core
223
    /// module, in `StaticModuleIndex` order.
224
0
    pub fn append_wasm_bytecode<'b>(&mut self, modules: impl IntoIterator<Item = &'b [u8]>) {
225
0
        let bytecode_id = self.obj.add_section(
226
0
            self.obj.segment_name(StandardSegment::Data).to_vec(),
227
0
            obj::ELF_WASMTIME_WASM_BYTECODE.as_bytes().to_vec(),
228
0
            SectionKind::ReadOnlyData,
229
        );
230
0
        let ends_id = self.obj.add_section(
231
0
            self.obj.segment_name(StandardSegment::Data).to_vec(),
232
0
            obj::ELF_WASMTIME_WASM_BYTECODE_ENDS.as_bytes().to_vec(),
233
0
            SectionKind::ReadOnlyData,
234
        );
235
0
        let mut end: u32 = 0;
236
0
        for wasm in modules {
237
0
            self.obj.append_section_data(bytecode_id, wasm, 1);
238
0
            end = end
239
0
                .checked_add(u32::try_from(wasm.len()).expect("module bytecode exceeds 4 GiB"))
240
0
                .expect("total bytecode exceeds 4 GiB");
241
0
            self.obj.append_section_data(ends_id, &end.to_le_bytes(), 4);
242
0
        }
243
0
    }
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::append_wasm_bytecode::<alloc::vec::Vec<&[u8]>>
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::append_wasm_bytecode::<core::iter::sources::once::Once<&[u8]>>
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::append_wasm_bytecode::<_>
244
245
    /// Creates the `ELF_WASMTIME_INFO` section from the given serializable data
246
    /// structure.
247
99.9k
    pub fn serialize_info<T>(&mut self, info: &T)
248
99.9k
    where
249
99.9k
        T: serde::Serialize,
250
    {
251
99.9k
        let section = self.obj.add_section(
252
99.9k
            self.obj.segment_name(StandardSegment::Data).to_vec(),
253
99.9k
            obj::ELF_WASMTIME_INFO.as_bytes().to_vec(),
254
99.9k
            SectionKind::ReadOnlyData,
255
        );
256
99.9k
        let data = postcard::to_allocvec(info).unwrap();
257
99.9k
        self.obj.set_section_data(section, data, 1);
258
99.9k
    }
<wasmtime_environ::compile::module_artifacts::ObjectBuilder>::serialize_info::<wasmtime_environ::component::artifacts::ComponentArtifacts>
Line
Count
Source
247
4.58k
    pub fn serialize_info<T>(&mut self, info: &T)
248
4.58k
    where
249
4.58k
        T: serde::Serialize,
250
    {
251
4.58k
        let section = self.obj.add_section(
252
4.58k
            self.obj.segment_name(StandardSegment::Data).to_vec(),
253
4.58k
            obj::ELF_WASMTIME_INFO.as_bytes().to_vec(),
254
4.58k
            SectionKind::ReadOnlyData,
255
        );
256
4.58k
        let data = postcard::to_allocvec(info).unwrap();
257
4.58k
        self.obj.set_section_data(section, data, 1);
258
4.58k
    }
<wasmtime_environ::compile::module_artifacts::ObjectBuilder>::serialize_info::<(&wasmtime_environ::module_artifacts::CompiledModuleInfo, &wasmtime_environ::module_artifacts::CompiledFunctionsTable, &wasmtime_environ::module_types::ModuleTypes)>
Line
Count
Source
247
95.3k
    pub fn serialize_info<T>(&mut self, info: &T)
248
95.3k
    where
249
95.3k
        T: serde::Serialize,
250
    {
251
95.3k
        let section = self.obj.add_section(
252
95.3k
            self.obj.segment_name(StandardSegment::Data).to_vec(),
253
95.3k
            obj::ELF_WASMTIME_INFO.as_bytes().to_vec(),
254
95.3k
            SectionKind::ReadOnlyData,
255
        );
256
95.3k
        let data = postcard::to_allocvec(info).unwrap();
257
95.3k
        self.obj.set_section_data(section, data, 1);
258
95.3k
    }
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::serialize_info::<_>
259
260
    /// Serializes `self` into a buffer. This can be used for execution as well
261
    /// as serialization.
262
99.9k
    pub fn finish<T: WritableBuffer>(self, t: &mut T) -> Result<()> {
263
99.9k
        self.obj.emit(t).map_err(|e| e.into())
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::finish::<<wasmtime::compile::runtime::MmapVecWrapper as wasmtime_environ::compile::module_artifacts::FinishedObject>::finish_object::ObjectMmap>::{closure#0}
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::finish::<<alloc::vec::Vec<u8> as wasmtime_environ::compile::module_artifacts::FinishedObject>::finish_object::ObjectVec>::{closure#0}
264
99.9k
    }
<wasmtime_environ::compile::module_artifacts::ObjectBuilder>::finish::<<wasmtime::compile::runtime::MmapVecWrapper as wasmtime_environ::compile::module_artifacts::FinishedObject>::finish_object::ObjectMmap>
Line
Count
Source
262
99.9k
    pub fn finish<T: WritableBuffer>(self, t: &mut T) -> Result<()> {
263
99.9k
        self.obj.emit(t).map_err(|e| e.into())
264
99.9k
    }
Unexecuted instantiation: <wasmtime_environ::compile::module_artifacts::ObjectBuilder>::finish::<<alloc::vec::Vec<u8> as wasmtime_environ::compile::module_artifacts::FinishedObject>::finish_object::ObjectVec>
265
}
266
267
/// A type which can be the result of serializing an object.
268
pub trait FinishedObject: Sized {
269
    /// State required for `finish_object`, if any.
270
    type State;
271
272
    /// Emit the object as `Self`.
273
    fn finish_object(obj: ObjectBuilder<'_>, state: &Self::State) -> Result<Self>;
274
}
275
276
impl FinishedObject for Vec<u8> {
277
    type State = ();
278
0
    fn finish_object(obj: ObjectBuilder<'_>, _state: &Self::State) -> Result<Self> {
279
0
        let mut result = ObjectVec::default();
280
0
        obj.finish(&mut result)?;
281
0
        return Ok(result.0);
282
283
        #[derive(Default)]
284
        struct ObjectVec(Vec<u8>);
285
286
        impl WritableBuffer for ObjectVec {
287
0
            fn reserve(&mut self, additional: u64) -> Result<(), ()> {
288
0
                assert_eq!(self.0.len(), 0, "cannot reserve twice");
289
0
                self.0 = Vec::with_capacity(additional as usize);
290
0
                Ok(())
291
0
            }
292
293
0
            fn write_zeros(&mut self, additional: u64) {
294
0
                self.0.extend(vec![0; additional as usize])
295
0
            }
296
297
0
            fn write_bytes(&mut self, val: &[u8]) {
298
0
                self.0.extend(val);
299
0
            }
300
        }
301
0
    }
302
}