| #!/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) |
| # GNU Binutils readelf |
| # Vtable-Dumper (1.1 or newer) |
| # Binutils (objdump) |
| # Universal Ctags |
| # GCC (g++) |
| # |
| # COMPATIBILITY |
| # ============= |
| # ABI Compliance Checker >= 1.99.24 |
| # |
| # |
| # 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 $READELF = "readelf"; |
| my $READELF_L = $LOCALE." ".$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, $Quiet); |
| |
| 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, |
| "quiet!" => \$Quiet, |
| "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, |
| #get dependencies from the command line |
| "objdump=s" => \$OBJDUMP, |
| "gpp=s" => \$GPP, |
| "readelf=s" => \$READELF |
| ) 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. |
| |
| -quiet |
| Do not warn about incompatible build options. |
| |
| -debug |
| Enable debug messages. |
| |
| -readelf |
| Path to readelf. |
| |
| -gpp |
| Path to g++. |
| |
| -objdump |
| Path to objdump. |
| |
| 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; |
| my $TooBig = 0; |
| |
| # 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", |
| "restrict_type"=>"Restrict", |
| "typedef"=>"Typedef", |
| "ptr_to_member_type"=>"FieldPtr", |
| "string_type"=>"String" |
| ); |
| |
| my %Qual = ( |
| "Pointer"=>"*", |
| "Ref"=>"&", |
| "RvalueRef"=>"&&", |
| "Volatile"=>"volatile", |
| "Restrict"=>"restrict", |
| "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|x|inl|ads"; |
| 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_CLANGV = undef; |
| 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 "UND"); |
| 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($READELF)) { |
| exitStatus("Not_Found", "can't find \"eu-readelf\""); |
| } |
| |
| my %SectionInfo; |
| my %KSect; |
| |
| # Modified to match readelf instead of eu-readelf. |
| my $Cmd = $READELF_L." --wide -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 |
| # Modified to match readelf instead of eu-readelf. |
| $Cmd = $READELF_L." --wide -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"; |
| } |
| |
| # Modified to match readelf instead of eu-readelf. |
| $Cmd = $READELF_L." --wide -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; |
| } |
| # Modified to match readelf instead of eu-readelf. |
| if($Ndx eq "UND") |
| { # 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($READELF)) { |
| exitStatus("Not_Found", "can't find \"$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) |
| { |
| # Modified to match readelf instead of eu-readelf. |
| system($READELF_L." --wide -N --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); |
| open(SRC, $ExtraPath); |
| } |
| else { |
| # Modified to match readelf instead of eu-readelf. |
| open(SRC, $READELF_L." --wide -N --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" |"); |
| } |
| |
| my $DirTable_Def = undef; |
| my %DirTable = (); |
| |
| while(<SRC>) |
| { |
| if(defined $AddDirs) |
| { #Modified to match readelf instead of eu-readelf. |
| 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) |
| { |
| # Modified to match readelf instead of eu-readelf. |
| system($READELF_L." --wide -N --debug-dump=info \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); |
| open(INFO, $ExtraPath); |
| } |
| else { |
| # Modified to match readelf instead of eu-readelf. |
| open(INFO, $READELF_L." --wide -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($READELF)) { |
| exitStatus("Not_Found", "can't find \"$READELF\" command"); |
| } |
| |
| if(-s $Path > 1024*1024*100) { |
| $TooBig = 1; |
| } |
| |
| my $AddOpt = ""; |
| if(not defined $AddrToName) |
| { # disable search of symbol names |
| $AddOpt .= " -N"; |
| } |
| |
| # Modified to match readelf instead of eu-readelf. |
| my $Sect = `$READELF_L --wide -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); |
| |
| printMsg("INFO", "Found link to $DName (gnu_debuglink)"); |
| |
| if(my $DDir = getDirname($Path)) |
| { |
| $DPath = $DDir."/".$DPath; |
| } |
| |
| my $Found = undef; |
| |
| if(defined $SearchDirDebuginfo) |
| { |
| if(-f $SearchDirDebuginfo."/".$DName) { |
| $Found = $SearchDirDebuginfo."/".$DName; |
| } |
| else |
| { |
| my @Files = findFiles($SearchDirDebuginfo, "f"); |
| |
| foreach my $F (@Files) |
| { |
| if(getFilename($F) eq $DName) |
| { |
| $Found = $F; |
| last; |
| } |
| } |
| } |
| } |
| elsif(-f $DPath |
| and $DPath ne $Path) { |
| $Found = $DPath; |
| } |
| |
| if($Found and $Found ne $Path) |
| { |
| printMsg("INFO", "Reading debug-info file $DName linked from gnu_debuglink"); |
| return read_DWARF_Info($Found); |
| } |
| else |
| { |
| printMsg("ERROR", "missed debug-info file $DName linked from gnu_debuglink (try --search-debuginfo=DIR option)"); |
| 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"); |
| } |
| } |
| } |
| |
| if($AltDebugInfo) |
| { |
| if($TooBig) { |
| printMsg("WARNING", "input object is too big and compressed, may require a lot of RAM memory to proceed"); |
| } |
| } |
| |
| printMsg("INFO", "Reading debug-info"); |
| |
| my $ExtraPath = undef; |
| |
| # ELF header |
| if($ExtraInfo) |
| { |
| mkpath($ExtraInfo); |
| $ExtraPath = $ExtraInfo."/elf-header"; |
| } |
| |
| if($ExtraPath) |
| { |
| # Modified to match readelf instead of eu-readelf. |
| system($READELF_L." --wide -h \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); |
| open(HEADER, $ExtraPath); |
| } |
| else { |
| # Modified to match readelf instead of eu-readelf. |
| open(HEADER, $READELF_L." --wide -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) |
| { |
| # Modified to match readelf instead of eu-readelf. |
| system($READELF_L." --wide -S \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); |
| open(HEADER, $ExtraPath); |
| } |
| |
| # source info |
| if($ExtraInfo) |
| { |
| mkpath($ExtraInfo); |
| $ExtraPath = $ExtraInfo."/debug_line"; |
| } |
| |
| if($ExtraPath) |
| { |
| # Modified to match readelf instead of eu-readelf. |
| system($READELF_L." --wide $AddOpt --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); |
| open(SRC, $ExtraPath); |
| } |
| else { |
| # Modified to match readelf instead of eu-readelf. |
| open(SRC, $READELF_L." --wide $AddOpt --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" |"); |
| } |
| |
| my $Offset = undef; |
| my $DirTable_Def = undef; |
| my %DirTable = (); |
| while(<SRC>) |
| { |
| if(defined $AddDirs) |
| { # Modified to match readelf instead of eu-readelf. |
| if(/Directory Table/i) |
| { |
| $DirTable_Def = 1; |
| %DirTable = (); |
| next; |
| } |
| # Modified to match readelf instead of eu-readelf. |
| elsif(/File Name Table/i) |
| { |
| $DirTable_Def = undef; |
| next; |
| } |
| |
| if(defined $DirTable_Def) |
| { |
| # Modified to match readelf instead of eu-readelf. |
| if(/\A[0-9]+\s*(.+?)\Z/) { |
| $DirTable{keys(%DirTable)+1} = $1; |
| } |
| } |
| } |
| |
| # Modified to match readelf instead of eu-readelf. |
| if(/Offset:\s+(\w+)/) { |
| $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) |
| { |
| # Modified to match readelf instead of eu-readelf. |
| system($READELF_L." --wide $AddOpt --debug-dump=loc \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); |
| open(LOC, $ExtraPath); |
| } |
| else { |
| # Modified to match readelf instead of eu-readelf. |
| open(LOC, $READELF_L." --wide $AddOpt --debug-dump=loc \"$Path\" 2>\"$TMP_DIR/error\" |"); |
| } |
| |
| while(<LOC>) |
| { |
| # Modified to match readelf instead of eu-readelf. |
| if(/(\w+)\s+[0-9a-fA-F]+\s+[0-9a-fA-F]+\s+\(DW_OP_(\w+:?\s+-?[0-9]*)+[\(;]/) { |
| $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) |
| { |
| # Modified to match readelf instead of eu-readelf. |
| system($READELF_L." --wide $AddOpt --debug-dump=info \"$Name\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); |
| open($INFO_fh, $ExtraPath); |
| } |
| else { |
| # Modified to match readelf instead of eu-readelf. |
| open($INFO_fh, $READELF_L." --wide $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; |
| } |
| |
| # Modified to match readelf instead of eu-readelf. |
| if(defined $ID and $Line=~/\s*DW_AT_(\w+)\s*:\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") |
| { |
| # Modified to match readelf instead of eu-readelf. |
| if($Attr eq "data_member_location") |
| { |
| #data_meber_location value is handled later in the |
| #attr "location" clause. |
| 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; |
| } |
| |
| # Modified to match readelf instead of eu-readelf. |
| 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; |
| } |
| } |
| # Modified to match readelf instead of eu-readelf. |
| # type : <0x...>, abstract_origin, specification etc |
| if($Val=~/\A<0x(\w+)>\Z/) |
| { |
| $Val = hex($1); |
| } |
| elsif($Attr eq "name") |
| { |
| # Modified to match readelf instead of eu-readelf. |
| $Val=~s/\A\([^()]*\):\s+(.*)\Z/$1/; |
| |
| } |
| elsif(index($Attr, "linkage_name")!=-1) |
| { |
| # Modified to match readelf instead of eu-readelf. |
| $Val=~s/\A\([^()]*\):\s+(\w+)\Z/$1/; |
| $Attr = "linkage_name"; |
| |
| } |
| elsif(index($Attr, "location")!=-1) |
| { |
| # Modified to match readelf instead of eu-readelf. |
| if($Val=~/\A(-?)(\d+)\Z/) |
| { # (data1) 1c |
| # Modified to match readelf instead of eu-readelf. |
| # Eg: data_member_location : 8 |
| $Val = $2; |
| if($1) { |
| $Val = -$Val; |
| } |
| } |
| else |
| { |
| if ($Val=~/\(DW_OP_(\w+:?\s+-?[0-9]*)+[\(\)]/) { |
| $Val = $1; |
| } |
| if($Val=~/\A(-?\d+)\Z/) { |
| $Val = $1; |
| } |
| else |
| { |
| if($Attr eq "location" |
| and $Kind eq "formal_parameter") |
| { |
| # Modified to match readelf instead of eu-readelf. |
| if($Val=~/0x(\w+)\s+\(location list\)\Z/) |
| { |
| $Attr = "location_list"; |
| $Val = $1; |
| } |
| # Modified to match readelf instead of eu-readelf. |
| elsif($Val=~/\(reg(\d+)\s+\(.*\)\)\Z/) |
| { |
| $Attr = "register"; |
| $Val = $1; |
| } |
| } |
| # Modified to match readelf instead of eu-readelf. |
| elsif($Attr eq "vtable_elem_location") { |
| if($Val=~/const.:\s+(-)?(\d+)/) |
| { |
| $Val = $2; |
| if ($1) { |
| $Val = -$Val; |
| } |
| } |
| } |
| |
| } |
| } |
| } |
| elsif($Attr eq "accessibility") |
| { |
| # Modified to match readelf instead of eu-readelf. |
| $Val=~s/\A(\d+)\s+\((\w+)\)\Z/$2/; |
| # 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) |
| { |
| # Modified to match readelf instead of eu-readelf. |
| $Val=~s/\s*\((.+?\))\Z/$1/; |
| |
| 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; |
| } |
| } |
| elsif($Val=~/clang\s+version\s+([^\s\(]+)/) { |
| $SYS_CLANGV = $1; |
| } |
| else { |
| $SYS_COMP = $Val; |
| } |
| |
| 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") |
| { |
| if(not defined $Quiet) { |
| printMsg("WARNING", "incompatible build option detected: ".$Opts{keys(%Opts)-1}." (required -Og for better analysis)"); |
| } |
| $IncompatibleOpt = 1; |
| } |
| } |
| else |
| { |
| if(not defined $Quiet) { |
| printMsg("WARNING", "the object should be compiled with -Og option for better analysis"); |
| } |
| $IncompatibleOpt = 1; |
| } |
| } |
| } |
| } |
| } |
| } |
| 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; |
| } |
| } |
| } |
| # Modified to match readelf instead of eu-readelf. |
| elsif($Line=~/\A <(\w+)><(\w+)>:\s+.+\(DW_TAG_(\w+)\)/) |
| { |
| $ID = hex($2); |
| # NS is used to identify namespace / scope. Mentioned along with ID. |
| $NS = hex($1); |
| $Kind = $3; |
| |
| if(not defined $Compressed) |
| { |
| if($Kind eq "partial_unit" or $Kind eq "type_unit") |
| { # compressed debug_info |
| $Compressed = 1; |
| |
| if($TooBig) { |
| printMsg("WARNING", "input object is too big and compressed, may require a lot of RAM memory to proceed"); |
| } |
| } |
| } |
| |
| 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; |
| } |
| } |
| # Modified to match readelf instead of eu-readelf. |
| elsif(not defined $SYS_WORD |
| and $Line=~/Pointer\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)) |
| { # Modified to match vndk-vtable-dumper |
| if(my $Version = `$VTABLE_DUMPER -version`) |
| { |
| 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"; |
| } |
| # Modified to match the vtable dumper using LLVM's ELF api. |
| system("LD_LIBRARY_PATH=\"$Dir\" $VTABLE_DUMPER \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); |
| |
| my $Content = readFile($ExtraPath); |
| foreach my $ClassInfo (split(/\n\n\n/, $Content)) |
| { |
| # Modified to match the vtable dumper using LLVM's ELF api. |
| 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; |
| } |
| elsif($SYS_CLANGV) { |
| $ABI{"ClangVersion"} = $SYS_CLANGV; |
| } |
| 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"}; |
| # Modified to match readelf instead of eu-readelf. In readelf, the child's |
| # scope level will be the parent's scope level + 1. |
| my $Scope = $CurID{$NS-1}; |
| |
| if($Kind eq "typedef") |
| { |
| if($DWARF_Info{$Scope}{"Kind"} eq "subprogram") |
| { |
| $NS = $DWARF_Info{$Scope}{"NS"}; |
| # Modified to match readelf instead of eu-readelf. |
| $Scope = $CurID{$NS-1}; |
| } |
| } |
| |
| 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}); |
| } |
| # Modified to match readelf instead of eu-readelf. |
| elsif($Kind eq "template_type_param" |
| or $Kind eq "template_value_param") |
| { |
| 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"; |
| } |
| } |
| |
| if(defined $SYS_CLANGV |
| and $TInfo{"Type"} eq "FieldPtr") |
| { # Support for Clang |
| if(my $T = $DWARF_Info{$ID}{"type"}) |
| { |
| if($DWARF_Info{$T}{"Kind"} eq "subroutine_type") |
| { |
| $TInfo{"Type"} = "MethodPtr"; |
| $DWARF_Info{$ID}{"sibling"} = $T; |
| $DWARF_Info{$T}{"object_pointer"} = $DWARF_Info{$ID}{"containing_type"}; |
| } |
| } |
| } |
| |
| 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 "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"; |
| } |
| } |
| } |
| |
| if($TInfo{"Type"}=~/Pointer|Ptr|Ref/) |
| { |
| if(not $TInfo{"Size"}) { |
| $TInfo{"Size"} = $SYS_WORD; |
| } |
| } |
| |
| 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 "MethodPtr") |
| { |
| if(my $Sb = $DWARF_Info{$ID}{"sibling"}) |
| { |
| 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).")"; |
| |
| delete($TInfo{"BaseType"}); |
| } |
| } |
| 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/i) |
| { # 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) = ("", ""); |
| #Modified to match readelf instead of eu-readelf. |
| if($Loc=~/0x(.+)/) |
| { |
| $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"}})) |
|