Skip to content

Commit f1225df

Browse files
Add a compilation section to disable address maps (#3598)
* Add a compilation section to disable address maps This commit adds a new `Config::generate_address_map` compilation setting which is used to disable emission of the `.wasmtime.addrmap` section of compiled artifacts. This section is currently around the size of the entire `.text` section itself unfortunately and for size reasons may wish to be omitted. Functionality-wise all that is lost is knowing the precise wasm module offset address of a faulting instruction or in a backtrace of instructions. This also means that if the module has DWARF debugging information available with it Wasmtime isn't able to produce a filename and line number in the backtrace. This option remains enabled by default. This option may not be needed in the future with #3547 perhaps, but in the meantime it seems reasonable enough to support a configuration mode where the section is entirely omitted if the smallest module possible is desired. * Fix some CI issues * Update tests/all/traps.rs Co-authored-by: Nick Fitzgerald <fitzgen@gmail.com> * Do less work in compilation for address maps But only when disabled Co-authored-by: Nick Fitzgerald <fitzgen@gmail.com>
1 parent c1c4c59 commit f1225df

12 files changed

Lines changed: 147 additions & 49 deletions

File tree

crates/c-api/src/trap.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,9 @@ pub extern "C" fn wasmtime_frame_module_name(frame: &wasm_frame_t) -> Option<&wa
157157

158158
#[no_mangle]
159159
pub extern "C" fn wasm_frame_func_offset(frame: &wasm_frame_t) -> usize {
160-
frame.trap.trace()[frame.idx].func_offset()
160+
frame.trap.trace()[frame.idx]
161+
.func_offset()
162+
.unwrap_or(usize::MAX)
161163
}
162164

163165
#[no_mangle]
@@ -167,7 +169,9 @@ pub extern "C" fn wasm_frame_instance(_arg1: *const wasm_frame_t) -> *mut wasm_i
167169

168170
#[no_mangle]
169171
pub extern "C" fn wasm_frame_module_offset(frame: &wasm_frame_t) -> usize {
170-
frame.trap.trace()[frame.idx].module_offset()
172+
frame.trap.trace()[frame.idx]
173+
.module_offset()
174+
.unwrap_or(usize::MAX)
171175
}
172176

173177
#[no_mangle]

crates/cranelift/src/compiler.rs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ impl Compiler {
6363
context: &Context,
6464
data: &FunctionBodyData<'_>,
6565
body_len: u32,
66+
tunables: &Tunables,
6667
) -> FunctionAddressMap {
6768
// Generate artificial srcloc for function start/end to identify boundary
6869
// within module.
@@ -75,17 +76,21 @@ impl Compiler {
7576

7677
// New-style backend: we have a `MachCompileResult` that will give us `MachSrcLoc` mapping
7778
// tuples.
78-
let instructions = collect_address_maps(
79-
body_len,
80-
context
81-
.mach_compile_result
82-
.as_ref()
83-
.unwrap()
84-
.buffer
85-
.get_srclocs_sorted()
86-
.into_iter()
87-
.map(|&MachSrcLoc { start, end, loc }| (loc, start, (end - start))),
88-
);
79+
let instructions = if tunables.generate_address_map {
80+
collect_address_maps(
81+
body_len,
82+
context
83+
.mach_compile_result
84+
.as_ref()
85+
.unwrap()
86+
.buffer
87+
.get_srclocs_sorted()
88+
.into_iter()
89+
.map(|&MachSrcLoc { start, end, loc }| (loc, start, (end - start))),
90+
)
91+
} else {
92+
Vec::new()
93+
};
8994

9095
FunctionAddressMap {
9196
instructions: instructions.into(),
@@ -179,7 +184,7 @@ impl wasmtime_environ::Compiler for Compiler {
179184
.map_err(|error| CompileError::Codegen(pretty_error(&context.func, error)))?;
180185

181186
let address_transform =
182-
self.get_function_address_map(&context, &input, code_buf.len() as u32);
187+
self.get_function_address_map(&context, &input, code_buf.len() as u32, tunables);
183188

184189
let ranges = if tunables.generate_native_debuginfo {
185190
Some(
@@ -221,7 +226,7 @@ impl wasmtime_environ::Compiler for Compiler {
221226
translation: &ModuleTranslation,
222227
types: &TypeTables,
223228
funcs: PrimaryMap<DefinedFuncIndex, Box<dyn Any + Send>>,
224-
emit_dwarf: bool,
229+
tunables: &Tunables,
225230
obj: &mut Object<'static>,
226231
) -> Result<(PrimaryMap<DefinedFuncIndex, FunctionInfo>, Vec<Trampoline>)> {
227232
let funcs: crate::CompiledFunctions = funcs
@@ -244,7 +249,9 @@ impl wasmtime_environ::Compiler for Compiler {
244249
let mut func_starts = Vec::with_capacity(funcs.len());
245250
for (i, func) in funcs.iter() {
246251
let range = builder.func(i, func);
247-
addrs.push(range.clone(), &func.address_map.instructions);
252+
if tunables.generate_address_map {
253+
addrs.push(range.clone(), &func.address_map.instructions);
254+
}
248255
traps.push(range.clone(), &func.traps);
249256
func_starts.push(range.start);
250257
if self.linkopts.padding_between_functions > 0 {
@@ -266,7 +273,7 @@ impl wasmtime_environ::Compiler for Compiler {
266273

267274
builder.unwind_info();
268275

269-
if emit_dwarf && funcs.len() > 0 {
276+
if tunables.generate_native_debuginfo && funcs.len() > 0 {
270277
let ofs = VMOffsets::new(
271278
self.isa
272279
.triple()
@@ -297,7 +304,10 @@ impl wasmtime_environ::Compiler for Compiler {
297304
}
298305

299306
builder.finish()?;
300-
addrs.append_to(obj);
307+
308+
if tunables.generate_address_map {
309+
addrs.append_to(obj);
310+
}
301311
traps.append_to(obj);
302312

303313
Ok((

crates/environ/src/compilation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ pub trait Compiler: Send + Sync {
171171
module: &ModuleTranslation,
172172
types: &TypeTables,
173173
funcs: PrimaryMap<DefinedFuncIndex, Box<dyn Any + Send>>,
174-
emit_dwarf: bool,
174+
tunables: &Tunables,
175175
obj: &mut Object<'static>,
176176
) -> Result<(PrimaryMap<DefinedFuncIndex, FunctionInfo>, Vec<Trampoline>)>;
177177

crates/environ/src/tunables.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ pub struct Tunables {
4242
/// Whether or not linear memory allocations will have a guard region at the
4343
/// beginning of the allocation in addition to the end.
4444
pub guard_before_linear_memory: bool,
45+
46+
/// Indicates whether an address map from compiled native code back to wasm
47+
/// offsets in the original file is generated.
48+
pub generate_address_map: bool,
4549
}
4650

4751
impl Default for Tunables {
@@ -86,6 +90,7 @@ impl Default for Tunables {
8690
consume_fuel: false,
8791
static_memory_bound_is_maximum: false,
8892
guard_before_linear_memory: true,
93+
generate_address_map: true,
8994
}
9095
}
9196
}

crates/jit/src/instantiate.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,16 +297,21 @@ impl CompiledModule {
297297
};
298298

299299
let mut ret = Self {
300-
meta: info.meta,
301300
module: Arc::new(info.module),
302301
funcs: info.funcs,
303302
trampolines: info.trampolines,
304303
wasm_data: subslice_range(section(ELF_WASM_DATA)?, code.mmap),
305-
address_map_data: subslice_range(section(ELF_WASMTIME_ADDRMAP)?, code.mmap),
304+
address_map_data: code
305+
.obj
306+
.section_by_name(ELF_WASMTIME_ADDRMAP)
307+
.and_then(|s| s.data().ok())
308+
.map(|slice| subslice_range(slice, code.mmap))
309+
.unwrap_or(0..0),
306310
trap_data: subslice_range(section(ELF_WASMTIME_TRAPS)?, code.mmap),
307311
code: subslice_range(code.text, code.mmap),
308312
dbg_jit_registration: None,
309313
code_memory,
314+
meta: info.meta,
310315
};
311316
ret.register_debug_and_profiling(profiler)?;
312317

@@ -500,6 +505,15 @@ impl CompiledModule {
500505
pub fn has_unparsed_debuginfo(&self) -> bool {
501506
self.meta.has_unparsed_debuginfo
502507
}
508+
509+
/// Indicates whether this module came with n address map such that lookups
510+
/// via `wasmtime_environ::lookup_file_pos` will succeed.
511+
///
512+
/// If this function returns `false` then `lookup_file_pos` will always
513+
/// return `None`.
514+
pub fn has_address_map(&self) -> bool {
515+
!self.address_map_data().is_empty()
516+
}
503517
}
504518

505519
type Addr2LineContext<'a> = addr2line::Context<gimli::EndianSlice<'a, gimli::LittleEndian>>;

crates/wasmtime/src/config.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,20 @@ impl Config {
10521052
self
10531053
}
10541054

1055+
/// Configures whether compiled artifacts will contain information to map
1056+
/// native program addresses back to the original wasm module.
1057+
///
1058+
/// This configuration option is `true` by default and, if enables,
1059+
/// generates the appropriate tables in compiled modules to map from native
1060+
/// address back to wasm source addresses. This is used for displaying wasm
1061+
/// program counters in backtraces as well as generating filenames/line
1062+
/// numbers if so configured as well (and the original wasm module has DWARF
1063+
/// debugging information present).
1064+
pub fn generate_address_map(&mut self, generate: bool) -> &mut Self {
1065+
self.tunables.generate_address_map = generate;
1066+
self
1067+
}
1068+
10551069
pub(crate) fn build_allocator(&self) -> Result<Box<dyn InstanceAllocator>> {
10561070
#[cfg(feature = "async")]
10571071
let stack_size = self.async_stack_size;

crates/wasmtime/src/engine.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ impl Engine {
5050
// Ensure that wasmtime_runtime's signal handlers are configured. This
5151
// is the per-program initialization required for handling traps, such
5252
// as configuring signals, vectored exception handlers, etc.
53-
wasmtime_runtime::init_traps(crate::module::GlobalModuleRegistry::is_wasm_pc);
53+
wasmtime_runtime::init_traps(crate::module::GlobalModuleRegistry::is_wasm_trap_pc);
5454
debug_builtins::ensure_exported();
5555

5656
let registry = SignatureRegistry::new();

crates/wasmtime/src/module.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -398,13 +398,10 @@ impl Module {
398398
.collect();
399399

400400
let mut obj = engine.compiler().object()?;
401-
let (funcs, trampolines) = engine.compiler().emit_obj(
402-
&translation,
403-
&types,
404-
funcs,
405-
tunables.generate_native_debuginfo,
406-
&mut obj,
407-
)?;
401+
let (funcs, trampolines) =
402+
engine
403+
.compiler()
404+
.emit_obj(&translation, &types, funcs, tunables, &mut obj)?;
408405

409406
// If configured, attempt to use paged memory initialization
410407
// instead of the default mode of memory initialization

crates/wasmtime/src/module/registry.rs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -172,13 +172,12 @@ pub struct GlobalModuleRegistry(BTreeMap<usize, GlobalRegisteredModule>);
172172
impl GlobalModuleRegistry {
173173
/// Returns whether the `pc`, according to globally registered information,
174174
/// is a wasm trap or not.
175-
pub(crate) fn is_wasm_pc(pc: usize) -> bool {
175+
pub(crate) fn is_wasm_trap_pc(pc: usize) -> bool {
176176
let modules = GLOBAL_MODULES.read().unwrap();
177177

178178
match modules.module(pc) {
179179
Some((entry, text_offset)) => {
180-
wasmtime_environ::lookup_file_pos(entry.module.address_map_data(), text_offset)
181-
.is_some()
180+
wasmtime_environ::lookup_trap_code(entry.module.trap_data(), text_offset).is_some()
182181
}
183182
None => false,
184183
}
@@ -275,14 +274,15 @@ impl GlobalRegisteredModule {
275274
// the function, because otherwise something is buggy along the way and
276275
// not accounting for all the instructions. This isn't super critical
277276
// though so we can omit this check in release mode.
277+
//
278+
// Note that if the module doesn't even have an address map due to
279+
// compilation settings then it's expected that `instr` is `None`.
278280
debug_assert!(
279-
instr.is_some(),
281+
instr.is_some() || !self.module.has_address_map(),
280282
"failed to find instruction for {:#x}",
281283
text_offset
282284
);
283285

284-
let instr = instr.unwrap_or(info.start_srcloc);
285-
286286
// Use our wasm-relative pc to symbolize this frame. If there's a
287287
// symbolication context (dwarf debug info) available then we can try to
288288
// look this up there.
@@ -294,7 +294,7 @@ impl GlobalRegisteredModule {
294294
let mut symbols = Vec::new();
295295

296296
if let Some(s) = &self.module.symbolize_context().ok().and_then(|c| c) {
297-
if let Some(offset) = instr.file_offset() {
297+
if let Some(offset) = instr.and_then(|i| i.file_offset()) {
298298
let to_lookup = u64::from(offset) - s.code_section_offset();
299299
if let Ok(mut frames) = s.addr2line().find_frames(to_lookup) {
300300
while let Ok(Some(frame)) = frames.next() {
@@ -344,7 +344,7 @@ pub struct FrameInfo {
344344
func_index: u32,
345345
func_name: Option<String>,
346346
func_start: FilePos,
347-
instr: FilePos,
347+
instr: Option<FilePos>,
348348
symbols: Vec<FrameSymbol>,
349349
}
350350

@@ -393,8 +393,14 @@ impl FrameInfo {
393393
///
394394
/// The offset here is the offset from the beginning of the original wasm
395395
/// module to the instruction that this frame points to.
396-
pub fn module_offset(&self) -> usize {
397-
self.instr.file_offset().unwrap_or(u32::MAX) as usize
396+
///
397+
/// Note that `None` may be returned if the original module was not
398+
/// compiled with mapping information to yield this information. This is
399+
/// controlled by the
400+
/// [`Config::generate_address_map`](crate::Config::generate_address_map)
401+
/// configuration option.
402+
pub fn module_offset(&self) -> Option<usize> {
403+
Some(self.instr?.file_offset()? as usize)
398404
}
399405

400406
/// Returns the offset from the original wasm module's function to this
@@ -403,11 +409,15 @@ impl FrameInfo {
403409
/// The offset here is the offset from the beginning of the defining
404410
/// function of this frame (within the wasm module) to the instruction this
405411
/// frame points to.
406-
pub fn func_offset(&self) -> usize {
407-
match self.instr.file_offset() {
408-
Some(i) => (i - self.func_start.file_offset().unwrap()) as usize,
409-
None => u32::MAX as usize,
410-
}
412+
///
413+
/// Note that `None` may be returned if the original module was not
414+
/// compiled with mapping information to yield this information. This is
415+
/// controlled by the
416+
/// [`Config::generate_address_map`](crate::Config::generate_address_map)
417+
/// configuration option.
418+
pub fn func_offset(&self) -> Option<usize> {
419+
let instr_offset = self.instr?.file_offset()?;
420+
Some((instr_offset - self.func_start.file_offset()?) as usize)
411421
}
412422

413423
/// Returns the debug symbols found, if any, for this function frame.

crates/wasmtime/src/module/serialization.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,12 @@ impl<'a> SerializedModule<'a> {
601601

602602
// This doesn't affect compilation, it's just a runtime setting.
603603
dynamic_memory_growth_reserve: _,
604+
605+
// This does technically affect compilation but modules with/without
606+
// trap information can be loaded into engines with the opposite
607+
// setting just fine (it's just a section in the compiled file and
608+
// whether it's present or not)
609+
generate_address_map: _,
604610
} = self.metadata.tunables;
605611

606612
Self::check_int(

0 commit comments

Comments
 (0)