22//!
33//! 1. ELF `.symtab` 函数符号(地址 + mangled name)
44//! 2. DWARF 行号表(每条指令地址 → 源文件 + 行号)
5- //! 3. 函数形参名(`DW_TAG_formal_parameter` / `DW_AT_name `)
5+ //! 3. 函数形参的栈偏移 + 类型(`DW_AT_location` + `DW_AT_type `)
66//!
77//! 结果嵌入为 `const` 数组,供运行时栈回溯逐帧显示
8- //! `fn=name(params ) at file:line`。
8+ //! `fn=name(param=value, ... ) at file:line`。
99
1010fn main ( ) {
1111 use std:: { env, fs, path:: PathBuf } ;
@@ -132,10 +132,19 @@ fn extract_func_symbols(data: &[u8]) -> Vec<(u64, u64, String)> {
132132const TEXT_BASE : u64 = 0x80200000 ;
133133const TEXT_LIMIT : u64 = 0x81000000 ;
134134
135+ struct ParamLoc {
136+ func_addr : u64 ,
137+ name : String ,
138+ fbreg_offset : i16 ,
139+ byte_size : u8 ,
140+ /// 0=unsigned, 1=signed, 2=bool, 3=str_ref, 4=raw_hex
141+ kind : u8 ,
142+ }
143+
135144struct DwarfInfo {
136145 line_table : Vec < ( u32 , u16 , u32 ) > ,
137146 file_paths : Vec < String > ,
138- func_params : std :: collections :: HashMap < u64 , Vec < String > > ,
147+ param_locs : Vec < ParamLoc > ,
139148}
140149
141150fn extract_dwarf_info ( data : & [ u8 ] ) -> DwarfInfo {
@@ -145,7 +154,7 @@ fn extract_dwarf_info(data: &[u8]) -> DwarfInfo {
145154 let empty = DwarfInfo {
146155 line_table : Vec :: new ( ) ,
147156 file_paths : Vec :: new ( ) ,
148- func_params : std :: collections :: HashMap :: new ( ) ,
157+ param_locs : Vec :: new ( ) ,
149158 } ;
150159 if data. len ( ) < 64 {
151160 return empty;
@@ -173,15 +182,13 @@ fn extract_dwarf_info(data: &[u8]) -> DwarfInfo {
173182 Err ( _) => return empty,
174183 } ;
175184
176- // Extract function parameters first (borrows dwarf)
177- let func_params = extract_func_params ( & dwarf) ;
185+ let param_locs = extract_param_locs ( & dwarf) ;
178186
179- // Create addr2line Context (takes ownership of dwarf)
180187 let ctx = match addr2line:: Context :: from_dwarf ( dwarf) {
181188 Ok ( c) => c,
182189 Err ( _) => {
183190 return DwarfInfo {
184- func_params ,
191+ param_locs ,
185192 ..empty
186193 }
187194 }
@@ -226,18 +233,18 @@ fn extract_dwarf_info(data: &[u8]) -> DwarfInfo {
226233 DwarfInfo {
227234 line_table : line_entries,
228235 file_paths,
229- func_params ,
236+ param_locs ,
230237 }
231238}
232239
233- fn extract_func_params (
234- dwarf : & addr2line :: gimli :: Dwarf <
235- addr2line:: gimli:: EndianSlice < ' _ , addr2line:: gimli:: RunTimeEndian > ,
236- > ,
237- ) -> std :: collections :: HashMap < u64 , Vec < String > > {
240+ // ===== DWARF parameter location + type extraction =====
241+
242+ type DwarfSlice < ' a > = addr2line:: gimli:: EndianSlice < ' a , addr2line:: gimli:: RunTimeEndian > ;
243+
244+ fn extract_param_locs ( dwarf : & addr2line :: gimli :: Dwarf < DwarfSlice < ' _ > > ) -> Vec < ParamLoc > {
238245 use addr2line:: gimli;
239246
240- let mut result = std :: collections :: HashMap :: new ( ) ;
247+ let mut result = Vec :: new ( ) ;
241248
242249 let mut units = dwarf. units ( ) ;
243250 while let Ok ( Some ( header) ) = units. next ( ) {
@@ -248,19 +255,14 @@ fn extract_func_params(
248255
249256 let mut entries = unit. entries ( ) ;
250257 let mut current_func: Option < u64 > = None ;
251- let mut current_params: Vec < String > = Vec :: new ( ) ;
252258 let mut depth: isize = 0 ;
253259 let mut func_depth: isize = -1 ;
254260
255261 while let Ok ( Some ( ( delta, entry) ) ) = entries. next_dfs ( ) {
256262 depth += delta;
257263
258264 if current_func. is_some ( ) && depth <= func_depth {
259- if let Some ( addr) = current_func. take ( ) {
260- if !current_params. is_empty ( ) {
261- result. insert ( addr, std:: mem:: take ( & mut current_params) ) ;
262- }
263- }
265+ current_func = None ;
264266 func_depth = -1 ;
265267 }
266268
@@ -277,35 +279,156 @@ fn extract_func_params(
277279 if let Some ( a) = addr {
278280 if a >= TEXT_BASE && a < TEXT_LIMIT {
279281 current_func = Some ( a) ;
280- current_params. clear ( ) ;
281282 func_depth = depth;
282283 }
283284 }
284285 }
285286 gimli:: DW_TAG_formal_parameter
286287 if current_func. is_some ( ) && depth == func_depth + 1 =>
287288 {
288- if let Ok ( Some ( attr) ) = entry. attr ( gimli:: DW_AT_name ) {
289- if let Ok ( name) = dwarf. attr_string ( & unit, attr. value ( ) ) {
290- if let Ok ( s) = name. to_string ( ) {
291- let s = s. to_string ( ) ;
292- if !s. is_empty ( ) && !s. starts_with ( "__" ) && s != "self" {
293- current_params. push ( s) ;
294- }
289+ let func_addr = current_func. unwrap ( ) ;
290+
291+ let name = entry
292+ . attr ( gimli:: DW_AT_name )
293+ . ok ( )
294+ . flatten ( )
295+ . and_then ( |a| dwarf. attr_string ( & unit, a. value ( ) ) . ok ( ) )
296+ . and_then ( |s| s. to_string ( ) . ok ( ) . map ( |x| x. to_string ( ) ) )
297+ . unwrap_or_default ( ) ;
298+ if name. is_empty ( ) || name. starts_with ( "__" ) || name == "self" {
299+ continue ;
300+ }
301+
302+ let fbreg_offset = entry
303+ . attr_value ( gimli:: DW_AT_location )
304+ . ok ( )
305+ . flatten ( )
306+ . and_then ( |v| match v {
307+ gimli:: AttributeValue :: Exprloc ( expr) => {
308+ parse_fbreg_offset ( expr. 0 . slice ( ) )
295309 }
310+ _ => None ,
311+ } ) ;
312+ let fbreg_offset = match fbreg_offset {
313+ Some ( off) if off >= i16:: MIN as i64 && off <= i16:: MAX as i64 => {
314+ off as i16
296315 }
297- }
316+ _ => continue ,
317+ } ;
318+
319+ let ( byte_size, kind) = entry
320+ . attr_value ( gimli:: DW_AT_type )
321+ . ok ( )
322+ . flatten ( )
323+ . and_then ( |v| match v {
324+ gimli:: AttributeValue :: UnitRef ( offset) => {
325+ resolve_type ( & unit, dwarf, offset)
326+ }
327+ _ => None ,
328+ } )
329+ . unwrap_or ( ( 8 , 4 ) ) ;
330+
331+ result. push ( ParamLoc {
332+ func_addr,
333+ name,
334+ fbreg_offset,
335+ byte_size,
336+ kind,
337+ } ) ;
298338 }
299339 _ => { }
300340 }
301341 }
302- if let Some ( addr) = current_func {
303- if !current_params. is_empty ( ) {
304- result. insert ( addr, current_params) ;
342+ }
343+ result. sort_by ( |a, b| a. func_addr . cmp ( & b. func_addr ) . then ( a. fbreg_offset . cmp ( & b. fbreg_offset ) ) ) ;
344+ result
345+ }
346+
347+ /// Parse a `DW_OP_fbreg(N)` DWARF expression. Returns the SLEB128-decoded offset N.
348+ fn parse_fbreg_offset ( bytes : & [ u8 ] ) -> Option < i64 > {
349+ if bytes. is_empty ( ) || bytes[ 0 ] != 0x91 {
350+ return None ;
351+ }
352+ let mut result: i64 = 0 ;
353+ let mut shift: u32 = 0 ;
354+ for & byte in & bytes[ 1 ..] {
355+ result |= ( ( byte & 0x7f ) as i64 ) << shift;
356+ shift += 7 ;
357+ if byte & 0x80 == 0 {
358+ if shift < 64 && ( byte & 0x40 ) != 0 {
359+ result |= !0i64 << shift;
305360 }
361+ return Some ( result) ;
306362 }
307363 }
308- result
364+ None
365+ }
366+
367+ /// Resolve a DWARF type DIE to (byte_size, kind).
368+ fn resolve_type (
369+ unit : & addr2line:: gimli:: Unit < DwarfSlice < ' _ > > ,
370+ dwarf : & addr2line:: gimli:: Dwarf < DwarfSlice < ' _ > > ,
371+ offset : addr2line:: gimli:: UnitOffset < usize > ,
372+ ) -> Option < ( u8 , u8 ) > {
373+ use addr2line:: gimli;
374+
375+ let mut cursor = match unit. entries_at_offset ( offset) {
376+ Ok ( c) => c,
377+ Err ( _) => return None ,
378+ } ;
379+ match cursor. next_entry ( ) {
380+ Ok ( Some ( ( ) ) ) => { }
381+ _ => return None ,
382+ }
383+ let entry = cursor. current ( ) ?;
384+
385+ match entry. tag ( ) {
386+ gimli:: DW_TAG_base_type => {
387+ let byte_size = entry
388+ . attr_value ( gimli:: DW_AT_byte_size )
389+ . ok ( )
390+ . flatten ( )
391+ . and_then ( |v : gimli:: AttributeValue < DwarfSlice < ' _ > > | v. udata_value ( ) )
392+ . unwrap_or ( 8 ) as u8 ;
393+ let kind = entry
394+ . attr_value ( gimli:: DW_AT_encoding )
395+ . ok ( )
396+ . flatten ( )
397+ . and_then ( |v : gimli:: AttributeValue < DwarfSlice < ' _ > > | match v {
398+ gimli:: AttributeValue :: Encoding ( e) => Some ( e) ,
399+ _ => None ,
400+ } )
401+ . map ( |e| match e {
402+ gimli:: DW_ATE_boolean => 2u8 ,
403+ gimli:: DW_ATE_signed | gimli:: DW_ATE_signed_char => 1 ,
404+ _ => 0 ,
405+ } )
406+ . unwrap_or ( 0 ) ;
407+ Some ( ( byte_size, kind) )
408+ }
409+ gimli:: DW_TAG_structure_type => {
410+ let name = entry
411+ . attr ( gimli:: DW_AT_name )
412+ . ok ( )
413+ . flatten ( )
414+ . and_then ( |a : gimli:: Attribute < DwarfSlice < ' _ > > | {
415+ dwarf. attr_string ( unit, a. value ( ) ) . ok ( )
416+ } )
417+ . and_then ( |s : DwarfSlice < ' _ > | s. to_string ( ) . ok ( ) . map ( |x| x. to_string ( ) ) ) ;
418+ if name. as_deref ( ) == Some ( "&str" ) {
419+ Some ( ( 16 , 3 ) )
420+ } else {
421+ let byte_size = entry
422+ . attr_value ( gimli:: DW_AT_byte_size )
423+ . ok ( )
424+ . flatten ( )
425+ . and_then ( |v : gimli:: AttributeValue < DwarfSlice < ' _ > > | v. udata_value ( ) )
426+ . unwrap_or ( 8 ) as u8 ;
427+ Some ( ( byte_size, 4 ) )
428+ }
429+ }
430+ _ => Some ( ( 8 , 4 ) ) ,
431+ }
309432}
310433
311434/// Strip build-dir prefixes to produce short display paths like `src/main.rs`.
@@ -370,14 +493,21 @@ fn write_generated_rs(
370493 }
371494 writeln ! ( c, "];" ) . unwrap ( ) ;
372495
373- // --- FUNC_PARAMS: (func_addr, "p1,p2,...") ---
374- writeln ! ( c, "const FUNC_PARAMS: &[(u64, &str)] = &[" ) . unwrap ( ) ;
375- let mut param_entries: Vec < _ > = dwarf. func_params . iter ( ) . collect ( ) ;
376- param_entries. sort_by_key ( |( a, _) | * a) ;
377- for ( addr, params) in & param_entries {
378- let joined = params. join ( "," ) ;
379- let esc = joined. replace ( '\\' , "\\ \\ " ) . replace ( '"' , "\\ \" " ) ;
380- writeln ! ( c, " (0x{addr:x}, \" {esc}\" )," ) . unwrap ( ) ;
496+ // --- FUNC_PARAM_LOCS: (func_addr, name, fbreg_offset, byte_size, kind) ---
497+ // kind: 0=unsigned 1=signed 2=bool 3=str_ref 4=raw_hex
498+ writeln ! (
499+ c,
500+ "const FUNC_PARAM_LOCS: &[(u64, &str, i16, u8, u8)] = &["
501+ )
502+ . unwrap ( ) ;
503+ for p in & dwarf. param_locs {
504+ let esc = p. name . replace ( '\\' , "\\ \\ " ) . replace ( '"' , "\\ \" " ) ;
505+ writeln ! (
506+ c,
507+ " (0x{:x}, \" {esc}\" , {}, {}, {})," ,
508+ p. func_addr, p. fbreg_offset, p. byte_size, p. kind
509+ )
510+ . unwrap ( ) ;
381511 }
382512 writeln ! ( c, "];" ) . unwrap ( ) ;
383513
0 commit comments