| #!/usr/bin/perl |
| ########################################################################### |
| # ABI Dumper 0.99.18 |
| # Dump ABI of an ELF object containing DWARF debug info |
| # |
| # Copyright (C) 2013-2016 Andrey Ponomarenko's ABI Laboratory |
| # |
| # Written by Andrey Ponomarenko |
| # |
| # PLATFORMS |
| # ========= |
| # Linux |
| # |
| # REQUIREMENTS |
| # ============ |
| # Perl 5 (5.8 or newer) |
| # Elfutils (eu-readelf) |
| # Vtable-Dumper (1.1 or newer) |
| # Binutils (objdump) |
| # Universal Ctags |
| # GCC (g++) |
| # |
| # COMPATIBILITY |
| # ============= |
| # ABI Compliance Checker >= 1.99.14 |
| # |
| # |
| # This program is free software: you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License or the GNU Lesser |
| # General Public License as published by the Free Software Foundation. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # and the GNU Lesser General Public License along with this program. |
| # If not, see <http://www.gnu.org/licenses/>. |
| ########################################################################### |
| use Getopt::Long; |
| Getopt::Long::Configure ("posix_default", "no_ignore_case", "permute"); |
| use File::Path qw(mkpath rmtree); |
| use File::Temp qw(tempdir); |
| use Cwd qw(abs_path cwd realpath); |
| use Storable qw(dclone); |
| use Data::Dumper; |
| |
| my $TOOL_VERSION = "0.99.18"; |
| my $ABI_DUMP_VERSION = "3.3"; |
| my $ORIG_DIR = cwd(); |
| my $TMP_DIR = tempdir(CLEANUP=>1); |
| |
| my $VTABLE_DUMPER = "vtable-dumper"; |
| my $VTABLE_DUMPER_VERSION = "1.0"; |
| |
| my $LOCALE = "LANG=C.UTF-8"; |
| my $EU_READELF = "eu-readelf"; |
| my $EU_READELF_L = $LOCALE." ".$EU_READELF; |
| my $OBJDUMP = "objdump"; |
| my $CTAGS = "ctags"; |
| my $GPP = "g++"; |
| |
| my ($Help, $ShowVersion, $DumpVersion, $OutputDump, $SortDump, $StdOut, |
| $TargetVersion, $ExtraInfo, $FullDump, $AllTypes, $AllSymbols, $BinOnly, |
| $SkipCxx, $Loud, $AddrToName, $DumpStatic, $Compare, $AltDebugInfoOpt, |
| $AddDirs, $VTDumperPath, $SymbolsListPath, $PublicHeadersPath, |
| $IgnoreTagsPath, $KernelExport, $UseTU, $ReimplementStd, |
| $IncludePreamble, $IncludePaths, $CacheHeaders, $MixedHeaders, $Debug, |
| $SearchDirDebuginfo, $KeepRegsAndOffsets); |
| |
| my $CmdName = getFilename($0); |
| |
| my %ERROR_CODE = ( |
| "Success"=>0, |
| "Error"=>2, |
| # System command is not found |
| "Not_Found"=>3, |
| # Cannot access input files |
| "Access_Error"=>4, |
| # Cannot find a module |
| "Module_Error"=>9, |
| # No debug-info |
| "No_DWARF"=>10, |
| # Invalid debug-info |
| "Invalid_DWARF"=>11 |
| ); |
| |
| my $ShortUsage = "ABI Dumper $TOOL_VERSION |
| Dump ABI of an ELF object containing DWARF debug info |
| Copyright (C) 2016 Andrey Ponomarenko's ABI Laboratory |
| License: GNU LGPL or GNU GPL |
| |
| Usage: $CmdName [options] [object] |
| Example: |
| $CmdName libTest.so -o ABI.dump |
| $CmdName Module.ko.debug -o ABI.dump |
| |
| More info: $CmdName --help\n"; |
| |
| if($#ARGV==-1) |
| { |
| printMsg("INFO", $ShortUsage); |
| exit(0); |
| } |
| |
| GetOptions("h|help!" => \$Help, |
| "v|version!" => \$ShowVersion, |
| "dumpversion!" => \$DumpVersion, |
| # general options |
| "o|output|dump-path=s" => \$OutputDump, |
| "sort!" => \$SortDump, |
| "stdout!" => \$StdOut, |
| "loud!" => \$Loud, |
| "vnum|lver|lv=s" => \$TargetVersion, |
| "extra-info=s" => \$ExtraInfo, |
| "bin-only!" => \$BinOnly, |
| "all-types!" => \$AllTypes, |
| "all-symbols!" => \$AllSymbols, |
| "symbols-list=s" => \$SymbolsListPath, |
| "skip-cxx!" => \$SkipCxx, |
| "all!" => \$FullDump, |
| "dump-static!" => \$DumpStatic, |
| "compare!" => \$Compare, |
| "alt=s" => \$AltDebugInfoOpt, |
| "dir!" => \$AddDirs, |
| "vt-dumper=s" => \$VTDumperPath, |
| "public-headers=s" => \$PublicHeadersPath, |
| "ignore-tags=s" => \$IgnoreTagsPath, |
| "mixed-headers!" => \$MixedHeaders, |
| "kernel-export!" => \$KernelExport, |
| "search-debuginfo=s" => \$SearchDirDebuginfo, |
| "keep-registers-and-offsets!" => \$KeepRegsAndOffsets, |
| "debug!" => \$Debug, |
| # extra options |
| "use-tu-dump!" => \$UseTU, |
| "include-preamble=s" => \$IncludePreamble, |
| "include-paths=s" => \$IncludePaths, |
| "cache-headers=s" => \$CacheHeaders, |
| # internal options |
| "addr2name!" => \$AddrToName, |
| # obsolete |
| "reimplement-std!" => \$ReimplementStd |
| ) or ERR_MESSAGE(); |
| |
| sub ERR_MESSAGE() |
| { |
| printMsg("INFO", "\n".$ShortUsage); |
| exit($ERROR_CODE{"Error"}); |
| } |
| |
| my $HelpMessage=" |
| NAME: |
| ABI Dumper ($CmdName) |
| Dump ABI of an ELF object containing DWARF debug info |
| |
| DESCRIPTION: |
| ABI Dumper is a tool for dumping ABI information of an ELF object |
| containing DWARF debug info. |
| |
| The tool is intended to be used with ABI Compliance Checker tool for |
| tracking ABI changes of a C/C++ library or kernel module. |
| |
| This tool is free software: you can redistribute it and/or modify it |
| under the terms of the GNU LGPL or GNU GPL. |
| |
| USAGE: |
| $CmdName [options] [object] |
| |
| EXAMPLES: |
| $CmdName libTest.so -o ABI.dump |
| $CmdName Module.ko.debug -o ABI.dump |
| |
| INFORMATION OPTIONS: |
| -h|-help |
| Print this help. |
| |
| -v|-version |
| Print version information. |
| |
| -dumpversion |
| Print the tool version ($TOOL_VERSION) and don't do anything else. |
| |
| GENERAL OPTIONS: |
| -o|-output PATH |
| Path to the output ABI dump file. |
| Default: ./ABI.dump |
| |
| -sort |
| Sort data in ABI dump. |
| |
| -stdout |
| Print ABI dump to stdout. |
| |
| -loud |
| Print all warnings. |
| |
| -vnum NUM |
| Set version of the library to NUM. |
| |
| -extra-info DIR |
| Dump extra analysis info to DIR. |
| |
| -bin-only |
| Do not dump information about inline functions, |
| pure virtual functions and non-exported global data. |
| |
| -all-types |
| Dump unused data types. |
| |
| -all-symbols |
| Dump symbols not exported by the object. |
| |
| -symbols-list PATH |
| Specify a file with a list of symbols that should be dumped. |
| |
| -skip-cxx |
| Do not dump stdc++ and gnu c++ symbols. |
| |
| -all |
| Equal to: -all-types -all-symbols. |
| |
| -dump-static |
| Dump static (local) symbols. |
| |
| -compare OLD.dump NEW.dump |
| Show added/removed symbols between two ABI dumps. |
| |
| -alt PATH |
| Path to the alternate debug info (Fedora). It is |
| detected automatically from gnu_debugaltlink section |
| of the input object if not specified. |
| |
| -dir |
| Show full paths of source files. |
| |
| -vt-dumper PATH |
| Path to the vtable-dumper executable if it is installed |
| to non-default location (not in PATH). |
| |
| -public-headers PATH |
| Path to directory with public header files or to file with |
| the list of header files. This option allows to filter out |
| private symbols from the ABI dump. |
| |
| -ignore-tags PATH |
| Path to ignore.tags file to help ctags tool to read |
| symbols in header files. |
| |
| -reimplement-std |
| Do nothing. |
| |
| -mixed-headers |
| This option should be specified if you are using |
| -public-headers option and the names of public headers |
| intersect with the internal headers. |
| |
| -kernel-export |
| Dump symbols exported by the Linux kernel and modules, i.e. |
| symbols declared in the ksymtab section of the object and |
| system calls. |
| |
| -search-debuginfo DIR |
| Search for debug-info files referenced from gnu_debuglink |
| section of the object in DIR. |
| |
| -keep-registers-and-offsets |
| Dump used registers and stack offsets even if incompatible |
| build options detected. |
| |
| -debug |
| Enable debug messages. |
| |
| EXTRA OPTIONS: |
| -use-tu-dump |
| Use g++ -fdump-translation-unit instead of ctags to |
| list symbols in headers. This may be useful if all |
| functions are declared via macros in headers and |
| ctags can't recognize them. |
| |
| -include-preamble PATHS |
| Specify header files (separated by semicolon) that |
| should be included before others to compile without |
| errors. |
| |
| -include-paths DIRS |
| Specify include directories (separated by semicolon) |
| that should be passed to the compiler by -I option |
| in order to compile headers without errors. If this |
| option is not set then the tool will try to generate |
| include paths automatically. |
| |
| -cache-headers DIR |
| Cache headers analysis results to reuse later. |
| "; |
| |
| sub HELP_MESSAGE() { |
| printMsg("INFO", $HelpMessage); |
| } |
| |
| my %Cache; |
| |
| # Input |
| my %DWARF_Info; |
| |
| # Alternate |
| my %ImportedUnit; |
| my %ImportedDecl; |
| my $AltDebugInfo = undef; |
| |
| # Dump |
| my %TypeUnit; |
| my %Post_Change; |
| my %UsedUnit; |
| my %UsedDecl; |
| |
| # Output |
| my %SymbolInfo; |
| my %TypeInfo; |
| |
| # Reader |
| my %TypeMember; |
| my %ArrayCount; |
| my %FuncParam; |
| my %TmplParam; |
| my %Inheritance; |
| my %NameSpace; |
| my %SpecElem; |
| my %OrigElem; |
| my %ClassMethods; |
| my %TypeSpec; |
| my %ClassChild; |
| |
| my %MergedTypes; |
| my %LocalType; |
| |
| my %SourceFile; |
| my %SourceFile_Alt; |
| my %DebugLoc; |
| my %TName_Tid; |
| my %TName_Tids; |
| my %RegName; |
| |
| my $STDCXX_TARGET = 0; |
| my $GLOBAL_ID = 0; |
| my %ANON_TYPE_WARN = (); |
| |
| my %Mangled_ID; |
| my %Checked_Spec; |
| my %SelectedSymbols; |
| |
| my %TypeType = ( |
| "class_type"=>"Class", |
| "structure_type"=>"Struct", |
| "union_type"=>"Union", |
| "enumeration_type"=>"Enum", |
| "array_type"=>"Array", |
| "base_type"=>"Intrinsic", |
| "const_type"=>"Const", |
| "pointer_type"=>"Pointer", |
| "reference_type"=>"Ref", |
| "rvalue_reference_type"=>"RvalueRef", |
| "volatile_type"=>"Volatile", |
| "typedef"=>"Typedef", |
| "ptr_to_member_type"=>"FieldPtr", |
| "string_type"=>"String" |
| ); |
| |
| my %Qual = ( |
| "Pointer"=>"*", |
| "Ref"=>"&", |
| "RvalueRef"=>"&&", |
| "Volatile"=>"volatile", |
| "Const"=>"const" |
| ); |
| |
| my %ConstSuffix = ( |
| "unsigned int" => "u", |
| "unsigned long" => "ul", |
| "unsigned long long" => "ull", |
| "long" => "l", |
| "long long" => "ll" |
| ); |
| |
| my $HEADER_EXT = "h|hh|hp|hxx|hpp|h\\+\\+|tcc"; |
| my $SRC_EXT = "c|cpp|cxx|c\\+\\+"; |
| |
| # Other |
| my %NestedNameSpaces; |
| my $TargetName = undef; |
| my %HeadersInfo; |
| my %SourcesInfo; |
| my %SymVer; |
| my %UsedType; |
| |
| # ELF |
| my %Library_Symbol; |
| my %Library_UndefSymbol; |
| my %Library_Needed; |
| my %SymbolTable; |
| |
| # VTables |
| my %VirtualTable; |
| |
| # Env |
| my $SYS_ARCH; |
| my $SYS_WORD; |
| my $SYS_GCCV; |
| my $SYS_COMP; |
| my $LIB_LANG; |
| my $OBJ_LANG; |
| |
| my $IncompatibleOpt = undef; |
| |
| # Errors |
| my $InvalidDebugLoc; |
| |
| # Public Headers |
| my %SymbolToHeader; |
| my %TypeToHeader; |
| my %PublicHeader; |
| my $PublicSymbols_Detected; |
| |
| # Kernel |
| my %KSymTab; |
| |
| # Filter |
| my %SymbolsList; |
| |
| sub printMsg($$) |
| { |
| my ($Type, $Msg) = @_; |
| if($Type!~/\AINFO/) { |
| $Msg = $Type.": ".$Msg; |
| } |
| if($Type!~/_C\Z/) { |
| $Msg .= "\n"; |
| } |
| if($Type eq "ERROR" |
| or $Type eq "WARNING") { |
| print STDERR $Msg; |
| } |
| else { |
| print $Msg; |
| } |
| } |
| |
| sub exitStatus($$) |
| { |
| my ($Code, $Msg) = @_; |
| printMsg("ERROR", $Msg); |
| exit($ERROR_CODE{$Code}); |
| } |
| |
| sub cmpVersions($$) |
| { # compare two versions in dotted-numeric format |
| my ($V1, $V2) = @_; |
| return 0 if($V1 eq $V2); |
| return undef if($V1!~/\A\d+[\.\d+]*\Z/); |
| return undef if($V2!~/\A\d+[\.\d+]*\Z/); |
| my @V1Parts = split(/\./, $V1); |
| my @V2Parts = split(/\./, $V2); |
| for (my $i = 0; $i <= $#V1Parts && $i <= $#V2Parts; $i++) { |
| return -1 if(int($V1Parts[$i]) < int($V2Parts[$i])); |
| return 1 if(int($V1Parts[$i]) > int($V2Parts[$i])); |
| } |
| return -1 if($#V1Parts < $#V2Parts); |
| return 1 if($#V1Parts > $#V2Parts); |
| return 0; |
| } |
| |
| sub writeFile($$) |
| { |
| my ($Path, $Content) = @_; |
| return if(not $Path); |
| if(my $Dir = getDirname($Path)) { |
| mkpath($Dir); |
| } |
| open(FILE, ">", $Path) || die ("can't open file \'$Path\': $!\n"); |
| print FILE $Content; |
| close(FILE); |
| } |
| |
| sub readFile($) |
| { |
| my $Path = $_[0]; |
| return "" if(not $Path or not -f $Path); |
| open(FILE, $Path); |
| local $/ = undef; |
| my $Content = <FILE>; |
| close(FILE); |
| return $Content; |
| } |
| |
| sub getFilename($) |
| { # much faster than basename() from File::Basename module |
| if($_[0] and $_[0]=~/([^\/\\]+)[\/\\]*\Z/) { |
| return $1; |
| } |
| return ""; |
| } |
| |
| sub getDirname($) |
| { # much faster than dirname() from File::Basename module |
| if($_[0] and $_[0]=~/\A(.*?)[\/\\]+[^\/\\]*[\/\\]*\Z/) { |
| return $1; |
| } |
| return ""; |
| } |
| |
| sub check_Cmd($) |
| { |
| my $Cmd = $_[0]; |
| return "" if(not $Cmd); |
| if(defined $Cache{"check_Cmd"}{$Cmd}) { |
| return $Cache{"check_Cmd"}{$Cmd}; |
| } |
| |
| if(-x $Cmd) |
| { # relative or absolute path |
| return ($Cache{"check_Cmd"}{$Cmd} = 1); |
| } |
| |
| foreach my $Path (sort {length($a)<=>length($b)} split(/:/, $ENV{"PATH"})) |
| { |
| if(-x $Path."/".$Cmd) { |
| return ($Cache{"check_Cmd"}{$Cmd} = 1); |
| } |
| } |
| return ($Cache{"check_Cmd"}{$Cmd} = 0); |
| } |
| |
| my %ELF_BIND = map {$_=>1} ( |
| "WEAK", |
| "GLOBAL", |
| "LOCAL" |
| ); |
| |
| my %ELF_TYPE = map {$_=>1} ( |
| "FUNC", |
| "IFUNC", |
| "GNU_IFUNC", |
| "TLS", |
| "OBJECT", |
| "COMMON" |
| ); |
| |
| my %ELF_VIS = map {$_=>1} ( |
| "DEFAULT", |
| "PROTECTED" |
| ); |
| |
| sub readline_ELF($) |
| { # read the line of 'eu-readelf' output corresponding to the symbol |
| my @Info = split(/\s+/, $_[0]); |
| # Num: Value Size Type Bind Vis Ndx Name |
| # 3629: 000b09c0 32 FUNC GLOBAL DEFAULT 13 _ZNSt12__basic_fileIcED1Ev@@GLIBCXX_3.4 |
| # 135: 00000000 0 FUNC GLOBAL DEFAULT UNDEF av_image_fill_pointers@LIBAVUTIL_52 (3) |
| shift(@Info) if($Info[0] eq ""); # spaces |
| shift(@Info); # num |
| |
| if($#Info==7) |
| { # UNDEF SYMBOL (N) |
| if($Info[7]=~/\(\d+\)/) { |
| pop(@Info); |
| } |
| } |
| |
| if($#Info!=6) |
| { # other lines |
| return (); |
| } |
| return () if(not defined $ELF_TYPE{$Info[2]} and $Info[5] ne "UNDEF"); |
| return () if(not defined $ELF_BIND{$Info[3]}); |
| return () if(not defined $ELF_VIS{$Info[4]}); |
| if($Info[5] eq "ABS" and $Info[0]=~/\A0+\Z/) |
| { # 1272: 00000000 0 OBJECT GLOBAL DEFAULT ABS CXXABI_1.3 |
| return (); |
| } |
| if(index($Info[2], "0x") == 0) |
| { # size == 0x3d158 |
| $Info[2] = hex($Info[2]); |
| } |
| return @Info; |
| } |
| |
| sub read_Symbols($) |
| { |
| my $Lib_Path = $_[0]; |
| my $Lib_Name = getFilename($Lib_Path); |
| |
| my $Dynamic = ($Lib_Name=~/\.so(\.|\Z)/); |
| my $Dbg = ($Lib_Name=~/\.debug\Z/); |
| |
| if(not check_Cmd($EU_READELF)) { |
| exitStatus("Not_Found", "can't find \"eu-readelf\""); |
| } |
| |
| my %SectionInfo; |
| my %KSect; |
| |
| my $Cmd = $EU_READELF_L." -S \"$Lib_Path\" 2>\"$TMP_DIR/error\""; |
| foreach (split(/\n/, `$Cmd`)) |
| { |
| if(/\[\s*(\d+)\]\s+([\w\.]+)/) |
| { |
| my ($Num, $Name) = ($1, $2); |
| |
| $SectionInfo{$Num} = $Name; |
| |
| if(defined $KernelExport) |
| { |
| if($Name=~/\A(__ksymtab|__ksymtab_gpl)\Z/) { |
| $KSect{$1} = 1; |
| } |
| } |
| } |
| } |
| |
| if(defined $KernelExport) |
| { |
| if(not keys(%KSect)) |
| { |
| printMsg("ERROR", "can't find __ksymtab or __ksymtab_gpl sections in the object"); |
| exit(1); |
| } |
| |
| foreach my $Name (sort keys(%KSect)) |
| { |
| $Cmd = $OBJDUMP." --section=$Name -d \"$Lib_Path\" 2>\"$TMP_DIR/error\""; |
| |
| foreach my $Line (split(/\n/, qx/$Cmd/)) |
| { |
| if($Line=~/<__ksymtab_(.+?)>/) |
| { |
| $KSymTab{$1} = 1; |
| } |
| } |
| } |
| } |
| |
| if($Dynamic) |
| { # dynamic library specifics |
| $Cmd = $EU_READELF_L." -d \"$Lib_Path\" 2>\"$TMP_DIR/error\""; |
| foreach (split(/\n/, `$Cmd`)) |
| { |
| if(/NEEDED.+\[([^\[\]]+)\]/) |
| { # dependencies: |
| # 0x00000001 (NEEDED) Shared library: [libc.so.6] |
| $Library_Needed{$1} = 1; |
| } |
| } |
| } |
| |
| my $ExtraPath = undef; |
| |
| if($ExtraInfo) |
| { |
| mkpath($ExtraInfo); |
| $ExtraPath = $ExtraInfo."/elf-info"; |
| } |
| |
| $Cmd = $EU_READELF_L." -s \"$Lib_Path\" 2>\"$TMP_DIR/error\""; |
| |
| if($ExtraPath) |
| { # debug mode |
| # write to file |
| system($Cmd." >\"$ExtraPath\""); |
| open(LIB, $ExtraPath); |
| } |
| else |
| { # write to pipe |
| open(LIB, $Cmd." |"); |
| } |
| |
| my (%Symbol_Value, %Value_Symbol) = (); |
| |
| my $symtab = undef; # indicates that we are processing 'symtab' section of 'readelf' output |
| while(<LIB>) |
| { |
| if($Dynamic and not $Dbg) |
| { # dynamic library specifics |
| if(defined $symtab) |
| { |
| if(index($_, "'.dynsym'")!=-1) |
| { # dynamic table |
| $symtab = undef; |
| } |
| if(not $AllSymbols) |
| { # do nothing with symtab |
| #next; |
| } |
| } |
| elsif(index($_, "'.symtab'")!=-1) |
| { # symbol table |
| $symtab = 1; |
| } |
| } |
| if(my ($Value, $Size, $Type, $Bind, $Vis, $Ndx, $Symbol) = readline_ELF($_)) |
| { # read ELF entry |
| if(not $symtab) |
| { # dynsym |
| if(skipSymbol($Symbol)) { |
| next; |
| } |
| |
| if($Ndx eq "UNDEF") |
| { # ignore interfaces that are imported from somewhere else |
| $Library_UndefSymbol{$TargetName}{$Symbol} = 0; |
| next; |
| } |
| |
| if(defined $KernelExport) |
| { |
| if($Bind ne "LOCAL") |
| { |
| if(index($Symbol, "sys_")==0 |
| or index($Symbol, "SyS_")==0) { |
| $KSymTab{$Symbol} = 1; |
| } |
| } |
| |
| if(not defined $KSymTab{$Symbol}) { |
| next; |
| } |
| } |
| |
| if($Bind ne "LOCAL") { |
| $Library_Symbol{$TargetName}{$Symbol} = ($Type eq "OBJECT")?-$Size:1; |
| } |
| |
| $Symbol_Value{$Symbol} = $Value; |
| $Value_Symbol{$Value}{$Symbol} = 1; |
| |
| if(not defined $OBJ_LANG) |
| { |
| if(index($Symbol, "_Z")==0) |
| { |
| $OBJ_LANG = "C++"; |
| } |
| } |
| } |
| else |
| { |
| $Symbol_Value{$Symbol} = $Value; |
| $Value_Symbol{$Value}{$Symbol} = 1; |
| } |
| |
| if(not $symtab) |
| { |
| foreach ($SectionInfo{$Ndx}, "") |
| { |
| my $Val = $Value; |
| |
| $SymbolTable{$_}{$Val}{$Symbol} = 1; |
| |
| if($Val=~s/\A[0]+//) |
| { |
| if($Val eq "") { |
| $Val = "0"; |
| } |
| $SymbolTable{$_}{$Val}{$Symbol} = 1; |
| } |
| } |
| } |
| } |
| } |
| close(LIB); |
| |
| if(not defined $Library_Symbol{$TargetName}) { |
| return; |
| } |
| |
| my %Found = (); |
| foreach my $Symbol (sort keys(%Symbol_Value)) |
| { |
| next if(index($Symbol,"\@")==-1); |
| if(my $Value = $Symbol_Value{$Symbol}) |
| { |
| foreach my $Symbol_SameValue (sort keys(%{$Value_Symbol{$Value}})) |
| { |
| if($Symbol_SameValue ne $Symbol |
| and index($Symbol_SameValue,"\@")==-1) |
| { |
| $SymVer{$Symbol_SameValue} = $Symbol; |
| $Found{$Symbol} = 1; |
| #last; |
| } |
| } |
| } |
| } |
| |
| # default |
| foreach my $Symbol (sort keys(%Symbol_Value)) |
| { |
| next if(defined $Found{$Symbol}); |
| next if(index($Symbol,"\@\@")==-1); |
| |
| if($Symbol=~/\A([^\@]*)\@\@/ |
| and not $SymVer{$1}) |
| { |
| $SymVer{$1} = $Symbol; |
| $Found{$Symbol} = 1; |
| } |
| } |
| |
| # non-default |
| foreach my $Symbol (sort keys(%Symbol_Value)) |
| { |
| next if(defined $Found{$Symbol}); |
| next if(index($Symbol,"\@")==-1); |
| |
| if($Symbol=~/\A([^\@]*)\@([^\@]*)/ |
| and not $SymVer{$1}) |
| { |
| $SymVer{$1} = $Symbol; |
| $Found{$Symbol} = 1; |
| } |
| } |
| |
| if(not defined $OBJ_LANG) |
| { |
| $OBJ_LANG = "C"; |
| } |
| } |
| |
| sub read_Alt_Info($) |
| { |
| my $Path = $_[0]; |
| my $Name = getFilename($Path); |
| |
| if(not check_Cmd($EU_READELF)) { |
| exitStatus("Not_Found", "can't find \"$EU_READELF\" command"); |
| } |
| |
| printMsg("INFO", "Reading alternate debug-info"); |
| |
| my $ExtraPath = undef; |
| |
| # lines info |
| if($ExtraInfo) |
| { |
| $ExtraPath = $ExtraInfo."/alt"; |
| mkpath($ExtraPath); |
| $ExtraPath .= "/debug_line"; |
| } |
| |
| if($ExtraPath) |
| { |
| system($EU_READELF_L." -N --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); |
| open(SRC, $ExtraPath); |
| } |
| else { |
| open(SRC, $EU_READELF_L." -N --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" |"); |
| } |
| |
| my $DirTable_Def = undef; |
| my %DirTable = (); |
| |
| while(<SRC>) |
| { |
| if(defined $AddDirs) |
| { |
| if(/Directory table/i) |
| { |
| $DirTable_Def = 1; |
| next; |
| } |
| elsif(/File name table/i) |
| { |
| $DirTable_Def = undef; |
| next; |
| } |
| |
| if(defined $DirTable_Def) |
| { |
| if(/\A\s*(.+?)\Z/) { |
| $DirTable{keys(%DirTable)+1} = $1; |
| } |
| } |
| } |
| |
| if(/(\d+)\s+(\d+)\s+\d+\s+\d+\s+([^ ]+)/) |
| { |
| my ($Num, $Dir, $File) = ($1, $2, $3); |
| chomp($File); |
| |
| if(defined $AddDirs) |
| { |
| if(my $DName = $DirTable{$Dir}) |
| { |
| $File = $DName."/".$File; |
| } |
| } |
| |
| $SourceFile_Alt{0}{$Num} = $File; |
| } |
| } |
| close(SRC); |
| |
| # debug info |
| if($ExtraInfo) |
| { |
| $ExtraPath = $ExtraInfo."/alt"; |
| mkpath($ExtraPath); |
| $ExtraPath .= "/debug_info"; |
| } |
| |
| if($ExtraPath) |
| { |
| system($EU_READELF_L." -N --debug-dump=info \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); |
| open(INFO, $ExtraPath); |
| } |
| else { |
| open(INFO, $EU_READELF_L." -N --debug-dump=info \"$Path\" 2>\"$TMP_DIR/error\" |"); |
| } |
| |
| my $ID = undef; |
| my $Num = 0; |
| |
| while(<INFO>) |
| { |
| if(index($_, " ")==0) |
| { |
| if(defined $ID) { |
| $ImportedUnit{$ID}{$Num++} = $_; |
| } |
| } |
| elsif(index($_, " [")==0 |
| and /\A \[\s*(\w+?)\](\s+)(\w+)/) |
| { |
| if($3 eq "partial_unit") |
| { |
| $ID = $1; |
| $Num = 0; |
| $ImportedUnit{$ID}{0} = $_; |
| } |
| elsif(length($2)==2) |
| { # not a partial_unit |
| $ID = undef; |
| } |
| elsif(defined $ID) |
| { |
| $ImportedDecl{$1} = $ID; |
| $ImportedUnit{$ID}{$Num++} = $_; |
| } |
| } |
| } |
| } |
| |
| sub read_DWARF_Info($) |
| { |
| my $Path = $_[0]; |
| |
| my $Dir = getDirname($Path); |
| my $Name = getFilename($Path); |
| |
| if(not check_Cmd($EU_READELF)) { |
| exitStatus("Not_Found", "can't find \"$EU_READELF\" command"); |
| } |
| |
| my $AddOpt = ""; |
| if(not defined $AddrToName) |
| { # disable search of symbol names |
| $AddOpt .= " -N"; |
| } |
| |
| my $Sect = `$EU_READELF_L -S \"$Path\" 2>\"$TMP_DIR/error\"`; |
| |
| if($Sect!~/\.z?debug_info/) |
| { # No DWARF info |
| if(my $DebugFile = getDebugFile($Path, "gnu_debuglink")) |
| { |
| my $DPath = $DebugFile; |
| my $DName = getFilename($DPath); |
| |
| if(my $DDir = getDirname($Path)) |
| { |
| $DPath = $DDir."/".$DPath; |
| } |
| |
| my $Found = undef; |
| if(defined $SearchDirDebuginfo) |
| { |
| my @Files = findFiles($SearchDirDebuginfo, "f"); |
| |
| foreach my $F (@Files) |
| { |
| if(getFilename($F) eq $DName) |
| { |
| $Found = $F; |
| last; |
| } |
| } |
| } |
| |
| if($Found) |
| { |
| $DPath = $Found; |
| printMsg("INFO", "Found debuginfo $DName (gnu_debuglink)"); |
| } |
| |
| if($DPath ne $Path) |
| { |
| if(-e $DPath) |
| { |
| printMsg("INFO", "Reading debuginfo $DName (gnu_debuglink)"); |
| return read_DWARF_Info($DPath); |
| } |
| else { |
| printMsg("WARNING", "missed debuginfo $DName (gnu_debuglink)"); |
| } |
| } |
| else { |
| return 0; |
| } |
| } |
| return 0; |
| } |
| elsif(not defined $AltDebugInfoOpt) |
| { |
| if($Sect=~/\.gnu_debugaltlink/) |
| { |
| if(my $AltObj = getDebugAltLink($Path)) |
| { |
| $AltDebugInfo = $AltObj; |
| read_Alt_Info($AltObj); |
| } |
| else { |
| exitStatus("Error", "can't read gnu_debugaltlink"); |
| } |
| } |
| } |
| |
| printMsg("INFO", "Reading debug-info"); |
| |
| my $ExtraPath = undef; |
| |
| # ELF header |
| if($ExtraInfo) |
| { |
| mkpath($ExtraInfo); |
| $ExtraPath = $ExtraInfo."/elf-header"; |
| } |
| |
| if($ExtraPath) |
| { |
| system($EU_READELF_L." -h \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); |
| open(HEADER, $ExtraPath); |
| } |
| else { |
| open(HEADER, $EU_READELF_L." -h \"$Path\" 2>\"$TMP_DIR/error\" |"); |
| } |
| |
| my %Header = (); |
| while(<HEADER>) |
| { |
| if(/\A\s*([\w ]+?)\:\s*(.+?)\Z/) { |
| $Header{$1} = $2; |
| } |
| } |
| close(HEADER); |
| |
| $SYS_ARCH = $Header{"Machine"}; |
| |
| if($SYS_ARCH=~/80\d86/ |
| or $SYS_ARCH=~/i\d86/) |
| { # i386, i586, etc. |
| $SYS_ARCH = "x86"; |
| } |
| |
| if($SYS_ARCH=~/amd64/i |
| or $SYS_ARCH=~/x86\-64/i) |
| { # amd64 |
| $SYS_ARCH = "x86_64"; |
| } |
| |
| init_Registers(); |
| |
| # ELF sections |
| if($ExtraInfo) |
| { |
| mkpath($ExtraInfo); |
| $ExtraPath = $ExtraInfo."/elf-sections"; |
| } |
| |
| if($ExtraPath) |
| { |
| system($EU_READELF_L." -S \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); |
| open(HEADER, $ExtraPath); |
| } |
| |
| # source info |
| if($ExtraInfo) |
| { |
| mkpath($ExtraInfo); |
| $ExtraPath = $ExtraInfo."/debug_line"; |
| } |
| |
| if($ExtraPath) |
| { |
| system($EU_READELF_L." $AddOpt --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); |
| open(SRC, $ExtraPath); |
| } |
| else { |
| open(SRC, $EU_READELF_L." $AddOpt --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" |"); |
| } |
| |
| my $Offset = undef; |
| my $DirTable_Def = undef; |
| my %DirTable = (); |
| |
| while(<SRC>) |
| { |
| if(defined $AddDirs) |
| { |
| if(/Directory table/i) |
| { |
| $DirTable_Def = 1; |
| %DirTable = (); |
| next; |
| } |
| elsif(/File name table/i) |
| { |
| $DirTable_Def = undef; |
| next; |
| } |
| |
| if(defined $DirTable_Def) |
| { |
| if(/\A\s*(.+?)\Z/) { |
| $DirTable{keys(%DirTable)+1} = $1; |
| } |
| } |
| } |
| |
| if(/Table at offset (\w+)/i) { |
| $Offset = $1; |
| } |
| elsif(defined $Offset |
| and /(\d+)\s+(\d+)\s+\d+\s+\d+\s+([^ ]+)/) |
| { |
| my ($Num, $Dir, $File) = ($1, $2, $3); |
| chomp($File); |
| |
| if(defined $AddDirs) |
| { |
| if(my $DName = $DirTable{$Dir}) |
| { |
| $File = $DName."/".$File; |
| } |
| } |
| |
| $SourceFile{$Offset}{$Num} = $File; |
| } |
| } |
| close(SRC); |
| |
| # debug_loc |
| if($ExtraInfo) |
| { |
| mkpath($ExtraInfo); |
| $ExtraPath = $ExtraInfo."/debug_loc"; |
| } |
| |
| if($ExtraPath) |
| { |
| system($EU_READELF_L." $AddOpt --debug-dump=loc \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); |
| open(LOC, $ExtraPath); |
| } |
| else { |
| open(LOC, $EU_READELF_L." $AddOpt --debug-dump=loc \"$Path\" 2>\"$TMP_DIR/error\" |"); |
| } |
| |
| while(<LOC>) |
| { |
| if(/\A \[\s*(\w+)\].*\[\s*\w+\]\s*(.+)\Z/) { |
| $DebugLoc{$1} = $2; |
| } |
| elsif(/\A \[\s*(\w+)\]/) { |
| $DebugLoc{$1} = ""; |
| } |
| } |
| close(LOC); |
| |
| # dwarf |
| if($ExtraInfo) |
| { |
| mkpath($ExtraInfo); |
| $ExtraPath = $ExtraInfo."/debug_info"; |
| } |
| |
| my $INFO_fh; |
| |
| if($Dir) |
| { # to find ".dwz" directory (Fedora) |
| chdir($Dir); |
| } |
| if($ExtraPath) |
| { |
| system($EU_READELF_L." $AddOpt --debug-dump=info \"$Name\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); |
| open($INFO_fh, $ExtraPath); |
| } |
| else { |
| open($INFO_fh, $EU_READELF_L." $AddOpt --debug-dump=info \"$Name\" 2>\"$TMP_DIR/error\" |"); |
| } |
| chdir($ORIG_DIR); |
| |
| read_DWARF_Dump($INFO_fh, 1); |
| |
| close($INFO_fh); |
| |
| if(my $Err = readFile("$TMP_DIR/error")) |
| { # eu-readelf: cannot get next DIE: invalid DWARF |
| if($Err=~/invalid DWARF/i) |
| { |
| if($Loud) { |
| printMsg("ERROR", $Err); |
| } |
| exitStatus("Invalid_DWARF", "invalid DWARF info"); |
| } |
| } |
| |
| return 1; |
| } |
| |
| sub getSource($) |
| { |
| my $ID = $_[0]; |
| |
| if(defined $DWARF_Info{$ID}{"decl_file"}) |
| { |
| my $File = $DWARF_Info{$ID}{"decl_file"}; |
| my $Unit = $DWARF_Info{$ID}{"Unit"}; |
| |
| my $Name = undef; |
| |
| if($ID>=0) { |
| $Name = $SourceFile{$Unit}{$File}; |
| } |
| else |
| { # imported |
| $Name = $SourceFile_Alt{0}{$File}; |
| } |
| |
| return $Name; |
| } |
| |
| return undef; |
| } |
| |
| sub read_DWARF_Dump($$) |
| { |
| my ($FH, $Primary) = @_; |
| |
| my $TypeUnit_Sign = undef; |
| my $TypeUnit_Offset = undef; |
| my $Type_Offset = undef; |
| |
| my $Shift_Enabled = 1; |
| my $ID_Shift = undef; |
| |
| my $CUnit = undef; |
| |
| my $Compressed = undef; |
| |
| if($AltDebugInfo) { |
| $Compressed = 1; |
| } |
| |
| my $ID = undef; |
| my $Kind = undef; |
| my $NS = undef; |
| |
| my $MAX_ID = undef; |
| |
| my %Shift = map {$_=>1} ( |
| "specification", |
| "type", |
| "sibling", |
| "object_pointer", |
| "containing_type", |
| "abstract_origin", |
| "import", |
| "signature" |
| ); |
| |
| my $Line = undef; |
| my $Import = undef; |
| my $Import_Num = 0; |
| |
| my %SkipNode = ( |
| "imported_declaration" => 1, |
| "imported_module" => 1 |
| ); |
| |
| my %SkipAttr = ( |
| "high_pc" => 1, |
| "frame_base" => 1, |
| "encoding" => 1 |
| ); |
| |
| my %MarkByUnit = ( |
| "member" => 1, |
| "subprogram" => 1, |
| "variable" => 1 |
| ); |
| |
| my $Lexical_Block = undef; |
| my $Inlined_Block = undef; |
| my $Subprogram_Block = undef; |
| my $Skip_Block = undef; |
| |
| while(($Import and $Line = $ImportedUnit{$Import}{$Import_Num}) or $Line = <$FH>) |
| { |
| if($Import) |
| { |
| if(not defined $ImportedUnit{$Import}{$Import_Num}) |
| { |
| $Import_Num = 0; |
| delete($ImportedUnit{$Import}); |
| $Import = undef; |
| } |
| |
| $Import_Num+=1; |
| } |
| |
| if(defined $ID and $Line=~/\A\s*(\w+)\s*(.+?)\s*\Z/) |
| { |
| if(defined $Skip_Block) { |
| next; |
| } |
| |
| my $Attr = $1; |
| my $Val = $2; |
| |
| if(index($Val, "flag_present")!=-1) |
| { # Fedora |
| $Val = "Yes"; |
| } |
| |
| if(defined $Compressed) |
| { |
| if($Kind eq "imported_unit") |
| { |
| if($Attr eq "import") |
| { |
| if($Val=~/\(GNU_ref_alt\)\s*\[\s*(\w+?)\]/) |
| { |
| if(defined $ImportedUnit{$1}) |
| { |
| $Import = $1; |
| $Import_Num = 0; |
| $UsedUnit{$Import} = 1; |
| } |
| } |
| } |
| } |
| } |
| |
| if($Kind eq "member") |
| { |
| if($Attr eq "data_member_location") |
| { |
| delete($DWARF_Info{$ID}{"Unit"}); |
| } |
| } |
| |
| if($Attr eq "sibling") |
| { |
| if($Kind ne "structure_type") |
| { |
| next; |
| } |
| } |
| elsif($Attr eq "Type") |
| { |
| if($Line=~/Type\s+signature:\s*0x(\w+)/) { |
| $TypeUnit_Sign = $1; |
| } |
| if($Line=~/Type\s+offset:\s*0x(\w+)/) { |
| $Type_Offset = hex($1); |
| } |
| if($Line=~/Type\s+unit\s+at\s+offset\s+(\d+)/) { |
| $TypeUnit_Offset = $1; |
| } |
| next; |
| } |
| elsif(defined $SkipAttr{$Attr}) |
| { # unused |
| next; |
| } |
| |
| if($Val=~/\A\s*\(([^()]*)\)\s*\[\s*(\w+)\]\s*\Z/) |
| { # ref4, ref_udata, ref_addr, etc. |
| $Val = hex($2); |
| |
| if($1 eq "GNU_ref_alt") |
| { |
| $Val = -$Val; |
| $UsedDecl{$2} = 1; |
| } |
| } |
| elsif($Attr eq "name") |
| { |
| $Val=~s/\A\([^()]*\)\s*\"(.*)\"\Z/$1/; |
| } |
| elsif(index($Attr, "linkage_name")!=-1) |
| { |
| $Val=~s/\A\([^()]*\)\s*\"(.*)\"\Z/$1/; |
| $Attr = "linkage_name"; |
| } |
| elsif(index($Attr, "location")!=-1) |
| { |
| if($Val=~/\)\s*\Z/) |
| { # value on the next line |
| my $NL = ""; |
| |
| if($Import) { |
| $NL = $ImportedUnit{$Import}{$Import_Num} |
| } |
| else { |
| $NL = <$FH>; |
| } |
| |
| $Val .= $NL; |
| } |
| |
| if($Val=~/\A\(\w+\)\s*(-?)(\w+)\Z/) |
| { # (data1) 1c |
| $Val = hex($2); |
| if($1) { |
| $Val = -$Val; |
| } |
| } |
| else |
| { |
| if($Val=~/ (-?\d+)\Z/) { |
| $Val = $1; |
| } |
| else |
| { |
| if($Attr eq "location" |
| and $Kind eq "formal_parameter") |
| { |
| if($Val=~/location list\s+\[\s*(\w+)\]\Z/) |
| { |
| $Attr = "location_list"; |
| $Val = $1; |
| } |
| elsif($Val=~/ reg(\d+)\Z/) |
| { |
| $Attr = "register"; |
| $Val = $1; |
| } |
| } |
| } |
| } |
| } |
| elsif($Attr eq "accessibility") |
| { |
| $Val=~s/\A\(.+?\)\s*//; |
| $Val=~s/\s*\(.+?\)\Z//; |
| |
| # NOTE: members: private by default |
| } |
| else |
| { |
| $Val=~s/\A\(\w+\)\s*//; |
| |
| if(substr($Val, 0, 1) eq "{" |
| and $Val=~/{(.+)}/) |
| { # {ID} |
| $Val = $1; |
| $Post_Change{$ID} = 1; |
| } |
| } |
| |
| if(defined $Shift_Enabled and $ID_Shift) |
| { |
| if(defined $Shift{$Attr} |
| and not $Post_Change{$ID}) { |
| $Val += $ID_Shift; |
| } |
| |
| # $DWARF_Info{$ID}{"rID"} = $ID-$ID_Shift; |
| } |
| |
| if($Import or not $Primary) |
| { |
| if(defined $Shift{$Attr}) |
| { |
| $Val = -$Val; |
| } |
| } |
| |
| $DWARF_Info{$ID}{$Attr} = "$Val"; |
| |
| if($Kind eq "compile_unit") |
| { |
| if($Attr eq "stmt_list") { |
| $CUnit = $Val; |
| } |
| |
| if(not defined $LIB_LANG) |
| { |
| if($Attr eq "language") |
| { |
| if(index($Val, "Assembler")==-1) |
| { |
| $Val=~s/\s*\(.+?\)\Z//; |
| |
| if($Val=~/C\d/i) { |
| $LIB_LANG = "C"; |
| } |
| elsif($Val=~/C\+\+|C_plus_plus/i) { |
| $LIB_LANG = "C++"; |
| } |
| else { |
| $LIB_LANG = $Val; |
| } |
| } |
| } |
| } |
| |
| if(not defined $SYS_COMP and not defined $SYS_GCCV) |
| { |
| if($Attr eq "producer") |
| { |
| if(index($Val, "GNU AS")==-1) |
| { |
| $Val=~s/\A\"//; |
| $Val=~s/\"\Z//; |
| |
| if($Val=~/GNU\s+(C\d*|C\+\+)\s+(.+)\Z/) |
| { |
| $SYS_GCCV = $2; |
| if($SYS_GCCV=~/\A(\d+\.\d+)(\.\d+|)/) |
| { # 4.6.1 20110627 (Mandriva) |
| $SYS_GCCV = $1.$2; |
| } |
| |
| if(not defined $KeepRegsAndOffsets) |
| { |
| my %Opts = (); |
| while($Val=~s/(\A| )(\-O([0-3]|g))( |\Z)/ /) { |
| $Opts{keys(%Opts)} = $2; |
| } |
| |
| if(keys(%Opts)) |
| { |
| if($Opts{keys(%Opts)-1} ne "-Og") |
| { |
| printMsg("WARNING", "incompatible build option detected: ".$Opts{keys(%Opts)-1}." (required -Og for better analysis)"); |
| $IncompatibleOpt = 1; |
| } |
| } |
| else |
| { |
| printMsg("WARNING", "the object should be compiled with -Og option for better analysis"); |
| $IncompatibleOpt = 1; |
| } |
| } |
| } |
| else { |
| $SYS_COMP = $Val; |
| } |
| } |
| } |
| } |
| } |
| elsif($Kind eq "type_unit") |
| { |
| if($Attr eq "stmt_list") { |
| $CUnit = $Val; |
| } |
| } |
| elsif($Kind eq "partial_unit" and not $Import) |
| { # support for dwz |
| if($Attr eq "stmt_list") { |
| $CUnit = $Val; |
| } |
| } |
| } |
| elsif($Line=~/\A \[\s*(\w+)\](\s*)(\w+)/) |
| { |
| $ID = hex($1); |
| $NS = length($2); |
| $Kind = $3; |
| |
| if(not defined $Compressed) |
| { |
| if($Kind eq "partial_unit" or $Kind eq "type_unit") |
| { # compressed debug_info |
| $Compressed = 1; |
| } |
| } |
| |
| if(not $Compressed) |
| { # compile units can depend on each other in the compressed debug_info |
| # so reading them all integrally by one call of read_ABI() |
| if($Kind eq "compile_unit" and $CUnit) |
| { # read the previous compile unit |
| complete_Dump($Primary); |
| read_ABI(); |
| |
| if(not defined $Compressed) |
| { # normal debug_info |
| $Compressed = 0; |
| } |
| } |
| } |
| |
| $Skip_Block = undef; |
| |
| if(defined $SkipNode{$Kind}) |
| { |
| $Skip_Block = 1; |
| next; |
| } |
| |
| if($Kind eq "lexical_block") |
| { |
| $Lexical_Block = $NS; |
| $Skip_Block = 1; |
| next; |
| } |
| else |
| { |
| if(defined $Lexical_Block) |
| { |
| if($NS>$Lexical_Block) |
| { |
| $Skip_Block = 1; |
| next; |
| } |
| else |
| { # end of lexical block |
| $Lexical_Block = undef; |
| } |
| } |
| } |
| |
| if($Kind eq "inlined_subroutine") |
| { |
| $Inlined_Block = $NS; |
| $Skip_Block = 1; |
| next; |
| } |
| else |
| { |
| if(defined $Inlined_Block) |
| { |
| if($NS>$Inlined_Block) |
| { |
| $Skip_Block = 1; |
| next; |
| } |
| else |
| { # end of inlined subroutine |
| $Inlined_Block = undef; |
| } |
| } |
| } |
| |
| if($Kind eq "subprogram") |
| { |
| $Subprogram_Block = $NS; |
| } |
| else |
| { |
| if(defined $Subprogram_Block) |
| { |
| if($NS>$Subprogram_Block) |
| { |
| if($Kind eq "variable") |
| { # temp variables |
| $Skip_Block = 1; |
| next; |
| } |
| } |
| else |
| { # end of subprogram block |
| $Subprogram_Block = undef; |
| } |
| } |
| } |
| |
| if($Import or not $Primary) |
| { |
| $ID = -$ID; |
| } |
| |
| if(defined $Shift_Enabled) |
| { |
| if($Kind eq "type_unit") |
| { |
| if(not defined $ID_Shift) |
| { |
| if($ID_Shift<=$MAX_ID) { |
| $ID_Shift = $MAX_ID; |
| } |
| else { |
| $ID_Shift = 0; |
| } |
| } |
| } |
| |
| if($ID_Shift) { |
| $ID += $ID_Shift; |
| } |
| } |
| |
| if(defined $TypeUnit_Sign) |
| { |
| if($Kind ne "type_unit" |
| and $Kind ne "namespace") |
| { |
| if($TypeUnit_Offset+$Type_Offset+$ID_Shift==$ID) |
| { |
| $TypeUnit{$TypeUnit_Sign} = "$ID"; |
| $TypeUnit_Sign = undef; |
| } |
| } |
| } |
| |
| $DWARF_Info{$ID}{"Kind"} = $Kind; |
| $DWARF_Info{$ID}{"NS"} = $NS; |
| |
| if(defined $CUnit) |
| { |
| if(defined $MarkByUnit{$Kind} |
| or defined $TypeType{$Kind}) { |
| $DWARF_Info{$ID}{"Unit"} = $CUnit; |
| } |
| } |
| |
| if(not defined $ID_Shift) { |
| $MAX_ID = $ID; |
| } |
| } |
| elsif(not defined $SYS_WORD |
| and $Line=~/Address\s*size:\s*(\d+)/i) |
| { |
| $SYS_WORD = $1; |
| } |
| } |
| |
| if(not defined $ID) { |
| printMsg("ERROR", "the debuginfo looks empty or corrupted"); |
| } |
| |
| # read the last compile unit |
| # or all units if debug_info is compressed |
| complete_Dump($Primary); |
| read_ABI(); |
| } |
| |
| sub read_Vtables($) |
| { |
| my $Path = $_[0]; |
| |
| $Path = abs_path($Path); |
| |
| my $Dir = getDirname($Path); |
| |
| if(index($LIB_LANG, "C++")!=-1 |
| or $OBJ_LANG eq "C++") |
| { |
| printMsg("INFO", "Reading v-tables"); |
| |
| if(check_Cmd($VTABLE_DUMPER)) |
| { |
| if(my $Version = `$VTABLE_DUMPER -dumpversion`) |
| { |
| if(cmpVersions($Version, $VTABLE_DUMPER_VERSION)<0) |
| { |
| printMsg("ERROR", "the version of Vtable-Dumper should be $VTABLE_DUMPER_VERSION or newer"); |
| return; |
| } |
| } |
| } |
| else |
| { |
| printMsg("ERROR", "cannot find \'$VTABLE_DUMPER\'"); |
| return; |
| } |
| |
| my $ExtraPath = $TMP_DIR."/v-tables"; |
| |
| if($ExtraInfo) |
| { |
| mkpath($ExtraInfo); |
| $ExtraPath = $ExtraInfo."/v-tables"; |
| } |
| |
| system("LD_LIBRARY_PATH=\"$Dir\" $VTABLE_DUMPER -mangled -demangled \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); |
| |
| my $Content = readFile($ExtraPath); |
| foreach my $ClassInfo (split(/\n\n\n/, $Content)) |
| { |
| if($ClassInfo=~/\AVtable\s+for\s+(.+)\n((.|\n)+)\Z/i) |
| { |
| my ($CName, $VTable) = ($1, $2); |
| my @Entries = split(/\n/, $VTable); |
| |
| foreach (1 .. $#Entries) |
| { |
| my $Entry = $Entries[$_]; |
| if($Entry=~/\A(\d+)\s+(.+)\Z/) { |
| $VirtualTable{$CName}{$1} = $2; |
| } |
| } |
| } |
| } |
| } |
| |
| if(keys(%VirtualTable)) |
| { |
| foreach my $Tid (sort keys(%TypeInfo)) |
| { |
| if($TypeInfo{$Tid}{"Type"}=~/\A(Struct|Class)\Z/) |
| { |
| my $TName = $TypeInfo{$Tid}{"Name"}; |
| $TName=~s/\bstruct //g; |
| if(defined $VirtualTable{$TName}) |
| { |
| %{$TypeInfo{$Tid}{"VTable"}} = %{$VirtualTable{$TName}}; |
| } |
| } |
| } |
| } |
| } |
| |
| sub dump_ABI() |
| { |
| printMsg("INFO", "Creating ABI dump"); |
| |
| my %ABI = ( |
| "TypeInfo" => \%TypeInfo, |
| "SymbolInfo" => \%SymbolInfo, |
| "Symbols" => \%Library_Symbol, |
| "UndefinedSymbols" => \%Library_UndefSymbol, |
| "Needed" => \%Library_Needed, |
| "SymbolVersion" => \%SymVer, |
| "LibraryVersion" => $TargetVersion, |
| "LibraryName" => $TargetName, |
| "Language" => $LIB_LANG, |
| "Headers" => \%HeadersInfo, |
| "Sources" => \%SourcesInfo, |
| "NameSpaces" => \%NestedNameSpaces, |
| "Target" => "unix", |
| "Arch" => $SYS_ARCH, |
| "WordSize" => $SYS_WORD, |
| "ABI_DUMP_VERSION" => $ABI_DUMP_VERSION, |
| "ABI_DUMPER_VERSION" => $TOOL_VERSION, |
| ); |
| |
| if($SYS_GCCV) { |
| $ABI{"GccVersion"} = $SYS_GCCV; |
| } |
| else { |
| $ABI{"Compiler"} = $SYS_COMP; |
| } |
| |
| if(defined $PublicHeadersPath) { |
| $ABI{"PublicABI"} = "1"; |
| } |
| |
| if(defined $IncompatibleOpt) |
| { |
| $ABI{"MissedOffsets"} = "1"; |
| $ABI{"MissedRegs"} = "1"; |
| } |
| |
| my $ABI_DUMP = Dumper(\%ABI); |
| |
| if($StdOut) |
| { # --stdout option |
| print STDOUT $ABI_DUMP; |
| } |
| else |
| { |
| mkpath(getDirname($OutputDump)); |
| |
| open(DUMP, ">", $OutputDump) || die ("can't open file \'$OutputDump\': $!\n"); |
| print DUMP $ABI_DUMP; |
| close(DUMP); |
| |
| printMsg("INFO", "\nThe object ABI has been dumped to:\n $OutputDump"); |
| } |
| } |
| |
| sub unmangleString($) |
| { |
| my $Str = $_[0]; |
| |
| $Str=~s/\AN(.+)E\Z/$1/; |
| while($Str=~s/\A(\d+)//) |
| { |
| if(length($Str)==$1) { |
| last; |
| } |
| |
| $Str = substr($Str, $1, length($Str) - $1); |
| } |
| |
| return $Str; |
| } |
| |
| sub init_ABI() |
| { |
| # register "void" type |
| %{$TypeInfo{"1"}} = ( |
| "Name"=>"void", |
| "Type"=>"Intrinsic" |
| ); |
| $TName_Tid{"Intrinsic"}{"void"} = "1"; |
| $TName_Tids{"Intrinsic"}{"void"}{"1"} = 1; |
| $Cache{"getTypeInfo"}{"1"} = 1; |
| |
| # register "..." type |
| %{$TypeInfo{"-1"}} = ( |
| "Name"=>"...", |
| "Type"=>"Intrinsic" |
| ); |
| $TName_Tid{"Intrinsic"}{"..."} = "-1"; |
| $TName_Tids{"Intrinsic"}{"..."}{"-1"} = 1; |
| $Cache{"getTypeInfo"}{"-1"} = 1; |
| } |
| |
| sub complete_Dump($) |
| { |
| my $Primary = $_[0]; |
| |
| foreach my $ID (keys(%Post_Change)) |
| { |
| if(my $Type = $DWARF_Info{$ID}{"type"}) |
| { |
| if(my $To = $TypeUnit{$Type}) { |
| $DWARF_Info{$ID}{"type"} = $To; |
| } |
| } |
| if(my $Signature = $DWARF_Info{$ID}{"signature"}) |
| { |
| if(my $To = $TypeUnit{$Signature}) { |
| $DWARF_Info{$ID}{"signature"} = $To; |
| } |
| } |
| } |
| |
| %Post_Change = (); |
| %TypeUnit = (); |
| |
| if($Primary) |
| { |
| my %AddUnits = (); |
| |
| foreach my $ID (keys(%UsedDecl)) |
| { |
| if(my $U_ID = $ImportedDecl{$ID}) |
| { |
| if(not $UsedUnit{$U_ID}) |
| { |
| $AddUnits{$U_ID} = 1; |
| } |
| } |
| } |
| |
| if(keys(%AddUnits)) |
| { |
| my $ADD_DUMP = ""; |
| |
| foreach my $U_ID (sort {hex($a)<=>hex($b)} keys(%AddUnits)) |
| { |
| foreach my $N (sort {int($a)<=>int($b)} keys(%{$ImportedUnit{$U_ID}})) |
| { |
| $ADD_DUMP .= $ImportedUnit{$U_ID}{$N}; |
| } |
| } |
| |
| my $AddUnit_F = $TMP_DIR."/add_unit.dump"; |
| |
| writeFile($AddUnit_F, $ADD_DUMP); |
| |
| my $FH_add; |
| open($FH_add, $AddUnit_F); |
| read_DWARF_Dump($FH_add, 0); |
| close($FH_add); |
| |
| unlink($AddUnit_F); |
| } |
| } |
| |
| %UsedUnit = (); |
| %UsedDecl = (); |
| } |
| |
| sub read_ABI() |
| { |
| my %CurID = (); |
| |
| my @IDs = sort {int($a) <=> int($b)} keys(%DWARF_Info); |
| |
| if($AltDebugInfo) { |
| @IDs = sort {$b>0 <=> $a>0} sort {abs(int($a)) <=> abs(int($b))} @IDs; |
| } |
| |
| my $TPack = undef; |
| my $PPack = undef; |
| |
| foreach my $ID (@IDs) |
| { |
| $ID = "$ID"; |
| |
| my $Kind = $DWARF_Info{$ID}{"Kind"}; |
| my $NS = $DWARF_Info{$ID}{"NS"}; |
| my $Scope = $CurID{$NS-2}; |
| |
| if($Kind eq "typedef") |
| { |
| if($DWARF_Info{$Scope}{"Kind"} eq "subprogram") |
| { |
| $NS = $DWARF_Info{$Scope}{"NS"}; |
| $Scope = $CurID{$NS-2}; |
| } |
| } |
| |
| if($Kind ne "subprogram") { |
| delete($DWARF_Info{$ID}{"NS"}); |
| } |
| |
| my $IsType = ($Kind=~/(struct|structure|class|union|enumeration|subroutine|array)_type/); |
| |
| if($IsType |
| or $Kind eq "typedef" |
| or $Kind eq "subprogram" |
| or $Kind eq "variable" |
| or $Kind eq "namespace") |
| { |
| if($Kind ne "variable" |
| and $Kind ne "typedef") |
| { |
| $CurID{$NS} = $ID; |
| } |
| |
| if($Scope) |
| { |
| $NameSpace{$ID} = $Scope; |
| if($Kind eq "subprogram" |
| or $Kind eq "variable") |
| { |
| if($DWARF_Info{$Scope}{"Kind"}=~/class|struct/) |
| { |
| $ClassMethods{$Scope}{$ID} = 1; |
| if(my $Sp = $DWARF_Info{$Scope}{"specification"}) { |
| $ClassMethods{$Sp}{$ID} = 1; |
| } |
| } |
| } |
| } |
| |
| if(my $Spec = $DWARF_Info{$ID}{"specification"}) { |
| $SpecElem{$Spec} = $ID; |
| } |
| |
| if(my $Orig = $DWARF_Info{$ID}{"abstract_origin"}) { |
| $OrigElem{$Orig} = $ID; |
| } |
| |
| if($IsType) |
| { |
| if(not $DWARF_Info{$ID}{"name"} |
| and $DWARF_Info{$ID}{"linkage_name"}) |
| { |
| $DWARF_Info{$ID}{"name"} = unmangleString($DWARF_Info{$ID}{"linkage_name"}); |
| |
| # free memory |
| delete($DWARF_Info{$ID}{"linkage_name"}); |
| } |
| } |
| } |
| elsif($Kind eq "member") |
| { |
| if($Scope) |
| { |
| $NameSpace{$ID} = $Scope; |
| |
| if($DWARF_Info{$Scope}{"Kind"}=~/class|struct/ |
| and not defined $DWARF_Info{$ID}{"data_member_location"}) |
| { # variable (global data) |
| next; |
| } |
| } |
| |
| $TypeMember{$Scope}{keys(%{$TypeMember{$Scope}})} = $ID; |
| } |
| elsif($Kind eq "enumerator") |
| { |
| $TypeMember{$Scope}{keys(%{$TypeMember{$Scope}})} = $ID; |
| } |
| elsif($Kind eq "inheritance") |
| { |
| my %In = (); |
| $In{"id"} = $DWARF_Info{$ID}{"type"}; |
| |
| if(my $Access = $DWARF_Info{$ID}{"accessibility"}) |
| { |
| if($Access ne "public") |
| { # default inheritance access in ABI dump is "public" |
| $In{"access"} = $Access; |
| } |
| } |
| |
| if(defined $DWARF_Info{$ID}{"virtuality"}) { |
| $In{"virtual"} = 1; |
| } |
| $Inheritance{$Scope}{keys(%{$Inheritance{$Scope}})} = \%In; |
| |
| # free memory |
| delete($DWARF_Info{$ID}); |
| } |
| elsif($Kind eq "formal_parameter") |
| { |
| if(defined $PPack) { |
| $FuncParam{$PPack}{keys(%{$FuncParam{$PPack}})} = $ID; |
| } |
| else { |
| $FuncParam{$Scope}{keys(%{$FuncParam{$Scope}})} = $ID; |
| } |
| } |
| elsif($Kind eq "unspecified_parameters") |
| { |
| $FuncParam{$Scope}{keys(%{$FuncParam{$Scope}})} = $ID; |
| $DWARF_Info{$ID}{"type"} = "-1"; # "..." |
| } |
| elsif($Kind eq "subrange_type") |
| { |
| if((my $Bound = $DWARF_Info{$ID}{"upper_bound"}) ne "") { |
| $ArrayCount{$Scope} = $Bound + 1; |
| } |
| |
| # free memory |
| delete($DWARF_Info{$ID}); |
| } |
| elsif($Kind eq "template_type_parameter" |
| or $Kind eq "template_value_parameter") |
| { |
| my %Info = ("type"=>$DWARF_Info{$ID}{"type"}, "key"=>$DWARF_Info{$ID}{"name"}); |
| |
| if(defined $DWARF_Info{$ID}{"const_value"}) { |
| $Info{"value"} = $DWARF_Info{$ID}{"const_value"}; |
| } |
| |
| if(defined $DWARF_Info{$ID}{"default_value"}) { |
| $Info{"default"} = 1; |
| } |
| |
| if(defined $TPack) { |
| $TmplParam{$TPack}{keys(%{$TmplParam{$TPack}})} = \%Info; |
| } |
| else { |
| $TmplParam{$Scope}{keys(%{$TmplParam{$Scope}})} = \%Info; |
| } |
| } |
| elsif($Kind eq "GNU_template_parameter_pack") { |
| $TPack = $Scope; |
| } |
| elsif($Kind eq "GNU_formal_parameter_pack") { |
| $PPack = $Scope; |
| } |
| |
| if($Kind ne "GNU_template_parameter_pack") |
| { |
| if(index($Kind, "template_")==-1) { |
| $TPack = undef; |
| } |
| } |
| |
| if($Kind ne "GNU_formal_parameter_pack") |
| { |
| if($Kind ne "formal_parameter") { |
| $PPack = undef; |
| } |
| } |
| |
| } |
| |
| my @IDs = sort {int($a) <=> int($b)} keys(%DWARF_Info); |
| |
| if($AltDebugInfo) { |
| @IDs = sort {$b>0 <=> $a>0} sort {abs(int($a)) <=> abs(int($b))} @IDs; |
| } |
| |
| foreach my $ID (@IDs) |
| { |
| if(my $Kind = $DWARF_Info{$ID}{"Kind"}) |
| { |
| if(defined $TypeType{$Kind}) { |
| getTypeInfo($ID); |
| } |
| } |
| } |
| |
| foreach my $Tid (@IDs) |
| { |
| if(defined $TypeInfo{$Tid}) |
| { |
| my $Type = $TypeInfo{$Tid}{"Type"}; |
| |
| if(not defined $TypeInfo{$Tid}{"Memb"}) |
| { |
| if($Type=~/Struct|Class|Union|Enum/) |
| { |
| if(my $Signature = $DWARF_Info{$Tid}{"signature"}) |
| { |
| if(defined $TypeInfo{$Signature}) |
| { |
| foreach my $Attr (keys(%{$TypeInfo{$Signature}})) |
| { |
| if(not defined $TypeInfo{$Tid}{$Attr}) { |
| $TypeInfo{$Tid}{$Attr} = $TypeInfo{$Signature}{$Attr}; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| # delete types info |
| foreach (keys(%DWARF_Info)) |
| { |
| if(my $Kind = $DWARF_Info{$_}{"Kind"}) |
| { |
| if(defined $TypeType{$Kind}) { |
| delete($DWARF_Info{$_}); |
| } |
| } |
| } |
| |
| foreach my $ID (sort {int($a) <=> int($b)} keys(%DWARF_Info)) |
| { |
| if($ID<0) |
| { # imported |
| next; |
| } |
| |
| if($DWARF_Info{$ID}{"Kind"} eq "subprogram" |
| or $DWARF_Info{$ID}{"Kind"} eq "variable") |
| { |
| getSymbolInfo($ID); |
| } |
| } |
| |
| %DWARF_Info = (); |
| |
| # free memory |
| %TypeMember = (); |
| %ArrayCount = (); |
| %FuncParam = (); |
| %TmplParam = (); |
| %Inheritance = (); |
| %NameSpace = (); |
| %SpecElem = (); |
| %OrigElem = (); |
| %ClassMethods = (); |
| |
| $Cache{"getTypeInfo"} = {"1"=>1, "-1"=>1}; |
| } |
| |
| sub complete_ABI() |
| { |
| # types |
| my %Incomplete = (); |
| my %Incomplete_TN = (); |
| |
| my @IDs = sort {int($a) <=> int($b)} keys(%TypeInfo); |
| |
| if($AltDebugInfo) { |
| @IDs = sort {$b>0 <=> $a>0} sort {abs(int($a)) <=> abs(int($b))} @IDs; |
| } |
| |
| foreach my $Tid (@IDs) |
| { |
| my $Name = $TypeInfo{$Tid}{"Name"}; |
| my $Type = $TypeInfo{$Tid}{"Type"}; |
| |
| if(not defined $SpecElem{$Tid} |
| and not defined $Incomplete_TN{$Type}{$Name}) |
| { |
| if(not defined $TypeInfo{$Tid}{"Size"}) |
| { |
| if($Type=~/Struct|Class|Union|Enum/) |
| { |
| $Incomplete{$Tid} = 1; |
| } |
| } |
| } |
| |
| $Incomplete_TN{$Type}{$Name} = 1; |
| } |
| |
| # free memory |
| %Incomplete_TN = (); |
| |
| foreach my $Tid (sort {int($a) <=> int($b)} keys(%Incomplete)) |
| { |
| my $Name = $TypeInfo{$Tid}{"Name"}; |
| my $Type = $TypeInfo{$Tid}{"Type"}; |
| |
| my @Adv_IDs = sort {int($a) <=> int($b)} keys(%{$TName_Tids{$Type}{$Name}}); |
| |
| if($AltDebugInfo) { |
| @Adv_IDs = sort {$b>0 <=> $a>0} sort {abs(int($a)) <=> abs(int($b))} @Adv_IDs; |
| } |
| |
| foreach my $Tid_Adv (@Adv_IDs) |
| { |
| if($Tid_Adv!=$Tid) |
| { |
| if(defined $SpecElem{$Tid_Adv} |
| or defined $TypeInfo{$Tid_Adv}{"Size"}) |
| { |
| foreach my $Attr (keys(%{$TypeInfo{$Tid_Adv}})) |
| { |
| if(not defined $TypeInfo{$Tid}{$Attr}) |
| { |
| if(ref($TypeInfo{$Tid_Adv}{$Attr}) eq "HASH") { |
| $TypeInfo{$Tid}{$Attr} = dclone($TypeInfo{$Tid_Adv}{$Attr}); |
| } |
| else { |
| $TypeInfo{$Tid}{$Attr} = $TypeInfo{$Tid_Adv}{$Attr}; |
| } |
| |
| } |
| } |
| last; |
| } |
| } |
| } |
| } |
| |
| # free memory |
| %Incomplete = (); |
| |
| my %Delete = (); |
| |
| foreach my $Tid (sort {int($a) <=> int($b)} keys(%TypeInfo)) |
| { |
| if($TypeInfo{$Tid}{"Type"} eq "Typedef") |
| { |
| my $TN = $TypeInfo{$Tid}{"Name"}; |
| my $TL = $TypeInfo{$Tid}{"Line"}; |
| my $NS = $TypeInfo{$Tid}{"NameSpace"}; |
| |
| if(my $BTid = $TypeInfo{$Tid}{"BaseType"}) |
| { |
| if(defined $TypeInfo{$BTid} |
| and $TypeInfo{$BTid}{"Name"}=~/\Aanon\-(\w+)\-/ |
| and $TypeInfo{$BTid}{"Type"}=~/Enum|Struct|Union/) |
| { |
| %{$TypeInfo{$Tid}} = %{$TypeInfo{$BTid}}; |
| $TypeInfo{$Tid}{"Name"} = lc($TypeInfo{$BTid}{"Type"})." ".$TN; |
| $TypeInfo{$Tid}{"Line"} = $TL; |
| |
| my $Name = $TypeInfo{$Tid}{"Name"}; |
| my $Type = $TypeInfo{$Tid}{"Type"}; |
| |
| if(not defined $TName_Tid{$Type}{$Name} |
| or ($Tid>0 and $Tid<$TName_Tid{$Type}{$Name}) |
| or ($Tid>0 and $TName_Tid{$Type}{$Name}<0)) { |
| $TName_Tid{$Type}{$Name} = $Tid; |
| } |
| $TName_Tids{$Type}{$Name}{$Tid} = 1; |
| |
| if($NS) { |
| $TypeInfo{$Tid}{"NameSpace"} = $NS; |
| } |
| $Delete{$BTid} = $Tid; |
| } |
| } |
| } |
| elsif($TypeInfo{$Tid}{"Type"} eq "Pointer") |
| { |
| if(my $BTid = $TypeInfo{$Tid}{"BaseType"}) |
| { |
| if(my $To = $Delete{$BTid}) |
| { |
| $TypeInfo{$Tid}{"BaseType"} = $To; |
| $TypeInfo{$Tid}{"Name"} = $TypeInfo{$To}{"Name"}."*"; |
| |
| my $Name = $TypeInfo{$Tid}{"Name"}; |
| my $Type = $TypeInfo{$Tid}{"Type"}; |
| |
| $TName_Tid{$Type}{$Name} = $Tid; |
| $TName_Tids{$Type}{$Name}{$Tid} = 1; |
| } |
| } |
| } |
| } |
| |
| foreach my $Tid (keys(%Delete)) |
| { |
| my $TN = $TypeInfo{$Tid}{"Name"}; |
| my $TT = $TypeInfo{$Tid}{"Type"}; |
| |
| delete($TName_Tid{$TT}{$TN}); |
| delete($TName_Tids{$TT}{$TN}{$Tid}); |
| |
| if(my @IDs = sort {int($a) <=> int($b)} keys(%{$TName_Tids{$TT}{$TN}})) |
| { # minimal ID |
| $TName_Tid{$TT}{$TN} = $IDs[0]; |
| } |
| |
| delete($TypeInfo{$Tid}); |
| } |
| |
| # free memory |
| %Delete = (); |
| |
| # symbols |
| foreach my $ID (sort {int($a) <=> int($b)} keys(%SymbolInfo)) |
| { |
| # add missed c-tors |
| if($SymbolInfo{$ID}{"Constructor"}) |
| { |
| if($SymbolInfo{$ID}{"MnglName"}=~/(C[1-2])([EI]).+/) |
| { |
| my ($K1, $K2) = ($1, $2); |
| foreach ("C1", "C2") |
| { |
| if($K1 ne $_) |
| { |
| my $Name = $SymbolInfo{$ID}{"MnglName"}; |
| $Name=~s/$K1$K2/$_$K2/; |
| |
| if(not defined $Mangled_ID{$Name}) { |
| cloneSymbol($ID, $Name); |
| } |
| } |
| } |
| } |
| } |
| |
| # add missed d-tors |
| if($SymbolInfo{$ID}{"Destructor"}) |
| { |
| if($SymbolInfo{$ID}{"MnglName"}=~/(D[0-2])([EI]).+/) |
| { |
| my ($K1, $K2) = ($1, $2); |
| foreach ("D0", "D1", "D2") |
| { |
| if($K1 ne $_) |
| { |
| my $Name = $SymbolInfo{$ID}{"MnglName"}; |
| $Name=~s/$K1$K2/$_$K2/; |
| |
| if(not defined $Mangled_ID{$Name}) { |
| cloneSymbol($ID, $Name); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| foreach my $ID (sort {int($a) <=> int($b)} keys(%SymbolInfo)) |
| { |
| my $Symbol = $SymbolInfo{$ID}{"MnglName"}; |
| |
| if(not $Symbol) { |
| $Symbol = $SymbolInfo{$ID}{"ShortName"}; |
| } |
| |
| if($LIB_LANG eq "C++") |
| { |
| if(not $SymbolInfo{$ID}{"MnglName"}) |
| { |
| if($SymbolInfo{$ID}{"Artificial"} |
| or index($SymbolInfo{$ID}{"ShortName"}, "~")==0) |
| { |
| delete($SymbolInfo{$ID}); |
| next; |
| } |
| } |
| } |
| |
| if($SymbolInfo{$ID}{"Class"} |
| and not $SymbolInfo{$ID}{"Data"} |
| and not $SymbolInfo{$ID}{"Constructor"} |
| and not $SymbolInfo{$ID}{"Destructor"} |
| and not $SymbolInfo{$ID}{"Virt"} |
| and not $SymbolInfo{$ID}{"PureVirt"}) |
| { |
| if(not defined $SymbolInfo{$ID}{"Param"} |
| or $SymbolInfo{$ID}{"Param"}{0}{"name"} ne "this") |
| { |
| $SymbolInfo{$ID}{"Static"} = 1; |
| } |
| } |
| |
| if(not $SymbolInfo{$ID}{"Return"}) |
| { # void |
| if(not $SymbolInfo{$ID}{"Constructor"} |
| and not $SymbolInfo{$ID}{"Destructor"}) |
| { |
| $SymbolInfo{$ID}{"Return"} = "1"; |
| } |
| } |
| |
| if(defined $SymbolInfo{$ID}{"Source"} and defined $SymbolInfo{$ID}{"SourceLine"}) |
| { |
| if(not defined $SymbolInfo{$ID}{"Header"} and not defined $SymbolInfo{$ID}{"Line"}) |
| { |
| $SymbolInfo{$ID}{"Line"} = $SymbolInfo{$ID}{"SourceLine"}; |
| delete($SymbolInfo{$ID}{"SourceLine"}); |
| } |
| } |
| |
| my $S = selectSymbol($ID); |
| |
| if($S==0) |
| { |
| if(defined $AllSymbols) |
| { |
| if($SymbolInfo{$ID}{"External"}) |
| { |
| $S = 1; |
| } |
| else |
| { # local |
| if(defined $DumpStatic) { |
| $S = 1; |
| } |
| } |
| } |
| } |
| |
| if($S==0) |
| { |
| delete($SymbolInfo{$ID}); |
| next; |
| } |
| elsif(defined $PublicHeadersPath) |
| { |
| if(not selectPublic($Symbol, $ID) |
| and (not defined $SymbolInfo{$ID}{"Alias"} or not selectPublic($SymbolInfo{$ID}{"Alias"}, $ID))) |
| { |
| delete($SymbolInfo{$ID}); |
| next; |
| } |
| } |
| elsif(defined $KernelExport) |
| { |
| if(not defined $KSymTab{$Symbol}) |
| { |
| delete($SymbolInfo{$ID}); |
| next; |
| } |
| } |
| |
| $SelectedSymbols{$ID} = $S; |
| |
| delete($SymbolInfo{$ID}{"External"}); |
| } |
| } |
| |
| sub warnPrivateType($$) |
| { |
| my ($Name, $Note) = @_; |
| |
| if($Name=~/Private|Opaque/i) |
| { # _GstClockPrivate |
| # _Eo_Opaque |
| return; |
| } |
| |
| if($Name=~/(\A| )_/i) |
| { # _GstBufferList |
| return; |
| } |
| |
| if($Name=~/_\Z/i) |
| { # FT_RasterRec_ |
| return; |
| } |
| |
| printMsg("WARNING", "Private data type \'".$Name."\' ($Note)"); |
| } |
| |
| sub warnPrivateSymbol($$) |
| { |
| my ($Name, $Note) = @_; |
| printMsg("WARNING", "Private symbol \'".$Name."\' ($Note)"); |
| } |
| |
| sub selectPublicType($) |
| { |
| my $Tid = $_[0]; |
| |
| if($TypeInfo{$Tid}{"Type"}!~/\A(Struct|Class|Union|Enum)\Z/) { |
| return 1; |
| } |
| |
| my $TName = $TypeInfo{$Tid}{"Name"}; |
| $TName=~s/\A(struct|class|union|enum) //g; |
| |
| my $Header = getFilename($TypeInfo{$Tid}{"Header"}); |
| |
| if($OBJ_LANG eq "C++" |
| or index($TName, "anon-")==0) { |
| return ($Header and defined $PublicHeader{$Header}); |
| } |
| |
| if($Header) |
| { |
| if(not defined $PublicHeader{$Header}) |
| { |
| if(not defined $TypeToHeader{$TName}) { |
| return 0; |
| } |
| } |
| elsif($MixedHeaders) |
| { |
| if(not defined $TypeToHeader{$TName}) |
| { |
| if(defined $Debug) { |
| warnPrivateType($TypeInfo{$Tid}{"Name"}, "NOT_FOUND"); |
| } |
| return 0; |
| } |
| } |
| } |
| else |
| { |
| if(not defined $TypeToHeader{$TName}) |
| { |
| # if(defined $Debug) { |
| # warnPrivateType($TypeInfo{$Tid}{"Name"}, "NO_HEADER"); |
| # } |
| return 0; |
| } |
| } |
| |
| return 1; |
| } |
| |
| sub selectPublic($$) |
| { |
| my ($Symbol, $ID) = @_; |
| |
| my $Header = getFilename($SymbolInfo{$ID}{"Header"}); |
| |
| if($OBJ_LANG eq "C++") { |
| return ($Header and defined $PublicHeader{$Header}); |
| } |
| |
| if($Header) |
| { |
| if(not defined $PublicHeader{$Header}) |
| { |
| if(not defined $SymbolToHeader{$Symbol}) { |
| return 0; |
| } |
| } |
| elsif($MixedHeaders) |
| { |
| if(not defined $SymbolToHeader{$Symbol}) |
| { |
| if(defined $Debug) { |
| warnPrivateSymbol($Symbol, "NOT_FOUND"); |
| } |
| return 0; |
| } |
| } |
| } |
| else |
| { |
| if(not defined $SymbolToHeader{$Symbol}) |
| { |
| # if(defined $Debug) { |
| # warnPrivateSymbol($Symbol, "NO_HEADER"); |
| # } |
| return 0; |
| } |
| } |
| |
| return 1; |
| } |
| |
| sub cloneSymbol($$) |
| { |
| my ($ID, $Symbol) = @_; |
| |
| my $nID = undef; |
| if(not defined $SymbolInfo{$ID + 1}) { |
| $nID = $ID + 1; |
| } |
| else { |
| $nID = ++$GLOBAL_ID; |
| } |
| foreach my $Attr (keys(%{$SymbolInfo{$ID}})) |
| { |
| if(ref($SymbolInfo{$ID}{$Attr}) eq "HASH") { |
| $SymbolInfo{$nID}{$Attr} = dclone($SymbolInfo{$ID}{$Attr}); |
| } |
| else { |
| $SymbolInfo{$nID}{$Attr} = $SymbolInfo{$ID}{$Attr}; |
| } |
| } |
| $SymbolInfo{$nID}{"MnglName"} = $Symbol; |
| } |
| |
| sub selectSymbol($) |
| { |
| my $ID = $_[0]; |
| |
| my $MnglName = $SymbolInfo{$ID}{"MnglName"}; |
| |
| if(not $MnglName) { |
| $MnglName = $SymbolInfo{$ID}{"ShortName"}; |
| } |
| |
| if($SymbolsListPath |
| and not $SymbolsList{$MnglName}) |
| { |
| next; |
| } |
| |
| my $Exp = 0; |
| |
| if($Library_Symbol{$TargetName}{$MnglName} |
| or $Library_Symbol{$TargetName}{$SymVer{$MnglName}}) |
| { |
| $Exp = 1; |
| } |
| |
| if(my $Alias = $SymbolInfo{$ID}{"Alias"}) |
| { |
| if($Library_Symbol{$TargetName}{$Alias} |
| or $Library_Symbol{$TargetName}{$SymVer{$Alias}}) |
| { |
| $Exp = 1; |
| } |
| } |
| |
| if(not $Exp) |
| { |
| if(defined $Library_UndefSymbol{$TargetName}{$MnglName} |
| or defined $Library_UndefSymbol{$TargetName}{$SymVer{$MnglName}}) |
| { |
| return 0; |
| } |
| |
| if($SymbolInfo{$ID}{"Data"} |
| or $SymbolInfo{$ID}{"InLine"} |
| or $SymbolInfo{$ID}{"PureVirt"}) |
| { |
| if(not $SymbolInfo{$ID}{"External"}) |
| { # skip static |
| return 0; |
| } |
| |
| if(defined $BinOnly) |
| { # data, inline, pure |
| return 0; |
| } |
| elsif(not defined $SymbolInfo{$ID}{"Header"}) |
| { # defined in source files |
| return 0; |
| } |
| else |
| { |
| return 2; |
| } |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| return 1; |
| } |
| |
| sub formatName($$) |
| { # type name correction |
| if(defined $Cache{"formatName"}{$_[1]}{$_[0]}) { |
| return $Cache{"formatName"}{$_[1]}{$_[0]}; |
| } |
| |
| my $N = $_[0]; |
| |
| if($_[1] ne "S") |
| { |
| $N=~s/\A[ ]+//g; |
| $N=~s/[ ]+\Z//g; |
| $N=~s/[ ]{2,}/ /g; |
| } |
| |
| $N=~s/[ ]*(\W)[ ]*/$1/g; # std::basic_string<char> const |
| |
| $N=~s/\b(const|volatile) ([\w\:]+)([\*&,>]|\Z)/$2 $1$3/g; # "const void" to "void const" |
| |
| $N=~s/\bvolatile const\b/const volatile/g; |
| |
| $N=~s/\b(long long|short|long) unsigned\b/unsigned $1/g; |
| $N=~s/\b(short|long) int\b/$1/g; |
| |
| $N=~s/([\)\]])(const|volatile)\b/$1 $2/g; |
| |
| while($N=~s/>>/> >/g) {}; |
| |
| if($_[1] eq "S") |
| { |
| if(index($N, "operator")!=-1) { |
| $N=~s/\b(operator[ ]*)> >/$1>>/; |
| } |
| } |
| |
| $N=~s/,/, /g; |
| |
| return ($Cache{"formatName"}{$_[1]}{$_[0]} = $N); |
| } |
| |
| sub separate_Params($) |
| { |
| my $Str = $_[0]; |
| my @Parts = (); |
| my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 ); |
| my $Part = 0; |
| foreach my $Pos (0 .. length($Str) - 1) |
| { |
| my $S = substr($Str, $Pos, 1); |
| if(defined $B{$S}) { |
| $B{$S} += 1; |
| } |
| if($S eq "," and |
| $B{"("}==$B{")"} and $B{"<"}==$B{">"}) { |
| $Part += 1; |
| } |
| else { |
| $Parts[$Part] .= $S; |
| } |
| } |
| # remove spaces |
| foreach (@Parts) |
| { |
| s/\A //g; |
| s/ \Z//g; |
| } |
| return @Parts; |
| } |
| |
| sub init_FuncType($$$) |
| { |
| my ($TInfo, $FTid, $Type) = @_; |
| |
| $TInfo->{"Type"} = $Type; |
| |
| if($TInfo->{"Return"} = $DWARF_Info{$FTid}{"type"}) { |
| getTypeInfo($TInfo->{"Return"}); |
| } |
| else |
| { # void |
| $TInfo->{"Return"} = "1"; |
| } |
| delete($TInfo->{"BaseType"}); |
| |
| my @Prms = (); |
| my $PPos = 0; |
| foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$FuncParam{$FTid}})) |
| { |
| my $ParamId = $FuncParam{$FTid}{$Pos}; |
| my %PInfo = %{$DWARF_Info{$ParamId}}; |
| |
| if(defined $PInfo{"artificial"}) |
| { # this |
| next; |
| } |
| |
| if(my $PTypeId = $PInfo{"type"}) |
| { |
| $TInfo->{"Param"}{$PPos}{"type"} = $PTypeId; |
| getTypeInfo($PTypeId); |
| push(@Prms, $TypeInfo{$PTypeId}{"Name"}); |
| } |
| |
| $PPos += 1; |
| } |
| |
| $TInfo->{"Name"} = $TypeInfo{$TInfo->{"Return"}}{"Name"}; |
| if($Type eq "FuncPtr") { |
| $TInfo->{"Name"} .= "(*)"; |
| } |
| else { |
| $TInfo->{"Name"} .= "()"; |
| } |
| $TInfo->{"Name"} .= "(".join(",", @Prms).")"; |
| } |
| |
| sub getShortName($) |
| { |
| my $Name = $_[0]; |
| |
| if(my $C = find_center($Name, "<")) |
| { |
| return substr($Name, 0, $C); |
| } |
| |
| return $Name; |
| } |
| |
| sub get_TParams($) |
| { |
| my $ID = $_[0]; |
| |
| my @TParams = (); |
| |
| foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$TmplParam{$ID}})) |
| { |
| my $TTid = $TmplParam{$ID}{$Pos}{"type"}; |
| my $Val = undef; |
| my $Key = undef; |
| |
| if(defined $TmplParam{$ID}{$Pos}{"value"}) { |
| $Val = $TmplParam{$ID}{$Pos}{"value"}; |
| } |
| |
| if(defined $TmplParam{$ID}{$Pos}{"key"}) { |
| $Key = $TmplParam{$ID}{$Pos}{"key"}; |
| } |
| |
| if($Pos>0) |
| { |
| if(defined $TmplParam{$ID}{$Pos}{"default"}) |
| { |
| if($Key=~/\A(_Alloc|_Traits|_Compare)\Z/) |
| { |
| next; |
| } |
| } |
| } |
| |
| getTypeInfo($TTid); |
| |
| my $TTName = $TypeInfo{$TTid}{"Name"}; |
| |
| if(defined $Val) |
| { |
| if($TTName eq "bool") |
| { |
| if($Val eq "1") { |
| push(@TParams, "true"); |
| } |
| elsif($Val eq "0") { |
| push(@TParams, "false"); |
| } |
| } |
| else |
| { |
| if($Val=~/\A\d+\Z/) |
| { |
| if(my $S = $ConstSuffix{$TTName}) |
| { |
| $Val .= $S; |
| } |
| } |
| push(@TParams, $Val); |
| } |
| } |
| else |
| { |
| push(@TParams, simpleName($TTName)); |
| } |
| } |
| |
| return @TParams; |
| } |
| |
| sub parse_TParams($) |
| { |
| my $Name = $_[0]; |
| if(my $Cent = find_center($Name, "<")) |
| { |
| my $TParams = substr($Name, $Cent); |
| $TParams=~s/\A<|>\Z//g; |
| |
| $TParams = simpleName($TParams); |
| |
| my $Short = substr($Name, 0, $Cent); |
| |
| my @Params = separate_Params($TParams); |
| foreach my $Pos (0 .. $#Params) |
| { |
| my $Param = $Params[$Pos]; |
| if($Param=~/\A(.+>)(.*?)\Z/) |
| { |
| my ($Tm, $Suf) = ($1, $2); |
| my ($Sh, @Prm) = parse_TParams($Tm); |
| $Param = $Sh."<".join(", ", @Prm).">".$Suf; |
| } |
| $Params[$Pos] = formatName($Param, "T"); |
| } |
| |
| @Params = shortTParams($Short, @Params); |
| |
| return ($Short, @Params); |
| } |
| |
| return $Name; # error |
| } |
| |
| sub shortTParams(@) |
| { |
| my $Short = shift(@_); |
| my @Params = @_; |
| |
| # default arguments |
| if($Short eq "std::vector") |
| { |
| if($#Params==1) |
| { |
| if($Params[1] eq "std::allocator<".$Params[0].">") |
| { # std::vector<T, std::allocator<T> > |
| splice(@Params, 1, 1); |
| } |
| } |
| } |
| elsif($Short eq "std::set") |
| { |
| if($#Params==2) |
| { |
| if($Params[1] eq "std::less<".$Params[0].">" |
| and $Params[2] eq "std::allocator<".$Params[0].">") |
| { # std::set<T, std::less<T>, std::allocator<T> > |
| splice(@Params, 1, 2); |
| } |
| } |
| } |
| elsif($Short eq "std::basic_string") |
| { |
| if($#Params==2) |
| { |
| if($Params[1] eq "std::char_traits<".$Params[0].">" |
| and $Params[2] eq "std::allocator<".$Params[0].">") |
| { # std::basic_string<T, std::char_traits<T>, std::allocator<T> > |
| splice(@Params, 1, 2); |
| } |
| } |
| } |
| |
| return @Params; |
| } |
| |
| sub getTypeInfo($) |
| { |
| my $ID = $_[0]; |
| my $Kind = $DWARF_Info{$ID}{"Kind"}; |
| |
| if(defined $Cache{"getTypeInfo"}{$ID}) { |
| return; |
| } |
| |
| if(my $N = $NameSpace{$ID}) |
| { |
| if($DWARF_Info{$N}{"Kind"} eq "subprogram") |
| { # local code |
| # template instances are declared in the subprogram (constructor) |
| my $Tmpl = 0; |
| if(my $ObjP = $DWARF_Info{$N}{"object_pointer"}) |
| { |
| while($DWARF_Info{$ObjP}{"type"}) { |
| $ObjP = $DWARF_Info{$ObjP}{"type"}; |
| } |
| my $CName = $DWARF_Info{$ObjP}{"name"}; |
| $CName=~s/<.*//g; |
| if($CName eq $DWARF_Info{$N}{"name"}) { |
| $Tmpl = 1; |
| } |
| } |
| if(not $Tmpl) |
| { # local types |
| $LocalType{$ID} = 1; |
| } |
| } |
| elsif($DWARF_Info{$N}{"Kind"} eq "lexical_block") |
| { # local code |
| return; |
| } |
| } |
| |
| $Cache{"getTypeInfo"}{$ID} = 1; |
| |
| my %TInfo = (); |
| |
| $TInfo{"Type"} = $TypeType{$Kind}; |
| |
| if(not $TInfo{"Type"}) |
| { |
| if($DWARF_Info{$ID}{"Kind"} eq "subroutine_type") { |
| $TInfo{"Type"} = "Func"; |
| } |
| } |
| |
| my $RealType = $TInfo{"Type"}; |
| |
| if(defined $ClassMethods{$ID}) |
| { |
| if($TInfo{"Type"} eq "Struct") { |
| $RealType = "Class"; |
| } |
| } |
| |
| if($TInfo{"Type"} ne "Enum" |
| and my $BaseType = $DWARF_Info{$ID}{"type"}) |
| { |
| $TInfo{"BaseType"} = "$BaseType"; |
| |
| if(defined $TypeType{$DWARF_Info{$BaseType}{"Kind"}}) |
| { |
| getTypeInfo($TInfo{"BaseType"}); |
| |
| if(not defined $TypeInfo{$TInfo{"BaseType"}} |
| or not $TypeInfo{$TInfo{"BaseType"}}{"Name"}) |
| { # local code |
| delete($TypeInfo{$ID}); |
| return; |
| } |
| } |
| } |
| |
| if($RealType eq "Class") { |
| $TInfo{"Copied"} = 1; # will be changed in getSymbolInfo() |
| } |
| |
| if(defined $TypeMember{$ID}) |
| { |
| my $Unnamed = 0; |
| foreach my $Pos (sort {int($a) <=> int($b)} keys(%{$TypeMember{$ID}})) |
| { |
| my $MemId = $TypeMember{$ID}{$Pos}; |
| my %MInfo = %{$DWARF_Info{$MemId}}; |
| |
| if(my $Name = $MInfo{"name"}) |
| { |
| if(index($Name, "_vptr.")==0) |
| { # v-table pointer |
| $Name="_vptr"; |
| } |
| $TInfo{"Memb"}{$Pos}{"name"} = $Name; |
| } |
| else |
| { |
| $TInfo{"Memb"}{$Pos}{"name"} = "unnamed".$Unnamed; |
| $Unnamed += 1; |
| } |
| if($TInfo{"Type"} eq "Enum") { |
| $TInfo{"Memb"}{$Pos}{"value"} = $MInfo{"const_value"}; |
| } |
| else |
| { |
| $TInfo{"Memb"}{$Pos}{"type"} = $MInfo{"type"}; |
| if(my $Access = $MInfo{"accessibility"}) |
| { |
| if($Access ne "public") |
| { # NOTE: default access of members in the ABI dump is "public" |
| $TInfo{"Memb"}{$Pos}{"access"} = $Access; |
| } |
| } |
| else |
| { |
| if($DWARF_Info{$ID}{"Kind"} eq "class_type") |
| { # NOTE: default access of class members in the debug info is "private" |
| $TInfo{"Memb"}{$Pos}{"access"} = "private"; |
| } |
| else |
| { |
| # NOTE: default access of struct members in the debug info is "public" |
| } |
| } |
| if($TInfo{"Type"} eq "Union") { |
| $TInfo{"Memb"}{$Pos}{"offset"} = "0"; |
| } |
| elsif(defined $MInfo{"data_member_location"}) { |
| $TInfo{"Memb"}{$Pos}{"offset"} = $MInfo{"data_member_location"}; |
| } |
| } |
| |
| if((my $BitSize = $MInfo{"bit_size"}) ne "") { |
| $TInfo{"Memb"}{$Pos}{"bitfield"} = $BitSize; |
| } |
| } |
| } |
| |
| my $NS = $NameSpace{$ID}; |
| if(not $NS) |
| { |
| if(my $Sp = $DWARF_Info{$ID}{"specification"}) { |
| $NS = $NameSpace{$Sp}; |
| } |
| } |
| |
| if($NS and $DWARF_Info{$NS}{"Kind"}=~/\A(class_type|structure_type)\Z/) |
| { # member class |
| if(my $Access = $DWARF_Info{$ID}{"accessibility"}) |
| { |
| if($Access ne "public") |
| { # NOTE: default access of member classes in the ABI dump is "public" |
| $TInfo{ucfirst($Access)} = 1; |
| } |
| } |
| else |
| { |
| if($DWARF_Info{$NS}{"Kind"} eq "class_type") |
| { |
| # NOTE: default access of member classes in the debug info is "private" |
| $TInfo{"Private"} = 1; |
| } |
| else |
| { |
| # NOTE: default access to struct member classes in the debug info is "public" |
| } |
| } |
| } |
| else |
| { |
| if(my $Access = $DWARF_Info{$ID}{"accessibility"}) |
| { |
| if($Access ne "public") |
| { # NOTE: default access of classes in the ABI dump is "public" |
| $TInfo{ucfirst($Access)} = 1; |
| } |
| } |
| } |
| |
| if(my $Size = $DWARF_Info{$ID}{"byte_size"}) { |
| $TInfo{"Size"} = $Size; |
| } |
| |
| setSource(\%TInfo, $ID); |
| |
| if(not $DWARF_Info{$ID}{"name"} |
| and my $Spec = $DWARF_Info{$ID}{"specification"}) { |
| $DWARF_Info{$ID}{"name"} = $DWARF_Info{$Spec}{"name"}; |
| } |
| |
| if($NS) |
| { |
| if($DWARF_Info{$NS}{"Kind"} eq "namespace") |
| { |
| if(my $NS_F = completeNS($ID)) |
| { |
| $TInfo{"NameSpace"} = $NS_F; |
| } |
| } |
| elsif($DWARF_Info{$NS}{"Kind"} eq "class_type" |
| or $DWARF_Info{$NS}{"Kind"} eq "structure_type") |
| { # class |
| getTypeInfo($NS); |
| |
| if(my $Sp = $SpecElem{$NS}) { |
| getTypeInfo($Sp); |
| } |
| |
| if($TypeInfo{$NS}{"Name"}) |
| { |
| $TInfo{"NameSpace"} = $TypeInfo{$NS}{"Name"}; |
| $TInfo{"NameSpace"}=~s/\Astruct //; |
| } |
| } |
| } |
| |
| if(my $Name = $DWARF_Info{$ID}{"name"}) |
| { |
| $TInfo{"Name"} = $Name; |
| |
| if($TInfo{"NameSpace"}) { |
| $TInfo{"Name"} = $TInfo{"NameSpace"}."::".$TInfo{"Name"}; |
| } |
| |
| if($TInfo{"Type"}=~/\A(Struct|Enum|Union)\Z/) { |
| $TInfo{"Name"} = lc($TInfo{"Type"})." ".$TInfo{"Name"}; |
| } |
| } |
| |
| if($TInfo{"Type"} eq "Pointer") |
| { |
| if($DWARF_Info{$TInfo{"BaseType"}}{"Kind"} eq "subroutine_type") |
| { |
| init_FuncType(\%TInfo, $TInfo{"BaseType"}, "FuncPtr"); |
| } |
| } |
| elsif($TInfo{"Type"}=~/Typedef|Const|Volatile/) |
| { |
| if($DWARF_Info{$TInfo{"BaseType"}}{"Kind"} eq "subroutine_type") |
| { |
| getTypeInfo($TInfo{"BaseType"}); |
| } |
| } |
| elsif($TInfo{"Type"} eq "Func") |
| { |
| init_FuncType(\%TInfo, $ID, "Func"); |
| } |
| elsif($TInfo{"Type"} eq "Struct") |
| { |
| if(not $TInfo{"Name"} |
| and my $Sb = $DWARF_Info{$ID}{"sibling"}) |
| { |
| if($DWARF_Info{$Sb}{"Kind"} eq "subroutine_type" |
| and defined $TInfo{"Memb"} |
| and $TInfo{"Memb"}{0}{"name"} eq "__pfn") |
| { # __pfn and __delta |
| $TInfo{"Type"} = "MethodPtr"; |
| |
| my @Prms = (); |
| my $PPos = 0; |
| foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$FuncParam{$Sb}})) |
| { |
| my $ParamId = $FuncParam{$Sb}{$Pos}; |
| my %PInfo = %{$DWARF_Info{$ParamId}}; |
| |
| if(defined $PInfo{"artificial"}) |
| { # this |
| next; |
| } |
| |
| if(my $PTypeId = $PInfo{"type"}) |
| { |
| $TInfo{"Param"}{$PPos}{"type"} = $PTypeId; |
| getTypeInfo($PTypeId); |
| push(@Prms, $TypeInfo{$PTypeId}{"Name"}); |
| } |
| |
| $PPos += 1; |
| } |
| |
| if(my $ClassId = $DWARF_Info{$Sb}{"object_pointer"}) |
| { |
| while($DWARF_Info{$ClassId}{"type"}) { |
| $ClassId = $DWARF_Info{$ClassId}{"type"}; |
| } |
| $TInfo{"Class"} = $ClassId; |
| getTypeInfo($TInfo{"Class"}); |
| } |
| |
| if($TInfo{"Return"} = $DWARF_Info{$Sb}{"type"}) { |
| getTypeInfo($TInfo{"Return"}); |
| } |
| else |
| { # void |
| $TInfo{"Return"} = "1"; |
| } |
| |
| $TInfo{"Name"} = $TypeInfo{$TInfo{"Return"}}{"Name"}; |
| $TInfo{"Name"} .= "(".$TypeInfo{$TInfo{"Class"}}{"Name"}."::*)"; |
| $TInfo{"Name"} .= "(".join(",", @Prms).")"; |
| } |
| } |
| } |
| elsif($TInfo{"Type"} eq "FieldPtr") |
| { |
| $TInfo{"Return"} = $TInfo{"BaseType"}; |
| delete($TInfo{"BaseType"}); |
| |
| if(my $Class = $DWARF_Info{$ID}{"containing_type"}) |
| { |
| $TInfo{"Class"} = $Class; |
| getTypeInfo($TInfo{"Class"}); |
| |
| $TInfo{"Name"} = $TypeInfo{$TInfo{"Return"}}{"Name"}."(".$TypeInfo{$TInfo{"Class"}}{"Name"}."::*)"; |
| } |
| |
| $TInfo{"Size"} = $SYS_WORD; |
| } |
| elsif($TInfo{"Type"} eq "String") |
| { |
| $TInfo{"Type"} = "Pointer"; |
| $TInfo{"Name"} = "char*"; |
| $TInfo{"BaseType"} = $TName_Tid{"Intrinsic"}{"char"}; |
| } |
| |
| foreach my $Pos (sort {int($a) <=> int($b)} keys(%{$Inheritance{$ID}})) |
| { |
| if(my $BaseId = $Inheritance{$ID}{$Pos}{"id"}) |
| { |
| if(my $E = $SpecElem{$BaseId}) { |
| $BaseId = $E; |
| } |
| |
| $TInfo{"Base"}{$BaseId}{"pos"} = "$Pos"; |
| if(my $Access = $Inheritance{$ID}{$Pos}{"access"}) { |
| $TInfo{"Base"}{$BaseId}{"access"} = $Access; |
| } |
| if($Inheritance{$ID}{$Pos}{"virtual"}) { |
| $TInfo{"Base"}{$BaseId}{"virtual"} = 1; |
| } |
| |
| $ClassChild{$BaseId}{$ID} = 1; |
| } |
| } |
| |
| if(not $TInfo{"BaseType"}) |
| { |
| if($TInfo{"Type"} eq "Pointer") |
| { |
| $TInfo{"Name"} = "void*"; |
| $TInfo{"BaseType"} = "1"; |
| } |
| elsif($TInfo{"Type"} eq "Const") |
| { |
| $TInfo{"Name"} = "const void"; |
| $TInfo{"BaseType"} = "1"; |
| } |
| elsif($TInfo{"Type"} eq "Volatile") |
| { |
| $TInfo{"Name"} = "volatile void"; |
| $TInfo{"BaseType"} = "1"; |
| } |
| elsif($TInfo{"Type"} eq "Typedef") |
| { |
| $TInfo{"BaseType"} = "1"; |
| } |
| } |
| |
| if(not $TInfo{"Name"} |
| and $TInfo{"Type"} ne "Enum") |
| { |
| my $ID_ = $ID; |
| my $BaseID = undef; |
| my $Name = ""; |
| |
| while($BaseID = $DWARF_Info{$ID_}{"type"}) |
| { |
| my $Kind = $DWARF_Info{$ID_}{"Kind"}; |
| if(my $Q = $Qual{$TypeType{$Kind}}) |
| { |
| $Name = $Q.$Name; |
| if($Q=~/\A\w/) { |
| $Name = " ".$Name; |
| } |
| } |
| if(my $BName = $TypeInfo{$BaseID}{"Name"}) |
| { |
| $Name = $BName.$Name; |
| last; |
| } |
| elsif(my $BName2 = $DWARF_Info{$BaseID}{"name"}) |
| { |
| $Name = $BName2.$Name; |
| } |
| $ID_ = $BaseID; |
| } |
| |
| if($Name) { |
| $TInfo{"Name"} = $Name; |
| } |
| |
| if($TInfo{"Type"} eq "Array") |
| { |
| if(my $Count = $ArrayCount{$ID}) |
| { |
| $TInfo{"Name"} .= "[".$Count."]"; |
| if(my $BType = $TInfo{"BaseType"}) |
| { |
| if(my $BSize = $TypeInfo{$BType}{"Size"}) |
| { |
| if(my $Size = $Count*$BSize) |
| { |
| $TInfo{"Size"} = "$Size"; |
| } |
| } |
| } |
| } |
| else |
| { |
| $TInfo{"Name"} .= "[]"; |
| $TInfo{"Size"} = $SYS_WORD; |
| } |
| } |
| elsif($TInfo{"Type"} eq "Pointer") |
| { |
| if(my $BType = $TInfo{"BaseType"}) |
| { |
| if($TypeInfo{$BType}{"Type"}=~/MethodPtr|FuncPtr/) |
| { # void(GTestSuite::**)() |
| # int(**)(...) |
| if($TInfo{"Name"}=~s/\*\Z//) { |
| $TInfo{"Name"}=~s/\*(\))/\*\*$1/; |
| } |
| } |
| } |
| } |
| } |
| |
| if(my $Bid = $TInfo{"BaseType"}) |
| { |
| if(not $TInfo{"Size"} |
| and $TypeInfo{$Bid}{"Size"}) { |
| $TInfo{"Size"} = $TypeInfo{$Bid}{"Size"}; |
| } |
| } |
| if($TInfo{"Name"}) { |
| $TInfo{"Name"} = formatName($TInfo{"Name"}, "T"); # simpleName() |
| } |
| |
| if($TInfo{"Name"}=~/>\Z/) |
| { |
| my ($Short, @TParams) = (); |
| |
| if(defined $TmplParam{$ID}) |
| { |
| $Short = getShortName($TInfo{"Name"}); |
| @TParams = get_TParams($ID); |
| @TParams = shortTParams($Short, @TParams); |
| } |
| else { |
| ($Short, @TParams) = parse_TParams($TInfo{"Name"}); |
| } |
| |
| if(@TParams) |
| { |
| delete($TInfo{"TParam"}); |
| |
| foreach my $Pos (0 .. $#TParams) { |
| $TInfo{"TParam"}{$Pos}{"name"} = $TParams[$Pos]; |
| } |
| |
| $TInfo{"Name"} = formatName($Short."<".join(", ", @TParams).">", "T"); |
| } |
| } |
| |
| if(not $TInfo{"Name"}) |
| { |
| if($TInfo{"Type"}=~/\A(Class|Struct|Enum|Union)\Z/) |
| { |
| if($TInfo{"Header"}) { |
| $TInfo{"Name"} = "anon-".lc($TInfo{"Type"})."-".$TInfo{"Header"}."-".$TInfo{"Line"}; |
| } |
| elsif($TInfo{"Source"}) { |
| $TInfo{"Name"} = "anon-".lc($TInfo{"Type"})."-".$TInfo{"Source"}."-".$TInfo{"SourceLine"}; |
| } |
| else |
| { |
| if(not defined $TypeMember{$ID}) |
| { |
| if(not defined $ANON_TYPE_WARN{$TInfo{"Type"}}) |
| { |
| printMsg("WARNING", "a \"".$TInfo{"Type"}."\" type with no attributes detected in the DWARF dump ($ID)"); |
| $ANON_TYPE_WARN{$TInfo{"Type"}} = 1; |
| } |
| $TInfo{"Name"} = "anon-".lc($TInfo{"Type"}); |
| } |
| } |
| |
| if($TInfo{"Name"} and $TInfo{"NameSpace"}) { |
| $TInfo{"Name"} = $TInfo{"NameSpace"}."::".$TInfo{"Name"}; |
| } |
| } |
| } |
| |
| if($TInfo{"Name"}) |
| { |
| if(not defined $TName_Tid{$TInfo{"Type"}}{$TInfo{"Name"}} |
| or ($ID>0 and $ID<$TName_Tid{$TInfo{"Type"}}{$TInfo{"Name"}}) |
| or ($ID>0 and $TName_Tid{$TInfo{"Type"}}{$TInfo{"Name"}}<0)) |
| { |
| $TName_Tid{$TInfo{"Type"}}{$TInfo{"Name"}} = "$ID"; |
| } |
| $TName_Tids{$TInfo{"Type"}}{$TInfo{"Name"}}{$ID} = 1; |
| } |
| |
| if(defined $TInfo{"Source"}) |
| { |
| if(not defined $TInfo{"Header"}) |
| { |
| $TInfo{"Line"} = $TInfo{"SourceLine"}; |
| delete($TInfo{"SourceLine"}); |
| } |
| } |
| |
| foreach my $Attr (keys(%TInfo)) { |
| $TypeInfo{$ID}{$Attr} = $TInfo{$Attr}; |
| } |
| |
| if(my $BASE_ID = $DWARF_Info{$ID}{"specification"}) |
| { |
| foreach my $Attr (keys(%{$TypeInfo{$BASE_ID}})) |
| { |
| if($Attr ne "Type") { |
| $TypeInfo{$ID}{$Attr} = $TypeInfo{$BASE_ID}{$Attr}; |
| } |
| } |
| |
| foreach my $Attr (keys(%{$TypeInfo{$ID}})) { |
| $TypeInfo{$BASE_ID}{$Attr} = $TypeInfo{$ID}{$Attr}; |
| } |
| |
| $TypeSpec{$ID} = $BASE_ID; |
| } |
| } |
| |
| sub setSource($$) |
| { |
| my ($R, $ID) = @_; |
| |
| my $File = $DWARF_Info{$ID}{"decl_file"}; |
| my $Line = $DWARF_Info{$ID}{"decl_line"}; |
| |
| my $Unit = $DWARF_Info{$ID}{"Unit"}; |
| |
| if(defined $File) |
| { |
| my $Name = undef; |
| |
| if($ID>=0) { |
| $Name = $SourceFile{$Unit}{$File}; |
| } |
| else |
| { # imported |
| $Name = $SourceFile_Alt{0}{$File}; |
| } |
| |
| if($Name=~/\.($HEADER_EXT)\Z/) |
| { # header |
| $R->{"Header"} = $Name; |
| if(defined $Line) { |
| $R->{"Line"} = $Line; |
| } |
| } |
| elsif(index($Name, "<built-in>")==-1) |
| { # source |
| $R->{"Source"} = $Name; |
| if(defined $Line) { |
| $R->{"SourceLine"} = $Line; |
| } |
| } |
| } |
| } |
| |
| sub skipSymbol($) |
| { |
| if($SkipCxx and not $STDCXX_TARGET) |
| { |
| if($_[0]=~/\A(_ZS|_ZNS|_ZNKS|_ZN9__gnu_cxx|_ZNK9__gnu_cxx|_ZTIS|_ZTSS|_Zd|_Zn)/) |
| { # stdc++ symbols |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| sub find_center($$) |
| { |
| my ($Name, $Target) = @_; |
| my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 ); |
| foreach my $Pos (0 .. length($Name)-1) |
| { |
| my $S = substr($Name, length($Name)-1-$Pos, 1); |
| if(defined $B{$S}) { |
| $B{$S}+=1; |
| } |
| if($S eq $Target) |
| { |
| if($B{"("}==$B{")"} |
| and $B{"<"}==$B{">"}) { |
| return length($Name)-1-$Pos; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| sub isExternal($) |
| { |
| my $ID = $_[0]; |
| |
| if($DWARF_Info{$ID}{"external"}) { |
| return 1; |
| } |
| elsif(my $Spec = $DWARF_Info{$ID}{"specification"}) |
| { |
| if($DWARF_Info{$Spec}{"external"}) { |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| sub symByAddr($) |
| { |
| my $Loc = $_[0]; |
| |
| my ($Addr, $Sect) = ("", ""); |
| if($Loc=~/\+(.+)/) |
| { |
| $Addr = $1; |
| if(not $Addr=~s/\A0x//) |
| { |
| $Addr=~s/\A00//; |
| } |
| } |
| if($Loc=~/([\w\.]+)\+/) { |
| $Sect = $1; |
| } |
| |
| if($Addr ne "") |
| { |
| foreach ($Sect, "") |
| { |
| if(defined $SymbolTable{$_}{$Addr}) |
| { |
| if(my @Symbols = sort keys(%{$SymbolTable{$_}{$Addr}})) { |
| return $Symbols[0]; |
| } |
| } |
| } |
| } |
| |
| return undef; |
| } |
| |
| sub get_Mangled($) |
| { |
| my $ID = $_[0]; |
| |
| if(not defined $AddrToName) |
| { |
| if(my $Link = $DWARF_Info{$ID}{"linkage_name"}) |
| { |
| return $Link; |
| } |
| } |
| |
| if(my $Low_Pc = $DWARF_Info{$ID}{"low_pc"}) |
| { |
| if($Low_Pc=~/<([\w\@\.]+)>/) { |
| return $1; |
| } |
| else |
| { |
| if(my $Symbol = symByAddr($Low_Pc)) { |
| return $Symbol; |
| } |
| } |
| } |
| |
| if(my $Loc = $DWARF_Info{$ID}{"location"}) |
| { |
| if($Loc=~/<([\w\@\.]+)>/) { |
| return $1; |
| } |
| else |
| { |
| if(my $Symbol = symByAddr($Loc)) { |
| return $Symbol; |
| } |
| } |
| } |
| |
| if(my $Link = $DWARF_Info{$ID}{"linkage_name"}) |
| { |
| return $Link; |
| } |
| |
| return undef; |
| } |
| |
| sub completeNS($) |
| { |
| my $ID = $_[0]; |
| |
| my $NS = undef; |
| my $ID_ = $ID; |
| my @NSs = (); |
| |
| while($NS = $NameSpace{$ID_} |
| or $NS = $NameSpace{$DWARF_Info{$ID_}{"specification"}}) |
| { |
| if(my $N = $DWARF_Info{$NS}{"name"}) { |
| push(@NSs, $N); |
| } |
| $ID_ = $NS; |
| } |
| |
| if(@NSs) |
| { |
| my $N = join("::", reverse(@NSs)); |
| $NestedNameSpaces{$N} = 1; |
| return $N; |
| } |
| |
| return undef; |
| } |
| |
| sub getSymbolInfo($) |
| { |
| my $ID = $_[0]; |
| |
| if(my $N = $NameSpace{$ID}) |
| { |
| if($DWARF_Info{$N}{"Kind"} eq "lexical_block" |
| or $DWARF_Info{$N}{"Kind"} eq "subprogram") |
| { # local variables |
| return; |
| } |
| } |
| |
| if(my $Loc = $DWARF_Info{$ID}{"location"}) |
| { |
| if($Loc=~/ reg\d+\Z/) |
| { # local variables |
| return; |
| } |
| } |
| |
| my $ShortName = $DWARF_Info{$ID}{"name"}; |
| my $MnglName = get_Mangled($ID); |
| |
| if(not $MnglName) |
| { |
| if(my $Sp = $SpecElem{$ID}) |
| { |
| $MnglName = get_Mangled($Sp); |
| |
| if(not $MnglName) |
| { |
| if(my $Orig = $OrigElem{$Sp}) |
| { |
| $MnglName = get_Mangled($Orig); |
| } |
| } |
| } |
| } |
| |
| if(not $MnglName) |
| { |
| if(index($ShortName, "<")!=-1) |
| { # template |
| return; |
| } |
| $MnglName = $ShortName; |
| } |
| |
| if(skipSymbol($MnglName)) { |
| return; |
| } |
| |
| if(index($MnglName, "\@")!=-1) { |
| $MnglName=~s/([\@]+.*?)\Z//; |
| } |
| |
| if(not $MnglName) { |
| return; |
| } |
| |
| if(index($MnglName, ".")!=-1) |
| { # foo.part.14 |
| # bar.isra.15 |
| return; |
| } |
| |
| if($MnglName=~/\W/) |
| { # unmangled operators, etc. |
| return; |
| } |
| |
| if($MnglName) |
| { |
| if(my $OLD_ID = $Mangled_ID{$MnglName}) |
| { # duplicates |
| if(not defined $SymbolInfo{$OLD_ID}{"Header"} |
| or not defined $SymbolInfo{$OLD_ID}{"Source"}) |
| { |
| setSource($SymbolInfo{$OLD_ID}, $ID); |
| } |
| |
| if(not defined $SymbolInfo{$OLD_ID}{"ShortName"} |
| and $ShortName) { |
| $SymbolInfo{$OLD_ID}{"ShortName"} = $ShortName; |
| } |
| |
| if(defined $DWARF_Info{$OLD_ID}{"low_pc"} |
| or not defined $DWARF_Info{$ID}{"low_pc"}) |
| { |
| if(defined $Checked_Spec{$MnglName} |
| or not $DWARF_Info{$ID}{"specification"}) |
| { |
| if(not defined $SpecElem{$ID} |
| and not defined $OrigElem{$ID}) { |
| delete($DWARF_Info{$ID}); |
| } |
| return; |
| } |
| } |
| } |
| } |
| |
| my %SInfo = (); |
| |
| if($ShortName) { |
| $SInfo{"ShortName"} = $ShortName; |
| } |
| $SInfo{"MnglName"} = $MnglName; |
| |
| if($ShortName) |
| { |
| if($MnglName eq $ShortName) |
| { |
| delete($SInfo{"MnglName"}); |
| $MnglName = $ShortName; |
| } |
| elsif(index($MnglName, "_Z")!=0) |
| { |
| if($SInfo{"ShortName"}) |
| { |
| if(index($SInfo{"ShortName"}, ".")==-1) { |
| $SInfo{"Alias"} = $SInfo{"ShortName"}; |
| } |
| $SInfo{"ShortName"} = $SInfo{"MnglName"}; |
| } |
| |
| delete($SInfo{"MnglName"}); |
| $MnglName = $ShortName; |
| # $ShortName = $SInfo{"ShortName"}; |
| } |
| } |
| else |
| { |
| if(index($MnglName, "_Z")!=0) |
| { |
| $SInfo{"ShortName"} = $SInfo{"MnglName"}; |
| delete($SInfo{"MnglName"}); |
| } |
| } |
| |
| if(isExternal($ID)) { |
| $SInfo{"External"} = 1; |
| } |
| |
| if(my $Orig = $DWARF_Info{$ID}{"abstract_origin"}) |
| { |
| if(isExternal($Orig)) { |
| $SInfo{"External"} = 1; |
| } |
| } |
| |
| if(index($MnglName, "_ZNVK")==0) |
| { |
| $SInfo{"Const"} = 1; |
| $SInfo{"Volatile"} = 1; |
| } |
| elsif(index($MnglName, "_ZNV")==0) { |
| $SInfo{"Volatile"} = 1; |
| } |
| elsif(index($MnglName, "_ZNK")==0) { |
| $SInfo{"Const"} = 1; |
| } |
| |
| if($DWARF_Info{$ID}{"artificial"}) { |
| $SInfo{"Artificial"} = 1; |
| } |
| |
| my ($C, $D) = (); |
| |
| if($MnglName=~/C[1-4][EI].+/) |
| { |
| $C = 1; |
| $SInfo{"Constructor"} = 1; |
| } |
| |
| if($MnglName=~/D[0-4][EI].+/) |
| { |
| $D = 1; |
| $SInfo{"Destructor"} = 1; |
| } |
| |
| if($C or $D) |
| { |
| if(my $Orig = $DWARF_Info{$ID}{"abstract_origin"}) |
| { |
| if(my $InLine = $DWARF_Info{$Orig}{"inline"}) |
| { |
| if(index($InLine, "declared_not_inlined")==0) |
| { |
| $SInfo{"InLine"} = 1; |
| $SInfo{"Artificial"} = 1; |
| } |
| } |
| |
| setSource(\%SInfo, $Orig); |
| |
| if(my $Spec = $DWARF_Info{$Orig}{"specification"}) |
| { |
| setSource(\%SInfo, $Spec); |
| |
| $SInfo{"ShortName"} = $DWARF_Info{$Spec}{"name"}; |
| if($D) { |
| $SInfo{"ShortName"}=~s/\A\~//g; |
| } |
| |
| if(my $Class = $NameSpace{$Spec}) { |
| $SInfo{"Class"} = $Class; |
| } |
| |
| if(my $Virt = $DWARF_Info{$Spec}{"virtuality"}) |
| { |
| if(index($Virt, "virtual")!=-1) { |
| $SInfo{"Virt"} = 1; |
| } |
| } |
| |
| if(my $Access = $DWARF_Info{$Spec}{"accessibility"}) |
| { |
| if($Access ne "public") |
| { # default access of methods in the ABI dump is "public" |
| $SInfo{ucfirst($Access)} = 1; |
| } |
| } |
| else |
| { # NOTE: default access of class methods in the debug info is "private" |
| if($TypeInfo{$SInfo{"Class"}}{"Type"} eq "Class") |
| { |
| $SInfo{"Private"} = 1; |
| } |
| } |
| |
| # clean origin |
| delete($SymbolInfo{$Spec}); |
| } |
| } |
| } |
| else |
| { |
| if(my $InLine = $DWARF_Info{$ID}{"inline"}) |
| { |
| if(index($InLine, "declared_inlined")==0) { |
| $SInfo{"InLine"} = 1; |
| } |
| } |
| } |
| |
| if(defined $AddrToName) |
| { |
| if(not $SInfo{"Alias"} |
| and not $SInfo{"Constructor"} |
| and not $SInfo{"Destructor"}) |
| { |
| if(my $Linkage = $DWARF_Info{$ID}{"linkage_name"}) |
| { |
| if($Linkage ne $MnglName) { |
| $SInfo{"Alias"} = $Linkage; |
| } |
| } |
| } |
| } |
| |
| if($DWARF_Info{$ID}{"Kind"} eq "variable") |
| { # global data |
| $SInfo{"Data"} = 1; |
| |
| if(my $Spec = $DWARF_Info{$ID}{"specification"}) |
| { |
| if($DWARF_Info{$Spec}{"Kind"} eq "member") |
| { |
| setSource(\%SInfo, $Spec); |
| $SInfo{"ShortName"} = $DWARF_Info{$Spec}{"name"}; |
| |
| if(my $NSp = $NameSpace{$Spec}) |
| { |
| if($DWARF_Info{$NSp}{"Kind"} eq "namespace") { |
| $SInfo{"NameSpace"} = completeNS($Spec); |
| } |
| else { |
| $SInfo{"Class"} = $NSp; |
| } |
| } |
| } |
| } |
| } |
| |
| if(my $Access = $DWARF_Info{$ID}{"accessibility"}) |
| { |
| if($Access ne "public") |
| { # default access of methods in the ABI dump is "public" |
| $SInfo{ucfirst($Access)} = 1; |
| } |
| } |
| elsif(not $DWARF_Info{$ID}{"specification"} |
| and not $DWARF_Info{$ID}{"abstract_origin"}) |
| { |
| if(my $NS = $NameSpace{$ID}) |
| { |
| if(defined $TypeInfo{$NS}) |
| { # NOTE: default access of class methods in the debug info is "private" |
| if($TypeInfo{$NS}{"Type"} eq "Class") |
| { |
| $SInfo{"Private"} = 1; |
| } |
| } |
| } |
| } |
| |
| if(my $Class = $DWARF_Info{$ID}{"containing_type"}) |
| { |
| $SInfo{"Class"} = $Class; |
| } |
| |
| if(my $NS = $NameSpace{$ID}) |
| { |
| if($DWARF_Info{$NS}{"Kind"} eq "namespace") { |
| $SInfo{"NameSpace"} = completeNS($ID); |
| } |
| else { |
| $SInfo{"Class"} = $NS; |
| } |
| } |
| |
| if($SInfo{"Class"} and $MnglName |
| and index($MnglName, "_Z")!=0) |
| { |
| return; |
| } |
| |
| if(my $Return = $DWARF_Info{$ID}{"type"}) |
| { |
| $SInfo{"Return"} = $Return; |
| } |
| if(my $Spec = $DWARF_Info{$ID}{"specification"}) |
| { |
| if(not $DWARF_Info{$ID}{"type"}) { |
| $SInfo{"Return"} = $DWARF_Info{$Spec}{"type"}; |
| } |
| if(my $Value = $DWARF_Info{$Spec}{"const_value"}) |
| { |
| if($Value=~/ block:\s*(.*?)\Z/) { |
| $Value = $1; |
| } |
| $SInfo{"Value"} = $Value; |
| } |
| } |
| |
| if($SInfo{"ShortName"}=~/>\Z/) |
| { # foo<T1, T2, ...> |
| my ($Short, @TParams) = (); |
| |
| if(defined $TmplParam{$ID}) |
| { |
| $Short = getShortName($SInfo{"ShortName"}); |
| @TParams = get_TParams($ID); |
| @TParams = shortTParams($Short, @TParams); |
| } |
| else { |
| ($Short, @TParams) = parse_TParams($SInfo{"ShortName"}); |
| } |
| |
| if(@TParams) |
| { |
| foreach my $Pos (0 .. $#TParams) { |
| $SInfo{"TParam"}{$Pos}{"name"} = formatName($TParams[$Pos], "T"); |
| } |
| # simplify short name |
| $SInfo{"ShortName"} = $Short.formatName("<".join(", ", @TParams).">", "T"); |
| } |
| } |
| elsif($SInfo{"ShortName"}=~/\Aoperator (\w.*)\Z/) |
| { # operator type<T1>::name |
| $SInfo{"ShortName"} = "operator ".simpleName($1); |
| } |
| |
| if(my $Virt = $DWARF_Info{$ID}{"virtuality"}) |
| { |
| if(index($Virt, "virtual")!=-1) |
| { |
| if($D or defined $SpecElem{$ID}) { |
| $SInfo{"Virt"} = 1; |
| } |
| else { |
| $SInfo{"PureVirt"} = 1; |
| } |
| } |
| |
| if((my $VirtPos = $DWARF_Info{$ID}{"vtable_elem_location"}) ne "") |
| { |
| $SInfo{"VirtPos"} = $VirtPos; |
| } |
| } |
| |
| setSource(\%SInfo, $ID); |
| |
| if(not $SInfo{"Header"}) |
| { |
| if($SInfo{"Class"}) |
| { # detect missed header by class |
| if(defined $TypeInfo{$SInfo{"Class"}}{"Header"}) { |
| $SInfo{"Header"} = $TypeInfo{$SInfo{"Class"}}{"Header"}; |
| } |
| } |
| } |
| |
| if(not $SInfo{"Header"} |
| or ($SInfo{"External"} and not defined $PublicHeader{$SInfo{"Header"}})) |
| { |
| if($SInfo{"MnglName"} and defined $SymbolToHeader{$SInfo{"MnglName"}}) { |
| $SInfo{"Header"} = chooseHeader($SInfo{"MnglName"}, $SInfo{"Source"}); |
| } |
| elsif(not $SInfo{"Class"} |
| and defined $SymbolToHeader{$SInfo{"ShortName"}}) { |
| $SInfo{"Header"} = chooseHeader($SInfo{"ShortName"}, $SInfo{"Source"}); |
| } |
| } |
| |
| if($SInfo{"Alias"}) |
| { |
| if(defined $SymbolToHeader{$SInfo{"Alias"}}) { |
| $SInfo{"Header"} = chooseHeader($SInfo{"Alias"}, $SInfo{"Source"}); |
| } |
| } |
| |
| my $PPos = 0; |
| |
| foreach my $Pos (sort {int($a) <=> int($b)} keys(%{$FuncParam{$ID}})) |
| { |
| my $ParamId = $FuncParam{$ID}{$Pos}; |
| my $Offset = undef; |
| my $Reg = undef; |
| |
| if(my $Sp = $SpecElem{$ID}) |
| { |
| if(defined $FuncParam{$Sp}) { |
| $ParamId = $FuncParam{$Sp}{$Pos}; |
| } |
| } |
| |
| if((my $Loc = $DWARF_Info{$ParamId}{"location"}) ne "") { |
| $Offset = $Loc; |
| } |
| elsif((my $R = $DWARF_Info{$ParamId}{"register"}) ne "") { |
| $Reg = $RegName{$R}; |
| } |
| elsif((my $LL = $DWARF_Info{$ParamId}{"location_list"}) ne "") |
| { |
| if(my $L = $DebugLoc{$LL}) |
| { |
| if($L=~/reg(\d+)/) { |
| $Reg = $RegName{$1}; |
| } |
| elsif($L=~/fbreg\s+(-?\w+)\Z/) { |
| $Offset = $1; |
| } |
| } |
| elsif(not defined $DebugLoc{$LL}) |
| { # invalid debug_loc |
| if(not $InvalidDebugLoc) |
| { |
| printMsg("ERROR", "invalid debug_loc section of object, please fix your elf utils"); |
| $InvalidDebugLoc = 1; |
| } |
| } |
| } |
| |
| if(my $Orig = $DWARF_Info{$ParamId}{"abstract_origin"}) { |
| $ParamId = $Orig; |
| } |
| |
| my %PInfo = %{$DWARF_Info{$ParamId}}; |
| |
| if(defined $Offset |
| and not defined $IncompatibleOpt) { |
| $SInfo{"Param"}{$Pos}{"offset"} = $Offset; |
| } |
| |
| if($TypeInfo{$PInfo{"type"}}{"Type"} eq "Const") |
| { |
| if(my $BTid = $TypeInfo{$PInfo{"type"}}{"BaseType"}) |
| { |
| if($TypeInfo{$BTid}{"Type"} eq "Ref") |
| { # const&const -> const& |
| $PInfo{"type"} = $BTid; |
| } |
| } |
| } |
| |
| $SInfo{"Param"}{$Pos}{"type"} = $PInfo{"type"}; |
| |
| if(defined $PInfo{"name"}) { |
| $SInfo{"Param"}{$Pos}{"name"} = $PInfo{"name"}; |
| } |
| elsif($TypeInfo{$PInfo{"type"}}{"Name"} ne "...") { |
| $SInfo{"Param"}{$Pos}{"name"} = "p".($PPos+1); |
| } |
| |
| if(defined $Reg |
| and not defined $IncompatibleOpt) |
| { |
| $SInfo{"Reg"}{$Pos} = $Reg; |
| } |
| |
| if($DWARF_Info{$ParamId}{"artificial"} and $Pos==0) |
| { |
| if($SInfo{"Param"}{$Pos}{"name"} eq "p1") { |
| $SInfo{"Param"}{$Pos}{"name"} = "this"; |
| } |
| } |
| |
| if($SInfo{"Param"}{$Pos}{"name"} ne "this") |
| { # this, p1, p2, etc. |
| $PPos += 1; |
| } |
| } |
| |
| if($SInfo{"Constructor"} and not $SInfo{"InLine"} |
| and $SInfo{"Class"}) { |
| delete($TypeInfo{$SInfo{"Class"}}{"Copied"}); |
| } |
| |
| if(my $BASE_ID = $Mangled_ID{$MnglName}) |
| { |
| if(defined $SInfo{"Param"}) |
| { |
| if(keys(%{$SInfo{"Param"}})!=keys(%{$SymbolInfo{$BASE_ID}{"Param"}})) |
| { # different symbols with the same name |
| delete($SymbolInfo{$BASE_ID}); |
| } |
| } |
| |
| $ID = $BASE_ID; |
| |
| if(defined $SymbolInfo{$ID}{"PureVirt"}) |
| { # if the specification of a symbol is located in other compile unit |
| delete($SymbolInfo{$ID}{"PureVirt"}); |
| $SymbolInfo{$ID}{"Virt"} = 1; |
| } |
| } |
| $Mangled_ID{$MnglName} = $ID; |
| |
| if($DWARF_Info{$ID}{"specification"}) { |
| $Checked_Spec{$MnglName} = 1; |
| } |
| |
| foreach my $Attr (keys(%SInfo)) |
| { |
| if(ref($SInfo{$Attr}) eq "HASH") |
| { |
| foreach my $K1 (keys(%{$SInfo{$Attr}})) |
| { |
| if(ref($SInfo{$Attr}{$K1}) eq "HASH") |
| { |
| foreach my $K2 (keys(%{$SInfo{$Attr}{$K1}})) |
| { |
| $SymbolInfo{$ID}{$Attr}{$K1}{$K2} = $SInfo{$Attr}{$K1}{$K2}; |
| } |
| } |
| else { |
| $SymbolInfo{$ID}{$Attr}{$K1} = $SInfo{$Attr}{$K1}; |
| } |
| } |
| } |
| else |
| { |
| $SymbolInfo{$ID}{$Attr} = $SInfo{$Attr}; |
| } |
| } |
| |
| if($ID>$GLOBAL_ID) { |
| $GLOBAL_ID = $ID; |
| } |
| } |
| |
| sub chooseHeader($$) |
| { |
| my ($Symbol, $Source) = @_; |
| |
| my @Headers = keys(%{$SymbolToHeader{$Symbol}}); |
| |
| if($#Headers==0) { |
| return $Headers[0]; |
| } |
| |
| $Source=~s/\.\w+\Z//g; |
| foreach my $Header (@Headers) |
| { |
| if($Header=~/\A\Q$Source\E(|\.[\w\+]+)\Z/) { |
| return $Header; |
| } |
| } |
| |
| @Headers = sort {length($a)<=>length($b)} sort {lc($a) cmp lc($b)} @Headers; |
| |
| return $Headers[0]; |
| } |
| |
| sub getTypeIdByName($$) |
| { |
| my ($Type, $Name) = @_; |
| return $TName_Tid{$Type}{formatName($Name, "T")}; |
| } |
| |
| sub getFirst($) |
| { |
| my $Tid = $_[0]; |
| if(not $Tid) { |
| return $Tid; |
| } |
| |
| if(defined $TypeSpec{$Tid}) { |
| $Tid = $TypeSpec{$Tid}; |
| } |
| |
| my $F = 0; |
| |
| if(my $Name = $TypeInfo{$Tid}{"Name"}) |
| { |
| my $Type = $TypeInfo{$Tid}{"Type"}; |
| if($Name=~s/\Astruct //) |
| { # search for class or derived types (const, *, etc.) |
| $F = 1; |
| } |
| |
| my $FTid = undef; |
| if($F) |
| { |
| foreach my $Type ("Class", "Const", "Ref", "RvalueRef", "Pointer") |
| { |
| if($FTid = $TName_Tid{$Type}{$Name}) |
| { |
| if($FTid ne $Tid) |
| { |
| $MergedTypes{$Tid} = 1; |
| } |
| return "$FTid"; |
| } |
| } |
| |
| $Name = "struct ".$Name; |
| } |
| |
| if(not $FTid) { |
| $FTid = $TName_Tid{$Type}{$Name}; |
| } |
| |
| if($FTid) { |
| return "$FTid"; |
| } |
| printMsg("ERROR", "internal error (missed type id $Tid)"); |
| } |
| |
| return $Tid; |
| } |
| |
| sub searchTypeID($) |
| { |
| my $Name = $_[0]; |
| |
| my %Pr = map {$_=>1} ( |
| "Struct", |
| "Union", |
| "Enum" |
| ); |
| |
| foreach my $Type ("Class", "Struct", "Union", "Enum", "Typedef", "Const", |
| "Volatile", "Ref", "RvalueRef", "Pointer", "FuncPtr", "MethodPtr", "FieldPtr") |
| { |
| my $Tid = $TName_Tid{$Type}{$Name}; |
| |
| if(not $Tid) |
| { |
| my $P = ""; |
| if(defined $Pr{$Type}) |
| { |
| $P = lc($Type)." "; |
| } |
| |
| $Tid = $TName_Tid{$Type}{$P.$Name} |
| } |
| if($Tid) { |
| return $Tid; |
| } |
| } |
| return undef; |
| } |
| |
| sub remove_Unused() |
| { # remove unused data types from the ABI dump |
| %HeadersInfo = (); |
| %SourcesInfo = (); |
| |
| my (%SelectedHeaders, %SelectedSources) = (); |
| |
| foreach my $ID (sort {int($a)<=>int($b)} keys(%SymbolInfo)) |
| { |
| if($SelectedSymbols{$ID}==2) |
| { # data, inline, pure |
| next; |
| } |
| |
| register_SymbolUsage($ID); |
| |
| if(my $H = $SymbolInfo{$ID}{"Header"}) { |
| $SelectedHeaders{$H} = 1; |
| } |
| if(my $S = $SymbolInfo{$ID}{"Source"}) { |
| $SelectedSources{$S} = 1; |
| } |
| } |
| |
| foreach my $ID (sort {int($a)<=>int($b)} keys(%SymbolInfo)) |
| { |
| if($SelectedSymbols{$ID}==2) |
| { # data, inline, pure |
| my $Save = 0; |
| if(my $Class = $SymbolInfo{$ID}{"Class"}) |
| { |
| if(defined $UsedType{$Class}) { |
| $Save = 1; |
| } |
| else |
| { |
| foreach (keys(%{$ClassChild{$Class}})) |
| { |
| if(defined $UsedType{$_}) |
| { |
| $Save = 1; |
| last; |
| } |
| } |
| } |
| } |
| if(my $Header = $SymbolInfo{$ID}{"Header"}) |
| { |
| if(defined $SelectedHeaders{$Header}) { |
| $Save = 1; |
| } |
| } |
| if(my $Source = $SymbolInfo{$ID}{"Source"}) |
| { |
| if(defined $SelectedSources{$Source}) { |
| $Save = 1; |
| } |
| } |
| if($Save) { |
| register_SymbolUsage($ID); |
| } |
| else { |
| delete($SymbolInfo{$ID}); |
| } |
| } |
| } |
| |
| if(defined $AllTypes) |
| { |
| # register all data types (except anon structs and unions) |
| foreach my $Tid (keys(%TypeInfo)) |
| { |
| if(defined $LocalType{$Tid}) |
| { # except local code |
| next; |
| } |
| if($TypeInfo{$Tid}{"Type"} eq "Enum" |
| or index($TypeInfo{$Tid}{"Name"}, "anon-")!=0) { |
| register_TypeUsage($Tid); |
| } |
| } |
| |
| # remove unused anons (except enums) |
| foreach my $Tid (keys(%TypeInfo)) |
| { |
| if(not $UsedType{$Tid}) |
| { |
| if($TypeInfo{$Tid}{"Type"} ne "Enum") |
| { |
| if(index($TypeInfo{$Tid}{"Name"}, "anon-")==0) { |
| delete($TypeInfo{$Tid}); |
| } |
| } |
| } |
| } |
| |
| # remove duplicates |
| foreach my $Tid (keys(%TypeInfo)) |
| { |
| my $Name = $TypeInfo{$Tid}{"Name"}; |
| my $Type = $TypeInfo{$Tid}{"Type"}; |
| |
| if($TName_Tid{$Type}{$Name} ne $Tid) { |
| delete($TypeInfo{$Tid}); |
| } |
| } |
| } |
| else |
| { |
| foreach my $Tid (keys(%TypeInfo)) |
| { # remove unused types |
| if(not $UsedType{$Tid}) { |
| delete($TypeInfo{$Tid}); |
| } |
| } |
| } |
| |
| foreach my $Tid (keys(%MergedTypes)) { |
| delete($TypeInfo{$Tid}); |
| } |
| |
| foreach my $Tid (keys(%LocalType)) |
| { |
| if(not $UsedType{$Tid}) { |
| delete($TypeInfo{$Tid}); |
| } |
| } |
| |
| # clean memory |
| %MergedTypes = (); |
| %LocalType = (); |
| |
| # completeness |
| foreach my $Tid (sort keys(%TypeInfo)) { |
| check_Completeness($TypeInfo{$Tid}); |
| } |
| |
| foreach my $Sid (sort keys(%SymbolInfo)) { |
| check_Completeness($SymbolInfo{$Sid}); |
| } |
| |
| # clean memory |
| %UsedType = (); |
| } |
| |
| sub simpleName($) |
| { |
| my $N = $_[0]; |
| |
| $N=~s/\A(struct|class|union|enum) //; # struct, class, union, enum |
| |
| if(index($N, "std::basic_string")!=-1) |
| { |
| $N=~s/std::basic_string<char, std::char_traits<char>, std::allocator<char> >/std::string /g; |
| $N=~s/std::basic_string<char, std::char_traits<char> >/std::string /g; |
| $N=~s/std::basic_string<char>/std::string /g; |
| } |
| |
| return formatName($N, "T"); |
| } |
| |
| sub register_SymbolUsage($) |
| { |
| my $InfoId = $_[0]; |
| |
| my %FuncInfo = %{$SymbolInfo{$InfoId}}; |
| |
| if(my $S = $FuncInfo{"Source"}) { |
| $SourcesInfo{$S} = 1; |
| } |
| if(my $H = $FuncInfo{"Header"}) { |
| $HeadersInfo{$H} = 1; |
| } |
| if(my $RTid = getFirst($FuncInfo{"Return"})) |
| { |
| register_TypeUsage($RTid); |
| $SymbolInfo{$InfoId}{"Return"} = $RTid; |
| } |
| if(my $FCid = getFirst($FuncInfo{"Class"})) |
| { |
| register_TypeUsage($FCid); |
| $SymbolInfo{$InfoId}{"Class"} = $FCid; |
| |
| if(my $ThisId = getTypeIdByName("Const", $TypeInfo{$FCid}{"Name"}."*const")) |
| { # register "this" pointer |
| register_TypeUsage($ThisId); |
| } |
| if(my $ThisId_C = getTypeIdByName("Const", $TypeInfo{$FCid}{"Name"}." const*const")) |
| { # register "this" pointer (const method) |
| register_TypeUsage($ThisId_C); |
| } |
| } |
| foreach my $PPos (keys(%{$FuncInfo{"Param"}})) |
| { |
| if(my $PTid = getFirst($FuncInfo{"Param"}{$PPos}{"type"})) |
| { |
| register_TypeUsage($PTid); |
| $SymbolInfo{$InfoId}{"Param"}{$PPos}{"type"} = $PTid; |
| } |
| } |
| foreach my $TPos (keys(%{$FuncInfo{"TParam"}})) |
| { |
| my $TPName = $FuncInfo{"TParam"}{$TPos}{"name"}; |
| if(my $TTid = searchTypeID($TPName)) |
| { |
| if(my $FTTid = getFirst($TTid)) { |
| register_TypeUsage($FTTid); |
| } |
| } |
| } |
| } |
| |
| sub register_TypeUsage($) |
| { |
| my $TypeId = $_[0]; |
| if(not $TypeId) { |
| return 0; |
| } |
| if($UsedType{$TypeId}) |
| { # already registered |
| return 1; |
| } |
| my %TInfo = %{$TypeInfo{$TypeId}}; |
| |
| if(my $S = $TInfo{"Source"}) { |
| $SourcesInfo{$S} = 1; |
| } |
| if(my $H = $TInfo{"Header"}) { |
| $HeadersInfo{$H} = 1; |
| } |
| |
| if($TInfo{"Type"}) |
| { |
| if(my $NS = $TInfo{"NameSpace"}) |
| { |
| if(my $NSTid = searchTypeID($NS)) |
| { |
| if(my $FNSTid = getFirst($NSTid)) { |
| register_TypeUsage($FNSTid); |
| } |
| } |
| } |
| |
| if($TInfo{"Type"}=~/\A(Struct|Union|Class|FuncPtr|Func|MethodPtr|FieldPtr|Enum)\Z/) |
| { |
| $UsedType{$TypeId} = 1; |
| if($TInfo{"Type"}=~/\A(Struct|Class)\Z/) |
| { |
| foreach my $BaseId (keys(%{$TInfo{"Base"}})) |
| { # register base classes |
| if(my $FBaseId = getFirst($BaseId)) |
| { |
| register_TypeUsage($FBaseId); |
| if($FBaseId ne $BaseId) |
| { |
| %{$TypeInfo{$TypeId}{"Base"}{$FBaseId}} = %{$TypeInfo{$TypeId}{"Base"}{$BaseId}}; |
| delete($TypeInfo{$TypeId}{"Base"}{$BaseId}); |
| } |
| } |
| } |
| foreach my $TPos (keys(%{$TInfo{"TParam"}})) |
| { |
| my $TPName = $TInfo{"TParam"}{$TPos}{"name"}; |
| if(my $TTid = searchTypeID($TPName)) |
| { |
| if(my $FTTid = getFirst($TTid)) { |
| register_TypeUsage($FTTid); |
| } |
| } |
| } |
| } |
| foreach my $Memb_Pos (keys(%{$TInfo{"Memb"}})) |
| { |
| if(my $MTid = getFirst($TInfo{"Memb"}{$Memb_Pos}{"type"})) |
| { |
| register_TypeUsage($MTid); |
| $TypeInfo{$TypeId}{"Memb"}{$Memb_Pos}{"type"} = $MTid; |
| } |
| } |
| if($TInfo{"Type"} eq "FuncPtr" |
| or $TInfo{"Type"} eq "MethodPtr" |
| or $TInfo{"Type"} eq "Func") |
| { |
| if(my $RTid = getFirst($TInfo{"Return"})) |
| { |
| register_TypeUsage($RTid); |
| $TypeInfo{$TypeId}{"Return"} = $RTid; |
| } |
| foreach my $Memb_Pos (keys(%{$TInfo{"Param"}})) |
| { |
| if(my $MTid = getFirst($TInfo{"Param"}{$Memb_Pos}{"type"})) |
| { |
| register_TypeUsage($MTid); |
| $TypeInfo{$TypeId}{"Param"}{$Memb_Pos}{"type"} = $MTid; |
| } |
| } |
| } |
| if($TInfo{"Type"} eq "FieldPtr") |
| { |
| if(my $RTid = getFirst($TInfo{"Return"})) |
| { |
| register_TypeUsage($RTid); |
| $TypeInfo{$TypeId}{"Return"} = $RTid; |
| } |
| if(my $CTid = getFirst($TInfo{"Class"})) |
| { |
| register_TypeUsage($CTid); |
| $TypeInfo{$TypeId}{"Class"} = $CTid; |
| } |
| } |
| if($TInfo{"Type"} eq "MethodPtr") |
| { |
| if(my $CTid = getFirst($TInfo{"Class"})) |
| { |
| register_TypeUsage($CTid); |
| $TypeInfo{$TypeId}{"Class"} = $CTid; |
| } |
| } |
| if($TInfo{"Type"} eq "Enum") |
| { |
| if(my $BTid = getFirst($TInfo{"BaseType"})) |
| { |
| register_TypeUsage($BTid); |
| $TypeInfo{$TypeId}{"BaseType"} = $BTid; |
| } |
| } |
| return 1; |
| } |
| elsif($TInfo{"Type"}=~/\A(Const|ConstVolatile|Volatile|Pointer|Ref|RvalueRef|Restrict|Array|Typedef)\Z/) |
| { |
| $UsedType{$TypeId} = 1; |
| if(my $BTid = getFirst($TInfo{"BaseType"})) |
| { |
| register_TypeUsage($BTid); |
| $TypeInfo{$TypeId}{"BaseType"} = $BTid; |
| } |
| return 1; |
| } |
| elsif($TInfo{"Type"} eq "Intrinsic") |
| { |
| $UsedType{$TypeId} = 1; |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| my %CheckedType = (); |
| |
| sub check_Completeness($) |
| { |
| my $Info = $_[0]; |
| |
| # data types |
| if(defined $Info->{"Memb"}) |
| { |
| foreach my $Pos (sort keys(%{$Info->{"Memb"}})) |
| { |
| if(defined $Info->{"Memb"}{$Pos}{"type"}) { |
| check_TypeInfo($Info->{"Memb"}{$Pos}{"type"}); |
| } |
| } |
| } |
| if(defined $Info->{"Base"}) |
| { |
| foreach my $Bid (sort keys(%{$Info->{"Base"}})) { |
| check_TypeInfo($Bid); |
| } |
| } |
| if(defined $Info->{"BaseType"}) { |
| check_TypeInfo($Info->{"BaseType"}); |
| } |
| if(defined $Info->{"TParam"}) |
| { |
| foreach my $Pos (sort keys(%{$Info->{"TParam"}})) |
| { |
| my $TName = $Info->{"TParam"}{$Pos}{"name"}; |
| if($TName=~/\A(true|false|\d.*)\Z/) { |
| next; |
| } |
| |
| if(my $Tid = searchTypeID($TName)) { |
| check_TypeInfo($Tid); |
| } |
| else |
| { |
| if(defined $Loud) { |
| printMsg("WARNING", "missed type $TName"); |
| } |
| } |
| } |
| } |
| |
| # symbols |
| if(defined $Info->{"Param"}) |
| { |
| foreach my $Pos (sort keys(%{$Info->{"Param"}})) |
| { |
| if(defined $Info->{"Param"}{$Pos}{"type"}) { |
| check_TypeInfo($Info->{"Param"}{$Pos}{"type"}); |
| } |
| } |
| } |
| if(defined $Info->{"Return"}) { |
| check_TypeInfo($Info->{"Return"}); |
| } |
| if(defined $Info->{"Class"}) { |
| check_TypeInfo($Info->{"Class"}); |
| } |
| } |
| |
| sub check_TypeInfo($) |
| { |
| my $Tid = $_[0]; |
| |
| if(defined $CheckedType{$Tid}) { |
| return; |
| } |
| $CheckedType{$Tid} = 1; |
| |
| if(defined $TypeInfo{$Tid}) |
| { |
| if(not $TypeInfo{$Tid}{"Name"}) { |
| printMsg("ERROR", "missed type name ($Tid)"); |
| } |
| check_Completeness($TypeInfo{$Tid}); |
| } |
| else { |
| printMsg("ERROR", "missed type id $Tid"); |
| } |
| } |
| |
| sub init_Registers() |
| { |
| if($SYS_ARCH eq "x86") |
| { |
| %RegName = ( |
| # integer registers |
| # 32 bits |
| "0"=>"eax", |
| "1"=>"ecx", |
| "2"=>"edx", |
| "3"=>"ebx", |
| "4"=>"esp", |
| "5"=>"ebp", |
| "6"=>"esi", |
| "7"=>"edi", |
| "8"=>"eip", |
| "9"=>"eflags", |
| "10"=>"trapno", |
| # FPU-control registers |
| # 16 bits |
| "37"=>"fctrl", |
| "38"=>"fstat", |
| # 32 bits |
| "39"=>"mxcsr", |
| # MMX registers |
| # 64 bits |
| "29"=>"mm0", |
| "30"=>"mm1", |
| "31"=>"mm2", |
| "32"=>"mm3", |
| "33"=>"mm4", |
| "34"=>"mm5", |
| "35"=>"mm6", |
| "36"=>"mm7", |
| # SSE registers |
| # 128 bits |
| "21"=>"xmm0", |
| "22"=>"xmm1", |
| "23"=>"xmm2", |
| "24"=>"xmm3", |
| "25"=>"xmm4", |
| "26"=>"xmm5", |
| "27"=>"xmm6", |
| "28"=>"xmm7", |
| # segment registers |
| # 16 bits |
| "40"=>"es", |
| "41"=>"cs", |
| "42"=>"ss", |
| "43"=>"ds", |
| "44"=>"fs", |
| "45"=>"gs", |
| # x87 registers |
| # 80 bits |
| "11"=>"st0", |
| "12"=>"st1", |
| "13"=>"st2", |
| "14"=>"st3", |
| "15"=>"st4", |
| "16"=>"st5", |
| "17"=>"st6", |
| "18"=>"st7" |
| ); |
| } |
| elsif($SYS_ARCH eq "x86_64") |
| { |
| %RegName = ( |
| # integer registers |
| # 64 bits |
| "0"=>"rax", |
| "1"=>"rdx", |
| "2"=>"rcx", |
| "3"=>"rbx", |
| "4"=>"rsi", |
| "5"=>"rdi", |
| "6"=>"rbp", |
| "7"=>"rsp", |
| "8"=>"r8", |
| "9"=>"r9", |
| "10"=>"r10", |
| "11"=>"r11", |
| "12"=>"r12", |
| "13"=>"r13", |
| "14"=>"r14", |
| "15"=>"r15", |
| "16"=>"rip", |
| "49"=>"rFLAGS", |
| # MMX registers |
| # 64 bits |
| "41"=>"mm0", |
| "42"=>"mm1", |
| "43"=>"mm2", |
| "44"=>"mm3", |
| "45"=>"mm4", |
| "46"=>"mm5", |
| "47"=>"mm6", |
| "48"=>"mm7", |
| # SSE registers |
| # 128 bits |
| "17"=>"xmm0", |
| "18"=>"xmm1", |
| "19"=>"xmm2", |
| "20"=>"xmm3", |
| "21"=>"xmm4", |
| "22"=>"xmm5", |
| "23"=>"xmm6", |
| "24"=>"xmm7", |
| "25"=>"xmm8", |
| "26"=>"xmm9", |
| "27"=>"xmm10", |
| "28"=>"xmm11", |
| "29"=>"xmm12", |
| "30"=>"xmm13", |
| "31"=>"xmm14", |
| "32"=>"xmm15", |
| # control registers |
| # 64 bits |
| "62"=>"tr", |
| "63"=>"ldtr", |
| "64"=>"mxcsr", |
| # 16 bits |
| "65"=>"fcw", |
| "66"=>"fsw", |
| # segment registers |
| # 16 bits |
| "50"=>"es", |
| "51"=>"cs", |
| "52"=>"ss", |
| "53"=>"ds", |
| "54"=>"fs", |
| "55"=>"gs", |
| # 64 bits |
| "58"=>"fs.base", |
| "59"=>"gs.base", |
| # x87 registers |
| # 80 bits |
| "33"=>"st0", |
| "34"=>"st1", |
| "35"=>"st2", |
| "36"=>"st3", |
| "37"=>"st4", |
| "38"=>"st5", |
| "39"=>"st6", |
| "40"=>"st7" |
| ); |
| } |
| elsif($SYS_ARCH eq "arm") |
| { |
| %RegName = ( |
| # integer registers |
| # 32-bit |
| "0"=>"r0", |
| "1"=>"r1", |
| "2"=>"r2", |
| "3"=>"r3", |
| "4"=>"r4", |
| "5"=>"r5", |
| "6"=>"r6", |
| "7"=>"r7", |
| "8"=>"r8", |
| "9"=>"r9", |
| "10"=>"r10", |
| "11"=>"r11", |
| "12"=>"r12", |
| "13"=>"r13", |
| "14"=>"r14", |
| "15"=>"r15" |
| ); |
| } |
| } |
| |
| sub dump_sorting($) |
| { |
| my $Hash = $_[0]; |
| return [] if(not $Hash); |
| my @Keys = keys(%{$Hash}); |
| return [] if($#Keys<0); |
| if($Keys[0]=~/\A\d+\Z/) |
| { # numbers |
| return [sort {int($a)<=>int($b)} @Keys]; |
| } |
| else |
| { # strings |
| return [sort {$a cmp $b} @Keys]; |
| } |
| } |
| |
| sub getDebugFile($$) |
| { |
| my ($Obj, $Header) = @_; |
| |
| my $Str = `$EU_READELF_L --strings=.$Header \"$Obj\" 2>\"$TMP_DIR/error\"`; |
| if($Str=~/(\s|\[)0\]\s*(.+)/) { |
| return $2; |
| } |
| |
| return undef; |
| } |
| |
| sub findFiles(@) |
| { |
| my ($Path, $Type) = @_; |
| my $Cmd = "find \"$Path\""; |
| |
| if($Type) { |
| $Cmd .= " -type ".$Type; |
| } |
| |
| my @Res = split(/\n/, `$Cmd`); |
| return @Res; |
| } |
| |
| sub isHeader($) |
| { |
| my $Path = $_[0]; |
| return ($Path=~/\.(h|hh|hp|hxx|hpp|h\+\+|tcc|x|inl)\Z/i); |
| } |
| |
| sub detectPublicSymbols($) |
| { |
| my $Path = $_[0]; |
| |
| if(not -e $Path) { |
| exitStatus("Access_Error", "can't access \'$Path\'"); |
| } |
| |
| my $Path_A = abs_path($Path); |
| |
| printMsg("INFO", "Detect public symbols"); |
| |
| if($UseTU) |
| { |
| if(not check_Cmd($GPP)) |
| { |
| printMsg("ERROR", "can't find \"$GPP\""); |
| return; |
| } |
| } |
| else |
| { |
| if(not check_Cmd($CTAGS)) |
| { |
| printMsg("ERROR", "can't find \"$CTAGS\""); |
| return; |
| } |
| } |
| |
| $PublicSymbols_Detected = 1; |
| |
| my @Files = (); |
| my @Headers = (); |
| my @DefaultInc = (); |
| |
| if(-f $Path) |
| { # list of headers |
| @Headers = split(/\n/, readFile($Path)); |
| } |
| elsif(-d $Path) |
| { # directory |
| @Files = findFiles($Path, "f"); |
| |
| foreach my $File (@Files) |
| { |
| if(isHeader($File)) { |
| push(@Headers, $File); |
| } |
| } |
| |
| push(@DefaultInc, $Path_A); |
| |
| if(-d $Path_A."/include") { |
| push(@DefaultInc, $Path_A."/include"); |
| } |
| } |
| |
| my $PublicHeader_F = $CacheHeaders."/PublicHeader.data"; |
| my $SymbolToHeader_F = $CacheHeaders."/SymbolToHeader.data"; |
| my $TypeToHeader_F = $CacheHeaders."/TypeToHeader.data"; |
| my $Path_F = $CacheHeaders."/PATH"; |
| |
| if($CacheHeaders |
| and -f $PublicHeader_F |
| and -f $SymbolToHeader_F |
| and -f $TypeToHeader_F |
| and -f $Path_F) |
| { |
| if(readFile($Path_F) eq $Path_A) |
| { |
| %PublicHeader = %{eval(readFile($PublicHeader_F))}; |
| %SymbolToHeader = %{eval(readFile($SymbolToHeader_F))}; |
| %TypeToHeader = %{eval(readFile($TypeToHeader_F))}; |
| |
| return; |
| } |
| } |
| |
| foreach my $File (@Headers) |
| { |
| $PublicHeader{getFilename($File)} = 1; |
| } |
| |
| my $Is_C = ($OBJ_LANG eq "C"); |
| |
| foreach my $File (sort {length($b)<=>length($a)} sort {lc($b) cmp lc($a)} @Headers) |
| { |
| my $HName = getFilename($File); |
| |
| if($UseTU) |
| { |
| my $TmpDir = $TMP_DIR."/tu"; |
| if(not -d $TmpDir) { |
| mkpath($TmpDir); |
| } |
| |
| my $File_A = abs_path($File); |
| |
| my $IncDir = getDirname($File_A); |
| my $IncDir_O = getDirname($IncDir); |
| |
| my $TmpInc = $TmpDir."/tmp-inc.h"; |
| my $TmpContent = ""; |
| if($IncludePreamble) |
| { |
| foreach my $P (split(/;/, $IncludePreamble)) |
| { |
| if($P=~/\A\//) { |
| $TmpContent = "#include \"".$P."\"\n"; |
| } |
| else { |
| $TmpContent = "#include <".$P.">\n"; |
| } |
| } |
| } |
| $TmpContent .= "#include \"$File_A\"\n"; |
| writeFile($TmpInc, $TmpContent); |
| |
| my $Cmd = $GPP." -w -fpermissive -fdump-translation-unit -fkeep-inline-functions -c \"$TmpInc\""; |
| |
| if(defined $IncludePaths) |
| { |
| foreach my $P (split(/;/, $IncludePaths)) |
| { |
| if($P!~/\A\//) { |
| $P = $Path_A."/".$P; |
| } |
| |
| $Cmd .= " -I\"".$P."\""; |
| } |
| } |
| else |
| { # automatic |
| $Cmd .= " -I\"$IncDir\" -I\"$IncDir_O\""; |
| } |
| |
| foreach my $P (@DefaultInc) { |
| $Cmd .= " -I\"$P\""; |
| } |
| |
| $Cmd .= " -o ./a.out >OUT 2>&1"; |
| |
| chdir($TmpDir); |
| system($Cmd); |
| chdir($ORIG_DIR); |
| |
| my $TuDump = $TmpDir."/tmp-inc.h.001t.tu"; |
| |
| if(not -e $TuDump) |
| { |
| printMsg("ERROR", "failed to list symbols in the header \'$HName\'"); |
| next; |
| } |
| elsif($?) { |
| printMsg("ERROR", "some errors occured when compiling header \'$HName\'"); |
| } |
| |
| my (%Fdecl, %Tdecl, %Tname, %Ident, %NotDecl) = (); |
| my $Content = readFile($TuDump); |
| $Content=~s/\n[ ]+/ /g; |
| |
| my @Lines = split(/\n/, $Content); |
| foreach my $N (0 .. $#Lines) |
| { |
| my $Line = $Lines[$N]; |
| if(index($Line, "function_decl")!=-1 |
| or index($Line, "var_decl")!=-1) |
| { |
| if($Line=~/name: \@(\d+)/) |
| { |
| my $Id = $1; |
| |
| if($Line=~/srcp: ([^:]+)\:\d/) |
| { |
| if(defined $PublicHeader{$1}) { |
| $Fdecl{$Id} = $1; |
| } |
| } |
| } |
| } |
| elsif($Line=~/\@(\d+)\s+identifier_node\s+strg:\s+(\w+)/) |
| { |
| $Ident{$1} = $2; |
| } |
| elsif($Is_C) |
| { |
| if(index($Line, "type_decl")!=-1) |
| { |
| if($Line=~/\A\@(\d+)/) |
| { |
| my $Id = $1; |
| if($Line=~/name: \@(\d+)/) |
| { |
| my $NId = $1; |
| |
| if($Line=~/srcp: ([^:]+)\:\d/) |
| { |
| if(defined $PublicHeader{$1}) |
| { |
| $Tdecl{$Id} = $1; |
| $Tname{$Id} = $NId; |
| } |
| } |
| } |
| } |
| } |
| elsif(index($Line, "record_type")!=-1 |
| or index($Line, "union_type")!=-1) |
| { |
| if($Line!~/ flds:/) |
| { |
| if($Line=~/name: \@(\d+)/) |
| { |
| $NotDecl{$1} = 1; |
| } |
| } |
| } |
| elsif(index($Line, "enumeral_type")!=-1) |
| { |
| if($Line!~/ csts:/) |
| { |
| if($Line=~/name: \@(\d+)/) |
| { |
| $NotDecl{$1} = 1; |
| } |
| } |
| } |
| elsif(index($Line, "integer_type")!=-1) |
| { |
| if($Line=~/name: \@(\d+)/) |
| { |
| $NotDecl{$1} = 1; |
| } |
| } |
| } |
| } |
| |
| foreach my $Id (keys(%Fdecl)) |
| { |
| if(my $Name = $Ident{$Id}) { |
| $SymbolToHeader{$Name}{$Fdecl{$Id}} = 1; |
| } |
| } |
| |
| if($Is_C) |
| { |
| foreach my $Id (keys(%Tdecl)) |
| { |
| if(defined $NotDecl{$Id}) { |
| next; |
| } |
| |
| if(my $Name = $Ident{$Tname{$Id}}) { |
| $TypeToHeader{$Name} = $Tdecl{$Id}; |
| } |
| } |
| } |
| |
| unlink($TuDump); |
| } |
| else |
| { # using Ctags |
| my $IgnoreTags = ""; |
| |
| if(defined $IgnoreTagsPath) { |
| $IgnoreTags = "-I \@".$IgnoreTagsPath; |
| } |
| |
| my $List_S = `$CTAGS -x --c-kinds=fpvx $IgnoreTags \"$File\"`; |
| foreach my $Line (split(/\n/, $List_S)) |
| { |
| if($Line=~/\A(\w+)/) { |
| $SymbolToHeader{$1}{$HName} = 1; |
| } |
| } |
| |
| if($Is_C) |
| { |
| my $List_T = `$CTAGS -x --c-kinds=gstu --language-force=c $IgnoreTags \"$File\"`; |
| foreach my $Line (split(/\n/, $List_T)) |
| { |
| if($Line=~/\A(\w+)/) |
| { |
| my $N = $1; |
| |
| if($Line!~/\b$N\s+$N\b/) { |
| $TypeToHeader{$N} = $HName; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if($CacheHeaders) |
| { |
| writeFile($PublicHeader_F, Dumper(\%PublicHeader)); |
| writeFile($SymbolToHeader_F, Dumper(\%SymbolToHeader)); |
| writeFile($TypeToHeader_F, Dumper(\%TypeToHeader)); |
| writeFile($Path_F, $Path_A); |
| } |
| } |
| |
| sub getDebugAltLink($) |
| { |
| my $Obj = $_[0]; |
| |
| my $AltDebugFile = getDebugFile($Obj, "gnu_debugaltlink"); |
| |
| if(not $AltDebugFile) { |
| return undef; |
| } |
| |
| my $Dir = getDirname($Obj); |
| |
| my $AltObj_R = $AltDebugFile; |
| if($Dir and $Dir ne ".") { |
| $AltObj_R = $Dir."/".$AltObj_R; |
| } |
| |
| my $AltObj = $AltObj_R; |
| |
| while($AltObj=~s&/[^/]+/\.\./&/&){}; |
| |
| if(-e $AltObj) |
| { |
| printMsg("INFO", "Set alternate debug-info file to \'$AltObj\' (use -alt option to change it)"); |
| return $AltObj; |
| } |
| else { |
| printMsg("WARNING", "can't access \'$AltObj_R\'"); |
| } |
| |
| return undef; |
| } |
| |
| sub scenario() |
| { |
| if($Help) |
| { |
| HELP_MESSAGE(); |
| exit(0); |
| } |
| if($ShowVersion) |
| { |
| printMsg("INFO", "ABI Dumper $TOOL_VERSION"); |
| printMsg("INFO", "Copyright (C) 2016 Andrey Ponomarenko's ABI Laboratory"); |
| printMsg("INFO", "License: LGPL or GPL <http://www.gnu.org/licenses/>"); |
| printMsg("INFO", "This program is free software: you can redistribute it and/or modify it.\n"); |
| printMsg("INFO", "Written by Andrey Ponomarenko."); |
| exit(0); |
| } |
| if($DumpVersion) |
| { |
| printMsg("INFO", $TOOL_VERSION); |
| exit(0); |
| } |
| |
| $Data::Dumper::Sortkeys = 1; |
| |
| if($SortDump) { |
| $Data::Dumper::Sortkeys = \&dump_sorting; |
| } |
| |
| if($SearchDirDebuginfo) |
| { |
| if(not -d $SearchDirDebuginfo) { |
| exitStatus("Access_Error", "can't access directory \'$SearchDirDebuginfo\'"); |
| } |
| } |
| |
| if($PublicHeadersPath) |
| { |
| if(not -e $PublicHeadersPath) { |
| exitStatus("Access_Error", "can't access \'$PublicHeadersPath\'"); |
| } |
| |
| foreach my $P (split(/;/, $IncludePaths)) |
| { |
| if($P!~/\A\//) { |
| $P = $PublicHeadersPath."/".$P; |
| } |
| |
| if(not -e $P) { |
| exitStatus("Access_Error", "can't access \'$P\'"); |
| } |
| } |
| } |
| |
| if($SymbolsListPath) |
| { |
| if(not -f $SymbolsListPath) { |
| exitStatus("Access_Error", "can't access file \'$SymbolsListPath\'"); |
| } |
| foreach my $S (split(/\s*\n\s*/, readFile($SymbolsListPath))) { |
| $SymbolsList{$S} = 1; |
| } |
| } |
| |
| if($VTDumperPath) |
| { |
| if(not -x $VTDumperPath) { |
| exitStatus("Access_Error", "can't access \'$VTDumperPath\'"); |
| } |
| |
| $VTABLE_DUMPER = $VTDumperPath; |
| } |
| |
| if(defined $Compare) |
| { |
| my $P1 = $ARGV[0]; |
| my $P2 = $ARGV[1]; |
| |
| if(not $P1) { |
| exitStatus("Error", "arguments are not specified"); |
| } |
| elsif(not -e $P1) { |
| exitStatus("Access_Error", "can't access \'$P1\'"); |
| } |
| |
| if(not $P2) { |
| exitStatus("Error", "second argument is not specified"); |
| } |
| elsif(not -e $P2) { |
| exitStatus("Access_Error", "can't access \'$P2\'"); |
| } |
| |
| my %ABI = (); |
| |
| $ABI{1} = eval(readFile($P1)); |
| $ABI{2} = eval(readFile($P2)); |
| |
| my %SymInfo = (); |
| |
| foreach (1, 2) |
| { |
| foreach my $ID (keys(%{$ABI{$_}->{"SymbolInfo"}})) |
| { |
| my $Info = $ABI{$_}->{"SymbolInfo"}{$ID}; |
| |
| if(my $MnglName = $Info->{"MnglName"}) { |
| $SymInfo{$_}{$MnglName} = $Info; |
| } |
| elsif(my $ShortName = $Info->{"ShortName"}) { |
| $SymInfo{$_}{$ShortName} = $Info; |
| } |
| } |
| } |
| |
| foreach my $Symbol (sort keys(%{$SymInfo{1}})) |
| { |
| if(not defined $SymInfo{2}{$Symbol}) { |
| printMsg("INFO", "Removed $Symbol"); |
| } |
| } |
| |
| foreach my $Symbol (sort keys(%{$SymInfo{2}})) |
| { |
| if(not defined $SymInfo{1}{$Symbol}) { |
| printMsg("INFO", "Added $Symbol"); |
| } |
| } |
| |
| exit(0); |
| } |
| |
| if(not $TargetVersion) { |
| printMsg("WARNING", "module version is not specified (-lver NUM)"); |
| } |
| |
| if($FullDump) |
| { |
| $AllTypes = 1; |
| $AllSymbols = 1; |
| } |
| |
| if(not $OutputDump) { |
| $OutputDump = "./ABI.dump"; |
| } |
| |
| if(not @ARGV) { |
| exitStatus("Error", "object path is not specified"); |
| } |
| |
| foreach my $Obj (@ARGV) |
| { |
| if(not -e $Obj) { |
| exitStatus("Access_Error", "can't access \'$Obj\'"); |
| } |
| } |
| |
| if($AltDebugInfoOpt) |
| { |
| if(not -e $AltDebugInfoOpt) { |
| exitStatus("Access_Error", "can't access \'$AltDebugInfoOpt\'"); |
| } |
| $AltDebugInfo = $AltDebugInfoOpt; |
| read_Alt_Info($AltDebugInfoOpt); |
| } |
| |
| if($ExtraInfo) |
| { |
| mkpath($ExtraInfo); |
| $ExtraInfo = abs_path($ExtraInfo); |
| } |
| |
| init_ABI(); |
| |
| my $Res = 0; |
| |
| foreach my $Obj (@ARGV) |
| { |
| if(not $TargetName) |
| { |
| $TargetName = getFilename(realpath($Obj)); |
| $TargetName=~s/\.debug\Z//; # nouveau.ko.debug |
| |
| if(index($TargetName, "libstdc++.so")==0) { |
| $STDCXX_TARGET = 1; |
| } |
| } |
| |
| read_Symbols($Obj); |
| |
| if(not defined $PublicSymbols_Detected) |
| { |
| if(defined $PublicHeadersPath) { |
| detectPublicSymbols($PublicHeadersPath); |
| } |
| } |
| |
| $Res += read_DWARF_Info($Obj); |
| |
| %DWARF_Info = (); |
| %ImportedUnit = (); |
| %ImportedDecl = (); |
| |
| read_Vtables($Obj); |
| } |
| |
| if(not defined $Library_Symbol{$TargetName}) { |
| exitStatus("Error", "can't find exported symbols in object(s), please add a shared object on command line"); |
| } |
| |
| if(not $Res) { |
| exitStatus("No_DWARF", "can't find debug info in object(s)"); |
| } |
| |
| %VirtualTable = (); |
| |
| complete_ABI(); |
| remove_Unused(); |
| |
| if(defined $PublicHeadersPath) |
| { |
| foreach my $Tid (sort {lc($TypeInfo{$a}{"Name"}) cmp lc($TypeInfo{$b}{"Name"})} keys(%TypeInfo)) |
| { |
| if(not $TypeInfo{$Tid}{"Header"} |
| or not defined $PublicHeader{$TypeInfo{$Tid}{"Header"}}) |
| { |
| if($TypeInfo{$Tid}{"Type"}=~/Struct|Union|Enum|Typedef/) |
| { |
| my $TName = $TypeInfo{$Tid}{"Name"}; |
| $TName=~s/\A(struct|class|union|enum) //g; |
| |
| if(defined $TypeToHeader{$TName}) { |
| $TypeInfo{$Tid}{"Header"} = $TypeToHeader{$TName}; |
| } |
| } |
| } |
| |
| if(not selectPublicType($Tid)) |
| { |
| $TypeInfo{$Tid}{"PrivateABI"} = 1; |
| } |
| } |
| } |
| |
| %Mangled_ID = (); |
| %Checked_Spec = (); |
| %SelectedSymbols = (); |
| %Cache = (); |
| |
| %ClassChild = (); |
| %TypeSpec = (); |
| |
| # clean memory |
| %SourceFile = (); |
| %SourceFile_Alt = (); |
| %DebugLoc = (); |
| %TName_Tid = (); |
| %TName_Tids = (); |
| %SymbolTable = (); |
| |
| if(defined $PublicHeadersPath) |
| { |
| foreach my $H (keys(%HeadersInfo)) |
| { |
| if(not defined $PublicHeader{getFilename($H)}) { |
| delete($HeadersInfo{$H}); |
| } |
| } |
| } |
| |
| dump_ABI(); |
| |
| exit(0); |
| } |
| |
| scenario(); |