use strict; | |
use warnings; | |
#*--------------------------------------------------------------------------* | |
#* Global variables | |
#*--------------------------------------------------------------------------* | |
my $LISFILE = $ARGV[0]; | |
my $DBGFILE = $ARGV[1]; | |
my %lang_pack_set; | |
my %cust_pack_set; | |
my %jump_table_set; | |
my %lib_set; | |
my %symbol_set; | |
my %symbol_startaddr_set; | |
my %symbol_endaddr_set; | |
my %filesrange_set; | |
my (@ref_sec, @dec_sec, @dec_region, @content, @total); # referencing obj, referencing function, declaration obj, declaraion region, referenced content | |
my ($initialtime, $finishtime, $exectime); | |
my $output = ""; | |
#*--------------------------------------------------------------------------* | |
#* Code Body | |
#*--------------------------------------------------------------------------* | |
warn " | |
============================================================================== | |
[Program] MBA_Check.pl | |
[Version] V2.0 | |
[Date] 2011-12-01 | |
[Author] yikuan.huang\@mediatek.com Timon.Lu\@mediatek.com | |
[Copyright] Copyright (C) 2011 MediaTek Incorporation. All Rights Reserved. | |
============================================================================== | |
"; | |
$initialtime = time(); | |
# parse the dbg file | |
parsedbgfile(); | |
# parse the lis file | |
parselisfile(); | |
# check the reference chain | |
getFalseReference(); | |
# output result to file | |
writeOutput(); | |
$finishtime = time(); | |
$exectime = $finishtime - $initialtime; | |
print "Total execution time: ".$exectime." seconds\n"; | |
#**************************************************************************** | |
# FUNCTION | |
# parsedbgfile | |
# DESCRIPTION | |
# This function parse DBG info file | |
# if you need to know detail, please refer to DBG file format | |
#**************************************************************************** | |
sub parsedbgfile{ | |
my $dbgfilesize = -s $ARGV[1]; | |
my ($mainversion, $subversion, $projectname, $hwversion, $swversion, $buildtime, $symtableoffset, $filetableoffset); | |
my $symtablelength; | |
my ($buf, $data, $n); | |
my @contents; | |
#------------------------------------------------------------- | |
die "$DBGFILE does not exist!\n" if (!-e $DBGFILE); | |
open (DBG_FILE_HANDLE, "<$DBGFILE") or die "Fail to open $DBGFILE\n"; | |
binmode DBG_FILE_HANDLE; | |
#------------------------------------------------------------- | |
$n = sysread (DBG_FILE_HANDLE, $data, $dbgfilesize); | |
#------------------------parse header -------------------------- | |
if($data =~ /CATI(.{4})(.{4})([\w\s\x2E\x3A\x2F\x5F]+)\x00([\w\s\x2E\x3A\x2F\x5F]+)\x00([\w\s\x2E\x3A\x2F\x5F]+)\x00([\w\s\x2E\x3A\x2F\x5F]+)\x00(.{4})(.{4})([\s\S]*)/){ | |
#mainversion of debug info | |
$mainversion = unpack('V', $1); | |
#subversion of debug info | |
$subversion = unpack('V', $2); | |
#project name of debug info | |
$projectname = $3; | |
#HW version of debug info | |
$hwversion = $4; | |
#SW version of debug info | |
$swversion = $5; | |
#build time of debug info which will be generated after building binary | |
$buildtime = $6; | |
#symtable offset of debug info which is append after debug info header (mainversion...etc) | |
$symtableoffset = unpack('V', $7); | |
#filetable offset of debug info which is append after symtable. | |
$filetableoffset = unpack('V', $8); | |
#debug info version match | |
if(($mainversion != 1) || ($subversion != 0)){ | |
die "debug info file version is incorrect! VER_MAIN: ".$mainversion.", VER_SUB: ".$subversion."\n"; | |
} | |
#get symbol table length | |
$symtablelength = $filetableoffset - $symtableoffset; | |
#truncate symbol table | |
$data = substr($9, 0, $symtablelength); | |
my $count = 3; | |
my $symbol; | |
#split symbol table by its format. name->null string->4 bytes for start addr->4 bytes for end addr ...repeat | |
my @symbols = split(/([\w:()]+).(.{4})(.{4})/, $data); | |
foreach my $val (@symbols) { | |
if(($count % 4 ) == 0){ | |
#print length($val)." ".$val."\n"; | |
$symbol = $val; | |
$symbol_set{$symbol} = $symbol; | |
} | |
elsif(($count % 4 ) == 1){ | |
#print length($val)." ".unpack('V', $val)."\n"; | |
$symbol_startaddr_set{$symbol} = unpack('V', $val); | |
} | |
elsif(($count % 4 ) == 2){ | |
#print length($val)." ".unpack('V', $val)."\n"; | |
$symbol_endaddr_set{$symbol} = unpack('V', $val); | |
} | |
$count++; | |
} | |
#truncate file table | |
$data = substr($9, $symtablelength+1); | |
$count = 1; | |
my $file_name=""; | |
#split file table by its format. name->null string->4 bytes for number of addr range pair-> 4 bytes for | |
#pair one start addr->4 bytes for pair one end addr->......pair loop | |
#my @files = split(/([\w\x2F]+\x2F[\w]+).[c|cpp]\x00/, $data); | |
my @files = split(/\w+\x2F([\w]+).[c|cpp]\x00/, $data); | |
foreach my $val (@files) { | |
if($count % 2 == 0){ | |
$file_name = $val; | |
#print $file_name."\n"; | |
} | |
elsif($count % 2 == 1){ | |
@contents = unpack("V*", $val); | |
$filesrange_set{$file_name} = [@contents]; | |
} | |
$count++; | |
} | |
} | |
else{ | |
} | |
close(DBG_FILE_HANDLE); | |
} | |
#**************************************************************************** | |
# FUNCTION | |
# parselisfile | |
# DESCRIPTION | |
# This function parse LIS file | |
#**************************************************************************** | |
sub parselisfile{ | |
my ($i, $ref_count); | |
my $loadregionswitch = 0; | |
my $langpackswitch = 0; | |
my $custpackswitch = 0; | |
my $jumptableswitch = 0; | |
$i = 0; | |
die "$LISFILE does not exist!\n" if (!-e $LISFILE); | |
open (FILE_HANDLE, "<$LISFILE") or die "Fail to open $LISFILE\n"; | |
while (<FILE_HANDLE>) { | |
if (/(\S+)\..*\((\S+)\)\srefers\sto\s(\S+)\..*\((\S+)\)\sfor\s(\S+)\s*/) { # AAA.obj(i.func/pragma) refers to BBB.obj(i.func/pragma/region) for content | |
$ref_sec[$i] = $2.$1; # AAA | |
$dec_sec[$i] = $4.$3; # BBB | |
$total[$i] = $_; | |
$i++; # should be indexed with the same $i | |
} | |
if (/Memory Map of the image/){ | |
$loadregionswitch = 1; | |
} | |
if (/Image component sizes/){ | |
$loadregionswitch = 0; | |
} | |
# turn on the switch if meet Load Region LANG_PACK_ROM | |
if (/Execution Region LANG_PACK_ROM_CONTENT/){ | |
$langpackswitch = 1; | |
} | |
else{ | |
# turn off the switch if meet another Load Region | |
if (/Execution Region/){ | |
$langpackswitch = 0; | |
} | |
} | |
# turn on the switch if meet Load Region CUSTPACK_ROM | |
if (/Execution Region CUSTPACK_ROM_CONTENT/){ | |
$custpackswitch = 1; | |
} | |
else{ | |
# turn off the switch if meet another Load Region | |
if (/Execution Region/){ | |
$custpackswitch = 0; | |
} | |
} | |
# turn on the switch if meet Load Region JUMP_TABLE | |
if (/Load Region JUMP_TABLE/){ | |
$jumptableswitch = 1; | |
} | |
else{ | |
# turn off the switch if meet another Load Region | |
if (/Execution Region/){ | |
$jumptableswitch = 0; | |
} | |
} | |
# parse the lang pack section | |
if ($langpackswitch == 1){ | |
if(/\s*0x\S*\s*\S*\s*\S*\s*\S*\s*\S*\s*(\S*)\s*(\w*)/){ | |
unless(trim($1.$2) eq ""){ | |
#print $2.$1."\n"; | |
$lang_pack_set{$1.$2}=$1.$2; | |
} | |
} | |
} | |
# parse the cust pack section | |
if ($custpackswitch == 1){ | |
if(/\s*0x\S*\s*\S*\s*\S*\s*\S*\s*\S*\s*(\S*)\s*(\w*)/){ | |
unless(trim($1.$2) eq ""){ | |
$cust_pack_set{$1.$2}=$1.$2; | |
} | |
} | |
} | |
# parse the jump table section | |
if ($jumptableswitch == 1){ | |
if(/\s*0x\S*\s*\S*\s*\S*\s*\S*\s*\S*\s*(\S*)\s*(\w*)/){ | |
unless(trim($1.$2) eq ""){ | |
#print $2.$1."\n"; | |
$jump_table_set{$1.$2}=$1.$2; | |
} | |
} | |
} | |
if ($loadregionswitch == 1){ | |
#parse function | |
if(/\s*0x\S*\s*\S*\s*\S*\s*\S*\s*\S*\s*(\S*)\s*(\S*)/){ | |
my $section = $1; | |
my $objandLib = $2; | |
if($objandLib =~ /(\S*)\.o.*\((\S*)\)/){ #$2 = library name | |
$lib_set{$section.$1} = $2; | |
#print "$section -> $1 -> $2 \n"; | |
} | |
} | |
} | |
} | |
close FILE_HANDLE or die "Fail to close $LISFILE\n"; | |
} | |
#**************************************************************************** | |
# FUNCTION | |
# replaceFileName | |
# DESCRIPTION | |
# this function use the result of parsedbgfile to get real file name! | |
# | |
#**************************************************************************** | |
sub replaceFileName{ | |
my $filename = ""; | |
my $libname = ""; | |
my ($fromfunctionaddr, $tofunctionaddr, $fromfilename, $tofilename); | |
if($_[0] =~ /(\S+)\..*[\(\.]+(\w+)\)\srefers\sto\s(\S+)\..*[\(\.]+(\w+)\)\sfor\s(\S+)\s*/){ | |
$fromfunctionaddr = $symbol_startaddr_set{$2}; # $2 = from function address | |
$tofunctionaddr = $symbol_startaddr_set{$4}; # $4 = to function address | |
# those two line are used to prevent warning msg. | |
$fromfunctionaddr = defined $fromfunctionaddr ? $fromfunctionaddr : 0; | |
$tofunctionaddr = defined $tofunctionaddr ? $tofunctionaddr : 0; | |
# look for file name by function address | |
for $filename (keys %filesrange_set){ | |
if(length($filename) != 0){ | |
for(my $i = 1; $i < ($filesrange_set{$filename}[0]*2+1); $i+=2){ | |
if($fromfunctionaddr != 0 && $fromfunctionaddr >= $filesrange_set{$filename}[$i] && $fromfunctionaddr <= $filesrange_set{$filename}[$i+1]){ | |
$fromfilename = $filename; | |
$_[0] =~ s/$1/$fromfilename/g; | |
} | |
if($tofunctionaddr != 0 && $tofunctionaddr >= $filesrange_set{$filename}[$i] && $tofunctionaddr <= $filesrange_set{$filename}[$i+1]){ | |
$tofilename = $filename; | |
$_[0] =~ s/$3/$tofilename/g; | |
} | |
} | |
} | |
} | |
} | |
# those two line are used to prevent warning msg. | |
$fromfilename = defined $fromfilename ? $fromfilename : ""; | |
$tofilename = defined $tofilename ? $tofilename : ""; | |
$libname = $lib_set{$_[1]}; | |
$libname = defined $libname ? $libname : ""; | |
if(length($fromfilename) != 0){ | |
$_[0] = "Warnning : $fromfilename.obj ($libname) \n ".$_[0]; | |
} | |
else{ | |
$_[0] = "Warnning : $1.obj ($libname) \n ".$_[0]; | |
} | |
} | |
#**************************************************************************** | |
# FUNCTION | |
# getFalseReference | |
# DESCRIPTION | |
# Check the function reference | |
#**************************************************************************** | |
sub getFalseReference{ | |
for (my $i = 0;$i<@ref_sec;$i++) { | |
my $from_sec = $ref_sec[$i]; | |
my $to_sec = $dec_sec[$i]; | |
my $line = $total[$i]; | |
# hard code to skip function InitializeResourceVariables and StandaloneResValidation | |
if(($from_sec =~ /resource_custpack_jtbl.*/) || ($from_sec =~ /resource_lang_pack_jtbl.*/)){ | |
next; | |
} | |
# check if lang not refer to lang | |
if(isLangPack($from_sec) eq "true"){ | |
unless(isLangPack($to_sec) eq "true"){ | |
replaceFileName($line, $from_sec); | |
$output = $output.$line."Recommend Correction ---> Cannot Cross Reference Out of Lang Pack Content, Please Move RO-CODE out of RESB or Revise Data Structure\n\n"; | |
} | |
} | |
# check if cust not refer to cust | |
if(isCustPack($from_sec) eq "true"){ | |
unless(isCustPack($to_sec) eq "true"){ | |
replaceFileName($line, $from_sec); | |
$output = $output.$line."Recommend Correction ---> Cannot Cross Reference Out of Cust Pack Content, Please Move RO-CODE out of RESB or Revise Data Structure\n\n"; | |
} | |
} | |
# check if jtb not refer to lang or cust | |
if(isJumpTable($from_sec) eq "true"){ | |
unless((isLangPack($to_sec) eq "true")||(isCustPack($to_sec) eq "true")){ | |
replaceFileName($line, $from_sec); | |
$output = $output.$line."Recommend Correction ---> Critial Probelm, Please Reach System Service To Help You\n\n"; | |
} | |
} | |
# check if not lang or jtb refer to lang | |
if(isLangPack($to_sec) eq "true"){ | |
unless((isLangPack($from_sec) eq "true")||(isJumpTable($from_sec) eq "true")){ | |
replaceFileName($line, $from_sec); | |
$output = $output.$line."Recommend Correction ---> Cannot Directly Reference To Lang Pack Content, Lookup JumpTable First\n\n"; | |
} | |
} | |
# check if not cust or jtb refer to cust | |
if(isCustPack($to_sec) eq "true"){ | |
unless((isCustPack($from_sec) eq "true")||(isJumpTable($from_sec) eq "true")){ | |
replaceFileName($line, $from_sec); | |
$output = $output.$line."Recommend Correction ---> Cannot Directly Reference To Cust Pack Content, Lookup JumpTable First\n\n"; | |
} | |
} | |
# check if lang or cust refer to jtb | |
if(isJumpTable($to_sec) eq "true"){ | |
if((isLangPack($from_sec) eq "true")||(isCustPack($from_sec) eq "true")){ | |
replaceFileName($line, $from_sec); | |
$output = $output.$line."Recommend Correction ---> Cannot Reverse Reference From RESB to Jump Table, Please Revise Data Structure\n\n"; | |
} | |
} | |
} | |
} | |
#**************************************************************************** | |
# FUNCTION | |
# isLangPack | |
# DESCRIPTION | |
# Check the obj if exist in Lang Pack region | |
#**************************************************************************** | |
sub isLangPack(){ | |
my $result = "false"; | |
my $obj = $_[0]; | |
while ( my ($key, $value) = each(%lang_pack_set) ) { | |
my $target = $key; | |
if($obj eq $target){ | |
$result = "true"; | |
} | |
} | |
return $result; | |
} | |
#**************************************************************************** | |
# FUNCTION | |
# isCustPack | |
# DESCRIPTION | |
# Check the obj if exist in Cust Pack region | |
#**************************************************************************** | |
sub isCustPack(){ | |
my $result = "false"; | |
my $obj = $_[0]; | |
while ( my ($key, $value) = each(%cust_pack_set) ) { | |
my $target = $key; | |
if($obj eq $target){ | |
$result = "true"; | |
} | |
} | |
return $result; | |
} | |
#**************************************************************************** | |
# FUNCTION | |
# isJumpTable | |
# DESCRIPTION | |
# Check the obj if exist in Jump Table region | |
#**************************************************************************** | |
sub isJumpTable(){ | |
my $result = "false"; | |
my $obj = $_[0]; | |
while ( my ($key, $value) = each(%jump_table_set) ) { | |
my $target = $key; | |
if($obj eq $target){ | |
$result = "true"; | |
} | |
} | |
return $result; | |
} | |
#**************************************************************************** | |
# FUNCTION | |
# writeOutput | |
# DESCRIPTION | |
# Output the result to file | |
#**************************************************************************** | |
sub writeOutput{ | |
# set the name of output file | |
my @part = split /\\/, $LISFILE; | |
my $outputFile = $part[-1]; | |
$outputFile =~ s/lis/txt/; | |
#write result to file | |
open(RESULT,">$outputFile") || die "File open error!\n"; | |
print RESULT $output; | |
close(RESULT) | |
} | |
#**************************************************************************** | |
# FUNCTION | |
# trim | |
# DESCRIPTION | |
# Trim the space in front and back of string | |
#**************************************************************************** | |
sub trim($) | |
{ | |
my $string = shift; | |
$string =~ s/^\s+//; | |
$string =~ s/\s+$//; | |
return $string; | |
} |