101 lines
2.1 KiB
Plaintext
101 lines
2.1 KiB
Plaintext
|
#!/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
|