Skip to content

Commit 5b07689

Browse files
authored
Add --stack-maps to wasmtime objdump (#10439)
* Add `--stack-maps` to `wasmtime objdump` Follow-up to #10404 and #10405 * Enable traps/stack maps by default in objdump
1 parent 45810e5 commit 5b07689

File tree

3 files changed

+145
-12
lines changed

3 files changed

+145
-12
lines changed

src/commands/objdump.rs

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use std::path::{Path, PathBuf};
1515
use std::str::FromStr;
1616
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
1717
use wasmtime::Engine;
18-
use wasmtime_environ::{obj, FilePos, Trap};
18+
use wasmtime_environ::{obj, FilePos, StackMap, Trap};
1919

2020
/// A helper utility in wasmtime to explore the compiled object file format of
2121
/// a `*.cwasm` file.
@@ -36,11 +36,11 @@ pub struct ObjdumpCommand {
3636
address_jumps: bool,
3737

3838
/// What functions should be printed (all|wasm|trampoline|builtin|libcall, default: wasm)
39-
#[arg(long, value_parser = Func::from_str)]
39+
#[arg(long, value_parser = Func::from_str, value_name = "KIND")]
4040
funcs: Vec<Func>,
4141

4242
/// String filter to apply to function names to only print some functions.
43-
#[arg(long)]
43+
#[arg(long, value_name = "STR")]
4444
filter: Option<String>,
4545

4646
/// Whether or not instruction bytes are disassembled.
@@ -52,19 +52,43 @@ pub struct ObjdumpCommand {
5252
color: ColorChoice,
5353

5454
/// Whether or not to interleave instructions with address maps.
55-
#[arg(long)]
56-
addrmap: bool,
55+
#[arg(long, require_equals = true, value_name = "true|false")]
56+
addrmap: Option<Option<bool>>,
5757

5858
/// Column width of how large an address is rendered as.
59-
#[arg(long, default_value = "10")]
59+
#[arg(long, default_value = "10", value_name = "N")]
6060
address_width: usize,
6161

6262
/// Whether or not to show information about what instructions can trap.
63-
#[arg(long)]
64-
traps: bool,
63+
#[arg(long, require_equals = true, value_name = "true|false")]
64+
traps: Option<Option<bool>>,
65+
66+
/// Whether or not to show information about stack maps.
67+
#[arg(long, require_equals = true, value_name = "true|false")]
68+
stack_maps: Option<Option<bool>>,
69+
}
70+
71+
fn optional_flag_with_default(flag: Option<Option<bool>>, default: bool) -> bool {
72+
match flag {
73+
None => default,
74+
Some(None) => true,
75+
Some(Some(val)) => val,
76+
}
6577
}
6678

6779
impl ObjdumpCommand {
80+
fn addrmap(&self) -> bool {
81+
optional_flag_with_default(self.addrmap, false)
82+
}
83+
84+
fn traps(&self) -> bool {
85+
optional_flag_with_default(self.traps, true)
86+
}
87+
88+
fn stack_maps(&self) -> bool {
89+
optional_flag_with_default(self.stack_maps, true)
90+
}
91+
6892
/// Executes the command.
6993
pub fn execute(self) -> Result<()> {
7094
// Setup stdout handling color options. Also build some variables used
@@ -107,6 +131,11 @@ impl ObjdumpCommand {
107131
.and_then(|section| section.data().ok())
108132
.and_then(|bytes| wasmtime_environ::iterate_traps(bytes))
109133
.map(|i| (Box::new(i) as Box<dyn Iterator<Item = _>>).peekable()),
134+
stack_maps: elf
135+
.section_by_name(obj::ELF_WASMTIME_STACK_MAP)
136+
.and_then(|section| section.data().ok())
137+
.and_then(|bytes| StackMap::iter(bytes))
138+
.map(|i| (Box::new(i) as Box<dyn Iterator<Item = _>>).peekable()),
110139
objdump: &self,
111140
};
112141

@@ -485,16 +514,18 @@ struct Decorator<'a> {
485514
objdump: &'a ObjdumpCommand,
486515
addrmap: Option<Peekable<Box<dyn Iterator<Item = (u32, FilePos)> + 'a>>>,
487516
traps: Option<Peekable<Box<dyn Iterator<Item = (u32, Trap)> + 'a>>>,
517+
stack_maps: Option<Peekable<Box<dyn Iterator<Item = (u32, StackMap<'a>)> + 'a>>>,
488518
}
489519

490520
impl Decorator<'_> {
491521
fn decorate(&mut self, address: u64, list: &mut Vec<String>) {
492522
self.addrmap(address, list);
493523
self.traps(address, list);
524+
self.stack_maps(address, list);
494525
}
495526

496527
fn addrmap(&mut self, address: u64, list: &mut Vec<String>) {
497-
if !self.objdump.addrmap {
528+
if !self.objdump.addrmap() {
498529
return;
499530
}
500531
let Some(addrmap) = &mut self.addrmap else {
@@ -511,7 +542,7 @@ impl Decorator<'_> {
511542
}
512543

513544
fn traps(&mut self, address: u64, list: &mut Vec<String>) {
514-
if !self.objdump.traps {
545+
if !self.objdump.traps() {
515546
return;
516547
}
517548
let Some(traps) = &mut self.traps else {
@@ -524,4 +555,25 @@ impl Decorator<'_> {
524555
list.push(format!("trap: {trap:?}"));
525556
}
526557
}
558+
559+
fn stack_maps(&mut self, address: u64, list: &mut Vec<String>) {
560+
if !self.objdump.stack_maps() {
561+
return;
562+
}
563+
let Some(stack_maps) = &mut self.stack_maps else {
564+
return;
565+
};
566+
while let Some((addr, stack_map)) =
567+
stack_maps.next_if(|(addr, _pos)| u64::from(*addr) <= address)
568+
{
569+
if u64::from(addr) != address {
570+
continue;
571+
}
572+
list.push(format!(
573+
"stack_map: frame_size={}, frame_offsets={:?}",
574+
stack_map.frame_size(),
575+
stack_map.offsets().collect::<Vec<_>>()
576+
));
577+
}
578+
}
527579
}

tests/disas.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,13 @@ fn assert_output(test: &Test, output: CompileOutput) -> Result<()> {
268268
.stdin(Stdio::piped())
269269
.stdout(Stdio::piped())
270270
.stderr(Stdio::piped());
271-
if let Some(args) = &test.config.objdump {
272-
cmd.args(args.to_vec());
271+
match &test.config.objdump {
272+
Some(args) => {
273+
cmd.args(args.to_vec());
274+
}
275+
None => {
276+
cmd.arg("--traps=false");
277+
}
273278
}
274279

275280
let mut child = cmd.spawn().context("failed to run wasmtime")?;
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
;;! target = "x86_64"
2+
;;! flags = "-W function-references,gc"
3+
;;! test = "compile"
4+
5+
(module
6+
(type $ty (struct (field (mut f32))
7+
(field (mut i8))
8+
(field (mut anyref))))
9+
10+
(func (param f32 i32 anyref) (result (ref $ty))
11+
(struct.new $ty (local.get 0) (local.get 1) (local.get 2))
12+
)
13+
)
14+
;; wasm[0]::function[0]:
15+
;; pushq %rbp
16+
;; movq %rsp, %rbp
17+
;; movq 8(%rdi), %r10
18+
;; movq 0x10(%r10), %r10
19+
;; addq $0x50, %r10
20+
;; cmpq %rsp, %r10
21+
;; ja 0xe4
22+
;; 19: subq $0x40, %rsp
23+
;; movq %r13, 0x20(%rsp)
24+
;; movq %r14, 0x28(%rsp)
25+
;; movq %r15, 0x30(%rsp)
26+
;; movq %rdx, %r15
27+
;; movdqu %xmm0, 8(%rsp)
28+
;; leaq (%rsp), %r14
29+
;; movl %ecx, (%r14)
30+
;; movl $0xb0000001, %esi
31+
;; xorl %edx, %edx
32+
;; movl $0x28, %ecx
33+
;; movl $8, %r8d
34+
;; movq %rdi, %r13
35+
;; callq 0x195
36+
;; movq 0x28(%r13), %r9
37+
;; ╰─╼ stack_map: frame_size=64, frame_offsets=[0]
38+
;; movq %rax, %r8
39+
;; movl %r8d, %r10d
40+
;; movdqu 8(%rsp), %xmm0
41+
;; movss %xmm0, 0x1c(%r9, %r10)
42+
;; movq %r15, %rdx
43+
;; movb %dl, 0x20(%r9, %r10)
44+
;; movl (%r14), %r11d
45+
;; movq %r11, %rdx
46+
;; andl $1, %edx
47+
;; testl %r11d, %r11d
48+
;; sete %sil
49+
;; movzbl %sil, %esi
50+
;; orl %esi, %edx
51+
;; testl %edx, %edx
52+
;; jne 0xc1
53+
;; 93: movl %r11d, %edi
54+
;; addq $8, %rdi
55+
;; jb 0xe6
56+
;; a0: movq %rdi, %rcx
57+
;; addq $8, %rcx
58+
;; jb 0xe8
59+
;; ad: cmpq 0x30(%r13), %rcx
60+
;; ja 0xea
61+
;; b7: movl $1, %r11d
62+
;; addq %r11, (%r9, %rdi)
63+
;; movl (%r14), %r11d
64+
;; movl %r11d, 0x18(%r9, %r10)
65+
;; movq %r8, %rax
66+
;; movq 0x20(%rsp), %r13
67+
;; movq 0x28(%rsp), %r14
68+
;; movq 0x30(%rsp), %r15
69+
;; addq $0x40, %rsp
70+
;; movq %rbp, %rsp
71+
;; popq %rbp
72+
;; retq
73+
;; e4: ud2
74+
;; e6: ud2
75+
;; e8: ud2
76+
;; ea: ud2

0 commit comments

Comments
 (0)