# HG changeset patch # User paul # Date 2017-04-07 09:54:01 # Node ID 61dabb2523bd9285f88b29b4f35792e1fe40a792 # Parent 9a3997f734227ca2d6f44db325bad8d1016ae9b5 3.2.0.15 DPU diff --git a/sparc/leon3ft-b2bst-scan.tcl b/sparc/leon3ft-b2bst-scan.tcl new file mode 100755 --- /dev/null +++ b/sparc/leon3ft-b2bst-scan.tcl @@ -0,0 +1,681 @@ +#! /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 +} + +} +