abi-dumper.pl revision 0f715a644ce0651ad507936d43f9a8f4165fde16
1#!/usr/bin/perl 2########################################################################### 3# ABI Dumper 0.99.17 4# Dump ABI of an ELF object containing DWARF debug info 5# 6# Copyright (C) 2013-2016 Andrey Ponomarenko's ABI Laboratory 7# 8# Written by Andrey Ponomarenko 9# 10# PLATFORMS 11# ========= 12# Linux 13# 14# REQUIREMENTS 15# ============ 16# Perl 5 (5.8 or newer) 17# Elfutils (eu-readelf) 18# Vtable-Dumper (1.1 or newer) 19# Binutils (objdump) 20# Universal Ctags 21# GCC (g++) 22# 23# COMPATIBILITY 24# ============= 25# ABI Compliance Checker >= 1.99.14 26# 27# 28# This program is free software: you can redistribute it and/or modify 29# it under the terms of the GNU General Public License or the GNU Lesser 30# General Public License as published by the Free Software Foundation. 31# 32# This program is distributed in the hope that it will be useful, 33# but WITHOUT ANY WARRANTY; without even the implied warranty of 34# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 35# GNU General Public License for more details. 36# 37# You should have received a copy of the GNU General Public License 38# and the GNU Lesser General Public License along with this program. 39# If not, see <http://www.gnu.org/licenses/>. 40########################################################################### 41use Getopt::Long; 42Getopt::Long::Configure ("posix_default", "no_ignore_case", "permute"); 43use File::Path qw(mkpath rmtree); 44use File::Temp qw(tempdir); 45use Cwd qw(abs_path cwd realpath); 46use Storable qw(dclone); 47use Data::Dumper; 48 49my $TOOL_VERSION = "0.99.17"; 50my $ABI_DUMP_VERSION = "3.2"; 51my $ORIG_DIR = cwd(); 52my $TMP_DIR = tempdir(CLEANUP=>1); 53 54my $VTABLE_DUMPER = "vtable-dumper"; 55my $VTABLE_DUMPER_VERSION = "1.0"; 56 57my $LOCALE = "LANG=C.UTF-8"; 58my $EU_READELF = "eu-readelf"; 59my $EU_READELF_L = $LOCALE." ".$EU_READELF; 60my $OBJDUMP = "objdump"; 61my $CTAGS = "ctags"; 62my $GPP = "g++"; 63 64my ($Help, $ShowVersion, $DumpVersion, $OutputDump, $SortDump, $StdOut, 65$TargetVersion, $ExtraInfo, $FullDump, $AllTypes, $AllSymbols, $BinOnly, 66$SkipCxx, $Loud, $AddrToName, $DumpStatic, $Compare, $AltDebugInfo, 67$AddDirs, $VTDumperPath, $SymbolsListPath, $PublicHeadersPath, 68$IgnoreTagsPath, $KernelExport, $UseTU, $ReimplementStd, 69$IncludePreamble, $IncludePaths, $CacheHeaders, $MixedHeaders, $Debug, 70$SearchDirDebuginfo); 71 72my $CmdName = getFilename($0); 73 74my %ERROR_CODE = ( 75 "Success"=>0, 76 "Error"=>2, 77 # System command is not found 78 "Not_Found"=>3, 79 # Cannot access input files 80 "Access_Error"=>4, 81 # Cannot find a module 82 "Module_Error"=>9, 83 # No debug-info 84 "No_DWARF"=>10, 85 # Invalid debug-info 86 "Invalid_DWARF"=>11 87); 88 89my $ShortUsage = "ABI Dumper $TOOL_VERSION 90Dump ABI of an ELF object containing DWARF debug info 91Copyright (C) 2016 Andrey Ponomarenko's ABI Laboratory 92License: GNU LGPL or GNU GPL 93 94Usage: $CmdName [options] [object] 95Example: 96 $CmdName libTest.so -o ABI.dump 97 $CmdName Module.ko.debug -o ABI.dump 98 99More info: $CmdName --help\n"; 100 101if($#ARGV==-1) 102{ 103 printMsg("INFO", $ShortUsage); 104 exit(0); 105} 106 107GetOptions("h|help!" => \$Help, 108 "v|version!" => \$ShowVersion, 109 "dumpversion!" => \$DumpVersion, 110# general options 111 "o|output|dump-path=s" => \$OutputDump, 112 "sort!" => \$SortDump, 113 "stdout!" => \$StdOut, 114 "loud!" => \$Loud, 115 "vnum|lver|lv=s" => \$TargetVersion, 116 "extra-info=s" => \$ExtraInfo, 117 "bin-only!" => \$BinOnly, 118 "all-types!" => \$AllTypes, 119 "all-symbols!" => \$AllSymbols, 120 "symbols-list=s" => \$SymbolsListPath, 121 "skip-cxx!" => \$SkipCxx, 122 "all!" => \$FullDump, 123 "dump-static!" => \$DumpStatic, 124 "compare!" => \$Compare, 125 "alt=s" => \$AltDebugInfo, 126 "dir!" => \$AddDirs, 127 "vt-dumper=s" => \$VTDumperPath, 128 "public-headers=s" => \$PublicHeadersPath, 129 "ignore-tags=s" => \$IgnoreTagsPath, 130 "reimplement-std!" => \$ReimplementStd, 131 "mixed-headers!" => \$MixedHeaders, 132 "kernel-export!" => \$KernelExport, 133 "search-debuginfo=s" => \$SearchDirDebuginfo, 134 "debug!" => \$Debug, 135# extra options 136 "use-tu-dump!" => \$UseTU, 137 "include-preamble=s" => \$IncludePreamble, 138 "include-paths=s" => \$IncludePaths, 139 "cache-headers=s" => \$CacheHeaders, 140# internal options 141 "addr2name!" => \$AddrToName 142) or ERR_MESSAGE(); 143 144sub ERR_MESSAGE() 145{ 146 printMsg("INFO", "\n".$ShortUsage); 147 exit($ERROR_CODE{"Error"}); 148} 149 150my $HelpMessage=" 151NAME: 152 ABI Dumper ($CmdName) 153 Dump ABI of an ELF object containing DWARF debug info 154 155DESCRIPTION: 156 ABI Dumper is a tool for dumping ABI information of an ELF object 157 containing DWARF debug info. 158 159 The tool is intended to be used with ABI Compliance Checker tool for 160 tracking ABI changes of a C/C++ library or kernel module. 161 162 This tool is free software: you can redistribute it and/or modify it 163 under the terms of the GNU LGPL or GNU GPL. 164 165USAGE: 166 $CmdName [options] [object] 167 168EXAMPLES: 169 $CmdName libTest.so -o ABI.dump 170 $CmdName Module.ko.debug -o ABI.dump 171 172INFORMATION OPTIONS: 173 -h|-help 174 Print this help. 175 176 -v|-version 177 Print version information. 178 179 -dumpversion 180 Print the tool version ($TOOL_VERSION) and don't do anything else. 181 182GENERAL OPTIONS: 183 -o|-output PATH 184 Path to the output ABI dump file. 185 Default: ./ABI.dump 186 187 -sort 188 Sort data in ABI dump. 189 190 -stdout 191 Print ABI dump to stdout. 192 193 -loud 194 Print all warnings. 195 196 -vnum NUM 197 Set version of the library to NUM. 198 199 -extra-info DIR 200 Dump extra analysis info to DIR. 201 202 -bin-only 203 Do not dump information about inline functions, 204 pure virtual functions and non-exported global data. 205 206 -all-types 207 Dump unused data types. 208 209 -all-symbols 210 Dump symbols not exported by the object. 211 212 -symbols-list PATH 213 Specify a file with a list of symbols that should be dumped. 214 215 -skip-cxx 216 Do not dump stdc++ and gnu c++ symbols. 217 218 -all 219 Equal to: -all-types -all-symbols. 220 221 -dump-static 222 Dump static (local) symbols. 223 224 -compare OLD.dump NEW.dump 225 Show added/removed symbols between two ABI dumps. 226 227 -alt PATH 228 Path to the alternate debug info (Fedora). It is 229 detected automatically from gnu_debugaltlink section 230 of the input object if not specified. 231 232 -dir 233 Show full paths of source files. 234 235 -vt-dumper PATH 236 Path to the vtable-dumper executable if it is installed 237 to non-default location (not in PATH). 238 239 -public-headers PATH 240 Path to directory with public header files or to file with 241 the list of header files. This option allows to filter out 242 private symbols from the ABI dump. 243 244 -ignore-tags PATH 245 Path to ignore.tags file to help ctags tool to read 246 symbols in header files. 247 248 -reimplement-std 249 This option should be specified if you are using 250 -public-headers option and the library reimplements 251 some standard symbols (e.g. malloc). 252 253 -mixed-headers 254 This option should be specified if you are using 255 -public-headers option and the names of public headers 256 intersect with the internal headers. 257 258 -kernel-export 259 Dump symbols exported by the Linux kernel and modules, i.e. 260 symbols declared in the ksymtab section of the object and 261 system calls. 262 263 -search-debuginfo DIR 264 Search for debug-info files referenced from gnu_debuglink 265 section of the object in DIR. 266 267 -debug 268 Enable debug messages. 269 270EXTRA OPTIONS: 271 -use-tu-dump 272 Use g++ -fdump-translation-unit instead of ctags to 273 list symbols in headers. This may be useful if all 274 functions are declared via macros in headers and 275 ctags can't recognize them. 276 277 -include-preamble PATHS 278 Specify header files (separated by semicolon) that 279 should be included before others to compile without 280 errors. 281 282 -include-paths DIRS 283 Specify include directories (separated by semicolon) 284 that should be passed to the compiler by -I option 285 in order to compile headers without errors. If this 286 option is not set then the tool will try to generate 287 include paths automatically. 288 289 -cache-headers DIR 290 Cache headers analysis results to reuse later. 291"; 292 293sub HELP_MESSAGE() { 294 printMsg("INFO", $HelpMessage); 295} 296 297my %Cache; 298 299# Input 300my %DWARF_Info; 301 302# Alternate 303my %ImportedUnit; 304my %ImportedDecl; 305 306# Dump 307my %TypeUnit; 308my %Post_Change; 309my %UsedUnit; 310my %UsedDecl; 311 312# Output 313my %SymbolInfo; 314my %TypeInfo; 315 316# Reader 317my %TypeMember; 318my %ArrayCount; 319my %FuncParam; 320my %TmplParam; 321my %Inheritance; 322my %NameSpace; 323my %SpecElem; 324my %OrigElem; 325my %ClassMethods; 326my %TypeSpec; 327my %ClassChild; 328 329my %MergedTypes; 330my %LocalType; 331 332my %SourceFile; 333my %SourceFile_Alt; 334my %DebugLoc; 335my %TName_Tid; 336my %TName_Tids; 337my %RegName; 338 339my $STDCXX_TARGET = 0; 340my $GLOBAL_ID = 0; 341my %ANON_TYPE_WARN = (); 342 343my %Mangled_ID; 344my %Checked_Spec; 345my %SelectedSymbols; 346 347my %TypeType = ( 348 "class_type"=>"Class", 349 "structure_type"=>"Struct", 350 "union_type"=>"Union", 351 "enumeration_type"=>"Enum", 352 "array_type"=>"Array", 353 "base_type"=>"Intrinsic", 354 "const_type"=>"Const", 355 "pointer_type"=>"Pointer", 356 "reference_type"=>"Ref", 357 "rvalue_reference_type"=>"RvalueRef", 358 "volatile_type"=>"Volatile", 359 "typedef"=>"Typedef", 360 "ptr_to_member_type"=>"FieldPtr", 361 "string_type"=>"String" 362); 363 364my %Qual = ( 365 "Pointer"=>"*", 366 "Ref"=>"&", 367 "RvalueRef"=>"&&", 368 "Volatile"=>"volatile", 369 "Const"=>"const" 370); 371 372my %ConstSuffix = ( 373 "unsigned int" => "u", 374 "unsigned long" => "ul", 375 "unsigned long long" => "ull", 376 "long" => "l", 377 "long long" => "ll" 378); 379 380my $HEADER_EXT = "h|hh|hp|hxx|hpp|h\\+\\+|tcc"; 381my $SRC_EXT = "c|cpp|cxx|c\\+\\+"; 382 383# Other 384my %NestedNameSpaces; 385my $TargetName; 386my %HeadersInfo; 387my %SourcesInfo; 388my %SymVer; 389my %UsedType; 390 391# ELF 392my %Library_Symbol; 393my %Library_UndefSymbol; 394my %Library_Needed; 395my %SymbolTable; 396 397# VTables 398my %VirtualTable; 399 400# Env 401my $SYS_ARCH; 402my $SYS_WORD; 403my $SYS_GCCV; 404my $SYS_COMP; 405my $LIB_LANG; 406my $OBJ_LANG; 407 408my $IncompatibleOpt = undef; 409 410# Errors 411my $InvalidDebugLoc; 412 413# Public Headers 414my %SymbolToHeader; 415my %TypeToHeader; 416my %PublicHeader; 417my $PublicSymbols_Detected; 418 419# Kernel 420my %KSymTab; 421 422# Filter 423my %SymbolsList; 424 425sub printMsg($$) 426{ 427 my ($Type, $Msg) = @_; 428 if($Type!~/\AINFO/) { 429 $Msg = $Type.": ".$Msg; 430 } 431 if($Type!~/_C\Z/) { 432 $Msg .= "\n"; 433 } 434 if($Type eq "ERROR" 435 or $Type eq "WARNING") { 436 print STDERR $Msg; 437 } 438 else { 439 print $Msg; 440 } 441} 442 443sub exitStatus($$) 444{ 445 my ($Code, $Msg) = @_; 446 printMsg("ERROR", $Msg); 447 exit($ERROR_CODE{$Code}); 448} 449 450sub cmpVersions($$) 451{ # compare two versions in dotted-numeric format 452 my ($V1, $V2) = @_; 453 return 0 if($V1 eq $V2); 454 return undef if($V1!~/\A\d+[\.\d+]*\Z/); 455 return undef if($V2!~/\A\d+[\.\d+]*\Z/); 456 my @V1Parts = split(/\./, $V1); 457 my @V2Parts = split(/\./, $V2); 458 for (my $i = 0; $i <= $#V1Parts && $i <= $#V2Parts; $i++) { 459 return -1 if(int($V1Parts[$i]) < int($V2Parts[$i])); 460 return 1 if(int($V1Parts[$i]) > int($V2Parts[$i])); 461 } 462 return -1 if($#V1Parts < $#V2Parts); 463 return 1 if($#V1Parts > $#V2Parts); 464 return 0; 465} 466 467sub writeFile($$) 468{ 469 my ($Path, $Content) = @_; 470 return if(not $Path); 471 if(my $Dir = getDirname($Path)) { 472 mkpath($Dir); 473 } 474 open(FILE, ">", $Path) || die ("can't open file \'$Path\': $!\n"); 475 print FILE $Content; 476 close(FILE); 477} 478 479sub readFile($) 480{ 481 my $Path = $_[0]; 482 return "" if(not $Path or not -f $Path); 483 open(FILE, $Path); 484 local $/ = undef; 485 my $Content = <FILE>; 486 close(FILE); 487 return $Content; 488} 489 490sub getFilename($) 491{ # much faster than basename() from File::Basename module 492 if($_[0] and $_[0]=~/([^\/\\]+)[\/\\]*\Z/) { 493 return $1; 494 } 495 return ""; 496} 497 498sub getDirname($) 499{ # much faster than dirname() from File::Basename module 500 if($_[0] and $_[0]=~/\A(.*?)[\/\\]+[^\/\\]*[\/\\]*\Z/) { 501 return $1; 502 } 503 return ""; 504} 505 506sub check_Cmd($) 507{ 508 my $Cmd = $_[0]; 509 return "" if(not $Cmd); 510 if(defined $Cache{"check_Cmd"}{$Cmd}) { 511 return $Cache{"check_Cmd"}{$Cmd}; 512 } 513 514 if(-x $Cmd) 515 { # relative or absolute path 516 return ($Cache{"check_Cmd"}{$Cmd} = 1); 517 } 518 519 foreach my $Path (sort {length($a)<=>length($b)} split(/:/, $ENV{"PATH"})) 520 { 521 if(-x $Path."/".$Cmd) { 522 return ($Cache{"check_Cmd"}{$Cmd} = 1); 523 } 524 } 525 return ($Cache{"check_Cmd"}{$Cmd} = 0); 526} 527 528my %ELF_BIND = map {$_=>1} ( 529 "WEAK", 530 "GLOBAL", 531 "LOCAL" 532); 533 534my %ELF_TYPE = map {$_=>1} ( 535 "FUNC", 536 "IFUNC", 537 "GNU_IFUNC", 538 "TLS", 539 "OBJECT", 540 "COMMON" 541); 542 543my %ELF_VIS = map {$_=>1} ( 544 "DEFAULT", 545 "PROTECTED" 546); 547 548sub readline_ELF($) 549{ # read the line of 'eu-readelf' output corresponding to the symbol 550 my @Info = split(/\s+/, $_[0]); 551 # Num: Value Size Type Bind Vis Ndx Name 552 # 3629: 000b09c0 32 FUNC GLOBAL DEFAULT 13 _ZNSt12__basic_fileIcED1Ev@@GLIBCXX_3.4 553 # 135: 00000000 0 FUNC GLOBAL DEFAULT UNDEF av_image_fill_pointers@LIBAVUTIL_52 (3) 554 shift(@Info) if($Info[0] eq ""); # spaces 555 shift(@Info); # num 556 557 if($#Info==7) 558 { # UNDEF SYMBOL (N) 559 if($Info[7]=~/\(\d+\)/) { 560 pop(@Info); 561 } 562 } 563 564 if($#Info!=6) 565 { # other lines 566 return (); 567 } 568 return () if(not defined $ELF_TYPE{$Info[2]} and $Info[5] ne "UNDEF"); 569 return () if(not defined $ELF_BIND{$Info[3]}); 570 return () if(not defined $ELF_VIS{$Info[4]}); 571 if($Info[5] eq "ABS" and $Info[0]=~/\A0+\Z/) 572 { # 1272: 00000000 0 OBJECT GLOBAL DEFAULT ABS CXXABI_1.3 573 return (); 574 } 575 if(index($Info[2], "0x") == 0) 576 { # size == 0x3d158 577 $Info[2] = hex($Info[2]); 578 } 579 return @Info; 580} 581 582sub read_Symbols($) 583{ 584 my $Lib_Path = $_[0]; 585 my $Lib_Name = getFilename($Lib_Path); 586 587 my $Dynamic = ($Lib_Name=~/\.so(\.|\Z)/); 588 my $Dbg = ($Lib_Name=~/\.debug\Z/); 589 590 if(not check_Cmd($EU_READELF)) { 591 exitStatus("Not_Found", "can't find \"eu-readelf\""); 592 } 593 594 my %SectionInfo; 595 my %KSect; 596 597 my $Cmd = $EU_READELF_L." -S \"$Lib_Path\" 2>\"$TMP_DIR/error\""; 598 foreach (split(/\n/, `$Cmd`)) 599 { 600 if(/\[\s*(\d+)\]\s+([\w\.]+)/) 601 { 602 my ($Num, $Name) = ($1, $2); 603 604 $SectionInfo{$Num} = $Name; 605 606 if(defined $KernelExport) 607 { 608 if($Name=~/\A(__ksymtab|__ksymtab_gpl)\Z/) { 609 $KSect{$1} = 1; 610 } 611 } 612 } 613 } 614 615 if(defined $KernelExport) 616 { 617 if(not keys(%KSect)) 618 { 619 printMsg("ERROR", "can't find __ksymtab or __ksymtab_gpl sections in the object"); 620 exit(1); 621 } 622 623 foreach my $Name (sort keys(%KSect)) 624 { 625 $Cmd = $OBJDUMP." --section=$Name -d \"$Lib_Path\" 2>\"$TMP_DIR/error\""; 626 627 foreach my $Line (split(/\n/, qx/$Cmd/)) 628 { 629 if($Line=~/<__ksymtab_(.+?)>/) 630 { 631 $KSymTab{$1} = 1; 632 } 633 } 634 } 635 } 636 637 if($Dynamic) 638 { # dynamic library specifics 639 $Cmd = $EU_READELF_L." -d \"$Lib_Path\" 2>\"$TMP_DIR/error\""; 640 foreach (split(/\n/, `$Cmd`)) 641 { 642 if(/NEEDED.+\[([^\[\]]+)\]/) 643 { # dependencies: 644 # 0x00000001 (NEEDED) Shared library: [libc.so.6] 645 $Library_Needed{$1} = 1; 646 } 647 } 648 } 649 650 my $ExtraPath = undef; 651 652 if($ExtraInfo) 653 { 654 mkpath($ExtraInfo); 655 $ExtraPath = $ExtraInfo."/elf-info"; 656 } 657 658 $Cmd = $EU_READELF_L." -s \"$Lib_Path\" 2>\"$TMP_DIR/error\""; 659 660 if($ExtraPath) 661 { # debug mode 662 # write to file 663 system($Cmd." >\"$ExtraPath\""); 664 open(LIB, $ExtraPath); 665 } 666 else 667 { # write to pipe 668 open(LIB, $Cmd." |"); 669 } 670 671 my (%Symbol_Value, %Value_Symbol) = (); 672 673 my $symtab = undef; # indicates that we are processing 'symtab' section of 'readelf' output 674 while(<LIB>) 675 { 676 if($Dynamic and not $Dbg) 677 { # dynamic library specifics 678 if(defined $symtab) 679 { 680 if(index($_, "'.dynsym'")!=-1) 681 { # dynamic table 682 $symtab = undef; 683 } 684 if(not $AllSymbols) 685 { # do nothing with symtab 686 #next; 687 } 688 } 689 elsif(index($_, "'.symtab'")!=-1) 690 { # symbol table 691 $symtab = 1; 692 } 693 } 694 if(my ($Value, $Size, $Type, $Bind, $Vis, $Ndx, $Symbol) = readline_ELF($_)) 695 { # read ELF entry 696 if(not $symtab) 697 { # dynsym 698 if(skipSymbol($Symbol)) { 699 next; 700 } 701 702 if($Ndx eq "UNDEF") 703 { # ignore interfaces that are imported from somewhere else 704 $Library_UndefSymbol{$TargetName}{$Symbol} = 0; 705 next; 706 } 707 708 if(defined $KernelExport) 709 { 710 if($Bind ne "LOCAL") 711 { 712 if(index($Symbol, "sys_")==0 713 or index($Symbol, "SyS_")==0) { 714 $KSymTab{$Symbol} = 1; 715 } 716 } 717 718 if(not defined $KSymTab{$Symbol}) { 719 next; 720 } 721 } 722 723 if($Bind ne "LOCAL") { 724 $Library_Symbol{$TargetName}{$Symbol} = ($Type eq "OBJECT")?-$Size:1; 725 } 726 727 $Symbol_Value{$Symbol} = $Value; 728 $Value_Symbol{$Value}{$Symbol} = 1; 729 730 if(not defined $OBJ_LANG) 731 { 732 if(index($Symbol, "_Z")==0) 733 { 734 $OBJ_LANG = "C++"; 735 } 736 } 737 } 738 else 739 { 740 $Symbol_Value{$Symbol} = $Value; 741 $Value_Symbol{$Value}{$Symbol} = 1; 742 } 743 744 if(not $symtab) 745 { 746 foreach ($SectionInfo{$Ndx}, "") 747 { 748 my $Val = $Value; 749 750 $SymbolTable{$_}{$Val}{$Symbol} = 1; 751 752 if($Val=~s/\A[0]+//) 753 { 754 if($Val eq "") { 755 $Val = "0"; 756 } 757 $SymbolTable{$_}{$Val}{$Symbol} = 1; 758 } 759 } 760 } 761 } 762 } 763 close(LIB); 764 765 if(not defined $Library_Symbol{$TargetName}) { 766 return; 767 } 768 769 my %Found = (); 770 foreach my $Symbol (sort keys(%Symbol_Value)) 771 { 772 next if(index($Symbol,"\@")==-1); 773 if(my $Value = $Symbol_Value{$Symbol}) 774 { 775 foreach my $Symbol_SameValue (sort keys(%{$Value_Symbol{$Value}})) 776 { 777 if($Symbol_SameValue ne $Symbol 778 and index($Symbol_SameValue,"\@")==-1) 779 { 780 $SymVer{$Symbol_SameValue} = $Symbol; 781 $Found{$Symbol} = 1; 782 #last; 783 } 784 } 785 } 786 } 787 788 # default 789 foreach my $Symbol (sort keys(%Symbol_Value)) 790 { 791 next if(defined $Found{$Symbol}); 792 next if(index($Symbol,"\@\@")==-1); 793 794 if($Symbol=~/\A([^\@]*)\@\@/ 795 and not $SymVer{$1}) 796 { 797 $SymVer{$1} = $Symbol; 798 $Found{$Symbol} = 1; 799 } 800 } 801 802 # non-default 803 foreach my $Symbol (sort keys(%Symbol_Value)) 804 { 805 next if(defined $Found{$Symbol}); 806 next if(index($Symbol,"\@")==-1); 807 808 if($Symbol=~/\A([^\@]*)\@([^\@]*)/ 809 and not $SymVer{$1}) 810 { 811 $SymVer{$1} = $Symbol; 812 $Found{$Symbol} = 1; 813 } 814 } 815 816 if(not defined $OBJ_LANG) 817 { 818 $OBJ_LANG = "C"; 819 } 820} 821 822sub read_Alt_Info($) 823{ 824 my $Path = $_[0]; 825 my $Name = getFilename($Path); 826 827 if(not check_Cmd($EU_READELF)) { 828 exitStatus("Not_Found", "can't find \"$EU_READELF\" command"); 829 } 830 831 printMsg("INFO", "Reading alternate debug-info"); 832 833 my $ExtraPath = undef; 834 835 # lines info 836 if($ExtraInfo) 837 { 838 $ExtraPath = $ExtraInfo."/alt"; 839 mkpath($ExtraPath); 840 $ExtraPath .= "/debug_line"; 841 } 842 843 if($ExtraPath) 844 { 845 system($EU_READELF_L." -N --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); 846 open(SRC, $ExtraPath); 847 } 848 else { 849 open(SRC, $EU_READELF_L." -N --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" |"); 850 } 851 852 my $DirTable_Def = undef; 853 my %DirTable = (); 854 855 while(<SRC>) 856 { 857 if(defined $AddDirs) 858 { 859 if(/Directory table/i) 860 { 861 $DirTable_Def = 1; 862 next; 863 } 864 elsif(/File name table/i) 865 { 866 $DirTable_Def = undef; 867 next; 868 } 869 870 if(defined $DirTable_Def) 871 { 872 if(/\A\s*(.+?)\Z/) { 873 $DirTable{keys(%DirTable)+1} = $1; 874 } 875 } 876 } 877 878 if(/(\d+)\s+(\d+)\s+\d+\s+\d+\s+([^ ]+)/) 879 { 880 my ($Num, $Dir, $File) = ($1, $2, $3); 881 chomp($File); 882 883 if(defined $AddDirs) 884 { 885 if(my $DName = $DirTable{$Dir}) 886 { 887 $File = $DName."/".$File; 888 } 889 } 890 891 $SourceFile_Alt{0}{$Num} = $File; 892 } 893 } 894 close(SRC); 895 896 # debug info 897 if($ExtraInfo) 898 { 899 $ExtraPath = $ExtraInfo."/alt"; 900 mkpath($ExtraPath); 901 $ExtraPath .= "/debug_info"; 902 } 903 904 if($ExtraPath) 905 { 906 system($EU_READELF_L." -N --debug-dump=info \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); 907 open(INFO, $ExtraPath); 908 } 909 else { 910 open(INFO, $EU_READELF_L." -N --debug-dump=info \"$Path\" 2>\"$TMP_DIR/error\" |"); 911 } 912 913 my $ID = undef; 914 my $Num = 0; 915 916 while(<INFO>) 917 { 918 if(index($_, " ")==0) 919 { 920 if(defined $ID) { 921 $ImportedUnit{$ID}{$Num++} = $_; 922 } 923 } 924 elsif(index($_, " [")==0 925 and /\A \[\s*(\w+?)\](\s+)(\w+)/) 926 { 927 if($3 eq "partial_unit") 928 { 929 $ID = $1; 930 $Num = 0; 931 $ImportedUnit{$ID}{0} = $_; 932 } 933 elsif(length($2)==2) 934 { # not a partial_unit 935 $ID = undef; 936 } 937 elsif(defined $ID) 938 { 939 $ImportedDecl{$1} = $ID; 940 $ImportedUnit{$ID}{$Num++} = $_; 941 } 942 } 943 } 944} 945 946sub read_DWARF_Info($) 947{ 948 my $Path = $_[0]; 949 950 my $Dir = getDirname($Path); 951 my $Name = getFilename($Path); 952 953 if(not check_Cmd($EU_READELF)) { 954 exitStatus("Not_Found", "can't find \"$EU_READELF\" command"); 955 } 956 957 my $AddOpt = ""; 958 if(not defined $AddrToName) 959 { # disable search of symbol names 960 $AddOpt .= " -N"; 961 } 962 963 my $Sect = `$EU_READELF_L -S \"$Path\" 2>\"$TMP_DIR/error\"`; 964 965 if($Sect!~/\.z?debug_info/) 966 { # No DWARF info 967 if(my $DebugFile = getDebugFile($Path, "gnu_debuglink")) 968 { 969 my $DPath = $DebugFile; 970 my $DName = getFilename($DPath); 971 972 if(my $DDir = getDirname($Path)) 973 { 974 $DPath = $DDir."/".$DPath; 975 } 976 977 my $Found = undef; 978 if(defined $SearchDirDebuginfo) 979 { 980 my @Files = findFiles($SearchDirDebuginfo, "f"); 981 982 foreach my $F (@Files) 983 { 984 if(getFilename($F) eq $DName) 985 { 986 $Found = $F; 987 last; 988 } 989 } 990 } 991 992 if($Found) 993 { 994 $DPath = $Found; 995 printMsg("INFO", "Found debuginfo $DName (gnu_debuglink)"); 996 } 997 998 if($DPath ne $Path) 999 { 1000 if(-e $DPath) 1001 { 1002 printMsg("INFO", "Reading debuginfo $DName (gnu_debuglink)"); 1003 return read_DWARF_Info($DPath); 1004 } 1005 else { 1006 printMsg("WARNING", "Missed debuginfo $DName (gnu_debuglink)"); 1007 } 1008 } 1009 else { 1010 return 0; 1011 } 1012 } 1013 return 0; 1014 } 1015 else 1016 { 1017 if($Sect=~/\.gnu_debugaltlink/) 1018 { 1019 if(my $AltObj = getDebugAltLink($Path)) 1020 { 1021 $AltDebugInfo = $AltObj; 1022 read_Alt_Info($AltObj); 1023 } 1024 } 1025 } 1026 1027 printMsg("INFO", "Reading debug-info"); 1028 1029 my $ExtraPath = undef; 1030 1031 # ELF header 1032 if($ExtraInfo) 1033 { 1034 mkpath($ExtraInfo); 1035 $ExtraPath = $ExtraInfo."/elf-header"; 1036 } 1037 1038 if($ExtraPath) 1039 { 1040 system($EU_READELF_L." -h \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); 1041 open(HEADER, $ExtraPath); 1042 } 1043 else { 1044 open(HEADER, $EU_READELF_L." -h \"$Path\" 2>\"$TMP_DIR/error\" |"); 1045 } 1046 1047 my %Header = (); 1048 while(<HEADER>) 1049 { 1050 if(/\A\s*([\w ]+?)\:\s*(.+?)\Z/) { 1051 $Header{$1} = $2; 1052 } 1053 } 1054 close(HEADER); 1055 1056 $SYS_ARCH = $Header{"Machine"}; 1057 1058 if($SYS_ARCH=~/80\d86/ 1059 or $SYS_ARCH=~/i\d86/) 1060 { # i386, i586, etc. 1061 $SYS_ARCH = "x86"; 1062 } 1063 1064 if($SYS_ARCH=~/amd64/i 1065 or $SYS_ARCH=~/x86\-64/i) 1066 { # amd64 1067 $SYS_ARCH = "x86_64"; 1068 } 1069 1070 init_Registers(); 1071 1072 # ELF sections 1073 if($ExtraInfo) 1074 { 1075 mkpath($ExtraInfo); 1076 $ExtraPath = $ExtraInfo."/elf-sections"; 1077 } 1078 1079 if($ExtraPath) 1080 { 1081 system($EU_READELF_L." -S \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); 1082 open(HEADER, $ExtraPath); 1083 } 1084 1085 # source info 1086 if($ExtraInfo) 1087 { 1088 mkpath($ExtraInfo); 1089 $ExtraPath = $ExtraInfo."/debug_line"; 1090 } 1091 1092 if($ExtraPath) 1093 { 1094 system($EU_READELF_L." $AddOpt --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); 1095 open(SRC, $ExtraPath); 1096 } 1097 else { 1098 open(SRC, $EU_READELF_L." $AddOpt --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" |"); 1099 } 1100 1101 my $Offset = undef; 1102 my $DirTable_Def = undef; 1103 my %DirTable = (); 1104 1105 while(<SRC>) 1106 { 1107 if(defined $AddDirs) 1108 { 1109 if(/Directory table/i) 1110 { 1111 $DirTable_Def = 1; 1112 %DirTable = (); 1113 next; 1114 } 1115 elsif(/File name table/i) 1116 { 1117 $DirTable_Def = undef; 1118 next; 1119 } 1120 1121 if(defined $DirTable_Def) 1122 { 1123 if(/\A\s*(.+?)\Z/) { 1124 $DirTable{keys(%DirTable)+1} = $1; 1125 } 1126 } 1127 } 1128 1129 if(/Table at offset (\w+)/i) { 1130 $Offset = $1; 1131 } 1132 elsif(defined $Offset 1133 and /(\d+)\s+(\d+)\s+\d+\s+\d+\s+([^ ]+)/) 1134 { 1135 my ($Num, $Dir, $File) = ($1, $2, $3); 1136 chomp($File); 1137 1138 if(defined $AddDirs) 1139 { 1140 if(my $DName = $DirTable{$Dir}) 1141 { 1142 $File = $DName."/".$File; 1143 } 1144 } 1145 1146 $SourceFile{$Offset}{$Num} = $File; 1147 } 1148 } 1149 close(SRC); 1150 1151 # debug_loc 1152 if($ExtraInfo) 1153 { 1154 mkpath($ExtraInfo); 1155 $ExtraPath = $ExtraInfo."/debug_loc"; 1156 } 1157 1158 if($ExtraPath) 1159 { 1160 system($EU_READELF_L." $AddOpt --debug-dump=loc \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); 1161 open(LOC, $ExtraPath); 1162 } 1163 else { 1164 open(LOC, $EU_READELF_L." $AddOpt --debug-dump=loc \"$Path\" 2>\"$TMP_DIR/error\" |"); 1165 } 1166 1167 while(<LOC>) 1168 { 1169 if(/\A \[\s*(\w+)\].*\[\s*\w+\]\s*(.+)\Z/) { 1170 $DebugLoc{$1} = $2; 1171 } 1172 elsif(/\A \[\s*(\w+)\]/) { 1173 $DebugLoc{$1} = ""; 1174 } 1175 } 1176 close(LOC); 1177 1178 # dwarf 1179 if($ExtraInfo) 1180 { 1181 mkpath($ExtraInfo); 1182 $ExtraPath = $ExtraInfo."/debug_info"; 1183 } 1184 1185 my $INFO_fh; 1186 1187 if($Dir) 1188 { # to find ".dwz" directory (Fedora) 1189 chdir($Dir); 1190 } 1191 if($ExtraPath) 1192 { 1193 system($EU_READELF_L." $AddOpt --debug-dump=info \"$Name\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); 1194 open($INFO_fh, $ExtraPath); 1195 } 1196 else { 1197 open($INFO_fh, $EU_READELF_L." $AddOpt --debug-dump=info \"$Name\" 2>\"$TMP_DIR/error\" |"); 1198 } 1199 chdir($ORIG_DIR); 1200 1201 read_DWARF_Dump($INFO_fh, 1); 1202 1203 close($INFO_fh); 1204 1205 if(my $Err = readFile("$TMP_DIR/error")) 1206 { # eu-readelf: cannot get next DIE: invalid DWARF 1207 if($Err=~/invalid DWARF/i) 1208 { 1209 if($Loud) { 1210 printMsg("ERROR", $Err); 1211 } 1212 exitStatus("Invalid_DWARF", "invalid DWARF info"); 1213 } 1214 } 1215 1216 return 1; 1217} 1218 1219sub getSource($) 1220{ 1221 my $ID = $_[0]; 1222 1223 if(defined $DWARF_Info{$ID}{"decl_file"}) 1224 { 1225 my $File = $DWARF_Info{$ID}{"decl_file"}; 1226 my $Unit = $DWARF_Info{$ID}{"Unit"}; 1227 1228 my $Name = undef; 1229 1230 if($ID>=0) { 1231 $Name = $SourceFile{$Unit}{$File}; 1232 } 1233 else 1234 { # imported 1235 $Name = $SourceFile_Alt{0}{$File}; 1236 } 1237 1238 return $Name; 1239 } 1240 1241 return undef; 1242} 1243 1244sub read_DWARF_Dump($$) 1245{ 1246 my ($FH, $Primary) = @_; 1247 1248 my $TypeUnit_Sign = undef; 1249 my $TypeUnit_Offset = undef; 1250 my $Type_Offset = undef; 1251 1252 my $Shift_Enabled = 1; 1253 my $ID_Shift = undef; 1254 1255 my $CUnit = undef; 1256 1257 my $Compressed = undef; 1258 1259 if($AltDebugInfo) { 1260 $Compressed = 1; 1261 } 1262 1263 my $ID = undef; 1264 my $Kind = undef; 1265 my $NS = undef; 1266 1267 my $MAX_ID = undef; 1268 1269 my %Shift = map {$_=>1} ( 1270 "specification", 1271 "type", 1272 "sibling", 1273 "object_pointer", 1274 "containing_type", 1275 "abstract_origin", 1276 "import", 1277 "signature" 1278 ); 1279 1280 my $Line = undef; 1281 my $Import = undef; 1282 my $Import_Num = 0; 1283 1284 my %SkipNode = ( 1285 "imported_declaration" => 1, 1286 "imported_module" => 1 1287 ); 1288 1289 my %SkipAttr = ( 1290 "high_pc" => 1, 1291 "frame_base" => 1, 1292 "encoding" => 1 1293 ); 1294 1295 my %MarkByUnit = ( 1296 "member" => 1, 1297 "subprogram" => 1, 1298 "variable" => 1 1299 ); 1300 1301 my $Lexical_Block = undef; 1302 my $Inlined_Block = undef; 1303 my $Subprogram_Block = undef; 1304 my $Skip_Block = undef; 1305 1306 while(($Import and $Line = $ImportedUnit{$Import}{$Import_Num}) or $Line = <$FH>) 1307 { 1308 if($Import) 1309 { 1310 if(not defined $ImportedUnit{$Import}{$Import_Num}) 1311 { 1312 $Import_Num = 0; 1313 delete($ImportedUnit{$Import}); 1314 $Import = undef; 1315 } 1316 1317 $Import_Num+=1; 1318 } 1319 1320 if(defined $ID and $Line=~/\A\s*(\w+)\s*(.+?)\s*\Z/) 1321 { 1322 if(defined $Skip_Block) { 1323 next; 1324 } 1325 1326 my $Attr = $1; 1327 my $Val = $2; 1328 1329 if(index($Val, "flag_present")!=-1) 1330 { # Fedora 1331 $Val = "Yes"; 1332 } 1333 1334 if(defined $Compressed) 1335 { 1336 if($Kind eq "imported_unit") 1337 { 1338 if($Attr eq "import") 1339 { 1340 if($Val=~/\(GNU_ref_alt\)\s*\[\s*(\w+?)\]/) 1341 { 1342 if(defined $ImportedUnit{$1}) 1343 { 1344 $Import = $1; 1345 $Import_Num = 0; 1346 $UsedUnit{$Import} = 1; 1347 } 1348 } 1349 } 1350 } 1351 } 1352 1353 if($Kind eq "member") 1354 { 1355 if($Attr eq "data_member_location") 1356 { 1357 delete($DWARF_Info{$ID}{"Unit"}); 1358 } 1359 } 1360 1361 if($Attr eq "sibling") 1362 { 1363 if($Kind ne "structure_type") 1364 { 1365 next; 1366 } 1367 } 1368 elsif($Attr eq "Type") 1369 { 1370 if($Line=~/Type\s+signature:\s*0x(\w+)/) { 1371 $TypeUnit_Sign = $1; 1372 } 1373 if($Line=~/Type\s+offset:\s*0x(\w+)/) { 1374 $Type_Offset = hex($1); 1375 } 1376 if($Line=~/Type\s+unit\s+at\s+offset\s+(\d+)/) { 1377 $TypeUnit_Offset = $1; 1378 } 1379 next; 1380 } 1381 elsif(defined $SkipAttr{$Attr}) 1382 { # unused 1383 next; 1384 } 1385 1386 if($Val=~/\A\s*\(([^()]*)\)\s*\[\s*(\w+)\]\s*\Z/) 1387 { # ref4, ref_udata, ref_addr, etc. 1388 $Val = hex($2); 1389 1390 if($1 eq "GNU_ref_alt") 1391 { 1392 $Val = -$Val; 1393 $UsedDecl{$2} = 1; 1394 } 1395 } 1396 elsif($Attr eq "name") 1397 { 1398 $Val=~s/\A\([^()]*\)\s*\"(.*)\"\Z/$1/; 1399 } 1400 elsif(index($Attr, "linkage_name")!=-1) 1401 { 1402 $Val=~s/\A\([^()]*\)\s*\"(.*)\"\Z/$1/; 1403 $Attr = "linkage_name"; 1404 } 1405 elsif(index($Attr, "location")!=-1) 1406 { 1407 if($Val=~/\)\s*\Z/) 1408 { # value on the next line 1409 my $NL = ""; 1410 1411 if($Import) { 1412 $NL = $ImportedUnit{$Import}{$Import_Num} 1413 } 1414 else { 1415 $NL = <$FH>; 1416 } 1417 1418 $Val .= $NL; 1419 } 1420 1421 if($Val=~/\A\(\w+\)\s*(-?)(\w+)\Z/) 1422 { # (data1) 1c 1423 $Val = hex($2); 1424 if($1) { 1425 $Val = -$Val; 1426 } 1427 } 1428 else 1429 { 1430 if($Val=~/ (-?\d+)\Z/) { 1431 $Val = $1; 1432 } 1433 else 1434 { 1435 if($Attr eq "location" 1436 and $Kind eq "formal_parameter") 1437 { 1438 if($Val=~/location list\s+\[\s*(\w+)\]\Z/) 1439 { 1440 $Attr = "location_list"; 1441 $Val = $1; 1442 } 1443 elsif($Val=~/ reg(\d+)\Z/) 1444 { 1445 $Attr = "register"; 1446 $Val = $1; 1447 } 1448 } 1449 } 1450 } 1451 } 1452 elsif($Attr eq "accessibility") 1453 { 1454 $Val=~s/\A\(.+?\)\s*//; 1455 $Val=~s/\s*\(.+?\)\Z//; 1456 1457 # NOTE: members: private by default 1458 } 1459 else 1460 { 1461 $Val=~s/\A\(\w+\)\s*//; 1462 1463 if(substr($Val, 0, 1) eq "{" 1464 and $Val=~/{(.+)}/) 1465 { # {ID} 1466 $Val = $1; 1467 $Post_Change{$ID} = 1; 1468 } 1469 } 1470 1471 if(defined $Shift_Enabled and $ID_Shift) 1472 { 1473 if(defined $Shift{$Attr} 1474 and not $Post_Change{$ID}) { 1475 $Val += $ID_Shift; 1476 } 1477 1478 # $DWARF_Info{$ID}{"rID"} = $ID-$ID_Shift; 1479 } 1480 1481 if($Import or not $Primary) 1482 { 1483 if(defined $Shift{$Attr}) 1484 { 1485 $Val = -$Val; 1486 } 1487 } 1488 1489 $DWARF_Info{$ID}{$Attr} = "$Val"; 1490 1491 if($Kind eq "compile_unit") 1492 { 1493 if($Attr eq "stmt_list") { 1494 $CUnit = $Val; 1495 } 1496 1497 if(not defined $LIB_LANG) 1498 { 1499 if($Attr eq "language") 1500 { 1501 if(index($Val, "Assembler")==-1) 1502 { 1503 $Val=~s/\s*\(.+?\)\Z//; 1504 1505 if($Val=~/C\d/i) { 1506 $LIB_LANG = "C"; 1507 } 1508 elsif($Val=~/C\+\+|C_plus_plus/i) { 1509 $LIB_LANG = "C++"; 1510 } 1511 else { 1512 $LIB_LANG = $Val; 1513 } 1514 } 1515 } 1516 } 1517 1518 if(not defined $SYS_COMP and not defined $SYS_GCCV) 1519 { 1520 if($Attr eq "producer") 1521 { 1522 if(index($Val, "GNU AS")==-1) 1523 { 1524 $Val=~s/\A\"//; 1525 $Val=~s/\"\Z//; 1526 1527 if($Val=~/GNU\s+(C\d*|C\+\+)\s+(.+)\Z/) 1528 { 1529 $SYS_GCCV = $2; 1530 if($SYS_GCCV=~/\A(\d+\.\d+)(\.\d+|)/) 1531 { # 4.6.1 20110627 (Mandriva) 1532 $SYS_GCCV = $1.$2; 1533 } 1534 1535 my %Opts = (); 1536 while($Val=~s/(\A| )(\-O([0-3]|g))( |\Z)/ /) { 1537 $Opts{keys(%Opts)} = $2; 1538 } 1539 1540 if(keys(%Opts)) 1541 { 1542 if($Opts{keys(%Opts)-1} ne "-Og") 1543 { 1544 printMsg("WARNING", "incompatible build option detected: ".$Opts{keys(%Opts)-1}." (required -Og for better analysis)"); 1545 $IncompatibleOpt = 1; 1546 } 1547 } 1548 else 1549 { 1550 printMsg("WARNING", "the object should be compiled with -Og option for better analysis"); 1551 $IncompatibleOpt = 1; 1552 } 1553 } 1554 else { 1555 $SYS_COMP = $Val; 1556 } 1557 } 1558 } 1559 } 1560 } 1561 elsif($Kind eq "type_unit") 1562 { 1563 if($Attr eq "stmt_list") { 1564 $CUnit = $Val; 1565 } 1566 } 1567 elsif($Kind eq "partial_unit" and not $Import) 1568 { # support for dwz 1569 if($Attr eq "stmt_list") { 1570 $CUnit = $Val; 1571 } 1572 } 1573 } 1574 elsif($Line=~/\A \[\s*(\w+)\](\s*)(\w+)/) 1575 { 1576 $ID = hex($1); 1577 $NS = length($2); 1578 $Kind = $3; 1579 1580 if(not defined $Compressed) 1581 { 1582 if($Kind eq "partial_unit" or $Kind eq "type_unit") 1583 { # compressed debug_info 1584 $Compressed = 1; 1585 } 1586 } 1587 1588 if(not $Compressed) 1589 { # compile units can depend on each other in the compressed debug_info 1590 # so reading them all integrally by one call of read_ABI() 1591 if($Kind eq "compile_unit" and $CUnit) 1592 { # read the previous compile unit 1593 complete_Dump($Primary); 1594 read_ABI(); 1595 1596 if(not defined $Compressed) 1597 { # normal debug_info 1598 $Compressed = 0; 1599 } 1600 } 1601 } 1602 1603 $Skip_Block = undef; 1604 1605 if(defined $SkipNode{$Kind}) 1606 { 1607 $Skip_Block = 1; 1608 next; 1609 } 1610 1611 if($Kind eq "lexical_block") 1612 { 1613 $Lexical_Block = $NS; 1614 $Skip_Block = 1; 1615 next; 1616 } 1617 else 1618 { 1619 if(defined $Lexical_Block) 1620 { 1621 if($NS>$Lexical_Block) 1622 { 1623 $Skip_Block = 1; 1624 next; 1625 } 1626 else 1627 { # end of lexical block 1628 $Lexical_Block = undef; 1629 } 1630 } 1631 } 1632 1633 if($Kind eq "inlined_subroutine") 1634 { 1635 $Inlined_Block = $NS; 1636 $Skip_Block = 1; 1637 next; 1638 } 1639 else 1640 { 1641 if(defined $Inlined_Block) 1642 { 1643 if($NS>$Inlined_Block) 1644 { 1645 $Skip_Block = 1; 1646 next; 1647 } 1648 else 1649 { # end of inlined subroutine 1650 $Inlined_Block = undef; 1651 } 1652 } 1653 } 1654 1655 if($Kind eq "subprogram") 1656 { 1657 $Subprogram_Block = $NS; 1658 } 1659 else 1660 { 1661 if(defined $Subprogram_Block) 1662 { 1663 if($NS>$Subprogram_Block) 1664 { 1665 if($Kind eq "variable") 1666 { # temp variables 1667 $Skip_Block = 1; 1668 next; 1669 } 1670 } 1671 else 1672 { # end of subprogram block 1673 $Subprogram_Block = undef; 1674 } 1675 } 1676 } 1677 1678 if($Import or not $Primary) 1679 { 1680 $ID = -$ID; 1681 } 1682 1683 if(defined $Shift_Enabled) 1684 { 1685 if($Kind eq "type_unit") 1686 { 1687 if(not defined $ID_Shift) 1688 { 1689 if($ID_Shift<=$MAX_ID) { 1690 $ID_Shift = $MAX_ID; 1691 } 1692 else { 1693 $ID_Shift = 0; 1694 } 1695 } 1696 } 1697 1698 if($ID_Shift) { 1699 $ID += $ID_Shift; 1700 } 1701 } 1702 1703 if(defined $TypeUnit_Sign) 1704 { 1705 if($Kind ne "type_unit" 1706 and $Kind ne "namespace") 1707 { 1708 if($TypeUnit_Offset+$Type_Offset+$ID_Shift==$ID) 1709 { 1710 $TypeUnit{$TypeUnit_Sign} = "$ID"; 1711 $TypeUnit_Sign = undef; 1712 } 1713 } 1714 } 1715 1716 $DWARF_Info{$ID}{"Kind"} = $Kind; 1717 $DWARF_Info{$ID}{"NS"} = $NS; 1718 1719 if(defined $CUnit) 1720 { 1721 if(defined $MarkByUnit{$Kind} 1722 or defined $TypeType{$Kind}) { 1723 $DWARF_Info{$ID}{"Unit"} = $CUnit; 1724 } 1725 } 1726 1727 if(not defined $ID_Shift) { 1728 $MAX_ID = $ID; 1729 } 1730 } 1731 elsif(not defined $SYS_WORD 1732 and $Line=~/Address\s*size:\s*(\d+)/i) 1733 { 1734 $SYS_WORD = $1; 1735 } 1736 } 1737 1738 if(not defined $ID) { 1739 printMsg("ERROR", "the debuginfo looks empty or corrupted"); 1740 } 1741 1742 # read the last compile unit 1743 # or all units if debug_info is compressed 1744 complete_Dump($Primary); 1745 read_ABI(); 1746} 1747 1748sub read_Vtables($) 1749{ 1750 my $Path = $_[0]; 1751 1752 $Path = abs_path($Path); 1753 1754 my $Dir = getDirname($Path); 1755 1756 if(index($LIB_LANG, "C++")!=-1 1757 or $OBJ_LANG eq "C++") 1758 { 1759 printMsg("INFO", "Reading v-tables"); 1760 1761 if(check_Cmd($VTABLE_DUMPER)) 1762 { 1763 if(my $Version = `$VTABLE_DUMPER -dumpversion`) 1764 { 1765 if(cmpVersions($Version, $VTABLE_DUMPER_VERSION)<0) 1766 { 1767 printMsg("ERROR", "the version of Vtable-Dumper should be $VTABLE_DUMPER_VERSION or newer"); 1768 return; 1769 } 1770 } 1771 } 1772 else 1773 { 1774 printMsg("ERROR", "cannot find \'$VTABLE_DUMPER\'"); 1775 return; 1776 } 1777 1778 my $ExtraPath = $TMP_DIR."/v-tables"; 1779 1780 if($ExtraInfo) 1781 { 1782 mkpath($ExtraInfo); 1783 $ExtraPath = $ExtraInfo."/v-tables"; 1784 } 1785 1786 system("LD_LIBRARY_PATH=\"$Dir\" $VTABLE_DUMPER -mangled -demangled \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); 1787 1788 my $Content = readFile($ExtraPath); 1789 foreach my $ClassInfo (split(/\n\n\n/, $Content)) 1790 { 1791 if($ClassInfo=~/\AVtable\s+for\s+(.+)\n((.|\n)+)\Z/i) 1792 { 1793 my ($CName, $VTable) = ($1, $2); 1794 my @Entries = split(/\n/, $VTable); 1795 1796 foreach (1 .. $#Entries) 1797 { 1798 my $Entry = $Entries[$_]; 1799 if($Entry=~/\A(\d+)\s+(.+)\Z/) { 1800 $VirtualTable{$CName}{$1} = $2; 1801 } 1802 } 1803 } 1804 } 1805 } 1806 1807 if(keys(%VirtualTable)) 1808 { 1809 foreach my $Tid (sort keys(%TypeInfo)) 1810 { 1811 if($TypeInfo{$Tid}{"Type"}=~/\A(Struct|Class)\Z/) 1812 { 1813 my $TName = $TypeInfo{$Tid}{"Name"}; 1814 $TName=~s/\bstruct //g; 1815 if(defined $VirtualTable{$TName}) 1816 { 1817 %{$TypeInfo{$Tid}{"VTable"}} = %{$VirtualTable{$TName}}; 1818 } 1819 } 1820 } 1821 } 1822} 1823 1824sub dump_ABI() 1825{ 1826 printMsg("INFO", "Creating ABI dump"); 1827 1828 my %ABI = ( 1829 "TypeInfo" => \%TypeInfo, 1830 "SymbolInfo" => \%SymbolInfo, 1831 "Symbols" => \%Library_Symbol, 1832 "UndefinedSymbols" => \%Library_UndefSymbol, 1833 "Needed" => \%Library_Needed, 1834 "SymbolVersion" => \%SymVer, 1835 "LibraryVersion" => $TargetVersion, 1836 "LibraryName" => $TargetName, 1837 "Language" => $LIB_LANG, 1838 "Headers" => \%HeadersInfo, 1839 "Sources" => \%SourcesInfo, 1840 "NameSpaces" => \%NestedNameSpaces, 1841 "Target" => "unix", 1842 "Arch" => $SYS_ARCH, 1843 "WordSize" => $SYS_WORD, 1844 "ABI_DUMP_VERSION" => $ABI_DUMP_VERSION, 1845 "ABI_DUMPER_VERSION" => $TOOL_VERSION, 1846 ); 1847 1848 if($SYS_GCCV) { 1849 $ABI{"GccVersion"} = $SYS_GCCV; 1850 } 1851 else { 1852 $ABI{"Compiler"} = $SYS_COMP; 1853 } 1854 1855 if(defined $PublicHeadersPath) { 1856 $ABI{"PublicABI"} = "1"; 1857 } 1858 1859 if(defined $IncompatibleOpt) 1860 { 1861 $ABI{"MissedOffsets"} = "1"; 1862 $ABI{"MissedRegs"} = "1"; 1863 } 1864 1865 my $ABI_DUMP = Dumper(\%ABI); 1866 1867 if($StdOut) 1868 { # --stdout option 1869 print STDOUT $ABI_DUMP; 1870 } 1871 else 1872 { 1873 mkpath(getDirname($OutputDump)); 1874 1875 open(DUMP, ">", $OutputDump) || die ("can't open file \'$OutputDump\': $!\n"); 1876 print DUMP $ABI_DUMP; 1877 close(DUMP); 1878 1879 printMsg("INFO", "\nThe object ABI has been dumped to:\n $OutputDump"); 1880 } 1881} 1882 1883sub unmangleString($) 1884{ 1885 my $Str = $_[0]; 1886 1887 $Str=~s/\AN(.+)E\Z/$1/; 1888 while($Str=~s/\A(\d+)//) 1889 { 1890 if(length($Str)==$1) { 1891 last; 1892 } 1893 1894 $Str = substr($Str, $1, length($Str) - $1); 1895 } 1896 1897 return $Str; 1898} 1899 1900sub init_ABI() 1901{ 1902 # register "void" type 1903 %{$TypeInfo{"1"}} = ( 1904 "Name"=>"void", 1905 "Type"=>"Intrinsic" 1906 ); 1907 $TName_Tid{"Intrinsic"}{"void"} = "1"; 1908 $TName_Tids{"Intrinsic"}{"void"}{"1"} = 1; 1909 $Cache{"getTypeInfo"}{"1"} = 1; 1910 1911 # register "..." type 1912 %{$TypeInfo{"-1"}} = ( 1913 "Name"=>"...", 1914 "Type"=>"Intrinsic" 1915 ); 1916 $TName_Tid{"Intrinsic"}{"..."} = "-1"; 1917 $TName_Tids{"Intrinsic"}{"..."}{"-1"} = 1; 1918 $Cache{"getTypeInfo"}{"-1"} = 1; 1919} 1920 1921sub complete_Dump($) 1922{ 1923 my $Primary = $_[0]; 1924 1925 foreach my $ID (keys(%Post_Change)) 1926 { 1927 if(my $Type = $DWARF_Info{$ID}{"type"}) 1928 { 1929 if(my $To = $TypeUnit{$Type}) { 1930 $DWARF_Info{$ID}{"type"} = $To; 1931 } 1932 } 1933 if(my $Signature = $DWARF_Info{$ID}{"signature"}) 1934 { 1935 if(my $To = $TypeUnit{$Signature}) { 1936 $DWARF_Info{$ID}{"signature"} = $To; 1937 } 1938 } 1939 } 1940 1941 %Post_Change = (); 1942 %TypeUnit = (); 1943 1944 if($Primary) 1945 { 1946 my %AddUnits = (); 1947 1948 foreach my $ID (keys(%UsedDecl)) 1949 { 1950 if(my $U_ID = $ImportedDecl{$ID}) 1951 { 1952 if(not $UsedUnit{$U_ID}) 1953 { 1954 $AddUnits{$U_ID} = 1; 1955 } 1956 } 1957 } 1958 1959 if(keys(%AddUnits)) 1960 { 1961 my $ADD_DUMP = ""; 1962 1963 foreach my $U_ID (sort {hex($a)<=>hex($b)} keys(%AddUnits)) 1964 { 1965 foreach my $N (sort {int($a)<=>int($b)} keys(%{$ImportedUnit{$U_ID}})) 1966 { 1967 $ADD_DUMP .= $ImportedUnit{$U_ID}{$N}; 1968 } 1969 } 1970 1971 my $AddUnit_F = $TMP_DIR."/add_unit.dump"; 1972 1973 writeFile($AddUnit_F, $ADD_DUMP); 1974 1975 my $FH_add; 1976 open($FH_add, $AddUnit_F); 1977 read_DWARF_Dump($FH_add, 0); 1978 close($FH_add); 1979 1980 unlink($AddUnit_F); 1981 } 1982 } 1983 1984 %UsedUnit = (); 1985 %UsedDecl = (); 1986} 1987 1988sub read_ABI() 1989{ 1990 my %CurID = (); 1991 1992 my @IDs = sort {int($a) <=> int($b)} keys(%DWARF_Info); 1993 1994 if($AltDebugInfo) { 1995 @IDs = sort {$b>0 <=> $a>0} sort {abs(int($a)) <=> abs(int($b))} @IDs; 1996 } 1997 1998 my $TPack = undef; 1999 my $PPack = undef; 2000 2001 foreach my $ID (@IDs) 2002 { 2003 $ID = "$ID"; 2004 2005 my $Kind = $DWARF_Info{$ID}{"Kind"}; 2006 my $NS = $DWARF_Info{$ID}{"NS"}; 2007 my $Scope = $CurID{$NS-2}; 2008 2009 if($Kind eq "typedef") 2010 { 2011 if($DWARF_Info{$Scope}{"Kind"} eq "subprogram") 2012 { 2013 $NS = $DWARF_Info{$Scope}{"NS"}; 2014 $Scope = $CurID{$NS-2}; 2015 } 2016 } 2017 2018 if($Kind ne "subprogram") { 2019 delete($DWARF_Info{$ID}{"NS"}); 2020 } 2021 2022 my $IsType = ($Kind=~/(struct|structure|class|union|enumeration|subroutine|array)_type/); 2023 2024 if($IsType 2025 or $Kind eq "typedef" 2026 or $Kind eq "subprogram" 2027 or $Kind eq "variable" 2028 or $Kind eq "namespace") 2029 { 2030 if($Kind ne "variable" 2031 and $Kind ne "typedef") 2032 { 2033 $CurID{$NS} = $ID; 2034 } 2035 2036 if($Scope) 2037 { 2038 $NameSpace{$ID} = $Scope; 2039 if($Kind eq "subprogram" 2040 or $Kind eq "variable") 2041 { 2042 if($DWARF_Info{$Scope}{"Kind"}=~/class|struct/) 2043 { 2044 $ClassMethods{$Scope}{$ID} = 1; 2045 if(my $Sp = $DWARF_Info{$Scope}{"specification"}) { 2046 $ClassMethods{$Sp}{$ID} = 1; 2047 } 2048 } 2049 } 2050 } 2051 2052 if(my $Spec = $DWARF_Info{$ID}{"specification"}) { 2053 $SpecElem{$Spec} = $ID; 2054 } 2055 2056 if(my $Orig = $DWARF_Info{$ID}{"abstract_origin"}) { 2057 $OrigElem{$Orig} = $ID; 2058 } 2059 2060 if($IsType) 2061 { 2062 if(not $DWARF_Info{$ID}{"name"} 2063 and $DWARF_Info{$ID}{"linkage_name"}) 2064 { 2065 $DWARF_Info{$ID}{"name"} = unmangleString($DWARF_Info{$ID}{"linkage_name"}); 2066 2067 # free memory 2068 delete($DWARF_Info{$ID}{"linkage_name"}); 2069 } 2070 } 2071 } 2072 elsif($Kind eq "member") 2073 { 2074 if($Scope) 2075 { 2076 $NameSpace{$ID} = $Scope; 2077 2078 if($DWARF_Info{$Scope}{"Kind"}=~/class|struct/ 2079 and not defined $DWARF_Info{$ID}{"data_member_location"}) 2080 { # variable (global data) 2081 next; 2082 } 2083 } 2084 2085 $TypeMember{$Scope}{keys(%{$TypeMember{$Scope}})} = $ID; 2086 } 2087 elsif($Kind eq "enumerator") 2088 { 2089 $TypeMember{$Scope}{keys(%{$TypeMember{$Scope}})} = $ID; 2090 } 2091 elsif($Kind eq "inheritance") 2092 { 2093 my %In = (); 2094 $In{"id"} = $DWARF_Info{$ID}{"type"}; 2095 2096 if(my $Access = $DWARF_Info{$ID}{"accessibility"}) 2097 { 2098 if($Access ne "public") 2099 { # default inheritance access in ABI dump is "public" 2100 $In{"access"} = $Access; 2101 } 2102 } 2103 2104 if(defined $DWARF_Info{$ID}{"virtuality"}) { 2105 $In{"virtual"} = 1; 2106 } 2107 $Inheritance{$Scope}{keys(%{$Inheritance{$Scope}})} = \%In; 2108 2109 # free memory 2110 delete($DWARF_Info{$ID}); 2111 } 2112 elsif($Kind eq "formal_parameter") 2113 { 2114 if(defined $PPack) { 2115 $FuncParam{$PPack}{keys(%{$FuncParam{$PPack}})} = $ID; 2116 } 2117 else { 2118 $FuncParam{$Scope}{keys(%{$FuncParam{$Scope}})} = $ID; 2119 } 2120 } 2121 elsif($Kind eq "unspecified_parameters") 2122 { 2123 $FuncParam{$Scope}{keys(%{$FuncParam{$Scope}})} = $ID; 2124 $DWARF_Info{$ID}{"type"} = "-1"; # "..." 2125 } 2126 elsif($Kind eq "subrange_type") 2127 { 2128 if((my $Bound = $DWARF_Info{$ID}{"upper_bound"}) ne "") { 2129 $ArrayCount{$Scope} = $Bound + 1; 2130 } 2131 2132 # free memory 2133 delete($DWARF_Info{$ID}); 2134 } 2135 elsif($Kind eq "template_type_parameter" 2136 or $Kind eq "template_value_parameter") 2137 { 2138 my %Info = ("type"=>$DWARF_Info{$ID}{"type"}, "key"=>$DWARF_Info{$ID}{"name"}); 2139 2140 if(defined $DWARF_Info{$ID}{"const_value"}) { 2141 $Info{"value"} = $DWARF_Info{$ID}{"const_value"}; 2142 } 2143 2144 if(defined $DWARF_Info{$ID}{"default_value"}) { 2145 $Info{"default"} = 1; 2146 } 2147 2148 if(defined $TPack) { 2149 $TmplParam{$TPack}{keys(%{$TmplParam{$TPack}})} = \%Info; 2150 } 2151 else { 2152 $TmplParam{$Scope}{keys(%{$TmplParam{$Scope}})} = \%Info; 2153 } 2154 } 2155 elsif($Kind eq "GNU_template_parameter_pack") { 2156 $TPack = $Scope; 2157 } 2158 elsif($Kind eq "GNU_formal_parameter_pack") { 2159 $PPack = $Scope; 2160 } 2161 2162 if($Kind ne "GNU_template_parameter_pack") 2163 { 2164 if(index($Kind, "template_")==-1) { 2165 $TPack = undef; 2166 } 2167 } 2168 2169 if($Kind ne "GNU_formal_parameter_pack") 2170 { 2171 if($Kind ne "formal_parameter") { 2172 $PPack = undef; 2173 } 2174 } 2175 2176 } 2177 2178 my @IDs = sort {int($a) <=> int($b)} keys(%DWARF_Info); 2179 2180 if($AltDebugInfo) { 2181 @IDs = sort {$b>0 <=> $a>0} sort {abs(int($a)) <=> abs(int($b))} @IDs; 2182 } 2183 2184 foreach my $ID (@IDs) 2185 { 2186 if(my $Kind = $DWARF_Info{$ID}{"Kind"}) 2187 { 2188 if(defined $TypeType{$Kind}) { 2189 getTypeInfo($ID); 2190 } 2191 } 2192 } 2193 2194 foreach my $Tid (@IDs) 2195 { 2196 if(defined $TypeInfo{$Tid}) 2197 { 2198 my $Type = $TypeInfo{$Tid}{"Type"}; 2199 2200 if(not defined $TypeInfo{$Tid}{"Memb"}) 2201 { 2202 if($Type=~/Struct|Class|Union|Enum/) 2203 { 2204 if(my $Signature = $DWARF_Info{$Tid}{"signature"}) 2205 { 2206 if(defined $TypeInfo{$Signature}) 2207 { 2208 foreach my $Attr (keys(%{$TypeInfo{$Signature}})) 2209 { 2210 if(not defined $TypeInfo{$Tid}{$Attr}) { 2211 $TypeInfo{$Tid}{$Attr} = $TypeInfo{$Signature}{$Attr}; 2212 } 2213 } 2214 } 2215 } 2216 } 2217 } 2218 } 2219 } 2220 2221 # delete types info 2222 foreach (keys(%DWARF_Info)) 2223 { 2224 if(my $Kind = $DWARF_Info{$_}{"Kind"}) 2225 { 2226 if(defined $TypeType{$Kind}) { 2227 delete($DWARF_Info{$_}); 2228 } 2229 } 2230 } 2231 2232 foreach my $ID (sort {int($a) <=> int($b)} keys(%DWARF_Info)) 2233 { 2234 if($ID<0) 2235 { # imported 2236 next; 2237 } 2238 2239 if($DWARF_Info{$ID}{"Kind"} eq "subprogram" 2240 or $DWARF_Info{$ID}{"Kind"} eq "variable") 2241 { 2242 getSymbolInfo($ID); 2243 } 2244 } 2245 2246 %DWARF_Info = (); 2247 2248 # free memory 2249 %TypeMember = (); 2250 %ArrayCount = (); 2251 %FuncParam = (); 2252 %TmplParam = (); 2253 %Inheritance = (); 2254 %NameSpace = (); 2255 %SpecElem = (); 2256 %OrigElem = (); 2257 %ClassMethods = (); 2258 2259 $Cache{"getTypeInfo"} = {"1"=>1, "-1"=>1}; 2260} 2261 2262sub complete_ABI() 2263{ 2264 # types 2265 my %Incomplete = (); 2266 my %Incomplete_TN = (); 2267 2268 my @IDs = sort {int($a) <=> int($b)} keys(%TypeInfo); 2269 2270 if($AltDebugInfo) { 2271 @IDs = sort {$b>0 <=> $a>0} sort {abs(int($a)) <=> abs(int($b))} @IDs; 2272 } 2273 2274 foreach my $Tid (@IDs) 2275 { 2276 my $Name = $TypeInfo{$Tid}{"Name"}; 2277 my $Type = $TypeInfo{$Tid}{"Type"}; 2278 2279 if(not defined $SpecElem{$Tid} 2280 and not defined $Incomplete_TN{$Type}{$Name}) 2281 { 2282 if(not defined $TypeInfo{$Tid}{"Size"}) 2283 { 2284 if($Type=~/Struct|Class|Union|Enum/) 2285 { 2286 $Incomplete{$Tid} = 1; 2287 } 2288 } 2289 } 2290 2291 $Incomplete_TN{$Type}{$Name} = 1; 2292 } 2293 2294 # free memory 2295 %Incomplete_TN = (); 2296 2297 foreach my $Tid (sort {int($a) <=> int($b)} keys(%Incomplete)) 2298 { 2299 my $Name = $TypeInfo{$Tid}{"Name"}; 2300 my $Type = $TypeInfo{$Tid}{"Type"}; 2301 2302 my @Adv_IDs = sort {int($a) <=> int($b)} keys(%{$TName_Tids{$Type}{$Name}}); 2303 2304 if($AltDebugInfo) { 2305 @Adv_IDs = sort {$b>0 <=> $a>0} sort {abs(int($a)) <=> abs(int($b))} @Adv_IDs; 2306 } 2307 2308 foreach my $Tid_Adv (@Adv_IDs) 2309 { 2310 if($Tid_Adv!=$Tid) 2311 { 2312 if(defined $SpecElem{$Tid_Adv} 2313 or defined $TypeInfo{$Tid_Adv}{"Size"}) 2314 { 2315 foreach my $Attr (keys(%{$TypeInfo{$Tid_Adv}})) 2316 { 2317 if(not defined $TypeInfo{$Tid}{$Attr}) 2318 { 2319 if(ref($TypeInfo{$Tid_Adv}{$Attr}) eq "HASH") { 2320 $TypeInfo{$Tid}{$Attr} = dclone($TypeInfo{$Tid_Adv}{$Attr}); 2321 } 2322 else { 2323 $TypeInfo{$Tid}{$Attr} = $TypeInfo{$Tid_Adv}{$Attr}; 2324 } 2325 2326 } 2327 } 2328 last; 2329 } 2330 } 2331 } 2332 } 2333 2334 # free memory 2335 %Incomplete = (); 2336 2337 my %Delete = (); 2338 2339 foreach my $Tid (sort {int($a) <=> int($b)} keys(%TypeInfo)) 2340 { 2341 if($TypeInfo{$Tid}{"Type"} eq "Typedef") 2342 { 2343 my $TN = $TypeInfo{$Tid}{"Name"}; 2344 my $TL = $TypeInfo{$Tid}{"Line"}; 2345 my $NS = $TypeInfo{$Tid}{"NameSpace"}; 2346 2347 if(my $BTid = $TypeInfo{$Tid}{"BaseType"}) 2348 { 2349 if(defined $TypeInfo{$BTid} 2350 and $TypeInfo{$BTid}{"Name"}=~/\Aanon\-(\w+)\-/ 2351 and $TypeInfo{$BTid}{"Type"}=~/Enum|Struct|Union/) 2352 { 2353 %{$TypeInfo{$Tid}} = %{$TypeInfo{$BTid}}; 2354 $TypeInfo{$Tid}{"Name"} = lc($TypeInfo{$BTid}{"Type"})." ".$TN; 2355 $TypeInfo{$Tid}{"Line"} = $TL; 2356 2357 my $Name = $TypeInfo{$Tid}{"Name"}; 2358 my $Type = $TypeInfo{$Tid}{"Type"}; 2359 2360 if(not defined $TName_Tid{$Type}{$Name} 2361 or ($Tid>0 and $Tid<$TName_Tid{$Type}{$Name}) 2362 or ($Tid>0 and $TName_Tid{$Type}{$Name}<0)) { 2363 $TName_Tid{$Type}{$Name} = $Tid; 2364 } 2365 $TName_Tids{$Type}{$Name}{$Tid} = 1; 2366 2367 if($NS) { 2368 $TypeInfo{$Tid}{"NameSpace"} = $NS; 2369 } 2370 $Delete{$BTid} = $Tid; 2371 } 2372 } 2373 } 2374 elsif($TypeInfo{$Tid}{"Type"} eq "Pointer") 2375 { 2376 if(my $BTid = $TypeInfo{$Tid}{"BaseType"}) 2377 { 2378 if(my $To = $Delete{$BTid}) 2379 { 2380 $TypeInfo{$Tid}{"BaseType"} = $To; 2381 $TypeInfo{$Tid}{"Name"} = $TypeInfo{$To}{"Name"}."*"; 2382 2383 my $Name = $TypeInfo{$Tid}{"Name"}; 2384 my $Type = $TypeInfo{$Tid}{"Type"}; 2385 2386 $TName_Tid{$Type}{$Name} = $Tid; 2387 $TName_Tids{$Type}{$Name}{$Tid} = 1; 2388 } 2389 } 2390 } 2391 } 2392 2393 foreach my $Tid (keys(%Delete)) 2394 { 2395 my $TN = $TypeInfo{$Tid}{"Name"}; 2396 my $TT = $TypeInfo{$Tid}{"Type"}; 2397 2398 delete($TName_Tid{$TT}{$TN}); 2399 delete($TName_Tids{$TT}{$TN}{$Tid}); 2400 2401 if(my @IDs = sort {int($a) <=> int($b)} keys(%{$TName_Tids{$TT}{$TN}})) 2402 { # minimal ID 2403 $TName_Tid{$TT}{$TN} = $IDs[0]; 2404 } 2405 2406 delete($TypeInfo{$Tid}); 2407 } 2408 2409 # free memory 2410 %Delete = (); 2411 2412 # symbols 2413 foreach my $ID (sort {int($a) <=> int($b)} keys(%SymbolInfo)) 2414 { 2415 # add missed c-tors 2416 if($SymbolInfo{$ID}{"Constructor"}) 2417 { 2418 if($SymbolInfo{$ID}{"MnglName"}=~/(C[1-2])([EI]).+/) 2419 { 2420 my ($K1, $K2) = ($1, $2); 2421 foreach ("C1", "C2") 2422 { 2423 if($K1 ne $_) 2424 { 2425 my $Name = $SymbolInfo{$ID}{"MnglName"}; 2426 $Name=~s/$K1$K2/$_$K2/; 2427 2428 if(not defined $Mangled_ID{$Name}) { 2429 cloneSymbol($ID, $Name); 2430 } 2431 } 2432 } 2433 } 2434 } 2435 2436 # add missed d-tors 2437 if($SymbolInfo{$ID}{"Destructor"}) 2438 { 2439 if($SymbolInfo{$ID}{"MnglName"}=~/(D[0-2])([EI]).+/) 2440 { 2441 my ($K1, $K2) = ($1, $2); 2442 foreach ("D0", "D1", "D2") 2443 { 2444 if($K1 ne $_) 2445 { 2446 my $Name = $SymbolInfo{$ID}{"MnglName"}; 2447 $Name=~s/$K1$K2/$_$K2/; 2448 2449 if(not defined $Mangled_ID{$Name}) { 2450 cloneSymbol($ID, $Name); 2451 } 2452 } 2453 } 2454 } 2455 } 2456 } 2457 2458 foreach my $ID (sort {int($a) <=> int($b)} keys(%SymbolInfo)) 2459 { 2460 my $Symbol = $SymbolInfo{$ID}{"MnglName"}; 2461 2462 if(not $Symbol) { 2463 $Symbol = $SymbolInfo{$ID}{"ShortName"}; 2464 } 2465 2466 if($LIB_LANG eq "C++") 2467 { 2468 if(not $SymbolInfo{$ID}{"MnglName"}) 2469 { 2470 if($SymbolInfo{$ID}{"Artificial"} 2471 or index($SymbolInfo{$ID}{"ShortName"}, "~")==0) 2472 { 2473 delete($SymbolInfo{$ID}); 2474 next; 2475 } 2476 } 2477 } 2478 2479 if($SymbolInfo{$ID}{"Class"} 2480 and not $SymbolInfo{$ID}{"Data"} 2481 and not $SymbolInfo{$ID}{"Constructor"} 2482 and not $SymbolInfo{$ID}{"Destructor"} 2483 and not $SymbolInfo{$ID}{"Virt"} 2484 and not $SymbolInfo{$ID}{"PureVirt"}) 2485 { 2486 if(not defined $SymbolInfo{$ID}{"Param"} 2487 or $SymbolInfo{$ID}{"Param"}{0}{"name"} ne "this") 2488 { 2489 $SymbolInfo{$ID}{"Static"} = 1; 2490 } 2491 } 2492 2493 if(not $SymbolInfo{$ID}{"Return"}) 2494 { # void 2495 if(not $SymbolInfo{$ID}{"Constructor"} 2496 and not $SymbolInfo{$ID}{"Destructor"}) 2497 { 2498 $SymbolInfo{$ID}{"Return"} = "1"; 2499 } 2500 } 2501 2502 if(defined $SymbolInfo{$ID}{"Source"} and defined $SymbolInfo{$ID}{"SourceLine"}) 2503 { 2504 if(not defined $SymbolInfo{$ID}{"Header"} and not defined $SymbolInfo{$ID}{"Line"}) 2505 { 2506 $SymbolInfo{$ID}{"Line"} = $SymbolInfo{$ID}{"SourceLine"}; 2507 delete($SymbolInfo{$ID}{"SourceLine"}); 2508 } 2509 } 2510 2511 my $S = selectSymbol($ID); 2512 2513 if($S==0) 2514 { 2515 if(defined $AllSymbols) 2516 { 2517 if($SymbolInfo{$ID}{"External"}) 2518 { 2519 $S = 1; 2520 } 2521 else 2522 { # local 2523 if(defined $DumpStatic) { 2524 $S = 1; 2525 } 2526 } 2527 } 2528 } 2529 2530 if($S==0) 2531 { 2532 delete($SymbolInfo{$ID}); 2533 next; 2534 } 2535 elsif(defined $PublicHeadersPath) 2536 { 2537 if(not selectPublic($Symbol, $ID) 2538 and (not defined $SymbolInfo{$ID}{"Alias"} or not selectPublic($SymbolInfo{$ID}{"Alias"}, $ID))) 2539 { 2540 delete($SymbolInfo{$ID}); 2541 next; 2542 } 2543 } 2544 elsif(defined $KernelExport) 2545 { 2546 if(not defined $KSymTab{$Symbol}) 2547 { 2548 delete($SymbolInfo{$ID}); 2549 next; 2550 } 2551 } 2552 2553 $SelectedSymbols{$ID} = $S; 2554 2555 delete($SymbolInfo{$ID}{"External"}); 2556 } 2557} 2558 2559sub warnPrivateType($$) 2560{ 2561 my ($Name, $Note) = @_; 2562 2563 if($Name=~/Private|Opaque/i) 2564 { # _GstClockPrivate 2565 # _Eo_Opaque 2566 return; 2567 } 2568 2569 if($Name=~/(\A| )_/i) 2570 { # _GstBufferList 2571 return; 2572 } 2573 2574 if($Name=~/_\Z/i) 2575 { # FT_RasterRec_ 2576 return; 2577 } 2578 2579 printMsg("WARNING", "Private data type \'".$Name."\' ($Note)"); 2580} 2581 2582sub warnPrivateSymbol($$) 2583{ 2584 my ($Name, $Note) = @_; 2585 printMsg("WARNING", "Private symbol \'".$Name."\' ($Note)"); 2586} 2587 2588sub selectPublicType($) 2589{ 2590 my $Tid = $_[0]; 2591 2592 if($TypeInfo{$Tid}{"Type"}!~/\A(Struct|Class|Union|Enum)\Z/) { 2593 return 1; 2594 } 2595 2596 my $TName = $TypeInfo{$Tid}{"Name"}; 2597 $TName=~s/\A(struct|class|union|enum) //g; 2598 2599 my $Header = getFilename($TypeInfo{$Tid}{"Header"}); 2600 2601 if($OBJ_LANG eq "C++" 2602 or index($TName, "anon-")==0) { 2603 return ($Header and defined $PublicHeader{$Header}); 2604 } 2605 2606 if($Header) 2607 { 2608 if(not defined $PublicHeader{$Header}) 2609 { 2610 if(not defined $TypeToHeader{$TName}) { 2611 return 0; 2612 } 2613 elsif($Header ne $TypeToHeader{$TName}) { 2614 return 0; 2615 } 2616 } 2617 elsif($MixedHeaders) 2618 { 2619 if(not defined $TypeToHeader{$TName}) 2620 { 2621 if(defined $Debug) { 2622 warnPrivateType($TypeInfo{$Tid}{"Name"}, "NOT_FOUND"); 2623 } 2624 return 0; 2625 } 2626 elsif($Header ne $TypeToHeader{$TName}) 2627 { 2628 if(defined $Debug) { 2629 warnPrivateType($TypeInfo{$Tid}{"Name"}, "OTHER_HEADER"); 2630 } 2631 return 0; 2632 } 2633 } 2634 } 2635 else 2636 { 2637 if(not defined $TypeToHeader{$TName}) 2638 { 2639 # if(defined $Debug) { 2640 # warnPrivateType($TypeInfo{$Tid}{"Name"}, "NO_HEADER"); 2641 # } 2642 return 0; 2643 } 2644 } 2645 2646 return 1; 2647} 2648 2649sub selectPublic($$) 2650{ 2651 my ($Symbol, $ID) = @_; 2652 2653 my $Header = getFilename($SymbolInfo{$ID}{"Header"}); 2654 2655 if($OBJ_LANG eq "C++") { 2656 return ($Header and defined $PublicHeader{$Header}); 2657 } 2658 2659 if($Header) 2660 { 2661 if(not defined $PublicHeader{$Header}) 2662 { 2663 if(not defined $SymbolToHeader{$Symbol}) { 2664 return 0; 2665 } 2666 elsif($Header ne $SymbolToHeader{$Symbol} 2667 and not defined $SymbolInfo{$ID}{"Alias"}) 2668 { 2669 if(not $ReimplementStd) { 2670 return 0; 2671 } 2672 } 2673 } 2674 elsif($MixedHeaders) 2675 { 2676 if(not defined $SymbolToHeader{$Symbol}) 2677 { 2678 if(defined $Debug) { 2679 warnPrivateSymbol($Symbol, "NOT_FOUND"); 2680 } 2681 return 0; 2682 } 2683 elsif($Header ne $SymbolToHeader{$Symbol} 2684 and not defined $SymbolInfo{$ID}{"Alias"}) 2685 { 2686 if(not $ReimplementStd) 2687 { 2688 if(defined $Debug) { 2689 warnPrivateSymbol($Symbol, "OTHER_HEADER"); 2690 } 2691 return 0; 2692 } 2693 } 2694 } 2695 } 2696 else 2697 { 2698 if(not defined $SymbolToHeader{$Symbol}) 2699 { 2700 # if(defined $Debug) { 2701 # warnPrivateSymbol($Symbol, "NO_HEADER"); 2702 # } 2703 return 0; 2704 } 2705 } 2706 2707 return 1; 2708} 2709 2710sub cloneSymbol($$) 2711{ 2712 my ($ID, $Symbol) = @_; 2713 2714 my $nID = undef; 2715 if(not defined $SymbolInfo{$ID + 1}) { 2716 $nID = $ID + 1; 2717 } 2718 else { 2719 $nID = ++$GLOBAL_ID; 2720 } 2721 foreach my $Attr (keys(%{$SymbolInfo{$ID}})) 2722 { 2723 if(ref($SymbolInfo{$ID}{$Attr}) eq "HASH") { 2724 $SymbolInfo{$nID}{$Attr} = dclone($SymbolInfo{$ID}{$Attr}); 2725 } 2726 else { 2727 $SymbolInfo{$nID}{$Attr} = $SymbolInfo{$ID}{$Attr}; 2728 } 2729 } 2730 $SymbolInfo{$nID}{"MnglName"} = $Symbol; 2731} 2732 2733sub selectSymbol($) 2734{ 2735 my $ID = $_[0]; 2736 2737 my $MnglName = $SymbolInfo{$ID}{"MnglName"}; 2738 2739 if(not $MnglName) { 2740 $MnglName = $SymbolInfo{$ID}{"ShortName"}; 2741 } 2742 2743 if($SymbolsListPath 2744 and not $SymbolsList{$MnglName}) 2745 { 2746 next; 2747 } 2748 2749 my $Exp = 0; 2750 2751 if($Library_Symbol{$TargetName}{$MnglName} 2752 or $Library_Symbol{$TargetName}{$SymVer{$MnglName}}) 2753 { 2754 $Exp = 1; 2755 } 2756 2757 if(my $Alias = $SymbolInfo{$ID}{"Alias"}) 2758 { 2759 if($Library_Symbol{$TargetName}{$Alias} 2760 or $Library_Symbol{$TargetName}{$SymVer{$Alias}}) 2761 { 2762 $Exp = 1; 2763 } 2764 } 2765 2766 if(not $Exp) 2767 { 2768 if(defined $Library_UndefSymbol{$TargetName}{$MnglName} 2769 or defined $Library_UndefSymbol{$TargetName}{$SymVer{$MnglName}}) 2770 { 2771 return 0; 2772 } 2773 2774 if($SymbolInfo{$ID}{"Data"} 2775 or $SymbolInfo{$ID}{"InLine"} 2776 or $SymbolInfo{$ID}{"PureVirt"}) 2777 { 2778 if(not $SymbolInfo{$ID}{"External"}) 2779 { # skip static 2780 return 0; 2781 } 2782 2783 if(defined $BinOnly) 2784 { # data, inline, pure 2785 return 0; 2786 } 2787 elsif(not defined $SymbolInfo{$ID}{"Header"}) 2788 { # defined in source files 2789 return 0; 2790 } 2791 else 2792 { 2793 return 2; 2794 } 2795 } 2796 else 2797 { 2798 return 0; 2799 } 2800 } 2801 2802 return 1; 2803} 2804 2805sub formatName($$) 2806{ # type name correction 2807 if(defined $Cache{"formatName"}{$_[1]}{$_[0]}) { 2808 return $Cache{"formatName"}{$_[1]}{$_[0]}; 2809 } 2810 2811 my $N = $_[0]; 2812 2813 if($_[1] ne "S") 2814 { 2815 $N=~s/\A[ ]+//g; 2816 $N=~s/[ ]+\Z//g; 2817 $N=~s/[ ]{2,}/ /g; 2818 } 2819 2820 $N=~s/[ ]*(\W)[ ]*/$1/g; # std::basic_string<char> const 2821 2822 $N=~s/\b(const|volatile) ([\w\:]+)([\*&,>]|\Z)/$2 $1$3/g; # "const void" to "void const" 2823 2824 $N=~s/\bvolatile const\b/const volatile/g; 2825 2826 $N=~s/\b(long long|short|long) unsigned\b/unsigned $1/g; 2827 $N=~s/\b(short|long) int\b/$1/g; 2828 2829 $N=~s/([\)\]])(const|volatile)\b/$1 $2/g; 2830 2831 while($N=~s/>>/> >/g) {}; 2832 2833 if($_[1] eq "S") 2834 { 2835 if(index($N, "operator")!=-1) { 2836 $N=~s/\b(operator[ ]*)> >/$1>>/; 2837 } 2838 } 2839 2840 $N=~s/,/, /g; 2841 2842 return ($Cache{"formatName"}{$_[1]}{$_[0]} = $N); 2843} 2844 2845sub separate_Params($) 2846{ 2847 my $Str = $_[0]; 2848 my @Parts = (); 2849 my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 ); 2850 my $Part = 0; 2851 foreach my $Pos (0 .. length($Str) - 1) 2852 { 2853 my $S = substr($Str, $Pos, 1); 2854 if(defined $B{$S}) { 2855 $B{$S} += 1; 2856 } 2857 if($S eq "," and 2858 $B{"("}==$B{")"} and $B{"<"}==$B{">"}) { 2859 $Part += 1; 2860 } 2861 else { 2862 $Parts[$Part] .= $S; 2863 } 2864 } 2865 # remove spaces 2866 foreach (@Parts) 2867 { 2868 s/\A //g; 2869 s/ \Z//g; 2870 } 2871 return @Parts; 2872} 2873 2874sub init_FuncType($$$) 2875{ 2876 my ($TInfo, $FTid, $Type) = @_; 2877 2878 $TInfo->{"Type"} = $Type; 2879 2880 if($TInfo->{"Return"} = $DWARF_Info{$FTid}{"type"}) { 2881 getTypeInfo($TInfo->{"Return"}); 2882 } 2883 else 2884 { # void 2885 $TInfo->{"Return"} = "1"; 2886 } 2887 delete($TInfo->{"BaseType"}); 2888 2889 my @Prms = (); 2890 my $PPos = 0; 2891 foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$FuncParam{$FTid}})) 2892 { 2893 my $ParamId = $FuncParam{$FTid}{$Pos}; 2894 my %PInfo = %{$DWARF_Info{$ParamId}}; 2895 2896 if(defined $PInfo{"artificial"}) 2897 { # this 2898 next; 2899 } 2900 2901 if(my $PTypeId = $PInfo{"type"}) 2902 { 2903 $TInfo->{"Param"}{$PPos}{"type"} = $PTypeId; 2904 getTypeInfo($PTypeId); 2905 push(@Prms, $TypeInfo{$PTypeId}{"Name"}); 2906 } 2907 2908 $PPos += 1; 2909 } 2910 2911 $TInfo->{"Name"} = $TypeInfo{$TInfo->{"Return"}}{"Name"}; 2912 if($Type eq "FuncPtr") { 2913 $TInfo->{"Name"} .= "(*)"; 2914 } 2915 else { 2916 $TInfo->{"Name"} .= "()"; 2917 } 2918 $TInfo->{"Name"} .= "(".join(",", @Prms).")"; 2919} 2920 2921sub getShortName($) 2922{ 2923 my $Name = $_[0]; 2924 2925 if(my $C = find_center($Name, "<")) 2926 { 2927 return substr($Name, 0, $C); 2928 } 2929 2930 return $Name; 2931} 2932 2933sub get_TParams($) 2934{ 2935 my $ID = $_[0]; 2936 2937 my @TParams = (); 2938 2939 foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$TmplParam{$ID}})) 2940 { 2941 my $TTid = $TmplParam{$ID}{$Pos}{"type"}; 2942 my $Val = undef; 2943 my $Key = undef; 2944 2945 if(defined $TmplParam{$ID}{$Pos}{"value"}) { 2946 $Val = $TmplParam{$ID}{$Pos}{"value"}; 2947 } 2948 2949 if(defined $TmplParam{$ID}{$Pos}{"key"}) { 2950 $Key = $TmplParam{$ID}{$Pos}{"key"}; 2951 } 2952 2953 if($Pos>0) 2954 { 2955 if(defined $TmplParam{$ID}{$Pos}{"default"}) 2956 { 2957 if($Key=~/\A(_Alloc|_Traits|_Compare)\Z/) 2958 { 2959 next; 2960 } 2961 } 2962 } 2963 2964 getTypeInfo($TTid); 2965 2966 my $TTName = $TypeInfo{$TTid}{"Name"}; 2967 2968 if(defined $Val) 2969 { 2970 if($TTName eq "bool") 2971 { 2972 if($Val eq "1") { 2973 push(@TParams, "true"); 2974 } 2975 elsif($Val eq "0") { 2976 push(@TParams, "false"); 2977 } 2978 } 2979 else 2980 { 2981 if($Val=~/\A\d+\Z/) 2982 { 2983 if(my $S = $ConstSuffix{$TTName}) 2984 { 2985 $Val .= $S; 2986 } 2987 } 2988 push(@TParams, $Val); 2989 } 2990 } 2991 else 2992 { 2993 push(@TParams, simpleName($TTName)); 2994 } 2995 } 2996 2997 return @TParams; 2998} 2999 3000sub parse_TParams($) 3001{ 3002 my $Name = $_[0]; 3003 if(my $Cent = find_center($Name, "<")) 3004 { 3005 my $TParams = substr($Name, $Cent); 3006 $TParams=~s/\A<|>\Z//g; 3007 3008 $TParams = simpleName($TParams); 3009 3010 my $Short = substr($Name, 0, $Cent); 3011 3012 my @Params = separate_Params($TParams); 3013 foreach my $Pos (0 .. $#Params) 3014 { 3015 my $Param = $Params[$Pos]; 3016 if($Param=~/\A(.+>)(.*?)\Z/) 3017 { 3018 my ($Tm, $Suf) = ($1, $2); 3019 my ($Sh, @Prm) = parse_TParams($Tm); 3020 $Param = $Sh."<".join(", ", @Prm).">".$Suf; 3021 } 3022 $Params[$Pos] = formatName($Param, "T"); 3023 } 3024 3025 @Params = shortTParams($Short, @Params); 3026 3027 return ($Short, @Params); 3028 } 3029 3030 return $Name; # error 3031} 3032 3033sub shortTParams(@) 3034{ 3035 my $Short = shift(@_); 3036 my @Params = @_; 3037 3038 # default arguments 3039 if($Short eq "std::vector") 3040 { 3041 if($#Params==1) 3042 { 3043 if($Params[1] eq "std::allocator<".$Params[0].">") 3044 { # std::vector<T, std::allocator<T> > 3045 splice(@Params, 1, 1); 3046 } 3047 } 3048 } 3049 elsif($Short eq "std::set") 3050 { 3051 if($#Params==2) 3052 { 3053 if($Params[1] eq "std::less<".$Params[0].">" 3054 and $Params[2] eq "std::allocator<".$Params[0].">") 3055 { # std::set<T, std::less<T>, std::allocator<T> > 3056 splice(@Params, 1, 2); 3057 } 3058 } 3059 } 3060 elsif($Short eq "std::basic_string") 3061 { 3062 if($#Params==2) 3063 { 3064 if($Params[1] eq "std::char_traits<".$Params[0].">" 3065 and $Params[2] eq "std::allocator<".$Params[0].">") 3066 { # std::basic_string<T, std::char_traits<T>, std::allocator<T> > 3067 splice(@Params, 1, 2); 3068 } 3069 } 3070 } 3071 3072 return @Params; 3073} 3074 3075sub getTypeInfo($) 3076{ 3077 my $ID = $_[0]; 3078 my $Kind = $DWARF_Info{$ID}{"Kind"}; 3079 3080 if(defined $Cache{"getTypeInfo"}{$ID}) { 3081 return; 3082 } 3083 3084 if(my $N = $NameSpace{$ID}) 3085 { 3086 if($DWARF_Info{$N}{"Kind"} eq "subprogram") 3087 { # local code 3088 # template instances are declared in the subprogram (constructor) 3089 my $Tmpl = 0; 3090 if(my $ObjP = $DWARF_Info{$N}{"object_pointer"}) 3091 { 3092 while($DWARF_Info{$ObjP}{"type"}) { 3093 $ObjP = $DWARF_Info{$ObjP}{"type"}; 3094 } 3095 my $CName = $DWARF_Info{$ObjP}{"name"}; 3096 $CName=~s/<.*//g; 3097 if($CName eq $DWARF_Info{$N}{"name"}) { 3098 $Tmpl = 1; 3099 } 3100 } 3101 if(not $Tmpl) 3102 { # local types 3103 $LocalType{$ID} = 1; 3104 } 3105 } 3106 elsif($DWARF_Info{$N}{"Kind"} eq "lexical_block") 3107 { # local code 3108 return; 3109 } 3110 } 3111 3112 $Cache{"getTypeInfo"}{$ID} = 1; 3113 3114 my %TInfo = (); 3115 3116 $TInfo{"Type"} = $TypeType{$Kind}; 3117 3118 if(not $TInfo{"Type"}) 3119 { 3120 if($DWARF_Info{$ID}{"Kind"} eq "subroutine_type") { 3121 $TInfo{"Type"} = "Func"; 3122 } 3123 } 3124 3125 my $RealType = $TInfo{"Type"}; 3126 3127 if(defined $ClassMethods{$ID}) 3128 { 3129 if($TInfo{"Type"} eq "Struct") { 3130 $RealType = "Class"; 3131 } 3132 } 3133 3134 if($TInfo{"Type"} ne "Enum" 3135 and my $BaseType = $DWARF_Info{$ID}{"type"}) 3136 { 3137 $TInfo{"BaseType"} = "$BaseType"; 3138 3139 if(defined $TypeType{$DWARF_Info{$BaseType}{"Kind"}}) 3140 { 3141 getTypeInfo($TInfo{"BaseType"}); 3142 3143 if(not defined $TypeInfo{$TInfo{"BaseType"}} 3144 or not $TypeInfo{$TInfo{"BaseType"}}{"Name"}) 3145 { # local code 3146 delete($TypeInfo{$ID}); 3147 return; 3148 } 3149 } 3150 } 3151 3152 if($RealType eq "Class") { 3153 $TInfo{"Copied"} = 1; # will be changed in getSymbolInfo() 3154 } 3155 3156 if(defined $TypeMember{$ID}) 3157 { 3158 my $Unnamed = 0; 3159 foreach my $Pos (sort {int($a) <=> int($b)} keys(%{$TypeMember{$ID}})) 3160 { 3161 my $MemId = $TypeMember{$ID}{$Pos}; 3162 my %MInfo = %{$DWARF_Info{$MemId}}; 3163 3164 if(my $Name = $MInfo{"name"}) 3165 { 3166 if(index($Name, "_vptr.")==0) 3167 { # v-table pointer 3168 $Name="_vptr"; 3169 } 3170 $TInfo{"Memb"}{$Pos}{"name"} = $Name; 3171 } 3172 else 3173 { 3174 $TInfo{"Memb"}{$Pos}{"name"} = "unnamed".$Unnamed; 3175 $Unnamed += 1; 3176 } 3177 if($TInfo{"Type"} eq "Enum") { 3178 $TInfo{"Memb"}{$Pos}{"value"} = $MInfo{"const_value"}; 3179 } 3180 else 3181 { 3182 $TInfo{"Memb"}{$Pos}{"type"} = $MInfo{"type"}; 3183 if(my $Access = $MInfo{"accessibility"}) 3184 { 3185 if($Access ne "public") 3186 { # NOTE: default access of members in the ABI dump is "public" 3187 $TInfo{"Memb"}{$Pos}{"access"} = $Access; 3188 } 3189 } 3190 else 3191 { 3192 if($DWARF_Info{$ID}{"Kind"} eq "class_type") 3193 { # NOTE: default access of class members in the debug info is "private" 3194 $TInfo{"Memb"}{$Pos}{"access"} = "private"; 3195 } 3196 else 3197 { 3198 # NOTE: default access of struct members in the debug info is "public" 3199 } 3200 } 3201 if($TInfo{"Type"} eq "Union") { 3202 $TInfo{"Memb"}{$Pos}{"offset"} = "0"; 3203 } 3204 elsif(defined $MInfo{"data_member_location"}) { 3205 $TInfo{"Memb"}{$Pos}{"offset"} = $MInfo{"data_member_location"}; 3206 } 3207 } 3208 3209 if((my $BitSize = $MInfo{"bit_size"}) ne "") { 3210 $TInfo{"Memb"}{$Pos}{"bitfield"} = $BitSize; 3211 } 3212 } 3213 } 3214 3215 my $NS = $NameSpace{$ID}; 3216 if(not $NS) 3217 { 3218 if(my $Sp = $DWARF_Info{$ID}{"specification"}) { 3219 $NS = $NameSpace{$Sp}; 3220 } 3221 } 3222 3223 if($NS and $DWARF_Info{$NS}{"Kind"}=~/\A(class_type|structure_type)\Z/) 3224 { # member class 3225 if(my $Access = $DWARF_Info{$ID}{"accessibility"}) 3226 { 3227 if($Access ne "public") 3228 { # NOTE: default access of member classes in the ABI dump is "public" 3229 $TInfo{ucfirst($Access)} = 1; 3230 } 3231 } 3232 else 3233 { 3234 if($DWARF_Info{$NS}{"Kind"} eq "class_type") 3235 { 3236 # NOTE: default access of member classes in the debug info is "private" 3237 $TInfo{"Private"} = 1; 3238 } 3239 else 3240 { 3241 # NOTE: default access to struct member classes in the debug info is "public" 3242 } 3243 } 3244 } 3245 else 3246 { 3247 if(my $Access = $DWARF_Info{$ID}{"accessibility"}) 3248 { 3249 if($Access ne "public") 3250 { # NOTE: default access of classes in the ABI dump is "public" 3251 $TInfo{ucfirst($Access)} = 1; 3252 } 3253 } 3254 } 3255 3256 if(my $Size = $DWARF_Info{$ID}{"byte_size"}) { 3257 $TInfo{"Size"} = $Size; 3258 } 3259 3260 setSource(\%TInfo, $ID); 3261 3262 if(not $DWARF_Info{$ID}{"name"} 3263 and my $Spec = $DWARF_Info{$ID}{"specification"}) { 3264 $DWARF_Info{$ID}{"name"} = $DWARF_Info{$Spec}{"name"}; 3265 } 3266 3267 if($NS) 3268 { 3269 if($DWARF_Info{$NS}{"Kind"} eq "namespace") 3270 { 3271 if(my $NS_F = completeNS($ID)) 3272 { 3273 $TInfo{"NameSpace"} = $NS_F; 3274 } 3275 } 3276 elsif($DWARF_Info{$NS}{"Kind"} eq "class_type" 3277 or $DWARF_Info{$NS}{"Kind"} eq "structure_type") 3278 { # class 3279 getTypeInfo($NS); 3280 3281 if(my $Sp = $SpecElem{$NS}) { 3282 getTypeInfo($Sp); 3283 } 3284 3285 if($TypeInfo{$NS}{"Name"}) 3286 { 3287 $TInfo{"NameSpace"} = $TypeInfo{$NS}{"Name"}; 3288 $TInfo{"NameSpace"}=~s/\Astruct //; 3289 } 3290 } 3291 } 3292 3293 if(my $Name = $DWARF_Info{$ID}{"name"}) 3294 { 3295 $TInfo{"Name"} = $Name; 3296 3297 if($TInfo{"NameSpace"}) { 3298 $TInfo{"Name"} = $TInfo{"NameSpace"}."::".$TInfo{"Name"}; 3299 } 3300 3301 if($TInfo{"Type"}=~/\A(Struct|Enum|Union)\Z/) { 3302 $TInfo{"Name"} = lc($TInfo{"Type"})." ".$TInfo{"Name"}; 3303 } 3304 } 3305 3306 if($TInfo{"Type"} eq "Pointer") 3307 { 3308 if($DWARF_Info{$TInfo{"BaseType"}}{"Kind"} eq "subroutine_type") 3309 { 3310 init_FuncType(\%TInfo, $TInfo{"BaseType"}, "FuncPtr"); 3311 } 3312 } 3313 elsif($TInfo{"Type"}=~/Typedef|Const|Volatile/) 3314 { 3315 if($DWARF_Info{$TInfo{"BaseType"}}{"Kind"} eq "subroutine_type") 3316 { 3317 getTypeInfo($TInfo{"BaseType"}); 3318 } 3319 } 3320 elsif($TInfo{"Type"} eq "Func") 3321 { 3322 init_FuncType(\%TInfo, $ID, "Func"); 3323 } 3324 elsif($TInfo{"Type"} eq "Struct") 3325 { 3326 if(not $TInfo{"Name"} 3327 and my $Sb = $DWARF_Info{$ID}{"sibling"}) 3328 { 3329 if($DWARF_Info{$Sb}{"Kind"} eq "subroutine_type" 3330 and defined $TInfo{"Memb"} 3331 and $TInfo{"Memb"}{0}{"name"} eq "__pfn") 3332 { # __pfn and __delta 3333 $TInfo{"Type"} = "MethodPtr"; 3334 3335 my @Prms = (); 3336 my $PPos = 0; 3337 foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$FuncParam{$Sb}})) 3338 { 3339 my $ParamId = $FuncParam{$Sb}{$Pos}; 3340 my %PInfo = %{$DWARF_Info{$ParamId}}; 3341 3342 if(defined $PInfo{"artificial"}) 3343 { # this 3344 next; 3345 } 3346 3347 if(my $PTypeId = $PInfo{"type"}) 3348 { 3349 $TInfo{"Param"}{$PPos}{"type"} = $PTypeId; 3350 getTypeInfo($PTypeId); 3351 push(@Prms, $TypeInfo{$PTypeId}{"Name"}); 3352 } 3353 3354 $PPos += 1; 3355 } 3356 3357 if(my $ClassId = $DWARF_Info{$Sb}{"object_pointer"}) 3358 { 3359 while($DWARF_Info{$ClassId}{"type"}) { 3360 $ClassId = $DWARF_Info{$ClassId}{"type"}; 3361 } 3362 $TInfo{"Class"} = $ClassId; 3363 getTypeInfo($TInfo{"Class"}); 3364 } 3365 3366 if($TInfo{"Return"} = $DWARF_Info{$Sb}{"type"}) { 3367 getTypeInfo($TInfo{"Return"}); 3368 } 3369 else 3370 { # void 3371 $TInfo{"Return"} = "1"; 3372 } 3373 3374 $TInfo{"Name"} = $TypeInfo{$TInfo{"Return"}}{"Name"}; 3375 $TInfo{"Name"} .= "(".$TypeInfo{$TInfo{"Class"}}{"Name"}."::*)"; 3376 $TInfo{"Name"} .= "(".join(",", @Prms).")"; 3377 } 3378 } 3379 } 3380 elsif($TInfo{"Type"} eq "FieldPtr") 3381 { 3382 $TInfo{"Return"} = $TInfo{"BaseType"}; 3383 delete($TInfo{"BaseType"}); 3384 3385 if(my $Class = $DWARF_Info{$ID}{"containing_type"}) 3386 { 3387 $TInfo{"Class"} = $Class; 3388 getTypeInfo($TInfo{"Class"}); 3389 3390 $TInfo{"Name"} = $TypeInfo{$TInfo{"Return"}}{"Name"}."(".$TypeInfo{$TInfo{"Class"}}{"Name"}."::*)"; 3391 } 3392 3393 $TInfo{"Size"} = $SYS_WORD; 3394 } 3395 elsif($TInfo{"Type"} eq "String") 3396 { 3397 $TInfo{"Type"} = "Pointer"; 3398 $TInfo{"Name"} = "char*"; 3399 $TInfo{"BaseType"} = $TName_Tid{"Intrinsic"}{"char"}; 3400 } 3401 3402 foreach my $Pos (sort {int($a) <=> int($b)} keys(%{$Inheritance{$ID}})) 3403 { 3404 if(my $BaseId = $Inheritance{$ID}{$Pos}{"id"}) 3405 { 3406 if(my $E = $SpecElem{$BaseId}) { 3407 $BaseId = $E; 3408 } 3409 3410 $TInfo{"Base"}{$BaseId}{"pos"} = "$Pos"; 3411 if(my $Access = $Inheritance{$ID}{$Pos}{"access"}) { 3412 $TInfo{"Base"}{$BaseId}{"access"} = $Access; 3413 } 3414 if($Inheritance{$ID}{$Pos}{"virtual"}) { 3415 $TInfo{"Base"}{$BaseId}{"virtual"} = 1; 3416 } 3417 3418 $ClassChild{$BaseId}{$ID} = 1; 3419 } 3420 } 3421 3422 if(not $TInfo{"BaseType"}) 3423 { 3424 if($TInfo{"Type"} eq "Pointer") 3425 { 3426 $TInfo{"Name"} = "void*"; 3427 $TInfo{"BaseType"} = "1"; 3428 } 3429 elsif($TInfo{"Type"} eq "Const") 3430 { 3431 $TInfo{"Name"} = "const void"; 3432 $TInfo{"BaseType"} = "1"; 3433 } 3434 elsif($TInfo{"Type"} eq "Volatile") 3435 { 3436 $TInfo{"Name"} = "volatile void"; 3437 $TInfo{"BaseType"} = "1"; 3438 } 3439 elsif($TInfo{"Type"} eq "Typedef") 3440 { 3441 $TInfo{"BaseType"} = "1"; 3442 } 3443 } 3444 3445 if(not $TInfo{"Name"} 3446 and $TInfo{"Type"} ne "Enum") 3447 { 3448 my $ID_ = $ID; 3449 my $BaseID = undef; 3450 my $Name = ""; 3451 3452 while($BaseID = $DWARF_Info{$ID_}{"type"}) 3453 { 3454 my $Kind = $DWARF_Info{$ID_}{"Kind"}; 3455 if(my $Q = $Qual{$TypeType{$Kind}}) 3456 { 3457 $Name = $Q.$Name; 3458 if($Q=~/\A\w/) { 3459 $Name = " ".$Name; 3460 } 3461 } 3462 if(my $BName = $TypeInfo{$BaseID}{"Name"}) 3463 { 3464 $Name = $BName.$Name; 3465 last; 3466 } 3467 elsif(my $BName2 = $DWARF_Info{$BaseID}{"name"}) 3468 { 3469 $Name = $BName2.$Name; 3470 } 3471 $ID_ = $BaseID; 3472 } 3473 3474 if($Name) { 3475 $TInfo{"Name"} = $Name; 3476 } 3477 3478 if($TInfo{"Type"} eq "Array") 3479 { 3480 if(my $Count = $ArrayCount{$ID}) 3481 { 3482 $TInfo{"Name"} .= "[".$Count."]"; 3483 if(my $BType = $TInfo{"BaseType"}) 3484 { 3485 if(my $BSize = $TypeInfo{$BType}{"Size"}) 3486 { 3487 if(my $Size = $Count*$BSize) 3488 { 3489 $TInfo{"Size"} = "$Size"; 3490 } 3491 } 3492 } 3493 } 3494 else 3495 { 3496 $TInfo{"Name"} .= "[]"; 3497 $TInfo{"Size"} = $SYS_WORD; 3498 } 3499 } 3500 elsif($TInfo{"Type"} eq "Pointer") 3501 { 3502 if(my $BType = $TInfo{"BaseType"}) 3503 { 3504 if($TypeInfo{$BType}{"Type"}=~/MethodPtr|FuncPtr/) 3505 { # void(GTestSuite::**)() 3506 # int(**)(...) 3507 if($TInfo{"Name"}=~s/\*\Z//) { 3508 $TInfo{"Name"}=~s/\*(\))/\*\*$1/; 3509 } 3510 } 3511 } 3512 } 3513 } 3514 3515 if(my $Bid = $TInfo{"BaseType"}) 3516 { 3517 if(not $TInfo{"Size"} 3518 and $TypeInfo{$Bid}{"Size"}) { 3519 $TInfo{"Size"} = $TypeInfo{$Bid}{"Size"}; 3520 } 3521 } 3522 if($TInfo{"Name"}) { 3523 $TInfo{"Name"} = formatName($TInfo{"Name"}, "T"); # simpleName() 3524 } 3525 3526 if($TInfo{"Name"}=~/>\Z/) 3527 { 3528 my ($Short, @TParams) = (); 3529 3530 if(defined $TmplParam{$ID}) 3531 { 3532 $Short = getShortName($TInfo{"Name"}); 3533 @TParams = get_TParams($ID); 3534 @TParams = shortTParams($Short, @TParams); 3535 } 3536 else { 3537 ($Short, @TParams) = parse_TParams($TInfo{"Name"}); 3538 } 3539 3540 if(@TParams) 3541 { 3542 delete($TInfo{"TParam"}); 3543 3544 foreach my $Pos (0 .. $#TParams) { 3545 $TInfo{"TParam"}{$Pos}{"name"} = $TParams[$Pos]; 3546 } 3547 3548 $TInfo{"Name"} = formatName($Short."<".join(", ", @TParams).">", "T"); 3549 } 3550 } 3551 3552 if(not $TInfo{"Name"}) 3553 { 3554 if($TInfo{"Type"}=~/\A(Class|Struct|Enum|Union)\Z/) 3555 { 3556 if($TInfo{"Header"}) { 3557 $TInfo{"Name"} = "anon-".lc($TInfo{"Type"})."-".$TInfo{"Header"}."-".$TInfo{"Line"}; 3558 } 3559 elsif($TInfo{"Source"}) { 3560 $TInfo{"Name"} = "anon-".lc($TInfo{"Type"})."-".$TInfo{"Source"}."-".$TInfo{"SourceLine"}; 3561 } 3562 else 3563 { 3564 if(not defined $TypeMember{$ID}) 3565 { 3566 if(not defined $ANON_TYPE_WARN{$TInfo{"Type"}}) 3567 { 3568 printMsg("WARNING", "a \"".$TInfo{"Type"}."\" type with no attributes detected in the DWARF dump ($ID)"); 3569 $ANON_TYPE_WARN{$TInfo{"Type"}} = 1; 3570 } 3571 $TInfo{"Name"} = "anon-".lc($TInfo{"Type"}); 3572 } 3573 } 3574 3575 if($TInfo{"Name"} and $TInfo{"NameSpace"}) { 3576 $TInfo{"Name"} = $TInfo{"NameSpace"}."::".$TInfo{"Name"}; 3577 } 3578 } 3579 } 3580 3581 if($TInfo{"Name"}) 3582 { 3583 if(not defined $TName_Tid{$TInfo{"Type"}}{$TInfo{"Name"}} 3584 or ($ID>0 and $ID<$TName_Tid{$TInfo{"Type"}}{$TInfo{"Name"}}) 3585 or ($ID>0 and $TName_Tid{$TInfo{"Type"}}{$TInfo{"Name"}}<0)) 3586 { 3587 $TName_Tid{$TInfo{"Type"}}{$TInfo{"Name"}} = "$ID"; 3588 } 3589 $TName_Tids{$TInfo{"Type"}}{$TInfo{"Name"}}{$ID} = 1; 3590 } 3591 3592 if(defined $TInfo{"Source"}) 3593 { 3594 if(not defined $TInfo{"Header"}) 3595 { 3596 $TInfo{"Line"} = $TInfo{"SourceLine"}; 3597 delete($TInfo{"SourceLine"}); 3598 } 3599 } 3600 3601 foreach my $Attr (keys(%TInfo)) { 3602 $TypeInfo{$ID}{$Attr} = $TInfo{$Attr}; 3603 } 3604 3605 if(my $BASE_ID = $DWARF_Info{$ID}{"specification"}) 3606 { 3607 foreach my $Attr (keys(%{$TypeInfo{$BASE_ID}})) 3608 { 3609 if($Attr ne "Type") { 3610 $TypeInfo{$ID}{$Attr} = $TypeInfo{$BASE_ID}{$Attr}; 3611 } 3612 } 3613 3614 foreach my $Attr (keys(%{$TypeInfo{$ID}})) { 3615 $TypeInfo{$BASE_ID}{$Attr} = $TypeInfo{$ID}{$Attr}; 3616 } 3617 3618 $TypeSpec{$ID} = $BASE_ID; 3619 } 3620} 3621 3622sub setSource($$) 3623{ 3624 my ($R, $ID) = @_; 3625 3626 my $File = $DWARF_Info{$ID}{"decl_file"}; 3627 my $Line = $DWARF_Info{$ID}{"decl_line"}; 3628 3629 my $Unit = $DWARF_Info{$ID}{"Unit"}; 3630 3631 if(defined $File) 3632 { 3633 my $Name = undef; 3634 3635 if($ID>=0) { 3636 $Name = $SourceFile{$Unit}{$File}; 3637 } 3638 else 3639 { # imported 3640 $Name = $SourceFile_Alt{0}{$File}; 3641 } 3642 3643 if($Name=~/\.($HEADER_EXT)\Z/) 3644 { # header 3645 $R->{"Header"} = $Name; 3646 if(defined $Line) { 3647 $R->{"Line"} = $Line; 3648 } 3649 } 3650 elsif(index($Name, "<built-in>")==-1) 3651 { # source 3652 $R->{"Source"} = $Name; 3653 if(defined $Line) { 3654 $R->{"SourceLine"} = $Line; 3655 } 3656 } 3657 } 3658} 3659 3660sub skipSymbol($) 3661{ 3662 if($SkipCxx and not $STDCXX_TARGET) 3663 { 3664 if($_[0]=~/\A(_ZS|_ZNS|_ZNKS|_ZN9__gnu_cxx|_ZNK9__gnu_cxx|_ZTIS|_ZTSS|_Zd|_Zn)/) 3665 { # stdc++ symbols 3666 return 1; 3667 } 3668 } 3669 return 0; 3670} 3671 3672sub find_center($$) 3673{ 3674 my ($Name, $Target) = @_; 3675 my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 ); 3676 foreach my $Pos (0 .. length($Name)-1) 3677 { 3678 my $S = substr($Name, length($Name)-1-$Pos, 1); 3679 if(defined $B{$S}) { 3680 $B{$S}+=1; 3681 } 3682 if($S eq $Target) 3683 { 3684 if($B{"("}==$B{")"} 3685 and $B{"<"}==$B{">"}) { 3686 return length($Name)-1-$Pos; 3687 } 3688 } 3689 } 3690 return 0; 3691} 3692 3693sub isExternal($) 3694{ 3695 my $ID = $_[0]; 3696 3697 if($DWARF_Info{$ID}{"external"}) { 3698 return 1; 3699 } 3700 elsif(my $Spec = $DWARF_Info{$ID}{"specification"}) 3701 { 3702 if($DWARF_Info{$Spec}{"external"}) { 3703 return 1; 3704 } 3705 } 3706 3707 return 0; 3708} 3709 3710sub symByAddr($) 3711{ 3712 my $Loc = $_[0]; 3713 3714 my ($Addr, $Sect) = ("", ""); 3715 if($Loc=~/\+(.+)/) 3716 { 3717 $Addr = $1; 3718 if(not $Addr=~s/\A0x//) 3719 { 3720 $Addr=~s/\A00//; 3721 } 3722 } 3723 if($Loc=~/([\w\.]+)\+/) { 3724 $Sect = $1; 3725 } 3726 3727 if($Addr ne "") 3728 { 3729 foreach ($Sect, "") 3730 { 3731 if(defined $SymbolTable{$_}{$Addr}) 3732 { 3733 if(my @Symbols = sort keys(%{$SymbolTable{$_}{$Addr}})) { 3734 return $Symbols[0]; 3735 } 3736 } 3737 } 3738 } 3739 3740 return undef; 3741} 3742 3743sub get_Mangled($) 3744{ 3745 my $ID = $_[0]; 3746 3747 if(not defined $AddrToName) 3748 { 3749 if(my $Link = $DWARF_Info{$ID}{"linkage_name"}) 3750 { 3751 return $Link; 3752 } 3753 } 3754 3755 if(my $Low_Pc = $DWARF_Info{$ID}{"low_pc"}) 3756 { 3757 if($Low_Pc=~/<([\w\@\.]+)>/) { 3758 return $1; 3759 } 3760 else 3761 { 3762 if(my $Symbol = symByAddr($Low_Pc)) { 3763 return $Symbol; 3764 } 3765 } 3766 } 3767 3768 if(my $Loc = $DWARF_Info{$ID}{"location"}) 3769 { 3770 if($Loc=~/<([\w\@\.]+)>/) { 3771 return $1; 3772 } 3773 else 3774 { 3775 if(my $Symbol = symByAddr($Loc)) { 3776 return $Symbol; 3777 } 3778 } 3779 } 3780 3781 if(my $Link = $DWARF_Info{$ID}{"linkage_name"}) 3782 { 3783 return $Link; 3784 } 3785 3786 return undef; 3787} 3788 3789sub completeNS($) 3790{ 3791 my $ID = $_[0]; 3792 3793 my $NS = undef; 3794 my $ID_ = $ID; 3795 my @NSs = (); 3796 3797 while($NS = $NameSpace{$ID_} 3798 or $NS = $NameSpace{$DWARF_Info{$ID_}{"specification"}}) 3799 { 3800 if(my $N = $DWARF_Info{$NS}{"name"}) { 3801 push(@NSs, $N); 3802 } 3803 $ID_ = $NS; 3804 } 3805 3806 if(@NSs) 3807 { 3808 my $N = join("::", reverse(@NSs)); 3809 $NestedNameSpaces{$N} = 1; 3810 return $N; 3811 } 3812 3813 return undef; 3814} 3815 3816sub getSymbolInfo($) 3817{ 3818 my $ID = $_[0]; 3819 3820 if(my $N = $NameSpace{$ID}) 3821 { 3822 if($DWARF_Info{$N}{"Kind"} eq "lexical_block" 3823 or $DWARF_Info{$N}{"Kind"} eq "subprogram") 3824 { # local variables 3825 return; 3826 } 3827 } 3828 3829 if(my $Loc = $DWARF_Info{$ID}{"location"}) 3830 { 3831 if($Loc=~/ reg\d+\Z/) 3832 { # local variables 3833 return; 3834 } 3835 } 3836 3837 my $ShortName = $DWARF_Info{$ID}{"name"}; 3838 my $MnglName = get_Mangled($ID); 3839 3840 if(not $MnglName) 3841 { 3842 if(my $Sp = $SpecElem{$ID}) 3843 { 3844 $MnglName = get_Mangled($Sp); 3845 3846 if(not $MnglName) 3847 { 3848 if(my $Orig = $OrigElem{$Sp}) 3849 { 3850 $MnglName = get_Mangled($Orig); 3851 } 3852 } 3853 } 3854 } 3855 3856 if(not $MnglName) 3857 { 3858 if(index($ShortName, "<")!=-1) 3859 { # template 3860 return; 3861 } 3862 $MnglName = $ShortName; 3863 } 3864 3865 if(skipSymbol($MnglName)) { 3866 return; 3867 } 3868 3869 if(index($MnglName, "\@")!=-1) { 3870 $MnglName=~s/([\@]+.*?)\Z//; 3871 } 3872 3873 if(not $MnglName) { 3874 return; 3875 } 3876 3877 if(index($MnglName, ".")!=-1) 3878 { # foo.part.14 3879 # bar.isra.15 3880 return; 3881 } 3882 3883 if($MnglName=~/\W/) 3884 { # unmangled operators, etc. 3885 return; 3886 } 3887 3888 if($MnglName) 3889 { 3890 if(my $OLD_ID = $Mangled_ID{$MnglName}) 3891 { # duplicates 3892 if(not defined $SymbolInfo{$OLD_ID}{"Header"} 3893 or not defined $SymbolInfo{$OLD_ID}{"Source"}) 3894 { 3895 setSource($SymbolInfo{$OLD_ID}, $ID); 3896 } 3897 3898 if(not defined $SymbolInfo{$OLD_ID}{"ShortName"} 3899 and $ShortName) { 3900 $SymbolInfo{$OLD_ID}{"ShortName"} = $ShortName; 3901 } 3902 3903 if(defined $DWARF_Info{$OLD_ID}{"low_pc"} 3904 or not defined $DWARF_Info{$ID}{"low_pc"}) 3905 { 3906 if(defined $Checked_Spec{$MnglName} 3907 or not $DWARF_Info{$ID}{"specification"}) 3908 { 3909 if(not defined $SpecElem{$ID} 3910 and not defined $OrigElem{$ID}) { 3911 delete($DWARF_Info{$ID}); 3912 } 3913 return; 3914 } 3915 } 3916 } 3917 } 3918 3919 my %SInfo = (); 3920 3921 if($ShortName) { 3922 $SInfo{"ShortName"} = $ShortName; 3923 } 3924 $SInfo{"MnglName"} = $MnglName; 3925 3926 if($ShortName) 3927 { 3928 if($MnglName eq $ShortName) 3929 { 3930 delete($SInfo{"MnglName"}); 3931 $MnglName = $ShortName; 3932 } 3933 elsif(index($MnglName, "_Z")!=0) 3934 { 3935 if($SInfo{"ShortName"}) 3936 { 3937 if(index($SInfo{"ShortName"}, ".")==-1) { 3938 $SInfo{"Alias"} = $SInfo{"ShortName"}; 3939 } 3940 $SInfo{"ShortName"} = $SInfo{"MnglName"}; 3941 } 3942 3943 delete($SInfo{"MnglName"}); 3944 $MnglName = $ShortName; 3945 # $ShortName = $SInfo{"ShortName"}; 3946 } 3947 } 3948 else 3949 { 3950 if(index($MnglName, "_Z")!=0) 3951 { 3952 $SInfo{"ShortName"} = $SInfo{"MnglName"}; 3953 delete($SInfo{"MnglName"}); 3954 } 3955 } 3956 3957 if(isExternal($ID)) { 3958 $SInfo{"External"} = 1; 3959 } 3960 3961 if(my $Orig = $DWARF_Info{$ID}{"abstract_origin"}) 3962 { 3963 if(isExternal($Orig)) { 3964 $SInfo{"External"} = 1; 3965 } 3966 } 3967 3968 if(index($MnglName, "_ZNVK")==0) 3969 { 3970 $SInfo{"Const"} = 1; 3971 $SInfo{"Volatile"} = 1; 3972 } 3973 elsif(index($MnglName, "_ZNV")==0) { 3974 $SInfo{"Volatile"} = 1; 3975 } 3976 elsif(index($MnglName, "_ZNK")==0) { 3977 $SInfo{"Const"} = 1; 3978 } 3979 3980 if($DWARF_Info{$ID}{"artificial"}) { 3981 $SInfo{"Artificial"} = 1; 3982 } 3983 3984 my ($C, $D) = (); 3985 3986 if($MnglName=~/C[1-4][EI].+/) 3987 { 3988 $C = 1; 3989 $SInfo{"Constructor"} = 1; 3990 } 3991 3992 if($MnglName=~/D[0-4][EI].+/) 3993 { 3994 $D = 1; 3995 $SInfo{"Destructor"} = 1; 3996 } 3997 3998 if($C or $D) 3999 { 4000 if(my $Orig = $DWARF_Info{$ID}{"abstract_origin"}) 4001 { 4002 if(my $InLine = $DWARF_Info{$Orig}{"inline"}) 4003 { 4004 if(index($InLine, "declared_not_inlined")==0) 4005 { 4006 $SInfo{"InLine"} = 1; 4007 $SInfo{"Artificial"} = 1; 4008 } 4009 } 4010 4011 setSource(\%SInfo, $Orig); 4012 4013 if(my $Spec = $DWARF_Info{$Orig}{"specification"}) 4014 { 4015 setSource(\%SInfo, $Spec); 4016 4017 $SInfo{"ShortName"} = $DWARF_Info{$Spec}{"name"}; 4018 if($D) { 4019 $SInfo{"ShortName"}=~s/\A\~//g; 4020 } 4021 4022 if(my $Class = $NameSpace{$Spec}) { 4023 $SInfo{"Class"} = $Class; 4024 } 4025 4026 if(my $Virt = $DWARF_Info{$Spec}{"virtuality"}) 4027 { 4028 if(index($Virt, "virtual")!=-1) { 4029 $SInfo{"Virt"} = 1; 4030 } 4031 } 4032 4033 if(my $Access = $DWARF_Info{$Spec}{"accessibility"}) 4034 { 4035 if($Access ne "public") 4036 { # default access of methods in the ABI dump is "public" 4037 $SInfo{ucfirst($Access)} = 1; 4038 } 4039 } 4040 else 4041 { # NOTE: default access of class methods in the debug info is "private" 4042 if($TypeInfo{$SInfo{"Class"}}{"Type"} eq "Class") 4043 { 4044 $SInfo{"Private"} = 1; 4045 } 4046 } 4047 4048 # clean origin 4049 delete($SymbolInfo{$Spec}); 4050 } 4051 } 4052 } 4053 else 4054 { 4055 if(my $InLine = $DWARF_Info{$ID}{"inline"}) 4056 { 4057 if(index($InLine, "declared_inlined")==0) { 4058 $SInfo{"InLine"} = 1; 4059 } 4060 } 4061 } 4062 4063 if(defined $AddrToName) 4064 { 4065 if(not $SInfo{"Alias"} 4066 and not $SInfo{"Constructor"} 4067 and not $SInfo{"Destructor"}) 4068 { 4069 if(my $Linkage = $DWARF_Info{$ID}{"linkage_name"}) 4070 { 4071 if($Linkage ne $MnglName) { 4072 $SInfo{"Alias"} = $Linkage; 4073 } 4074 } 4075 } 4076 } 4077 4078 if($DWARF_Info{$ID}{"Kind"} eq "variable") 4079 { # global data 4080 $SInfo{"Data"} = 1; 4081 4082 if(my $Spec = $DWARF_Info{$ID}{"specification"}) 4083 { 4084 if($DWARF_Info{$Spec}{"Kind"} eq "member") 4085 { 4086 setSource(\%SInfo, $Spec); 4087 $SInfo{"ShortName"} = $DWARF_Info{$Spec}{"name"}; 4088 4089 if(my $NSp = $NameSpace{$Spec}) 4090 { 4091 if($DWARF_Info{$NSp}{"Kind"} eq "namespace") { 4092 $SInfo{"NameSpace"} = completeNS($Spec); 4093 } 4094 else { 4095 $SInfo{"Class"} = $NSp; 4096 } 4097 } 4098 } 4099 } 4100 } 4101 4102 if(my $Access = $DWARF_Info{$ID}{"accessibility"}) 4103 { 4104 if($Access ne "public") 4105 { # default access of methods in the ABI dump is "public" 4106 $SInfo{ucfirst($Access)} = 1; 4107 } 4108 } 4109 elsif(not $DWARF_Info{$ID}{"specification"} 4110 and not $DWARF_Info{$ID}{"abstract_origin"}) 4111 { 4112 if(my $NS = $NameSpace{$ID}) 4113 { 4114 if(defined $TypeInfo{$NS}) 4115 { # NOTE: default access of class methods in the debug info is "private" 4116 if($TypeInfo{$NS}{"Type"} eq "Class") 4117 { 4118 $SInfo{"Private"} = 1; 4119 } 4120 } 4121 } 4122 } 4123 4124 if(my $Class = $DWARF_Info{$ID}{"containing_type"}) 4125 { 4126 $SInfo{"Class"} = $Class; 4127 } 4128 4129 if(my $NS = $NameSpace{$ID}) 4130 { 4131 if($DWARF_Info{$NS}{"Kind"} eq "namespace") { 4132 $SInfo{"NameSpace"} = completeNS($ID); 4133 } 4134 else { 4135 $SInfo{"Class"} = $NS; 4136 } 4137 } 4138 4139 if($SInfo{"Class"} and $MnglName 4140 and index($MnglName, "_Z")!=0) 4141 { 4142 return; 4143 } 4144 4145 if(my $Return = $DWARF_Info{$ID}{"type"}) 4146 { 4147 $SInfo{"Return"} = $Return; 4148 } 4149 if(my $Spec = $DWARF_Info{$ID}{"specification"}) 4150 { 4151 if(not $DWARF_Info{$ID}{"type"}) { 4152 $SInfo{"Return"} = $DWARF_Info{$Spec}{"type"}; 4153 } 4154 if(my $Value = $DWARF_Info{$Spec}{"const_value"}) 4155 { 4156 if($Value=~/ block:\s*(.*?)\Z/) { 4157 $Value = $1; 4158 } 4159 $SInfo{"Value"} = $Value; 4160 } 4161 } 4162 4163 if($SInfo{"ShortName"}=~/>\Z/) 4164 { # foo<T1, T2, ...> 4165 my ($Short, @TParams) = (); 4166 4167 if(defined $TmplParam{$ID}) 4168 { 4169 $Short = getShortName($SInfo{"ShortName"}); 4170 @TParams = get_TParams($ID); 4171 @TParams = shortTParams($Short, @TParams); 4172 } 4173 else { 4174 ($Short, @TParams) = parse_TParams($SInfo{"ShortName"}); 4175 } 4176 4177 if(@TParams) 4178 { 4179 foreach my $Pos (0 .. $#TParams) { 4180 $SInfo{"TParam"}{$Pos}{"name"} = formatName($TParams[$Pos], "T"); 4181 } 4182 # simplify short name 4183 $SInfo{"ShortName"} = $Short.formatName("<".join(", ", @TParams).">", "T"); 4184 } 4185 } 4186 elsif($SInfo{"ShortName"}=~/\Aoperator (\w.*)\Z/) 4187 { # operator type<T1>::name 4188 $SInfo{"ShortName"} = "operator ".simpleName($1); 4189 } 4190 4191 if(my $Virt = $DWARF_Info{$ID}{"virtuality"}) 4192 { 4193 if(index($Virt, "virtual")!=-1) 4194 { 4195 if($D or defined $SpecElem{$ID}) { 4196 $SInfo{"Virt"} = 1; 4197 } 4198 else { 4199 $SInfo{"PureVirt"} = 1; 4200 } 4201 } 4202 4203 if((my $VirtPos = $DWARF_Info{$ID}{"vtable_elem_location"}) ne "") 4204 { 4205 $SInfo{"VirtPos"} = $VirtPos; 4206 } 4207 } 4208 4209 setSource(\%SInfo, $ID); 4210 4211 if(not $SInfo{"Header"}) 4212 { 4213 if($SInfo{"Class"}) 4214 { # detect missed header by class 4215 if(defined $TypeInfo{$SInfo{"Class"}}{"Header"}) { 4216 $SInfo{"Header"} = $TypeInfo{$SInfo{"Class"}}{"Header"}; 4217 } 4218 } 4219 } 4220 4221 if(not $SInfo{"Header"}) 4222 { 4223 if(defined $SymbolToHeader{$MnglName}) { 4224 $SInfo{"Header"} = $SymbolToHeader{$MnglName}; 4225 } 4226 elsif(not $SInfo{"Class"} 4227 and defined $SymbolToHeader{$SInfo{"ShortName"}}) { 4228 $SInfo{"Header"} = $SymbolToHeader{$SInfo{"ShortName"}}; 4229 } 4230 } 4231 elsif($SInfo{"Alias"}) 4232 { 4233 if(defined $SymbolToHeader{$SInfo{"Alias"}} 4234 and $SymbolToHeader{$SInfo{"Alias"}} ne $SInfo{"Header"}) 4235 { # TODO: review this case 4236 $SInfo{"Header"} = $SymbolToHeader{$SInfo{"Alias"}}; 4237 } 4238 } 4239 4240 my $PPos = 0; 4241 4242 foreach my $Pos (sort {int($a) <=> int($b)} keys(%{$FuncParam{$ID}})) 4243 { 4244 my $ParamId = $FuncParam{$ID}{$Pos}; 4245 my $Offset = undef; 4246 my $Reg = undef; 4247 4248 if(my $Sp = $SpecElem{$ID}) 4249 { 4250 if(defined $FuncParam{$Sp}) { 4251 $ParamId = $FuncParam{$Sp}{$Pos}; 4252 } 4253 } 4254 4255 if((my $Loc = $DWARF_Info{$ParamId}{"location"}) ne "") { 4256 $Offset = $Loc; 4257 } 4258 elsif((my $R = $DWARF_Info{$ParamId}{"register"}) ne "") { 4259 $Reg = $RegName{$R}; 4260 } 4261 elsif((my $LL = $DWARF_Info{$ParamId}{"location_list"}) ne "") 4262 { 4263 if(my $L = $DebugLoc{$LL}) 4264 { 4265 if($L=~/reg(\d+)/) { 4266 $Reg = $RegName{$1}; 4267 } 4268 elsif($L=~/fbreg\s+(-?\w+)\Z/) { 4269 $Offset = $1; 4270 } 4271 } 4272 elsif(not defined $DebugLoc{$LL}) 4273 { # invalid debug_loc 4274 if(not $InvalidDebugLoc) 4275 { 4276 printMsg("ERROR", "invalid debug_loc section of object, please fix your elf utils"); 4277 $InvalidDebugLoc = 1; 4278 } 4279 } 4280 } 4281 4282 if(my $Orig = $DWARF_Info{$ParamId}{"abstract_origin"}) { 4283 $ParamId = $Orig; 4284 } 4285 4286 my %PInfo = %{$DWARF_Info{$ParamId}}; 4287 4288 if(defined $Offset 4289 and not defined $IncompatibleOpt) { 4290 $SInfo{"Param"}{$Pos}{"offset"} = $Offset; 4291 } 4292 4293 if($TypeInfo{$PInfo{"type"}}{"Type"} eq "Const") 4294 { 4295 if(my $BTid = $TypeInfo{$PInfo{"type"}}{"BaseType"}) 4296 { 4297 if($TypeInfo{$BTid}{"Type"} eq "Ref") 4298 { # const&const -> const& 4299 $PInfo{"type"} = $BTid; 4300 } 4301 } 4302 } 4303 4304 $SInfo{"Param"}{$Pos}{"type"} = $PInfo{"type"}; 4305 4306 if(defined $PInfo{"name"}) { 4307 $SInfo{"Param"}{$Pos}{"name"} = $PInfo{"name"}; 4308 } 4309 elsif($TypeInfo{$PInfo{"type"}}{"Name"} ne "...") { 4310 $SInfo{"Param"}{$Pos}{"name"} = "p".($PPos+1); 4311 } 4312 4313 if(defined $Reg 4314 and not defined $IncompatibleOpt) 4315 { 4316 $SInfo{"Reg"}{$Pos} = $Reg; 4317 } 4318 4319 if($DWARF_Info{$ParamId}{"artificial"} and $Pos==0) 4320 { 4321 if($SInfo{"Param"}{$Pos}{"name"} eq "p1") { 4322 $SInfo{"Param"}{$Pos}{"name"} = "this"; 4323 } 4324 } 4325 4326 if($SInfo{"Param"}{$Pos}{"name"} ne "this") 4327 { # this, p1, p2, etc. 4328 $PPos += 1; 4329 } 4330 } 4331 4332 if($SInfo{"Constructor"} and not $SInfo{"InLine"} 4333 and $SInfo{"Class"}) { 4334 delete($TypeInfo{$SInfo{"Class"}}{"Copied"}); 4335 } 4336 4337 if(my $BASE_ID = $Mangled_ID{$MnglName}) 4338 { 4339 if(defined $SInfo{"Param"}) 4340 { 4341 if(keys(%{$SInfo{"Param"}})!=keys(%{$SymbolInfo{$BASE_ID}{"Param"}})) 4342 { # different symbols with the same name 4343 delete($SymbolInfo{$BASE_ID}); 4344 } 4345 } 4346 4347 $ID = $BASE_ID; 4348 4349 if(defined $SymbolInfo{$ID}{"PureVirt"}) 4350 { # if the specification of a symbol is located in other compile unit 4351 delete($SymbolInfo{$ID}{"PureVirt"}); 4352 $SymbolInfo{$ID}{"Virt"} = 1; 4353 } 4354 } 4355 $Mangled_ID{$MnglName} = $ID; 4356 4357 if($DWARF_Info{$ID}{"specification"}) { 4358 $Checked_Spec{$MnglName} = 1; 4359 } 4360 4361 foreach my $Attr (keys(%SInfo)) 4362 { 4363 if(ref($SInfo{$Attr}) eq "HASH") 4364 { 4365 foreach my $K1 (keys(%{$SInfo{$Attr}})) 4366 { 4367 if(ref($SInfo{$Attr}{$K1}) eq "HASH") 4368 { 4369 foreach my $K2 (keys(%{$SInfo{$Attr}{$K1}})) 4370 { 4371 $SymbolInfo{$ID}{$Attr}{$K1}{$K2} = $SInfo{$Attr}{$K1}{$K2}; 4372 } 4373 } 4374 else { 4375 $SymbolInfo{$ID}{$Attr}{$K1} = $SInfo{$Attr}{$K1}; 4376 } 4377 } 4378 } 4379 else 4380 { 4381 $SymbolInfo{$ID}{$Attr} = $SInfo{$Attr}; 4382 } 4383 } 4384 4385 if($ID>$GLOBAL_ID) { 4386 $GLOBAL_ID = $ID; 4387 } 4388} 4389 4390sub getTypeIdByName($$) 4391{ 4392 my ($Type, $Name) = @_; 4393 return $TName_Tid{$Type}{formatName($Name, "T")}; 4394} 4395 4396sub getFirst($) 4397{ 4398 my $Tid = $_[0]; 4399 if(not $Tid) { 4400 return $Tid; 4401 } 4402 4403 if(defined $TypeSpec{$Tid}) { 4404 $Tid = $TypeSpec{$Tid}; 4405 } 4406 4407 my $F = 0; 4408 4409 if(my $Name = $TypeInfo{$Tid}{"Name"}) 4410 { 4411 my $Type = $TypeInfo{$Tid}{"Type"}; 4412 if($Name=~s/\Astruct //) 4413 { # search for class or derived types (const, *, etc.) 4414 $F = 1; 4415 } 4416 4417 my $FTid = undef; 4418 if($F) 4419 { 4420 foreach my $Type ("Class", "Const", "Ref", "RvalueRef", "Pointer") 4421 { 4422 if($FTid = $TName_Tid{$Type}{$Name}) 4423 { 4424 if($FTid ne $Tid) 4425 { 4426 $MergedTypes{$Tid} = 1; 4427 } 4428 return "$FTid"; 4429 } 4430 } 4431 4432 $Name = "struct ".$Name; 4433 } 4434 4435 if(not $FTid) { 4436 $FTid = $TName_Tid{$Type}{$Name}; 4437 } 4438 4439 if($FTid) { 4440 return "$FTid"; 4441 } 4442 printMsg("ERROR", "internal error (missed type id $Tid)"); 4443 } 4444 4445 return $Tid; 4446} 4447 4448sub searchTypeID($) 4449{ 4450 my $Name = $_[0]; 4451 4452 my %Pr = map {$_=>1} ( 4453 "Struct", 4454 "Union", 4455 "Enum" 4456 ); 4457 4458 foreach my $Type ("Class", "Struct", "Union", "Enum", "Typedef", "Const", 4459 "Volatile", "Ref", "RvalueRef", "Pointer", "FuncPtr", "MethodPtr", "FieldPtr") 4460 { 4461 my $Tid = $TName_Tid{$Type}{$Name}; 4462 4463 if(not $Tid) 4464 { 4465 my $P = ""; 4466 if(defined $Pr{$Type}) 4467 { 4468 $P = lc($Type)." "; 4469 } 4470 4471 $Tid = $TName_Tid{$Type}{$P.$Name} 4472 } 4473 if($Tid) { 4474 return $Tid; 4475 } 4476 } 4477 return undef; 4478} 4479 4480sub remove_Unused() 4481{ # remove unused data types from the ABI dump 4482 %HeadersInfo = (); 4483 %SourcesInfo = (); 4484 4485 my (%SelectedHeaders, %SelectedSources) = (); 4486 4487 foreach my $ID (sort {int($a)<=>int($b)} keys(%SymbolInfo)) 4488 { 4489 if($SelectedSymbols{$ID}==2) 4490 { # data, inline, pure 4491 next; 4492 } 4493 4494 register_SymbolUsage($ID); 4495 4496 if(my $H = $SymbolInfo{$ID}{"Header"}) { 4497 $SelectedHeaders{$H} = 1; 4498 } 4499 if(my $S = $SymbolInfo{$ID}{"Source"}) { 4500 $SelectedSources{$S} = 1; 4501 } 4502 } 4503 4504 foreach my $ID (sort {int($a)<=>int($b)} keys(%SymbolInfo)) 4505 { 4506 if($SelectedSymbols{$ID}==2) 4507 { # data, inline, pure 4508 my $Save = 0; 4509 if(my $Class = $SymbolInfo{$ID}{"Class"}) 4510 { 4511 if(defined $UsedType{$Class}) { 4512 $Save = 1; 4513 } 4514 else 4515 { 4516 foreach (keys(%{$ClassChild{$Class}})) 4517 { 4518 if(defined $UsedType{$_}) 4519 { 4520 $Save = 1; 4521 last; 4522 } 4523 } 4524 } 4525 } 4526 if(my $Header = $SymbolInfo{$ID}{"Header"}) 4527 { 4528 if(defined $SelectedHeaders{$Header}) { 4529 $Save = 1; 4530 } 4531 } 4532 if(my $Source = $SymbolInfo{$ID}{"Source"}) 4533 { 4534 if(defined $SelectedSources{$Source}) { 4535 $Save = 1; 4536 } 4537 } 4538 if($Save) { 4539 register_SymbolUsage($ID); 4540 } 4541 else { 4542 delete($SymbolInfo{$ID}); 4543 } 4544 } 4545 } 4546 4547 if(defined $AllTypes) 4548 { 4549 # register all data types (except anon structs and unions) 4550 foreach my $Tid (keys(%TypeInfo)) 4551 { 4552 if(defined $LocalType{$Tid}) 4553 { # except local code 4554 next; 4555 } 4556 if($TypeInfo{$Tid}{"Type"} eq "Enum" 4557 or index($TypeInfo{$Tid}{"Name"}, "anon-")!=0) { 4558 register_TypeUsage($Tid); 4559 } 4560 } 4561 4562 # remove unused anons (except enums) 4563 foreach my $Tid (keys(%TypeInfo)) 4564 { 4565 if(not $UsedType{$Tid}) 4566 { 4567 if($TypeInfo{$Tid}{"Type"} ne "Enum") 4568 { 4569 if(index($TypeInfo{$Tid}{"Name"}, "anon-")==0) { 4570 delete($TypeInfo{$Tid}); 4571 } 4572 } 4573 } 4574 } 4575 4576 # remove duplicates 4577 foreach my $Tid (keys(%TypeInfo)) 4578 { 4579 my $Name = $TypeInfo{$Tid}{"Name"}; 4580 my $Type = $TypeInfo{$Tid}{"Type"}; 4581 4582 if($TName_Tid{$Type}{$Name} ne $Tid) { 4583 delete($TypeInfo{$Tid}); 4584 } 4585 } 4586 } 4587 else 4588 { 4589 foreach my $Tid (keys(%TypeInfo)) 4590 { # remove unused types 4591 if(not $UsedType{$Tid}) { 4592 delete($TypeInfo{$Tid}); 4593 } 4594 } 4595 } 4596 4597 foreach my $Tid (keys(%MergedTypes)) { 4598 delete($TypeInfo{$Tid}); 4599 } 4600 4601 foreach my $Tid (keys(%LocalType)) 4602 { 4603 if(not $UsedType{$Tid}) { 4604 delete($TypeInfo{$Tid}); 4605 } 4606 } 4607 4608 # clean memory 4609 %MergedTypes = (); 4610 %LocalType = (); 4611 4612 # completeness 4613 foreach my $Tid (sort keys(%TypeInfo)) { 4614 check_Completeness($TypeInfo{$Tid}); 4615 } 4616 4617 foreach my $Sid (sort keys(%SymbolInfo)) { 4618 check_Completeness($SymbolInfo{$Sid}); 4619 } 4620 4621 # clean memory 4622 %UsedType = (); 4623} 4624 4625sub simpleName($) 4626{ 4627 my $N = $_[0]; 4628 4629 $N=~s/\A(struct|class|union|enum) //; # struct, class, union, enum 4630 4631 if(index($N, "std::basic_string")!=-1) 4632 { 4633 $N=~s/std::basic_string<char, std::char_traits<char>, std::allocator<char> >/std::string /g; 4634 $N=~s/std::basic_string<char, std::char_traits<char> >/std::string /g; 4635 $N=~s/std::basic_string<char>/std::string /g; 4636 } 4637 4638 return formatName($N, "T"); 4639} 4640 4641sub register_SymbolUsage($) 4642{ 4643 my $InfoId = $_[0]; 4644 4645 my %FuncInfo = %{$SymbolInfo{$InfoId}}; 4646 4647 if(my $S = $FuncInfo{"Source"}) { 4648 $SourcesInfo{$S} = 1; 4649 } 4650 if(my $H = $FuncInfo{"Header"}) { 4651 $HeadersInfo{$H} = 1; 4652 } 4653 if(my $RTid = getFirst($FuncInfo{"Return"})) 4654 { 4655 register_TypeUsage($RTid); 4656 $SymbolInfo{$InfoId}{"Return"} = $RTid; 4657 } 4658 if(my $FCid = getFirst($FuncInfo{"Class"})) 4659 { 4660 register_TypeUsage($FCid); 4661 $SymbolInfo{$InfoId}{"Class"} = $FCid; 4662 4663 if(my $ThisId = getTypeIdByName("Const", $TypeInfo{$FCid}{"Name"}."*const")) 4664 { # register "this" pointer 4665 register_TypeUsage($ThisId); 4666 } 4667 if(my $ThisId_C = getTypeIdByName("Const", $TypeInfo{$FCid}{"Name"}." const*const")) 4668 { # register "this" pointer (const method) 4669 register_TypeUsage($ThisId_C); 4670 } 4671 } 4672 foreach my $PPos (keys(%{$FuncInfo{"Param"}})) 4673 { 4674 if(my $PTid = getFirst($FuncInfo{"Param"}{$PPos}{"type"})) 4675 { 4676 register_TypeUsage($PTid); 4677 $SymbolInfo{$InfoId}{"Param"}{$PPos}{"type"} = $PTid; 4678 } 4679 } 4680 foreach my $TPos (keys(%{$FuncInfo{"TParam"}})) 4681 { 4682 my $TPName = $FuncInfo{"TParam"}{$TPos}{"name"}; 4683 if(my $TTid = searchTypeID($TPName)) 4684 { 4685 if(my $FTTid = getFirst($TTid)) { 4686 register_TypeUsage($FTTid); 4687 } 4688 } 4689 } 4690} 4691 4692sub register_TypeUsage($) 4693{ 4694 my $TypeId = $_[0]; 4695 if(not $TypeId) { 4696 return 0; 4697 } 4698 if($UsedType{$TypeId}) 4699 { # already registered 4700 return 1; 4701 } 4702 my %TInfo = %{$TypeInfo{$TypeId}}; 4703 4704 if(my $S = $TInfo{"Source"}) { 4705 $SourcesInfo{$S} = 1; 4706 } 4707 if(my $H = $TInfo{"Header"}) { 4708 $HeadersInfo{$H} = 1; 4709 } 4710 4711 if($TInfo{"Type"}) 4712 { 4713 if(my $NS = $TInfo{"NameSpace"}) 4714 { 4715 if(my $NSTid = searchTypeID($NS)) 4716 { 4717 if(my $FNSTid = getFirst($NSTid)) { 4718 register_TypeUsage($FNSTid); 4719 } 4720 } 4721 } 4722 4723 if($TInfo{"Type"}=~/\A(Struct|Union|Class|FuncPtr|Func|MethodPtr|FieldPtr|Enum)\Z/) 4724 { 4725 $UsedType{$TypeId} = 1; 4726 if($TInfo{"Type"}=~/\A(Struct|Class)\Z/) 4727 { 4728 foreach my $BaseId (keys(%{$TInfo{"Base"}})) 4729 { # register base classes 4730 if(my $FBaseId = getFirst($BaseId)) 4731 { 4732 register_TypeUsage($FBaseId); 4733 if($FBaseId ne $BaseId) 4734 { 4735 %{$TypeInfo{$TypeId}{"Base"}{$FBaseId}} = %{$TypeInfo{$TypeId}{"Base"}{$BaseId}}; 4736 delete($TypeInfo{$TypeId}{"Base"}{$BaseId}); 4737 } 4738 } 4739 } 4740 foreach my $TPos (keys(%{$TInfo{"TParam"}})) 4741 { 4742 my $TPName = $TInfo{"TParam"}{$TPos}{"name"}; 4743 if(my $TTid = searchTypeID($TPName)) 4744 { 4745 if(my $FTTid = getFirst($TTid)) { 4746 register_TypeUsage($FTTid); 4747 } 4748 } 4749 } 4750 } 4751 foreach my $Memb_Pos (keys(%{$TInfo{"Memb"}})) 4752 { 4753 if(my $MTid = getFirst($TInfo{"Memb"}{$Memb_Pos}{"type"})) 4754 { 4755 register_TypeUsage($MTid); 4756 $TypeInfo{$TypeId}{"Memb"}{$Memb_Pos}{"type"} = $MTid; 4757 } 4758 } 4759 if($TInfo{"Type"} eq "FuncPtr" 4760 or $TInfo{"Type"} eq "MethodPtr" 4761 or $TInfo{"Type"} eq "Func") 4762 { 4763 if(my $RTid = getFirst($TInfo{"Return"})) 4764 { 4765 register_TypeUsage($RTid); 4766 $TypeInfo{$TypeId}{"Return"} = $RTid; 4767 } 4768 foreach my $Memb_Pos (keys(%{$TInfo{"Param"}})) 4769 { 4770 if(my $MTid = getFirst($TInfo{"Param"}{$Memb_Pos}{"type"})) 4771 { 4772 register_TypeUsage($MTid); 4773 $TypeInfo{$TypeId}{"Param"}{$Memb_Pos}{"type"} = $MTid; 4774 } 4775 } 4776 } 4777 if($TInfo{"Type"} eq "FieldPtr") 4778 { 4779 if(my $RTid = getFirst($TInfo{"Return"})) 4780 { 4781 register_TypeUsage($RTid); 4782 $TypeInfo{$TypeId}{"Return"} = $RTid; 4783 } 4784 if(my $CTid = getFirst($TInfo{"Class"})) 4785 { 4786 register_TypeUsage($CTid); 4787 $TypeInfo{$TypeId}{"Class"} = $CTid; 4788 } 4789 } 4790 if($TInfo{"Type"} eq "MethodPtr") 4791 { 4792 if(my $CTid = getFirst($TInfo{"Class"})) 4793 { 4794 register_TypeUsage($CTid); 4795 $TypeInfo{$TypeId}{"Class"} = $CTid; 4796 } 4797 } 4798 if($TInfo{"Type"} eq "Enum") 4799 { 4800 if(my $BTid = getFirst($TInfo{"BaseType"})) 4801 { 4802 register_TypeUsage($BTid); 4803 $TypeInfo{$TypeId}{"BaseType"} = $BTid; 4804 } 4805 } 4806 return 1; 4807 } 4808 elsif($TInfo{"Type"}=~/\A(Const|ConstVolatile|Volatile|Pointer|Ref|RvalueRef|Restrict|Array|Typedef)\Z/) 4809 { 4810 $UsedType{$TypeId} = 1; 4811 if(my $BTid = getFirst($TInfo{"BaseType"})) 4812 { 4813 register_TypeUsage($BTid); 4814 $TypeInfo{$TypeId}{"BaseType"} = $BTid; 4815 } 4816 return 1; 4817 } 4818 elsif($TInfo{"Type"} eq "Intrinsic") 4819 { 4820 $UsedType{$TypeId} = 1; 4821 return 1; 4822 } 4823 } 4824 return 0; 4825} 4826 4827my %CheckedType = (); 4828 4829sub check_Completeness($) 4830{ 4831 my $Info = $_[0]; 4832 4833 # data types 4834 if(defined $Info->{"Memb"}) 4835 { 4836 foreach my $Pos (sort keys(%{$Info->{"Memb"}})) 4837 { 4838 if(defined $Info->{"Memb"}{$Pos}{"type"}) { 4839 check_TypeInfo($Info->{"Memb"}{$Pos}{"type"}); 4840 } 4841 } 4842 } 4843 if(defined $Info->{"Base"}) 4844 { 4845 foreach my $Bid (sort keys(%{$Info->{"Base"}})) { 4846 check_TypeInfo($Bid); 4847 } 4848 } 4849 if(defined $Info->{"BaseType"}) { 4850 check_TypeInfo($Info->{"BaseType"}); 4851 } 4852 if(defined $Info->{"TParam"}) 4853 { 4854 foreach my $Pos (sort keys(%{$Info->{"TParam"}})) 4855 { 4856 my $TName = $Info->{"TParam"}{$Pos}{"name"}; 4857 if($TName=~/\A(true|false|\d.*)\Z/) { 4858 next; 4859 } 4860 4861 if(my $Tid = searchTypeID($TName)) { 4862 check_TypeInfo($Tid); 4863 } 4864 else 4865 { 4866 if(defined $Loud) { 4867 printMsg("WARNING", "missed type $TName"); 4868 } 4869 } 4870 } 4871 } 4872 4873 # symbols 4874 if(defined $Info->{"Param"}) 4875 { 4876 foreach my $Pos (sort keys(%{$Info->{"Param"}})) 4877 { 4878 if(defined $Info->{"Param"}{$Pos}{"type"}) { 4879 check_TypeInfo($Info->{"Param"}{$Pos}{"type"}); 4880 } 4881 } 4882 } 4883 if(defined $Info->{"Return"}) { 4884 check_TypeInfo($Info->{"Return"}); 4885 } 4886 if(defined $Info->{"Class"}) { 4887 check_TypeInfo($Info->{"Class"}); 4888 } 4889} 4890 4891sub check_TypeInfo($) 4892{ 4893 my $Tid = $_[0]; 4894 4895 if(defined $CheckedType{$Tid}) { 4896 return; 4897 } 4898 $CheckedType{$Tid} = 1; 4899 4900 if(defined $TypeInfo{$Tid}) 4901 { 4902 if(not $TypeInfo{$Tid}{"Name"}) { 4903 printMsg("ERROR", "missed type name ($Tid)"); 4904 } 4905 check_Completeness($TypeInfo{$Tid}); 4906 } 4907 else { 4908 printMsg("ERROR", "missed type id $Tid"); 4909 } 4910} 4911 4912sub init_Registers() 4913{ 4914 if($SYS_ARCH eq "x86") 4915 { 4916 %RegName = ( 4917 # integer registers 4918 # 32 bits 4919 "0"=>"eax", 4920 "1"=>"ecx", 4921 "2"=>"edx", 4922 "3"=>"ebx", 4923 "4"=>"esp", 4924 "5"=>"ebp", 4925 "6"=>"esi", 4926 "7"=>"edi", 4927 "8"=>"eip", 4928 "9"=>"eflags", 4929 "10"=>"trapno", 4930 # FPU-control registers 4931 # 16 bits 4932 "37"=>"fctrl", 4933 "38"=>"fstat", 4934 # 32 bits 4935 "39"=>"mxcsr", 4936 # MMX registers 4937 # 64 bits 4938 "29"=>"mm0", 4939 "30"=>"mm1", 4940 "31"=>"mm2", 4941 "32"=>"mm3", 4942 "33"=>"mm4", 4943 "34"=>"mm5", 4944 "35"=>"mm6", 4945 "36"=>"mm7", 4946 # SSE registers 4947 # 128 bits 4948 "21"=>"xmm0", 4949 "22"=>"xmm1", 4950 "23"=>"xmm2", 4951 "24"=>"xmm3", 4952 "25"=>"xmm4", 4953 "26"=>"xmm5", 4954 "27"=>"xmm6", 4955 "28"=>"xmm7", 4956 # segment registers 4957 # 16 bits 4958 "40"=>"es", 4959 "41"=>"cs", 4960 "42"=>"ss", 4961 "43"=>"ds", 4962 "44"=>"fs", 4963 "45"=>"gs", 4964 # x87 registers 4965 # 80 bits 4966 "11"=>"st0", 4967 "12"=>"st1", 4968 "13"=>"st2", 4969 "14"=>"st3", 4970 "15"=>"st4", 4971 "16"=>"st5", 4972 "17"=>"st6", 4973 "18"=>"st7" 4974 ); 4975 } 4976 elsif($SYS_ARCH eq "x86_64") 4977 { 4978 %RegName = ( 4979 # integer registers 4980 # 64 bits 4981 "0"=>"rax", 4982 "1"=>"rdx", 4983 "2"=>"rcx", 4984 "3"=>"rbx", 4985 "4"=>"rsi", 4986 "5"=>"rdi", 4987 "6"=>"rbp", 4988 "7"=>"rsp", 4989 "8"=>"r8", 4990 "9"=>"r9", 4991 "10"=>"r10", 4992 "11"=>"r11", 4993 "12"=>"r12", 4994 "13"=>"r13", 4995 "14"=>"r14", 4996 "15"=>"r15", 4997 "16"=>"rip", 4998 "49"=>"rFLAGS", 4999 # MMX registers 5000 # 64 bits 5001 "41"=>"mm0", 5002 "42"=>"mm1", 5003 "43"=>"mm2", 5004 "44"=>"mm3", 5005 "45"=>"mm4", 5006 "46"=>"mm5", 5007 "47"=>"mm6", 5008 "48"=>"mm7", 5009 # SSE registers 5010 # 128 bits 5011 "17"=>"xmm0", 5012 "18"=>"xmm1", 5013 "19"=>"xmm2", 5014 "20"=>"xmm3", 5015 "21"=>"xmm4", 5016 "22"=>"xmm5", 5017 "23"=>"xmm6", 5018 "24"=>"xmm7", 5019 "25"=>"xmm8", 5020 "26"=>"xmm9", 5021 "27"=>"xmm10", 5022 "28"=>"xmm11", 5023 "29"=>"xmm12", 5024 "30"=>"xmm13", 5025 "31"=>"xmm14", 5026 "32"=>"xmm15", 5027 # control registers 5028 # 64 bits 5029 "62"=>"tr", 5030 "63"=>"ldtr", 5031 "64"=>"mxcsr", 5032 # 16 bits 5033 "65"=>"fcw", 5034 "66"=>"fsw", 5035 # segment registers 5036 # 16 bits 5037 "50"=>"es", 5038 "51"=>"cs", 5039 "52"=>"ss", 5040 "53"=>"ds", 5041 "54"=>"fs", 5042 "55"=>"gs", 5043 # 64 bits 5044 "58"=>"fs.base", 5045 "59"=>"gs.base", 5046 # x87 registers 5047 # 80 bits 5048 "33"=>"st0", 5049 "34"=>"st1", 5050 "35"=>"st2", 5051 "36"=>"st3", 5052 "37"=>"st4", 5053 "38"=>"st5", 5054 "39"=>"st6", 5055 "40"=>"st7" 5056 ); 5057 } 5058 elsif($SYS_ARCH eq "arm") 5059 { 5060 %RegName = ( 5061 # integer registers 5062 # 32-bit 5063 "0"=>"r0", 5064 "1"=>"r1", 5065 "2"=>"r2", 5066 "3"=>"r3", 5067 "4"=>"r4", 5068 "5"=>"r5", 5069 "6"=>"r6", 5070 "7"=>"r7", 5071 "8"=>"r8", 5072 "9"=>"r9", 5073 "10"=>"r10", 5074 "11"=>"r11", 5075 "12"=>"r12", 5076 "13"=>"r13", 5077 "14"=>"r14", 5078 "15"=>"r15" 5079 ); 5080 } 5081} 5082 5083sub dump_sorting($) 5084{ 5085 my $Hash = $_[0]; 5086 return [] if(not $Hash); 5087 my @Keys = keys(%{$Hash}); 5088 return [] if($#Keys<0); 5089 if($Keys[0]=~/\A\d+\Z/) 5090 { # numbers 5091 return [sort {int($a)<=>int($b)} @Keys]; 5092 } 5093 else 5094 { # strings 5095 return [sort {$a cmp $b} @Keys]; 5096 } 5097} 5098 5099sub getDebugFile($$) 5100{ 5101 my ($Obj, $Header) = @_; 5102 5103 my $Str = `$EU_READELF_L --strings=.$Header \"$Obj\" 2>\"$TMP_DIR/error\"`; 5104 if($Str=~/0\]\s*(.+)/) { 5105 return $1; 5106 } 5107 5108 return undef; 5109} 5110 5111sub findFiles(@) 5112{ 5113 my ($Path, $Type) = @_; 5114 my $Cmd = "find \"$Path\""; 5115 5116 if($Type) { 5117 $Cmd .= " -type ".$Type; 5118 } 5119 5120 my @Res = split(/\n/, `$Cmd`); 5121 return @Res; 5122} 5123 5124sub isHeader($) 5125{ 5126 my $Path = $_[0]; 5127 return ($Path=~/\.(h|hh|hp|hxx|hpp|h\+\+|tcc|x|inl)\Z/i); 5128} 5129 5130sub detectPublicSymbols($) 5131{ 5132 my $Path = $_[0]; 5133 5134 if(not -e $Path) { 5135 exitStatus("Access_Error", "can't access \'$Path\'"); 5136 } 5137 5138 my $Path_A = abs_path($Path); 5139 5140 printMsg("INFO", "Detect public symbols"); 5141 5142 if($UseTU) 5143 { 5144 if(not check_Cmd($GPP)) 5145 { 5146 printMsg("ERROR", "can't find \"$GPP\""); 5147 return; 5148 } 5149 } 5150 else 5151 { 5152 if(not check_Cmd($CTAGS)) 5153 { 5154 printMsg("ERROR", "can't find \"$CTAGS\""); 5155 return; 5156 } 5157 } 5158 5159 $PublicSymbols_Detected = 1; 5160 5161 my @Files = (); 5162 my @Headers = (); 5163 my @DefaultInc = (); 5164 5165 if(-f $Path) 5166 { # list of headers 5167 @Headers = split(/\n/, readFile($Path)); 5168 } 5169 elsif(-d $Path) 5170 { # directory 5171 @Files = findFiles($Path, "f"); 5172 5173 foreach my $File (@Files) 5174 { 5175 if(isHeader($File)) { 5176 push(@Headers, $File); 5177 } 5178 } 5179 5180 push(@DefaultInc, $Path_A); 5181 5182 if(-d $Path_A."/include") { 5183 push(@DefaultInc, $Path_A."/include"); 5184 } 5185 } 5186 5187 my $PublicHeader_F = $CacheHeaders."/PublicHeader.data"; 5188 my $SymbolToHeader_F = $CacheHeaders."/SymbolToHeader.data"; 5189 my $TypeToHeader_F = $CacheHeaders."/TypeToHeader.data"; 5190 my $Path_F = $CacheHeaders."/PATH"; 5191 5192 if($CacheHeaders 5193 and -f $PublicHeader_F 5194 and -f $SymbolToHeader_F 5195 and -f $TypeToHeader_F 5196 and -f $Path_F) 5197 { 5198 if(readFile($Path_F) eq $Path_A) 5199 { 5200 %PublicHeader = %{eval(readFile($PublicHeader_F))}; 5201 %SymbolToHeader = %{eval(readFile($SymbolToHeader_F))}; 5202 %TypeToHeader = %{eval(readFile($TypeToHeader_F))}; 5203 5204 return; 5205 } 5206 } 5207 5208 foreach my $File (@Headers) 5209 { 5210 $PublicHeader{getFilename($File)} = 1; 5211 } 5212 5213 my $Is_C = ($OBJ_LANG eq "C"); 5214 5215 foreach my $File (@Headers) 5216 { 5217 my $HName = getFilename($File); 5218 5219 if($UseTU) 5220 { 5221 my $TmpDir = $TMP_DIR."/tu"; 5222 if(not -d $TmpDir) { 5223 mkpath($TmpDir); 5224 } 5225 5226 my $File_A = abs_path($File); 5227 my $IncDir = getDirname($File_A); 5228 my $IncDir_O = getDirname($IncDir); 5229 5230 my $TmpInc = $TmpDir."/tmp-inc.h"; 5231 my $TmpContent = ""; 5232 if($IncludePreamble) 5233 { 5234 foreach my $P (split(/;/, $IncludePreamble)) 5235 { 5236 if($P=~/\A\//) { 5237 $TmpContent = "#include \"".$P."\"\n"; 5238 } 5239 else { 5240 $TmpContent = "#include <".$P.">\n"; 5241 } 5242 } 5243 } 5244 $TmpContent .= "#include \"$File_A\"\n"; 5245 writeFile($TmpInc, $TmpContent); 5246 5247 my $Cmd = $GPP." -w -fpermissive -fdump-translation-unit -fkeep-inline-functions -c \"$TmpInc\""; 5248 5249 if(defined $IncludePaths) 5250 { 5251 foreach my $P (split(/;/, $IncludePaths)) 5252 { 5253 if($P!~/\A\//) { 5254 $P = $Path_A."/".$P; 5255 } 5256 5257 $Cmd .= " -I\"".$P."\""; 5258 } 5259 } 5260 else 5261 { # automatic 5262 $Cmd .= " -I\"$IncDir\" -I\"$IncDir_O\""; 5263 } 5264 5265 foreach my $P (@DefaultInc) { 5266 $Cmd .= " -I\"$P\""; 5267 } 5268 5269 $Cmd .= " -o ./a.out >OUT 2>&1"; 5270 5271 chdir($TmpDir); 5272 system($Cmd); 5273 chdir($ORIG_DIR); 5274 5275 my $TuDump = $TmpDir."/tmp-inc.h.001t.tu"; 5276 5277 if(not -e $TuDump) { 5278 printMsg("ERROR", "failed to list symbols in the header \'$HName\'"); 5279 } 5280 elsif($?) { 5281 printMsg("ERROR", "some errors occured when compiling header \'$HName\'"); 5282 } 5283 else 5284 { 5285 my (%Fdecl, %Tdecl, %Tname, %Ident, %NotDecl) = (); 5286 my $Content = readFile($TuDump); 5287 $Content=~s/\n[ ]+/ /g; 5288 5289 my @Lines = split(/\n/, $Content); 5290 foreach my $N (0 .. $#Lines) 5291 { 5292 my $Line = $Lines[$N]; 5293 if(index($Line, "function_decl")!=-1 5294 or index($Line, "var_decl")!=-1) 5295 { 5296 if($Line=~/name: \@(\d+)/) 5297 { 5298 my $Id = $1; 5299 5300 if($Line=~/srcp: ([^:]+)\:\d/) 5301 { 5302 if(defined $PublicHeader{$1}) { 5303 $Fdecl{$Id} = $1; 5304 } 5305 } 5306 } 5307 } 5308 elsif($Line=~/\@(\d+)\s+identifier_node\s+strg:\s+(\w+)/) 5309 { 5310 $Ident{$1} = $2; 5311 } 5312 elsif($Is_C) 5313 { 5314 if(index($Line, "type_decl")!=-1) 5315 { 5316 if($Line=~/\A\@(\d+)/) 5317 { 5318 my $Id = $1; 5319 if($Line=~/name: \@(\d+)/) 5320 { 5321 my $NId = $1; 5322 5323 if($Line=~/srcp: ([^:]+)\:\d/) 5324 { 5325 if(defined $PublicHeader{$1}) 5326 { 5327 $Tdecl{$Id} = $1; 5328 $Tname{$Id} = $NId; 5329 } 5330 } 5331 } 5332 } 5333 } 5334 elsif(index($Line, "record_type")!=-1 5335 or index($Line, "union_type")!=-1) 5336 { 5337 if($Line!~/ flds:/) 5338 { 5339 if($Line=~/name: \@(\d+)/) 5340 { 5341 $NotDecl{$1} = 1; 5342 } 5343 } 5344 } 5345 elsif(index($Line, "enumeral_type")!=-1) 5346 { 5347 if($Line!~/ csts:/) 5348 { 5349 if($Line=~/name: \@(\d+)/) 5350 { 5351 $NotDecl{$1} = 1; 5352 } 5353 } 5354 } 5355 elsif(index($Line, "integer_type")!=-1) 5356 { 5357 if($Line=~/name: \@(\d+)/) 5358 { 5359 $NotDecl{$1} = 1; 5360 } 5361 } 5362 } 5363 } 5364 5365 foreach my $Id (keys(%Fdecl)) 5366 { 5367 if(my $Name = $Ident{$Id}) { 5368 $SymbolToHeader{$Name} = $Fdecl{$Id}; 5369 } 5370 } 5371 5372 if($Is_C) 5373 { 5374 foreach my $Id (keys(%Tdecl)) 5375 { 5376 if(defined $NotDecl{$Id}) { 5377 next; 5378 } 5379 5380 if(my $Name = $Ident{$Tname{$Id}}) { 5381 $TypeToHeader{$Name} = $Tdecl{$Id}; 5382 } 5383 } 5384 } 5385 5386 unlink($TuDump); 5387 } 5388 } 5389 else 5390 { # using Ctags 5391 my $IgnoreTags = ""; 5392 5393 if(defined $IgnoreTagsPath) { 5394 $IgnoreTags = "-I \@".$IgnoreTagsPath; 5395 } 5396 5397 my $List_S = `$CTAGS -x --c-kinds=fpvx $IgnoreTags \"$File\"`; 5398 foreach my $Line (split(/\n/, $List_S)) 5399 { 5400 if($Line=~/\A(\w+)/) { 5401 $SymbolToHeader{$1} = $HName; 5402 } 5403 } 5404 5405 if($Is_C) 5406 { 5407 my $List_T = `$CTAGS -x --c-kinds=gstu --language-force=c $IgnoreTags \"$File\"`; 5408 foreach my $Line (split(/\n/, $List_T)) 5409 { 5410 if($Line=~/\A(\w+)/) 5411 { 5412 my $N = $1; 5413 5414 if($Line!~/\b$N\s+$N\b/) { 5415 $TypeToHeader{$1} = $HName; 5416 } 5417 } 5418 } 5419 } 5420 } 5421 } 5422 5423 if($CacheHeaders) 5424 { 5425 writeFile($PublicHeader_F, Dumper(\%PublicHeader)); 5426 writeFile($SymbolToHeader_F, Dumper(\%SymbolToHeader)); 5427 writeFile($TypeToHeader_F, Dumper(\%TypeToHeader)); 5428 writeFile($Path_F, $Path_A); 5429 } 5430} 5431 5432sub getDebugAltLink($) 5433{ 5434 my $Obj = $_[0]; 5435 5436 my $AltDebugFile = getDebugFile($Obj, "gnu_debugaltlink"); 5437 5438 if(not $AltDebugFile) { 5439 return undef; 5440 } 5441 5442 my $Dir = getDirname($Obj); 5443 5444 my $AltObj_R = $AltDebugFile; 5445 if($Dir and $Dir ne ".") { 5446 $AltObj_R = $Dir."/".$AltObj_R; 5447 } 5448 5449 my $AltObj = $AltObj_R; 5450 5451 while($AltObj=~s&/[^/]+/\.\./&/&){}; 5452 5453 if(-e $AltObj) 5454 { 5455 printMsg("INFO", "Set alternate debug-info file to \'$AltObj\' (use -alt option to change it)"); 5456 return $AltObj; 5457 } 5458 else { 5459 printMsg("WARNING", "can't access \'$AltObj_R\'"); 5460 } 5461 5462 return undef; 5463} 5464 5465sub scenario() 5466{ 5467 if($Help) 5468 { 5469 HELP_MESSAGE(); 5470 exit(0); 5471 } 5472 if($ShowVersion) 5473 { 5474 printMsg("INFO", "ABI Dumper $TOOL_VERSION"); 5475 printMsg("INFO", "Copyright (C) 2016 Andrey Ponomarenko's ABI Laboratory"); 5476 printMsg("INFO", "License: LGPL or GPL <http://www.gnu.org/licenses/>"); 5477 printMsg("INFO", "This program is free software: you can redistribute it and/or modify it.\n"); 5478 printMsg("INFO", "Written by Andrey Ponomarenko."); 5479 exit(0); 5480 } 5481 if($DumpVersion) 5482 { 5483 printMsg("INFO", $TOOL_VERSION); 5484 exit(0); 5485 } 5486 5487 $Data::Dumper::Sortkeys = 1; 5488 5489 if($SortDump) { 5490 $Data::Dumper::Sortkeys = \&dump_sorting; 5491 } 5492 5493 if($SearchDirDebuginfo) 5494 { 5495 if(not -d $SearchDirDebuginfo) { 5496 exitStatus("Access_Error", "can't access directory \'$SearchDirDebuginfo\'"); 5497 } 5498 } 5499 5500 if($PublicHeadersPath) 5501 { 5502 if(not -e $PublicHeadersPath) { 5503 exitStatus("Access_Error", "can't access \'$PublicHeadersPath\'"); 5504 } 5505 5506 foreach my $P (split(/;/, $IncludePaths)) 5507 { 5508 if($P!~/\A\//) { 5509 $P = $PublicHeadersPath."/".$P; 5510 } 5511 5512 if(not -e $P) { 5513 exitStatus("Access_Error", "can't access \'$P\'"); 5514 } 5515 } 5516 } 5517 5518 if($SymbolsListPath) 5519 { 5520 if(not -f $SymbolsListPath) { 5521 exitStatus("Access_Error", "can't access file \'$SymbolsListPath\'"); 5522 } 5523 foreach my $S (split(/\s*\n\s*/, readFile($SymbolsListPath))) { 5524 $SymbolsList{$S} = 1; 5525 } 5526 } 5527 5528 if($VTDumperPath) 5529 { 5530 if(not -x $VTDumperPath) { 5531 exitStatus("Access_Error", "can't access \'$VTDumperPath\'"); 5532 } 5533 5534 $VTABLE_DUMPER = $VTDumperPath; 5535 } 5536 5537 if(defined $Compare) 5538 { 5539 my $P1 = $ARGV[0]; 5540 my $P2 = $ARGV[1]; 5541 5542 if(not $P1) { 5543 exitStatus("Error", "arguments are not specified"); 5544 } 5545 elsif(not -e $P1) { 5546 exitStatus("Access_Error", "can't access \'$P1\'"); 5547 } 5548 5549 if(not $P2) { 5550 exitStatus("Error", "second argument is not specified"); 5551 } 5552 elsif(not -e $P2) { 5553 exitStatus("Access_Error", "can't access \'$P2\'"); 5554 } 5555 5556 my %ABI = (); 5557 5558 $ABI{1} = eval(readFile($P1)); 5559 $ABI{2} = eval(readFile($P2)); 5560 5561 my %SymInfo = (); 5562 5563 foreach (1, 2) 5564 { 5565 foreach my $ID (keys(%{$ABI{$_}->{"SymbolInfo"}})) 5566 { 5567 my $Info = $ABI{$_}->{"SymbolInfo"}{$ID}; 5568 5569 if(my $MnglName = $Info->{"MnglName"}) { 5570 $SymInfo{$_}{$MnglName} = $Info; 5571 } 5572 elsif(my $ShortName = $Info->{"ShortName"}) { 5573 $SymInfo{$_}{$ShortName} = $Info; 5574 } 5575 } 5576 } 5577 5578 foreach my $Symbol (sort keys(%{$SymInfo{1}})) 5579 { 5580 if(not defined $SymInfo{2}{$Symbol}) { 5581 printMsg("INFO", "Removed $Symbol"); 5582 } 5583 } 5584 5585 foreach my $Symbol (sort keys(%{$SymInfo{2}})) 5586 { 5587 if(not defined $SymInfo{1}{$Symbol}) { 5588 printMsg("INFO", "Added $Symbol"); 5589 } 5590 } 5591 5592 exit(0); 5593 } 5594 5595 if(not $TargetVersion) { 5596 printMsg("WARNING", "module version is not specified (-lver NUM)"); 5597 } 5598 5599 if($FullDump) 5600 { 5601 $AllTypes = 1; 5602 $AllSymbols = 1; 5603 } 5604 5605 if(not $OutputDump) { 5606 $OutputDump = "./ABI.dump"; 5607 } 5608 5609 if(not @ARGV) { 5610 exitStatus("Error", "object path is not specified"); 5611 } 5612 5613 foreach my $Obj (@ARGV) 5614 { 5615 if(not -e $Obj) { 5616 exitStatus("Access_Error", "can't access \'$Obj\'"); 5617 } 5618 } 5619 5620 if($AltDebugInfo) 5621 { 5622 if(not -e $AltDebugInfo) { 5623 exitStatus("Access_Error", "can't access \'$AltDebugInfo\'"); 5624 } 5625 read_Alt_Info($AltDebugInfo); 5626 } 5627 5628 if($ExtraInfo) 5629 { 5630 mkpath($ExtraInfo); 5631 $ExtraInfo = abs_path($ExtraInfo); 5632 } 5633 5634 init_ABI(); 5635 5636 my $Res = 0; 5637 5638 foreach my $Obj (@ARGV) 5639 { 5640 $TargetName = getFilename(realpath($Obj)); 5641 $TargetName=~s/\.debug\Z//; # nouveau.ko.debug 5642 5643 if(index($TargetName, "libstdc++.so")==0) { 5644 $STDCXX_TARGET = 1; 5645 } 5646 5647 read_Symbols($Obj); 5648 5649 if(not defined $PublicSymbols_Detected) 5650 { 5651 if(defined $PublicHeadersPath) { 5652 detectPublicSymbols($PublicHeadersPath); 5653 } 5654 } 5655 5656 $Res += read_DWARF_Info($Obj); 5657 5658 %DWARF_Info = (); 5659 %ImportedUnit = (); 5660 %ImportedDecl = (); 5661 5662 read_Vtables($Obj); 5663 } 5664 5665 if(not $Res) { 5666 exitStatus("No_DWARF", "can't find debug info in object(s)"); 5667 } 5668 5669 %VirtualTable = (); 5670 5671 complete_ABI(); 5672 remove_Unused(); 5673 5674 if(defined $PublicHeadersPath) 5675 { 5676 foreach my $Tid (sort {lc($TypeInfo{$a}{"Name"}) cmp lc($TypeInfo{$b}{"Name"})} keys(%TypeInfo)) 5677 { 5678 if(not $TypeInfo{$Tid}{"Header"}) 5679 { 5680 if($TypeInfo{$Tid}{"Type"}=~/Struct|Union|Enum|Typedef/) 5681 { 5682 my $TName = $TypeInfo{$Tid}{"Name"}; 5683 $TName=~s/\A(struct|class|union|enum) //g; 5684 5685 if(defined $TypeToHeader{$TName}) { 5686 $TypeInfo{$Tid}{"Header"} = $TypeToHeader{$TName}; 5687 } 5688 } 5689 } 5690 5691 if(not selectPublicType($Tid)) 5692 { 5693 $TypeInfo{$Tid}{"PrivateABI"} = 1; 5694 } 5695 } 5696 } 5697 5698 %Mangled_ID = (); 5699 %Checked_Spec = (); 5700 %SelectedSymbols = (); 5701 %Cache = (); 5702 5703 %ClassChild = (); 5704 %TypeSpec = (); 5705 5706 # clean memory 5707 %SourceFile = (); 5708 %SourceFile_Alt = (); 5709 %DebugLoc = (); 5710 %TName_Tid = (); 5711 %TName_Tids = (); 5712 %SymbolTable = (); 5713 5714 if(defined $PublicHeadersPath) 5715 { 5716 foreach my $H (keys(%HeadersInfo)) 5717 { 5718 if(not defined $PublicHeader{getFilename($H)}) { 5719 delete($HeadersInfo{$H}); 5720 } 5721 } 5722 } 5723 5724 dump_ABI(); 5725 5726 exit(0); 5727} 5728 5729scenario(); 5730