iBoot/tools/symbolicate-panic

101 lines
2.1 KiB
Tcl

#!/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
#
# <whitespace><token><whitespace><hex number><hex number><newline>
# or
# <whitespace><hex number><hex number><newline>
#
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