这是很长时间以前写的东西,当时在弄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