##// END OF EJS Templates
Added ability to relocate gcda and gcno files and added gcovr wrapper...
Added ability to relocate gcda and gcno files and added gcovr wrapper This allows to run tests separatly and merge coverage at the end.

File last commit:

r369:3813be439915 R3++
r398:57611985e772 R3++
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
}
}