这是很长时间以前写的东西,当时在弄opencore里面的东西(好吧,我们公司反应比较慢,还在弄2.1里面的opencore)。用source insight看东西觉得还是有点慢,就想找找看有没有啥东西能直接把调用关系整个弄出来,于是找到这个 陈硕大牛写的
http://blog.csdn.net/Solstice/article/details/488865
动态分析的方法有点麻烦,还得把东西跑起来,于是想弄静态分析的,虽然有egypt这样的东西(Doxygen没有试用,为啥不试试?忘了),但是程序猿的毛病就是喜欢自己造轮子,正好自己没写过perl脚本,于是自己动手写一个出来。函数指针调用现在没办法,一般的虚函数还可以,好像还有个啥东西不行,也忘了。写完之后就去干别的东西了。这个东西也没怎么用,可能会有些bug。
里面的那些类似于 non-virtual thunk什么的处理当时也没写注释,现在已经想不起来了(本来也是一知半解的,只记得大概和纯虚函数有关,但是再具体些就说不出来了,得重新去翻翻书、翻翻网什么的)。有半年的时间一直想补上但是就是因为重度拖延症一直没动手。发出来就是希望逼着自己赶紧把它补齐。
共三个文件,gen.sh用来枚举.so目录下的所有文件,调用cg.pl生成一个叫callg.total的文本文件,里面记录的是每个函数里面调用的其他函数。ac.pl用来解析那个文件生成调用关系。现在是靠命令行来查询的,希望以后也把它生成一个调用图出来。
这个东西如果你要使用需要自己在三个文件里面指定.so的目录以及交叉编译链的位置。
希望有人用得上。
注意:17行那里把webcore给跳过去了,因为那个东西太大了,而我当时是用不上的https://github.com/wnfx/callGraph.pl
===============gen.sh=====================
#!/bin/bash
#下面三个需要自己指定位置
declare objd="/home/yk/work/ut6410-android2.1-v2.0/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi-objdump"
declare SoBase="/home/yk/work/ut6410-android2.1-v2.0s/out/target/product/ut6410/symbols/system/lib/"
declare readelf="/home/yk/work/ut6410-android2.1-v2.0/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi-readelf" for f in `ls $SoBase `
do
echo "disassembling $f and dumping rel information"
$objd -d $SoBase$f > asm.tmp #-d disassemble
$readelf -r -W $SoBase$f > elf.tmp # -r
echo "parsing ................ "
./cg.pl asm.tmp $f elf.tmp | tee -a callg.total
done
========================cg.pl=============================
#!/usr/bin/perl
#use diagnostics;
my $fnasm = "asm.opencore.player";
my $soname = "foo.so";
if(@ARGV[0])
{
$fnasm = @ARGV[0];
}
if(@ARGV[1])
{
$soname = @ARGV[1];
}
print $soname;#这里跳过了webcore
exit if(index(lc($soname), "webcore") >= 0);
$fnasm = "<" . $fnasm;
print "\n\n";#这里要自己指定位置
my $base = "/home/yk/work/ut6410-android2.1-v2.0/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi-";
my $flt = $base . "c++filt";
my %relMap = {};
if(@ARGV[2])
{#readelf
open(fasm, @ARGV[2]) or die "open file fail $! readelf \n" ;
while(my $line= <fasm>)
{
#0000936c 00000616 R_ARM_JUMP_SLOT 00000000 _ZN7android22MediaMetadataRetriever15extractAlbumArtEv
if($line =~ /^([0-9a-z]{8}).*R_ARM_JUMP_SLOT.*(\b[_0-9a-zA-Z]+)$/)
{
$t = `$flt $2`;
chomp($t);
$relMap{hex($1)} = $t;
#print "$1 $2 ", `$flt $2`;
}
}
close(fasm);
}
open(fasm, $fnasm) or die "open file fail $! $fnasm \n" ;
my $linecount = 0;
my $current_fun = "";
my %map = {};
my %AddrMap = {};
#for plt
my $inplt = 0;
my $lineidx = 0;
my $at1 = "";
my $at2 = "";
my %relAddrMap = {};
#end
my $intext = 0;
while(my $line=<fasm>)
{
chomp($line);
$linecount = $linecount + 1;
my $call_count = 0;
if(index($line, "Disassembly of section") >= 0)
{
if(index($line, "Disassembly of section .plt:") >= 0)
{
$inplt = 1;
}
else
{
$inplt = 0;
}
if(index($line, "Disassembly of section .text:") >= 0)
{
$intext = 1;
}
else
{
$intext = 0;
}
}
next unless ($intext || $inplt);
if($inplt)
{
# 39c8: e28fc600
add ip, pc, #0; 0x0
# 39cc: e28cca05
add ip, ip, #20480; 0x5000
# 39d0: e5bcfb0c
ldr pc, [ip, #2828]!
if(index($line, "add ip, ip") >= 0)
{
$line =~ /([0-9a-f]+):.*#([0-9]+).*/;
$at1 = $1;
$at2 = $2;
}
elsif($line =~ /\[ip, #([0-9]+)\]\!/)
{
#print $at1, "\t$at2\t$1\n";
$relAddrMap{hex($at1) - 4} = hex($at1) -4 + $at2 + $1 + 8;
}
}
elsif($line =~ /^([0-9a-f]{8}).*<([_0-9a-zA-Z]*)>/) #函数 /^0.*<(_.*)[\-|\+]?.*>/
{
my $addr = $1;
my $ret = `$flt $2`;
chomp($ret);
$externalcall = 0;
if(index($ret, "non-virtual thunk to ") >= 0)
{
$externalcall = 1;
$ret =~ s/non-virtual thunk to //;
}
elsif(index($ret,"virtual thunk to ") >= 0)
{
$externalcall = 1;
$ret =~ s/virtual thunk to //;
}
if($externalcall)
{
$ret = "!" . $ret;
}
my $matched = $ret;
if($current_fun ne $ret)
{
#push @arr, $current_fun;
$map{$ret} = () if($ret ne "");#$callcount;
$AddrMap{$ret} = $addr if($ret ne "");
}
$current_fun = $ret;
$callcount = 0;
}
elsif($line =~ /([0-9a-f]+) <([_0-9a-zA-Z]+)\-.*>/) # to .plt section
{
# 4194: f7ff ea8c
blx 36b0 <_ZN7_JNIEnv15DeleteGlobalRefEP8_jobject-0x3a8>
# print "found rel.plt $1\n";
# print $relAddrMap{hex($1)};
# print $relMap{$relAddrMap{hex($1)}};
$ret = $relMap{$relAddrMap{hex($1)}};
my $matched = $ret;
$callcount = $callcount + 1;
$flag = 0;
foreach $tv (@{$map{$current_fun}})
{
if($tv eq $matched)
{
$flag = 1;
}
}
$flag = 1 if($current_fun eq ""); #there is no parent function
if($flag < 1)
{
push @{$map{$current_fun}}, $matched;
}
}
elsif($line =~ /<([_0-9a-zA-Z]+)[\+]*.*>/) # /<([0-9a-z]+)\b([_0-9a-zA-Z]+).*>/) no asm code for external function
{ #print $1;
my $ret = `$flt $1`;
chomp($ret);
$externalcall = 0;
if(index($ret, "non-virtual thunk to ") >= 0)
{
$externalcall = 1;
$ret =~ s/non-virtual thunk to //;
}
elsif(index($ret,"virtual thunk to ") >= 0)
{
$externalcall = 1;
$ret =~ s/virtual thunk to //;
}
if($externalcall)
{
$ret = "!" . $ret;
}
my $matched = $ret;
if($matched ne $current_fun)
{
$callcount = $callcount + 1;
$flag = 0;
foreach $tv (@{$map{$current_fun}})
{
if($tv eq $matched)
{
$flag = 1;
}
}
$flag = 1 if($current_fun eq ""); #there is no parent function
if($flag < 1)
{
push @{$map{$current_fun}}, $matched;
}
}
}
}
#print "\ndumping====================================\n";
while(($key, @val) = each(%map))
{
print "[$AddrMap{$key}]$key" , "\n" ;
foreach $tt (@{$map{$key}})#(@map{"e"})
{
#print join(",", @$map{"e"}) , "\n";
print "\t->" , $tt , "\n";
}
}
close(fasm);
print "****************************\n";
__END__
=================================ac.pl===============================
#!/usr/bin/perl
#use diagnostics;
my $a2l = "/home/yk/work/ut6410-android2.1-v2.0/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi-addr2line";
my $SoBase = "/home/yk/work/ut6410-android2.1-v2.0s/out/target/product/ut6410/symbols/system/lib/";
my $ext = "so";
my $fnasm = "<" . "callg.total";
print $fnasm;
open(fasm, $fnasm) or die "open file fail $! $fnasm \n" ;
my $linecount = 0;
my $curr_fun = "";
my $curr_so = "";
my %Map = {};
my %Rmap = {};
while(my $line=<fasm>)
{
#if(index($line, "PVMFMP4FFParserNode::playResumeNotification(bool)") >= 0)
#{
# print $line;
#}
chomp($line);
$linecount = $linecount + 1;
if($line =~ /(.+\.${ext})/)
{
$curr_so = $1;
$map{$curr_so} = {};
}
elsif($line =~ /^\[[0-9a-z]+\]\!{1}(.*)/)
{
}
elsif($line =~ /^\[([0-9a-z]+)\]\!{0}(.*)/)
{
#s/!//;
$curr_fun = $2;
${$Map{$curr_so}}{$curr_fun} = ();
$Rmap{$curr_fun}{".so"} = $curr_so;
$Rmap{$2}{".addr"} = $1;
}
elsif($line =~ /\t->\!*(.*)/)
{
my $T = $1;
push @{${Map{$curr_so}{$curr_fun}}}, $T;
$Rmap{$T}{".so"} = $curr_so;
if(!($Rmap{$T}{".par"}))
{
${$Rmap{$T}}{".par"} = ();
}
push @{$Rmap{$T}{".par"}}, $curr_fun;
}
}
close(fasm);
print "\n======================Loaded====================================\n";
print "输入函数名,或 q 退出\n";
my $flag = "";
my $input = "";
my @T = ();
while($_ = <>)
{
chomp($_);
exit if $_ eq "q";
if($flag eq "check")
{
if($_ =~ /^-(\d)$/)
{
$_ = @{$Rmap{$curr_fun}{".par"}}[$1];
}
if($_ =~ /^(\d)$/)
{
$_ = @{$Map{$curr_so}{$curr_fun}}[$1];
}
$flag = "";
}
if($flag eq "")
{
@T = ();
my $tfun = $_;
my $count = 0;
while(($k, $v) = each(%Rmap))
{
if(index(lc($k), lc($_)) >= 0)
{
push @T, $k;
#print "$count:", $Rmap{$k}{".addr"}, "\t$k \n";
print "$count:", "\t$k \n";
$count++;
}
}
$len = scalar @T;
$input = 0;
if($len == 0)
{
print "啥都没找着啊,再次输入或者 q 退出\n";
next;
}
if($len > 1)
{
print "发现 $len 个函数, 输入编号选择一个, 或 r 重新输入函数名, 或 q 退出\n";
$input = <>;
chomp($input);
exit if($input eq "q");
if($input eq "r")
{
print "输入函数名,或 q 退出\n";
next;
}
}
$curr_fun = $T[$input];
$curr_so = ${Rmap{$curr_fun}{".so"}};
print $curr_fun, "\n";
print "\t模块: ", $curr_so, "\n";
$fso = $SoBase . $curr_so;
print "\t源文件: " ,`$a2l -e $fso ${Rmap{$curr_fun}{".addr"}}`;
print "\t被如下函数调用:\n";
my $pidx = 0;
my $sidx = 0;
foreach $item (@{$Rmap{$curr_fun}{".par"}})
{
print "\t\t-$pidx:\t$item\n";
$pidx++;
}
print "\t可能调用:\n";
foreach $item (@{$Map{$curr_so}{$curr_fun}})
{
print "\t\t$sidx:\t$item\n";
$sidx++;
}
print "输入函数名,或索引编号查看上下级函数, 或 q 退出\n";
$flag = "check"
}
}
__END__
===========================================================现在想想有源代码了还反过来搞这个东西好像有点吃饱了撑的。脚本call graph
http://blog.csdn.net/Solstice/article/details/488865
动态分析的方法有点麻烦,还得把东西跑起来,于是想弄静态分析的,虽然有egypt这样的东西(Doxygen没有试用,为啥不试试?忘了),但是程序猿的毛病就是喜欢自己造轮子,正好自己没写过perl脚本,于是自己动手写一个出来。函数指针调用现在没办法,一般的虚函数还可以,好像还有个啥东西不行,也忘了。写完之后就去干别的东西了。这个东西也没怎么用,可能会有些bug。
里面的那些类似于 non-virtual thunk什么的处理当时也没写注释,现在已经想不起来了(本来也是一知半解的,只记得大概和纯虚函数有关,但是再具体些就说不出来了,得重新去翻翻书、翻翻网什么的)。有半年的时间一直想补上但是就是因为重度拖延症一直没动手。发出来就是希望逼着自己赶紧把它补齐。
共三个文件,gen.sh用来枚举.so目录下的所有文件,调用cg.pl生成一个叫callg.total的文本文件,里面记录的是每个函数里面调用的其他函数。ac.pl用来解析那个文件生成调用关系。现在是靠命令行来查询的,希望以后也把它生成一个调用图出来。
这个东西如果你要使用需要自己在三个文件里面指定.so的目录以及交叉编译链的位置。
希望有人用得上。
注意:17行那里把webcore给跳过去了,因为那个东西太大了,而我当时是用不上的https://github.com/wnfx/callGraph.pl
===============gen.sh=====================
#!/bin/bash
#下面三个需要自己指定位置
declare objd="/home/yk/work/ut6410-android2.1-v2.0/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi-objdump"
declare SoBase="/home/yk/work/ut6410-android2.1-v2.0s/out/target/product/ut6410/symbols/system/lib/"
declare readelf="/home/yk/work/ut6410-android2.1-v2.0/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi-readelf" for f in `ls $SoBase `
do
echo "disassembling $f and dumping rel information"
$objd -d $SoBase$f > asm.tmp #-d disassemble
$readelf -r -W $SoBase$f > elf.tmp # -r
echo "parsing ................ "
./cg.pl asm.tmp $f elf.tmp | tee -a callg.total
done
========================cg.pl=============================
#!/usr/bin/perl
#use diagnostics;
my $fnasm = "asm.opencore.player";
my $soname = "foo.so";
if(@ARGV[0])
{
$fnasm = @ARGV[0];
}
if(@ARGV[1])
{
$soname = @ARGV[1];
}
print $soname;#这里跳过了webcore
exit if(index(lc($soname), "webcore") >= 0);
$fnasm = "<" . $fnasm;
print "\n\n";#这里要自己指定位置
my $base = "/home/yk/work/ut6410-android2.1-v2.0/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi-";
my $flt = $base . "c++filt";
my %relMap = {};
if(@ARGV[2])
{#readelf
open(fasm, @ARGV[2]) or die "open file fail $! readelf \n" ;
while(my $line= <fasm>)
{
#0000936c 00000616 R_ARM_JUMP_SLOT 00000000 _ZN7android22MediaMetadataRetriever15extractAlbumArtEv
if($line =~ /^([0-9a-z]{8}).*R_ARM_JUMP_SLOT.*(\b[_0-9a-zA-Z]+)$/)
{
$t = `$flt $2`;
chomp($t);
$relMap{hex($1)} = $t;
#print "$1 $2 ", `$flt $2`;
}
}
close(fasm);
}
open(fasm, $fnasm) or die "open file fail $! $fnasm \n" ;
my $linecount = 0;
my $current_fun = "";
my %map = {};
my %AddrMap = {};
#for plt
my $inplt = 0;
my $lineidx = 0;
my $at1 = "";
my $at2 = "";
my %relAddrMap = {};
#end
my $intext = 0;
while(my $line=<fasm>)
{
chomp($line);
$linecount = $linecount + 1;
my $call_count = 0;
if(index($line, "Disassembly of section") >= 0)
{
if(index($line, "Disassembly of section .plt:") >= 0)
{
$inplt = 1;
}
else
{
$inplt = 0;
}
if(index($line, "Disassembly of section .text:") >= 0)
{
$intext = 1;
}
else
{
$intext = 0;
}
}
next unless ($intext || $inplt);
if($inplt)
{
# 39c8: e28fc600
add ip, pc, #0; 0x0
# 39cc: e28cca05
add ip, ip, #20480; 0x5000
# 39d0: e5bcfb0c
ldr pc, [ip, #2828]!
if(index($line, "add ip, ip") >= 0)
{
$line =~ /([0-9a-f]+):.*#([0-9]+).*/;
$at1 = $1;
$at2 = $2;
}
elsif($line =~ /\[ip, #([0-9]+)\]\!/)
{
#print $at1, "\t$at2\t$1\n";
$relAddrMap{hex($at1) - 4} = hex($at1) -4 + $at2 + $1 + 8;
}
}
elsif($line =~ /^([0-9a-f]{8}).*<([_0-9a-zA-Z]*)>/) #函数 /^0.*<(_.*)[\-|\+]?.*>/
{
my $addr = $1;
my $ret = `$flt $2`;
chomp($ret);
$externalcall = 0;
if(index($ret, "non-virtual thunk to ") >= 0)
{
$externalcall = 1;
$ret =~ s/non-virtual thunk to //;
}
elsif(index($ret,"virtual thunk to ") >= 0)
{
$externalcall = 1;
$ret =~ s/virtual thunk to //;
}
if($externalcall)
{
$ret = "!" . $ret;
}
my $matched = $ret;
if($current_fun ne $ret)
{
#push @arr, $current_fun;
$map{$ret} = () if($ret ne "");#$callcount;
$AddrMap{$ret} = $addr if($ret ne "");
}
$current_fun = $ret;
$callcount = 0;
}
elsif($line =~ /([0-9a-f]+) <([_0-9a-zA-Z]+)\-.*>/) # to .plt section
{
# 4194: f7ff ea8c
blx 36b0 <_ZN7_JNIEnv15DeleteGlobalRefEP8_jobject-0x3a8>
# print "found rel.plt $1\n";
# print $relAddrMap{hex($1)};
# print $relMap{$relAddrMap{hex($1)}};
$ret = $relMap{$relAddrMap{hex($1)}};
my $matched = $ret;
$callcount = $callcount + 1;
$flag = 0;
foreach $tv (@{$map{$current_fun}})
{
if($tv eq $matched)
{
$flag = 1;
}
}
$flag = 1 if($current_fun eq ""); #there is no parent function
if($flag < 1)
{
push @{$map{$current_fun}}, $matched;
}
}
elsif($line =~ /<([_0-9a-zA-Z]+)[\+]*.*>/) # /<([0-9a-z]+)\b([_0-9a-zA-Z]+).*>/) no asm code for external function
{ #print $1;
my $ret = `$flt $1`;
chomp($ret);
$externalcall = 0;
if(index($ret, "non-virtual thunk to ") >= 0)
{
$externalcall = 1;
$ret =~ s/non-virtual thunk to //;
}
elsif(index($ret,"virtual thunk to ") >= 0)
{
$externalcall = 1;
$ret =~ s/virtual thunk to //;
}
if($externalcall)
{
$ret = "!" . $ret;
}
my $matched = $ret;
if($matched ne $current_fun)
{
$callcount = $callcount + 1;
$flag = 0;
foreach $tv (@{$map{$current_fun}})
{
if($tv eq $matched)
{
$flag = 1;
}
}
$flag = 1 if($current_fun eq ""); #there is no parent function
if($flag < 1)
{
push @{$map{$current_fun}}, $matched;
}
}
}
}
#print "\ndumping====================================\n";
while(($key, @val) = each(%map))
{
print "[$AddrMap{$key}]$key" , "\n" ;
foreach $tt (@{$map{$key}})#(@map{"e"})
{
#print join(",", @$map{"e"}) , "\n";
print "\t->" , $tt , "\n";
}
}
close(fasm);
print "****************************\n";
__END__
=================================ac.pl===============================
#!/usr/bin/perl
#use diagnostics;
my $a2l = "/home/yk/work/ut6410-android2.1-v2.0/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi-addr2line";
my $SoBase = "/home/yk/work/ut6410-android2.1-v2.0s/out/target/product/ut6410/symbols/system/lib/";
my $ext = "so";
my $fnasm = "<" . "callg.total";
print $fnasm;
open(fasm, $fnasm) or die "open file fail $! $fnasm \n" ;
my $linecount = 0;
my $curr_fun = "";
my $curr_so = "";
my %Map = {};
my %Rmap = {};
while(my $line=<fasm>)
{
#if(index($line, "PVMFMP4FFParserNode::playResumeNotification(bool)") >= 0)
#{
# print $line;
#}
chomp($line);
$linecount = $linecount + 1;
if($line =~ /(.+\.${ext})/)
{
$curr_so = $1;
$map{$curr_so} = {};
}
elsif($line =~ /^\[[0-9a-z]+\]\!{1}(.*)/)
{
}
elsif($line =~ /^\[([0-9a-z]+)\]\!{0}(.*)/)
{
#s/!//;
$curr_fun = $2;
${$Map{$curr_so}}{$curr_fun} = ();
$Rmap{$curr_fun}{".so"} = $curr_so;
$Rmap{$2}{".addr"} = $1;
}
elsif($line =~ /\t->\!*(.*)/)
{
my $T = $1;
push @{${Map{$curr_so}{$curr_fun}}}, $T;
$Rmap{$T}{".so"} = $curr_so;
if(!($Rmap{$T}{".par"}))
{
${$Rmap{$T}}{".par"} = ();
}
push @{$Rmap{$T}{".par"}}, $curr_fun;
}
}
close(fasm);
print "\n======================Loaded====================================\n";
print "输入函数名,或 q 退出\n";
my $flag = "";
my $input = "";
my @T = ();
while($_ = <>)
{
chomp($_);
exit if $_ eq "q";
if($flag eq "check")
{
if($_ =~ /^-(\d)$/)
{
$_ = @{$Rmap{$curr_fun}{".par"}}[$1];
}
if($_ =~ /^(\d)$/)
{
$_ = @{$Map{$curr_so}{$curr_fun}}[$1];
}
$flag = "";
}
if($flag eq "")
{
@T = ();
my $tfun = $_;
my $count = 0;
while(($k, $v) = each(%Rmap))
{
if(index(lc($k), lc($_)) >= 0)
{
push @T, $k;
#print "$count:", $Rmap{$k}{".addr"}, "\t$k \n";
print "$count:", "\t$k \n";
$count++;
}
}
$len = scalar @T;
$input = 0;
if($len == 0)
{
print "啥都没找着啊,再次输入或者 q 退出\n";
next;
}
if($len > 1)
{
print "发现 $len 个函数, 输入编号选择一个, 或 r 重新输入函数名, 或 q 退出\n";
$input = <>;
chomp($input);
exit if($input eq "q");
if($input eq "r")
{
print "输入函数名,或 q 退出\n";
next;
}
}
$curr_fun = $T[$input];
$curr_so = ${Rmap{$curr_fun}{".so"}};
print $curr_fun, "\n";
print "\t模块: ", $curr_so, "\n";
$fso = $SoBase . $curr_so;
print "\t源文件: " ,`$a2l -e $fso ${Rmap{$curr_fun}{".addr"}}`;
print "\t被如下函数调用:\n";
my $pidx = 0;
my $sidx = 0;
foreach $item (@{$Rmap{$curr_fun}{".par"}})
{
print "\t\t-$pidx:\t$item\n";
$pidx++;
}
print "\t可能调用:\n";
foreach $item (@{$Map{$curr_so}{$curr_fun}})
{
print "\t\t$sidx:\t$item\n";
$sidx++;
}
print "输入函数名,或索引编号查看上下级函数, 或 q 退出\n";
$flag = "check"
}
}
__END__
===========================================================现在想想有源代码了还反过来搞这个东西好像有点吃饱了撑的。脚本call graph
解决方案 »
免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货