##// END OF EJS Templates
Implemented Calibrations Task...
Implemented Calibrations Task This task will ensure the calibrations signal frequency sweep by dividing by 2 the sampling frequency between each snapshot.

File last commit:

r369:3813be439915 R3++
r374:79e8ac429728 No PWD scrub with...
Show More
leon3ft-b2bst-scan.tcl
681 lines | 24.5 KiB | application/x-tcl | TclLexer
/ sparc / leon3ft-b2bst-scan.tcl
#! /usr/bin/tclsh
#
# TCL script to scan disassembled programs against the "LEON3FT Stale Cache
# Entry After Store with Data Tag Parity Error" errata, GRLIB-TN-0009.
#
# The following Cobham components are affected by GRLIB-TN-0009:
# - GR712RC
# - LEON3FT-RTAX (all versions)
# - UT699
# - UT699E
# - UT700
#
# For information on how custom LEON3 systems may be affected, see
# GRLIB-TN-0009.
#
# Copyright (C) 2016, Cobham Gaisler
#
# Usage: sparc-elf-objdump -d program | leon3ft-b2bst-scan.tcl
#
# Command line parameters:
# -nonote: Do not print NOTE messages.
# -noinfo: Do not print INFO messages.
# -silent: Do not print NOTE or INFO messages.
#
# Return values:
# 0: No potential error locations found.
# 1: At least one potential error location found.
#
# Rev. history:
# rev 1, MH, Initial revision, based on ut699scan.tcl. Add scanning for
# store-store errata, reorganize comments
# rev 2, MA, Restruction and clean-up, fixed Sequence B.
# rev 3, MA, Added decoding of casa instruction, exit status and verbosity
# parameters.
# rev 4, MA, Reduced false warnings due to Store into Alternate space
# instructions which can not trig the errata. Script renamed to
# leon3ft-b2bst-scan.tcl.
#
# Note 1: Two modes are available with respect to call/ret (dslot_mode):
# 1) ret, and call are assumed not to have stores in their delay slots.
# The tool will warn if any such operations are found in the delay
# slot of ret,call during the scan
# 0) ret and call may have stores in their delay slots
# The tool will not warn if any such operations are found in the
# delay slot during the scan. Instead it will assume this
# possibility exists when evaluating insns following a function call
#
### CONFIGURATION
set b2bst_seqa_scan 1; # enable/disable scanning for Sequence A
set b2bst_seqb_scan 1; # enable/disable scanning for Sequence B
# set dslot_mode 0; # assume store can be in delay slot of call,jmpl
set dslot_mode 1; # do not allow store in delay slot of call,jmpl
###
# Mask values for types of output messages
set MSG_INFO 1; # Information on things to check manually.
set MSG_NOTE 2; # Notices on branch following
set msglevel [expr {$MSG_INFO | $MSG_NOTE}]
set errata_desc "LEON3FT Stale Cache Entry After Store with Data Tag Parity Error"
set util_rev 4
set util_date "20170215"
set b2bst_seqa_desc "Sequence A"
set b2bst_seqb_desc "Sequence B"
set op_count 0
set lineskip_count 0
set line_count 0
set err_count 0
set fn_count 0
set fninst_count 0
proc puts_info {str} {
global msglevel MSG_INFO
if {$msglevel & $MSG_INFO} { puts "INFO: $str" }
}
proc puts_note {str} {
global msglevel MSG_NOTE
if {$msglevel & $MSG_NOTE} { puts "NOTE: $str" }
}
# ----------------------------------------------------------------------------
# Routine that decodes specified opcode and returns list of
# 0 :optype Class of opcode
# one of call,jmpl,branch,sethi,ld,st,atomic,alu,fpop,save,restore
# 1 :target-reg Register modified by op
# 2 :source-reg1 First source register (rs1)
# 3 :source-reg2 Second source register (rs2)
# 4 :store-reg Source register for store data
# regs are numbered 0-31 integer regs, 32-63 FP regs
# 5 :double-flag
# ORed mask: 8) target-reg is DP, 4) sreg1 is DP, 2) sreg2 is DP, 1) store-reg is DP
# 6 :immed Immediate operand
# 7 :nnpc1 Next instructions nPC if branch not taken (normal case), normally nPC+4
# 8 :nnpc2 Next instruction's nPC if branch is taken, if not branch then nnpc2=nnpc1
# 9 :annul
# ORed mask: next inst annulled if 2) branch not taken 1) branch taken
# 10:bubbles Number of extra cycles spent in decode stage (bubbles inserted)
proc decode_inst { opcode {pc "0x00000000"} {npc "0x00000004"}} {
# Some "sta" instructions do not trig the errata. The related ASI:s are
# defined here.
set safeasi [list 0x02 0x03 0x04 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x13 0x14 0x15 0x18 0x19 0x1c 0x1d 0x1e]
#Default values if no cond matches
set opname "unknown"
set destreg 0
set sreg1 0
set sreg2 0
set streg 0
set dblflag 0
set immed 0
if { [string is integer $npc] } {
set nnpc1 [format "0x%08x" [ expr {$npc+4} ] ]
} else {
set nnpc1 "$npc+4"
}
set nnpc2 $nnpc1
set annul 0
set bubbles 0
#Decode
set op1 [ expr {$opcode >> 30} ]
if { $op1 == 1 } {
set opname "call"
set disp [ expr { ($opcode & 0x3fffffff)<<2 } ]
set nnpc1 [ format "0x%08x" [ expr { $pc + $disp } ] ]
set nnpc2 $nnpc1
} elseif { $op1 == 0 } {
set op2 [ expr {($opcode >> 22) & 7} ]
# NOTE: 2=Bicc, 6=FBcc, 7=CBcc
if { $op2 == 2 || $op2 == 6 } {
set opname "branch"
set disp [expr { ($opcode & 0x3fffff) << 2 } ]
if { $disp > 0x800000 } { set disp [expr {$disp - 0x1000000}] }
# Todo check cond,annul, handle always or never
set nnpc2 [ format "0x%08x" [ expr { $pc + $disp } ] ]
set cond [ expr { ($opcode >> 25) & 15 } ]
set annulbit [ expr { $opcode & 0x20000000 } ]
if { $cond == 0 } { # Branch never
set nnpc2 $nnpc1
if { $annulbit } { set annul 3 }
} elseif { $cond == 8 } { # Branch always
set nnpc1 $nnpc2
if { $annulbit } { set annul 3 }
} else { # True cond branch
if { $annulbit } { set annul 2 }
}
} elseif { $op2 == 4 } {
set opname "sethi"
set destreg [ expr { ($opcode >> 25) & 31 } ]
set immed [ expr { ($opcode & 0x3fffff) << 10 } ]
}
} elseif { $op1 == 3 } {
# SPARC V8 Table F-4
# set opname "mem" - changed to use ld/st/atomic instead
set op3 [ expr { ($opcode >> 19) & 63 } ]
set rs1 [ expr { (($opcode >> 14) & 31) } ]
set rs2 [ expr { (($opcode ) & 31) } ]
set rd [ expr { (($opcode >> 25) & 31) } ]
set sreg1 $rs1
if { $opcode & 0x2000 } {
set immed [ expr { $opcode & 0x1fff } ]
if { $immed > 0xfff } { set immed [ expr { $immed-(0x2000) } ] }
} else {
set sreg2 $rs2
}
if { (($op3 & 13) == 13) || ($op3 == 0x3c) } {
# Atomic ldst/swap/casa
set opname "atomic"
set destreg $rd
set streg $rd
} else {
# Adjust reg no if FP ld/st (op3[5]=1)
if { ($op3 & 0x20) != 0 } { set rd [expr {32 + $rd} ] }
# Check if read or write op3[2]
if { ($op3 & 4) != 0 } {
set opname "st"
set streg $rd
set allow_safe_sta true
if { $allow_safe_sta && (($op3 & 0x3c) == 0x14) } {
# op3 is 0x14, 0x15, 0x16 or 0x17 (sta, stba, stha or stda)
# (if i = 0)
set asi [expr { (($opcode >> 5) & 0xff) }]
set safematch [lsearch -exact -integer $safeasi $asi]
if {0 <= $safematch} {
set opname "sta_safe"
}
}
} else {
set opname "ld"
set destreg $rd
}
# Check size op3[1:0]
if { ($op3 & 3) == 3 } {
if { $opname eq "st" } { set dblflag 1 } else { set dblflag 8 }
}
}
} else {
set op3 [ expr { ($opcode >> 19) & 63 } ]
if { $op3 == 0x34 || $op3 == 0x35 } {
set opname "fpop"
set opf [ expr { ($opcode >> 5) & 0x1ff } ]
set rs1 [ expr { 32 + (($opcode >> 14) & 31) } ]
set rs2 [ expr { 32 + (($opcode ) & 31) } ]
set rd [ expr { 32 + (($opcode >> 25) & 31) } ]
# opf[1:0] corresponds to operand size of rs2
# opf[7:6]=11 is conv op, opf[3:2] gives opsize of rd, rs1 unused
# opf[7:6]=00 is single-operand op, rd same size as rs2, rs1 unused
# opf[7:4]=0100 regular op, rs1,rd same size as rs2
# opf[7:4]=0101 comp op, rd unused, rs1 same size as rs2
# opf[7:4]=0110 conv+mul, opf[3:2] gives opsize of rd, rs1 same size as rs2
set sreg2 $rs2
if { ($opf & 3) > 1 } { set dblflag 2 }
if { ($opf & 0xc0) == 0x40 } {
set sreg1 $rs1
if { $dblflag & 2 } { set dblflag [ expr { $dblflag | 4 } ] }
}
if { ($opf & 0xc0) == 0xc0 || ($opf & 0xf0) == 0x60 } {
set destreg $rd
if { ($opf & 0xc) > 4 } { set dblflag [ expr { $dblflag | 8 } ] }
} elseif { ($opf & 0xf0) != 0x50 } {
set destreg $rd
if { $dblflag & 2 } { set dblflag [ expr { $dblflag | 8 } ] }
}
} else {
set opname "alu"
set destreg [ expr { (($opcode >> 25) & 31) } ]
set sreg1 [ expr { (($opcode >> 14) & 31) } ]
if { $opcode & 0x2000 } {
set immed [ expr { $opcode & 0x1fff } ]
if { $immed > 0xfff } { set immed [ expr { $immed-(0x2000) } ] }
} else {
set sreg2 [ expr { (($opcode ) & 31) } ]
}
if { $op3 == 0x38 } {
set opname "jmpl"
set nnpc1 "jmpaddr"
set nnpc2 "jmpaddr"
set bubbles 2
} elseif { $op3 == 0x3c } {
set opname "save"
} elseif { $op3 == 0x3d } {
set opname "restore"
}
}
}
#Return values
return [list $opname $destreg $sreg1 $sreg2 $streg $dblflag $immed $nnpc1 $nnpc2 $annul $bubbles]
}
proc is_store { opcode } {
set op1 [ expr { ($opcode >> 30) } ]
set op3 [ expr { ($opcode >> 19) & 63 } ]
return [expr {($op1 == 3) && (($op3 & 0xc) == 0x4)}]
}
# -------------------------------------------------------------------------------------
proc unpack { l args } {
for { set i 0 } { $i < [llength $args] } { incr i } {
uplevel 1 "set [lindex $args $i] [lindex $l $i]"
}
}
# --------- Postprocessing on whole function
# Number of instructions printed is starti + 2
proc print_trace { fnpc state fnarr seqstart {starti 4}} {
upvar $fnarr fnops
puts " # PC Opcode"
set cnt [expr {$seqstart-$starti}]
for { set x $starti } { $x > -2 } { set x [expr {$x-1}] } {
incr cnt
if { $cnt > 0 } { set cntv $cnt } else { set cntv " " }
if { $x >= 0 } { set pc [lindex $state $x] } else { set pc $fnpc }
if { $pc eq "?" || $pc eq "call" || $pc eq "ret" || $pc eq "dslot" } {
puts " $cntv (other fn) $pc"
} else {
if { [string index $pc 0] eq "A" } {
set pc [string range $pc 1 end]
set annul " (annulled)"
} else {
set annul ""
}
if { [info exists fnops($pc)] } {
set op $fnops($pc)
} else {
set op [list "0x???????? (outside func)"]
}
puts " $cntv $pc [join $op] $annul"
}
}
puts ""
}
# Routine called by scan_wholefunc_main to check
# instruction sequences described in GRLIB-TN-009, Issue 1.0
#
# "Sequence A"
# 1. store of word size or less (st / stb / sth / stf)
# 2. any single instruction that is not a load or store
# 3. any store instruction (st / stb / sth / stf / std / stdf)
#
# "Sequence B"
# 1. store of double word size (std / stdf)
# 2. any store instruction (st / stb / sth / stf / std / stdf)
proc scan_b2bst { fnname fnpc fnop opdec fnarr state } {
global b2bst_seqa_scan
global b2bst_seqb_scan
upvar $fnarr fnops
global dslot_mode
# Check latest 3 insn if they can be st/std/regular
set pca [list $fnpc [lindex $state 0] [lindex $state 1]]
set can_be_arr [list]
for { set i 0 } { $i < 3 } { incr i } {
set can_be [list 0 0 0] ; # st std regular
set pcv [lindex $pca $i]
if { $pcv eq "?" } {
set can_be [list 1 1 1]
} elseif { $pcv eq "dslot" } {
set can_be [list [expr {1-$dslot_mode}] [expr {1-$dslot_mode}] 1]
} elseif { [string index $pcv 0] eq "A" || $pcv eq "call" || $pcv eq "ret" } {
set can_be [list 0 0 1]
} else {
set op $fnops($pcv)
set ophex [lindex $op 0]
set opdec [decode_inst $ophex]
set optype [lindex $opdec 0]
set opstorereg [lindex $opdec 4]
set opdblflag [lindex $opdec 5]
if { $optype eq "st" && ($opdblflag & 1) } {
set can_be [list 0 1 0]
} elseif { $optype eq "st" } {
set can_be [list 1 0 0]
} elseif { $optype ne "ld" && $optype ne "atomic" } {
# Any single instruction that is not a load or store
set can_be [list 0 0 1]
}
}
lappend can_be_arr $can_be
}
# can_be_arr has at index
# - at index 2: previous previous instruction
# - at index 1: previous instruction
# - at index 0: current instruction
set pp_insn [lindex $can_be_arr 2]
set p_insn [lindex $can_be_arr 1]
set c_insn [lindex $can_be_arr 0]
# Sequence A
# 1. store of word size or less
# 2. any single instruction that is not a load or store
# 3. any store instruction
if {
(
$b2bst_seqa_scan &&
[lindex $pp_insn 0] &&
[lindex $p_insn 2] &&
([lindex $c_insn 0] || [lindex $c_insn 1])
) } {
puts "WARNING: Possible sequence matching LEON3FT b2bst errata (sequence A) in function $fnname"
print_trace $fnpc $state fnops 1
puts ""
return 1
}
# Sequence B
# 1. any store instruction
# 2. store of double word size
if {
(
$b2bst_seqb_scan &&
[lindex $p_insn 1] &&
([lindex $c_insn 0] || [lindex $c_insn 1])
) } {
puts "WARNING: Possible sequence matching LEON3FT b2bst errata (sequence B) in function $fnname"
print_trace $fnpc $state fnops 0
puts ""
return 1
}
return 0
}
# Main recursive function doing scan
# fnname - funcion name
# fnpc, fnnpc, PC/nPC of current insn (8-digit hex, 0x prefix) prefix also with A if annulled,
# fnarr - array/hash-table, see descr for scan_wholefunc
# dpdist - number of SP FPOPs done minimum since last DP FPOP
# state - List of 5 last instruction addresses (hex w 0x prefix), special values "?"=unknown "call"=call from calling proc
# smap - Hash table used to track where we have been already
proc scan_wholefunc_main { fnname fnpc fnnpc fnarr savelevel dpdist state smap lastoptype } {
global dslot_mode
upvar $fnarr fnops
upvar $smap statemap
set r 0
# puts "entering scan_wholefunc_main: PC:$fnpc nPC:$fnnpc dpdist:$dpdist savelevel:$savelevel state:$state"
# puts "fnops exists [info exists fnops] [array exists fnops] [array names fnops]"
while {1} {
# puts "scan_wholefunc_main: PC:$fnpc nPC:$fnnpc dpdist:$dpdist savelevel:$savelevel state:$state"
set statedesc [list $fnpc $fnnpc $savelevel $dpdist $state]
if { [info exists statemap($statedesc)] } {
# Already been here
return $r
}
set statemap($statedesc) 1
set fnpca $fnpc
set pcannul 0
if { [string index $fnpc 0] eq "A" } {
set pcannul 1
set fnpc [string range $fnpc 1 end]
set ophex "0x01000000"
} else {
if {![info exists fnops($fnpc)]} {
puts_note "$fnname: Execution leaves function without return";
return $r
}
set op $fnops($fnpc)
set ophex [lindex $op 0]
set opname [lindex $op 1]
}
set opdec [ decode_inst $ophex $fnpc $fnnpc ]
unpack $opdec optype treg sreg1 sreg2 streg dblflag immed nnpc1 nnpc2 annul bubbles
set prev_savelevel $savelevel
if { $optype eq "fpop" } {
# update dpdist
if { ($dblflag & 6) == 0 } {
if { $dpdist < 2 } { incr dpdist }
} else {
set dpdist 0
}
} elseif { $optype eq "save" } {
if { $savelevel > 9 } {
puts_note "$fnname: More than 10 deep saves (possibly regfile clear loop)"
} else {
incr savelevel
}
# puts "save at $fnpc, new savelevel: $savelevel"
} elseif { $optype eq "restore" } {
if { $savelevel < -9 } {
puts_note "$fnname: More than 10 deep restores (possibly regfile clear loop)"
} else {
if { $savelevel == 0 } {
puts_note "$fnname: More restore than save instructions"
}
set savelevel [ expr { $savelevel-1} ]
}
# puts "restore at $fnpc, new savelevel: $savelevel"
}
if { true && ($optype eq "st") } {
if { [scan_b2bst $fnname $fnpc $op $opdec fnops $state] } { incr r }
}
# Handle jmpl and call
# If in delay slot of jmpl:
# If ret or retl, stop recursing further
# If other jmpl, check savelevel if it will return or not
# If in delay slot of call, set dpdist to 0 (called proc may have done DP operation)
set is_call 0
if {
(
$dslot_mode != 0 &&
($lastoptype eq "branch" || $lastoptype eq "jmpl" || $lastoptype eq "call")
) } {
if { $optype eq "st" } {
puts "WARNING: $fnname: $opname in delay slot of branch/call/ret at $fnpc may cause errata"
print_trace $fnpc $state fnops -1 1
puts ""
incr r
}
}
if { $lastoptype eq "jmpl" } {
set linst [lindex $state 0]
set q $fnops($linst)
set qh [lindex $q 0]
set qdec [ decode_inst $qh $linst $fnpc ]
unpack $qdec qdtype qdtreg qdsreg1 qdsreg2 qdstreg qddblflag qdimmed qdnnpc1 qdnnpc2 qdannul
if { $qdtreg==15 && $prev_savelevel==1 && $savelevel==1 } {
# Function call via pointer
incr is_call
} elseif { $qdtreg==0 && $savelevel==0 } {
# Return or tail recursion
return $r
} else {
# Computed goto or some other non-standard construct
puts_info "$fnname: Unable to trace jmpl at $linst - check manually"
return $r
}
} elseif { $lastoptype eq "call" } {
if { $savelevel==0 && $prev_savelevel==1 } {
# call,restore tail recursion into other fn
return $r
} elseif { $savelevel==0 && $prev_savelevel==0 && $treg == 15 } {
puts_note "$fnname: Leaf tail recursion construct at $fnpc"
return $r
} else {
incr is_call
}
}
# Update history
for { set z 0 } { $z <= $bubbles } { incr z } {
set state [concat $fnpca [lrange $state 0 4] ]
}
if { $is_call } {
if { $optype eq "branch" || $optype eq "call" || $optype eq "jmpl" } {
puts_info "$fnname: jump in delay slot of call - check manually"
}
# Setup state after call has returned
set dpdist 0
set state [list "dslot" "ret" "ret" "ret" "?" ]
set optype "?"; # for lastoptype
set fnnpc [ format "0x%08x" [expr {$fnpc+4}]]
set nnpc1 [ format "0x%08x" [expr {$fnpc+8}]]
set nnpc2 $nnpc1
set annul 0
}
# Handle annullment
if { ($annul & 2) != 0 } { set npc1 "A$fnnpc" } else { set npc1 $fnnpc }
if { ($annul & 1) != 0 } { set npc2 "A$fnnpc" } else { set npc2 $fnnpc }
# Recurse if multiple nPC:s, tail recurse for last element
if { $nnpc1 != $nnpc2 || $npc1 != $npc2 } {
# puts "Recursion nnpc:$nnpc2!"
set q [scan_wholefunc_main $fnname $npc1 $nnpc2 fnops $savelevel $dpdist $state statemap $optype]
set r [expr {$r+$q}]
}
set fnpc $npc1
set fnnpc $nnpc1
set lastoptype $optype
}
}
# Whole function scan for "LEON3FT Stale Cache After Store with Data Tag Parity Error" errata
# fnname - function name, fnaddr - function addres
# fnarr - name of array/hash-table containing operations
# key to the array is the address as 8-digit hex number prefixed with 0x
# value is a list containing 0:opcode, 1:mnemonic, 2:args
proc scan_wholefunc { fnname fnaddr fnarr } {
global fn_count fninst_count;
upvar $fnarr fnops
if { $fnname eq ".text" } {
puts "WARNING: The disassembled binary appears to be stripped, the script can not scan stripped binaries"
}
# Does the fn not contain any store at all, then we can skip it
set sid [array startsearch fnops]
set found_st 0
while { !$found_st } {
set op [array nextelement fnops $sid]
if { $op eq "" } { array donesearch fnops $sid; break }
set ophex [lindex $fnops($op) 0]
# puts "$ophex"
if { [is_store $ophex] } { incr found_st }
}
if { !$found_st } {
# puts "No store in $fnname!"
return 0
}
# -- Function contains store, proceed with scanning
# puts "Checking $fnname"
set pc "0x$fnaddr"
# puts "pc: $pc"
set npc [format "0x%08x" [expr {$pc+4}]]
array unset statemap
array set statemap ""
set ec [scan_wholefunc_main $fnname $pc $npc fnops 0 0 [list "dslot" "call" "?" "?" "?" "?"] statemap "?"]
# Update fn_count/fninst_count
incr fn_count
array unset itagchk
array set itagchk ""
set sid [array startsearch statemap]
while { [array anymore statemap $sid] } {
set x [array nextelement statemap $sid]
set iaddr [lindex $x 0]
# puts "iaddr: $iaddr"
if { [info exists fnops($iaddr)] && ![info exists itagchk($iaddr)]} {
incr fninst_count
set itagchk($iaddr) 1
}
}
array donesearch statemap $sid
return $ec
}
# --------- Main routine
if { ! [info exists sourcing] } {
# Parse command line
for {set i 0} {$i < $argc} {incr i} {
set arg [lindex $argv $i]
puts "arg=$arg"
if {"-noinfo" eq $arg} {
set msglevel [expr {$msglevel & ~($MSG_INFO)}]
}
if {"-nonote" eq $arg} {
set msglevel [expr {$msglevel & ~($MSG_NOTE)}]
}
if {"-silent" eq $arg} {
set msglevel 0
}
}
puts "\n$errata_desc errata scanning utility, rev $util_rev ($util_date)"
puts "Searching objdump -d output on standard input for:"
if {$b2bst_seqa_scan} {
puts " - $b2bst_seqa_desc"
}
if {$b2bst_seqb_scan} {
puts " - $b2bst_seqb_desc"
}
puts ""
set fn_valid 0
while {! [eof stdin]} {
set l [gets stdin]
incr line_count
# puts "line $l"
# Remove comment after '!'
set x [string first "!" "$l"]
if { $x >= 0 } then { set l [string range "$l" 0 $x] }
# check if entry point / function start
set m [regexp {^ *([0-9A-Fa-f]+) <([^ ]+)>:} "$l" t1 addr symname]
if { $m > 0 } then {
# puts "Function name=$symname addr=$addr"
if { $fn_valid } {
# puts $curfn_ops(0x40000000)
set q [scan_wholefunc $curfn $curfn_addr curfn_ops]
# puts "q=$q err_count=$err_count"
set err_count [ expr { $err_count + $q } ]
}
set curfn "$symname"
set curfn_addr "$addr"
array unset curfn_ops
array set curfn_ops ""
set fn_valid 0
} else {
# Check if opcode
set m [regexp {^ *([0-9A-Fa-f]+): *\t(.. .. .. ..) *\t([^ ]+)(.*)$} "$l" t1 addr hexcode opname opargs]
if { $m > 0 } then {
# puts "Opcode name=$opname args=$opargs addr=$addr"
incr op_count
set addr [format "%08x" 0x$addr]
set hexcode [string map {" " ""} $hexcode]
# puts "setting curfn_ops 0x$addr"
set curfn_ops(0x$addr) [list "0x$hexcode" $opname $opargs "" ""]
incr fn_valid
} else {
incr lineskip_count
}
}
}
if { $fn_valid } {
set q [scan_wholefunc $curfn $curfn_addr curfn_ops]
set err_count [ expr { $err_count + $q } ]
}
puts "\nObjdump lines processed: $line_count, lines skipped: $lineskip_count"
puts "Functions scanned: ${fn_count}, reachable instruction count: ${fninst_count}"
puts "Potential error locations found: $err_count\n"
if {$err_count == 0} {
exit 0
} else {
exit 1
}
}