#!/usr/bin/tclsh ################################################################################ # # Given a path to a mach-o binary and input that includes stack backtraces in # the format printed by iBoot's arch_backtrace_task, annotate the backtraces # with symbol names # # Lines in the backtrace have the form # # # or # # proc main { } { global argv set binfile [lindex $argv 0] if {![file readable $binfile]} { puts "can't read '$binfile'" exit 1 } # run nm and parse the output do_nm $binfile # read input lines while {[gets stdin line] != -1} { puts -nonewline $line if {([scan $line " %s %x %x%n" pad pc lr len] == 4) || ([scan $line " %x %x%n" pc lr len] == 3)} { if {$len == [string length $line]} { set detail [find_sym $pc] if {$detail != ""} { puts -nonewline " ($detail)" } } } puts "" } } proc do_nm {binfile} { global symbols global symbol_index # puts "reading symbols from $binfile" set lines [exec nm -n $binfile] foreach {_addr type _sym} $lines { set sym [string range $_sym 1 end] set addr "0x$_addr" set symbols($addr) $sym } set symbol_index [lsort -integer [array names symbols]] # puts "[llength [array names symbols]] symbols" } proc find_sym {pc} { global symbols global symbol_index # puts "looking for $pc" set candidate [lindex $symbol_index 0] # if the pc is before the first symbol, it's not in the object file if {$pc < $candidate} { # puts "too low (less than $candidate)" return "" } foreach addr [lrange $symbol_index 1 end] { # puts "trying $addr" # if the next symbol is above the pc, the candidate is where we are if {$pc < $addr} { break } # advance the candidate set candidate $addr } # the last symbol is a sentinel, if the PC is above it, it's not in the object file if {$pc > $addr} { # puts "too high" return "" } set key [format 0x%08x $candidate] set offset [format 0x%x [expr $addr - $key]] return "$symbols($key)+$offset" } main