1
1
use addr2line:: { Context , LookupResult } ;
2
- use anyhow:: { bail, Context as _, Result } ;
2
+ use anyhow:: { anyhow , bail, Context as _, Result } ;
3
3
use gimli:: EndianSlice ;
4
4
use std:: collections:: HashMap ;
5
5
use std:: io:: Write ;
6
+ use std:: ops:: Range ;
6
7
use std:: u64;
7
- use wasmparser:: { Parser , Payload } ;
8
+ use wasmparser:: { Encoding , Parser , Payload } ;
8
9
9
10
/// Translate a WebAssembly address to a filename and line number using DWARF
10
11
/// debugging information.
@@ -40,6 +41,12 @@ pub struct Opts {
40
41
code_section_relative : bool ,
41
42
}
42
43
44
+ struct Module < ' a > {
45
+ range : Range < u64 > ,
46
+ code_start : Option < u64 > ,
47
+ custom_sections : HashMap < & ' a str , & ' a [ u8 ] > ,
48
+ }
49
+
43
50
impl Opts {
44
51
pub fn general_opts ( & self ) -> & wasm_tools:: GeneralOpts {
45
52
self . io . general_opts ( )
@@ -48,54 +55,60 @@ impl Opts {
48
55
pub fn run ( & self ) -> Result < ( ) > {
49
56
let wasm = self . io . parse_input_wasm ( ) ?;
50
57
51
- let ( code_start , custom_sections ) = self
58
+ let modules = self
52
59
. parse_custom_sections ( & wasm)
53
60
. context ( "failed to parse input and read custom sections" ) ?;
54
-
55
- let dwarf = gimli:: Dwarf :: load ( |id| -> Result < _ > {
56
- let data = custom_sections. get ( id. name ( ) ) . copied ( ) . unwrap_or ( & [ ] ) ;
57
- Ok ( EndianSlice :: new ( data, gimli:: LittleEndian ) )
58
- } ) ?;
59
- let cx = Context :: from_dwarf ( dwarf)
60
- . context ( "failed to create addr2line dwarf mapping context" ) ?;
61
-
62
61
let mut output = self . io . output_writer ( ) ?;
63
62
64
63
for addr in self . addresses . iter ( ) {
65
- self . addr2line ( & addr, code_start , & cx , & mut output)
64
+ self . addr2line ( & addr, & modules , & mut output)
66
65
. with_context ( || format ! ( "failed to find frames for `{addr}`" ) ) ?;
67
66
}
68
67
69
68
Ok ( ( ) )
70
69
}
71
70
72
- fn parse_custom_sections < ' a > (
73
- & self ,
74
- wasm : & ' a [ u8 ] ,
75
- ) -> Result < ( Option < u64 > , HashMap < & ' a str , & ' a [ u8 ] > ) > {
76
- let mut ret = HashMap :: new ( ) ;
77
- let mut code_start = None ;
71
+ fn parse_custom_sections < ' a > ( & self , wasm : & ' a [ u8 ] ) -> Result < Vec < Module < ' a > > > {
72
+ let mut ret = Vec :: new ( ) ;
73
+ let mut cur_module = None ;
78
74
for payload in Parser :: new ( 0 ) . parse_all ( wasm) {
79
75
match payload? {
76
+ Payload :: Version {
77
+ encoding : Encoding :: Module ,
78
+ range,
79
+ ..
80
+ } => {
81
+ assert ! ( cur_module. is_none( ) ) ;
82
+ cur_module = Some ( Module {
83
+ range : range. start as u64 ..0 ,
84
+ code_start : None ,
85
+ custom_sections : HashMap :: new ( ) ,
86
+ } ) ;
87
+ }
88
+
80
89
Payload :: CustomSection ( s) => {
81
- ret. insert ( s. name ( ) , s. data ( ) ) ;
90
+ if let Some ( cur) = & mut cur_module {
91
+ cur. custom_sections . insert ( s. name ( ) , s. data ( ) ) ;
92
+ }
82
93
}
83
94
Payload :: CodeSectionStart { range, .. } => {
84
- code_start = Some ( range. start as u64 ) ;
95
+ assert ! ( cur_module. is_some( ) ) ;
96
+ cur_module. as_mut ( ) . unwrap ( ) . code_start = Some ( range. start as u64 ) ;
97
+ }
98
+
99
+ Payload :: End ( offset) => {
100
+ if let Some ( mut module) = cur_module. take ( ) {
101
+ module. range . end = offset as u64 ;
102
+ ret. push ( module) ;
103
+ }
85
104
}
86
105
_ => { }
87
106
}
88
107
}
89
- Ok ( ( code_start , ret) )
108
+ Ok ( ret)
90
109
}
91
110
92
- fn addr2line (
93
- & self ,
94
- addr : & str ,
95
- code_start : Option < u64 > ,
96
- cx : & Context < EndianSlice < gimli:: LittleEndian > > ,
97
- out : & mut dyn Write ,
98
- ) -> Result < ( ) > {
111
+ fn addr2line ( & self , addr : & str , modules : & [ Module < ' _ > ] , out : & mut dyn Write ) -> Result < ( ) > {
99
112
// Support either `0x` or `@` prefixes for hex addresses since 0x is
100
113
// standard and @ is used by wasmprinter (and web browsers I think?)
101
114
let addr = if let Some ( hex) = addr. strip_prefix ( "0x" ) . or_else ( || addr. strip_prefix ( "@" ) ) {
@@ -104,12 +117,28 @@ impl Opts {
104
117
addr. parse ( ) ?
105
118
} ;
106
119
120
+ let module = modules
121
+ . iter ( )
122
+ . find ( |module| module. range . start <= addr && addr <= module. range . end )
123
+ . ok_or_else ( || anyhow ! ( "no module found which contains this address" ) ) ?;
124
+
125
+ let dwarf = gimli:: Dwarf :: load ( |id| -> Result < _ > {
126
+ let data = module
127
+ . custom_sections
128
+ . get ( id. name ( ) )
129
+ . copied ( )
130
+ . unwrap_or ( & [ ] ) ;
131
+ Ok ( EndianSlice :: new ( data, gimli:: LittleEndian ) )
132
+ } ) ?;
133
+ let cx = Context :: from_dwarf ( dwarf)
134
+ . context ( "failed to create addr2line dwarf mapping context" ) ?;
135
+
107
136
// Addresses in DWARF are relative to the start of the text section, so
108
137
// factor that in here.
109
138
let text_relative_addr = if self . code_section_relative {
110
139
addr
111
140
} else {
112
- match code_start {
141
+ match module . code_start {
113
142
Some ( start) => addr
114
143
. checked_sub ( start)
115
144
. context ( "address is before the beginning of the text section" ) ?,
0 commit comments