abi-dumper.pl revision 5d886e3cb2269b158ebda6cbd4ffd49e2ac9d5ee
1#!/usr/bin/perl
2###########################################################################
3# ABI Dumper 0.99.7
4# Dump ABI of an ELF object containing DWARF debug info
5#
6# Copyright (C) 2012-2015 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.0 or newer)
19#
20# COMPATIBILITY
21# =============
22#  ABI Compliance Checker >= 1.99.8
23#
24#
25# This program is free software: you can redistribute it and/or modify
26# it under the terms of the GNU General Public License or the GNU Lesser
27# General Public License as published by the Free Software Foundation.
28#
29# This program is distributed in the hope that it will be useful,
30# but WITHOUT ANY WARRANTY; without even the implied warranty of
31# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32# GNU General Public License for more details.
33#
34# You should have received a copy of the GNU General Public License
35# and the GNU Lesser General Public License along with this program.
36# If not, see <http://www.gnu.org/licenses/>.
37###########################################################################
38use Getopt::Long;
39Getopt::Long::Configure ("posix_default", "no_ignore_case", "permute");
40use File::Path qw(mkpath rmtree);
41use File::Temp qw(tempdir);
42use Cwd qw(abs_path cwd realpath);
43use Storable qw(dclone);
44use Data::Dumper;
45
46my $TOOL_VERSION = "0.99.7";
47my $ABI_DUMP_VERSION = "3.2";
48my $ORIG_DIR = cwd();
49my $TMP_DIR = tempdir(CLEANUP=>1);
50
51my $VTABLE_DUMPER = "vtable-dumper";
52my $VTABLE_DUMPER_VERSION = "1.0";
53
54my ($Help, $ShowVersion, $DumpVersion, $OutputDump, $SortDump, $StdOut,
55$TargetVersion, $ExtraInfo, $FullDump, $AllTypes, $AllSymbols, $BinOnly,
56$SkipCxx, $Loud, $AddrToName, $DumpStatic, $Compare, $AltDebugInfo,
57$OptMem);
58
59my $CmdName = get_filename($0);
60
61my %ERROR_CODE = (
62    "Success"=>0,
63    "Error"=>2,
64    # System command is not found
65    "Not_Found"=>3,
66    # Cannot access input files
67    "Access_Error"=>4,
68    # Cannot find a module
69    "Module_Error"=>9,
70    # No debug-info
71    "No_DWARF"=>10,
72    # Invalid debug-info
73    "Invalid_DWARF"=>11
74);
75
76my $ShortUsage = "ABI Dumper $TOOL_VERSION
77Dump ABI of an ELF object containing DWARF debug info
78Copyright (C) 2015 Andrey Ponomarenko's ABI Laboratory
79License: GNU LGPL or GNU GPL
80
81Usage: $CmdName [options] [object]
82Example:
83  $CmdName libTest.so -o ABI.dump
84  $CmdName Module.ko.debug -o ABI.dump
85
86More info: $CmdName --help\n";
87
88if($#ARGV==-1)
89{
90    printMsg("INFO", $ShortUsage);
91    exit(0);
92}
93
94GetOptions("h|help!" => \$Help,
95  "v|version!" => \$ShowVersion,
96  "dumpversion!" => \$DumpVersion,
97# general options
98  "o|output|dump-path=s" => \$OutputDump,
99  "sort!" => \$SortDump,
100  "stdout!" => \$StdOut,
101  "loud!" => \$Loud,
102  "lver|lv=s" => \$TargetVersion,
103  "extra-info=s" => \$ExtraInfo,
104  "bin-only!" => \$BinOnly,
105  "all-types!" => \$AllTypes,
106  "all-symbols!" => \$AllSymbols,
107  "skip-cxx!" => \$SkipCxx,
108  "all!" => \$FullDump,
109  "dump-static!" => \$DumpStatic,
110  "compare!" => \$Compare,
111  "alt=s" => \$AltDebugInfo,
112  "mem!" =>\$OptMem,
113# internal options
114  "addr2name!" => \$AddrToName
115) or ERR_MESSAGE();
116
117sub ERR_MESSAGE()
118{
119    printMsg("INFO", "\n".$ShortUsage);
120    exit($ERROR_CODE{"Error"});
121}
122
123my $HelpMessage="
124NAME:
125  ABI Dumper ($CmdName)
126  Dump ABI of an ELF object containing DWARF debug info
127
128DESCRIPTION:
129  ABI Dumper is a tool for dumping ABI information of an ELF object
130  containing DWARF debug info.
131
132  The tool is intended to be used with ABI Compliance Checker tool for tracking
133  ABI changes of a C/C++ library or kernel module.
134
135  This tool is free software: you can redistribute it and/or modify it
136  under the terms of the GNU LGPL or GNU GPL.
137
138USAGE:
139  $CmdName [options] [object]
140
141EXAMPLES:
142  $CmdName libTest.so -o ABI.dump
143  $CmdName Module.ko.debug -o ABI.dump
144
145INFORMATION OPTIONS:
146  -h|-help
147      Print this help.
148
149  -v|-version
150      Print version information.
151
152  -dumpversion
153      Print the tool version ($TOOL_VERSION) and don't do anything else.
154
155GENERAL OPTIONS:
156  -o|-output PATH
157      Path to the output ABI dump file.
158      Default: ./ABI.dump
159
160  -sort
161      Sort data in ABI dump.
162
163  -stdout
164      Print ABI dump to stdout.
165
166  -loud
167      Print all warnings.
168
169  -lv|-lver NUM
170      Set version of the library to NUM.
171
172  -extra-info DIR
173      Dump extra analysis info to DIR.
174
175  -bin-only
176      Do not dump information about inline functions,
177      pure virtual functions and non-exported global data.
178
179  -all-types
180      Dump unused data types.
181
182  -all-symbols
183      Dump symbols not exported by the object.
184
185  -skip-cxx
186      Do not dump stdc++ and gnu c++ symbols.
187
188  -all
189      Equal to: -all-types -all-symbols.
190
191  -dump-static
192      Dump static (local) symbols.
193
194  -compare OLD.dump NEW.dump
195      Show added/removed symbols between two ABI dumps.
196
197  -alt PATH
198      Path to the alternate debug info (Fedora). It is
199      detected automatically from gnu_debugaltlink section
200      of the input object if not specified.
201
202  -mem
203      Try to optimize system memory usage.
204";
205
206sub HELP_MESSAGE() {
207    printMsg("INFO", $HelpMessage);
208}
209
210my %Cache;
211
212# Input
213my %DWARF_Info;
214
215# Alternate
216my %ImportedUnit;
217my %ImportedDecl;
218
219# Output
220my %SymbolInfo;
221my %TypeInfo;
222
223# Reader
224my %TypeMember;
225my %ArrayCount;
226my %FuncParam;
227my %Inheritance;
228my %NameSpace;
229my %SpecElem;
230my %OrigElem;
231my %ClassMethods;
232my %TypeSpec;
233my %ClassChild;
234
235my %MergedTypes;
236my %LocalType;
237
238my %SourceFile;
239my %SourceFile_Alt;
240my %DebugLoc;
241my %TName_Tid;
242my %TName_Tids;
243my %RegName;
244
245my $STDCXX_TARGET = 0;
246my $GLOBAL_ID = 0;
247my %ANON_TYPE = ();
248
249my %Mangled_ID;
250my %Checked_Spec;
251my %SelectedSymbols;
252
253my %TypeType = (
254    "class_type"=>"Class",
255    "structure_type"=>"Struct",
256    "union_type"=>"Union",
257    "enumeration_type"=>"Enum",
258    "array_type"=>"Array",
259    "base_type"=>"Intrinsic",
260    "const_type"=>"Const",
261    "pointer_type"=>"Pointer",
262    "reference_type"=>"Ref",
263    "volatile_type"=>"Volatile",
264    "typedef"=>"Typedef",
265    "ptr_to_member_type"=>"FieldPtr"
266);
267
268my %Qual = (
269    "Pointer"=>"*",
270    "Ref"=>"&",
271    "Volatile"=>"volatile",
272    "Const"=>"const"
273);
274
275my $HEADER_EXT = "h|hh|hp|hxx|hpp|h\\+\\+|tcc";
276my $SRC_EXT = "c|cpp|cxx|c\\+\\+";
277
278# Other
279my %NestedNameSpaces;
280my $TargetName;
281my %SysInfo;
282my %HeadersInfo;
283my %SourcesInfo;
284my %SymVer;
285my %UsedType;
286
287# ELF
288my %Library_Symbol;
289my %Library_LocalSymbol;
290my %Library_UndefSymbol;
291my %Library_Needed;
292my %SymbolTable;
293my %SectionInfo;
294
295# VTables
296my %VirtualTable;
297
298# Env
299my $SYS_ARCH;
300my $SYS_WORD;
301my $SYS_GCCV;
302my $SYS_COMP;
303
304my $LIB_LANG;
305
306sub printMsg($$)
307{
308    my ($Type, $Msg) = @_;
309    if($Type!~/\AINFO/) {
310        $Msg = $Type.": ".$Msg;
311    }
312    if($Type!~/_C\Z/) {
313        $Msg .= "\n";
314    }
315    if($Type eq "ERROR") {
316        print STDERR $Msg;
317    }
318    else {
319        print $Msg;
320    }
321}
322
323sub exitStatus($$)
324{
325    my ($Code, $Msg) = @_;
326    printMsg("ERROR", $Msg);
327    exit($ERROR_CODE{$Code});
328}
329
330sub cmpVersions($$)
331{ # compare two versions in dotted-numeric format
332    my ($V1, $V2) = @_;
333    return 0 if($V1 eq $V2);
334    return undef if($V1!~/\A\d+[\.\d+]*\Z/);
335    return undef if($V2!~/\A\d+[\.\d+]*\Z/);
336    my @V1Parts = split(/\./, $V1);
337    my @V2Parts = split(/\./, $V2);
338    for (my $i = 0; $i <= $#V1Parts && $i <= $#V2Parts; $i++) {
339        return -1 if(int($V1Parts[$i]) < int($V2Parts[$i]));
340        return 1 if(int($V1Parts[$i]) > int($V2Parts[$i]));
341    }
342    return -1 if($#V1Parts < $#V2Parts);
343    return 1 if($#V1Parts > $#V2Parts);
344    return 0;
345}
346
347sub writeFile($$)
348{
349    my ($Path, $Content) = @_;
350    return if(not $Path);
351    if(my $Dir = get_dirname($Path)) {
352        mkpath($Dir);
353    }
354    open(FILE, ">", $Path) || die ("can't open file \'$Path\': $!\n");
355    print FILE $Content;
356    close(FILE);
357}
358
359sub readFile($)
360{
361    my $Path = $_[0];
362    return "" if(not $Path or not -f $Path);
363    open(FILE, $Path);
364    local $/ = undef;
365    my $Content = <FILE>;
366    close(FILE);
367    return $Content;
368}
369
370sub get_filename($)
371{ # much faster than basename() from File::Basename module
372    if($_[0] and $_[0]=~/([^\/\\]+)[\/\\]*\Z/) {
373        return $1;
374    }
375    return "";
376}
377
378sub get_dirname($)
379{ # much faster than dirname() from File::Basename module
380    if($_[0] and $_[0]=~/\A(.*?)[\/\\]+[^\/\\]*[\/\\]*\Z/) {
381        return $1;
382    }
383    return "";
384}
385
386sub check_Cmd($)
387{
388    my $Cmd = $_[0];
389    return "" if(not $Cmd);
390    if(defined $Cache{"check_Cmd"}{$Cmd}) {
391        return $Cache{"check_Cmd"}{$Cmd};
392    }
393    foreach my $Path (sort {length($a)<=>length($b)} split(/:/, $ENV{"PATH"}))
394    {
395        if(-x $Path."/".$Cmd) {
396            return ($Cache{"check_Cmd"}{$Cmd} = 1);
397        }
398    }
399    return ($Cache{"check_Cmd"}{$Cmd} = 0);
400}
401
402my %ELF_BIND = map {$_=>1} (
403    "WEAK",
404    "GLOBAL",
405    "LOCAL"
406);
407
408my %ELF_TYPE = map {$_=>1} (
409    "FUNC",
410    "IFUNC",
411    "OBJECT",
412    "COMMON"
413);
414
415my %ELF_VIS = map {$_=>1} (
416    "DEFAULT",
417    "PROTECTED"
418);
419
420sub readline_ELF($)
421{ # read the line of 'eu-readelf' output corresponding to the symbol
422    my @Info = split(/\s+/, $_[0]);
423    #  Num:   Value      Size Type   Bind   Vis       Ndx  Name
424    #  3629:  000b09c0   32   FUNC   GLOBAL DEFAULT   13   _ZNSt12__basic_fileIcED1Ev@@GLIBCXX_3.4
425    #  135:   00000000    0   FUNC   GLOBAL DEFAULT   UNDEF  av_image_fill_pointers@LIBAVUTIL_52 (3)
426    shift(@Info) if($Info[0] eq ""); # spaces
427    shift(@Info); # num
428
429    if($#Info==7)
430    { # UNDEF SYMBOL (N)
431        if($Info[7]=~/\(\d+\)/) {
432            pop(@Info);
433        }
434    }
435
436    if($#Info!=6)
437    { # other lines
438        return ();
439    }
440    return () if(not defined $ELF_TYPE{$Info[2]} and $Info[5] ne "UNDEF");
441    return () if(not defined $ELF_BIND{$Info[3]});
442    return () if(not defined $ELF_VIS{$Info[4]});
443    if($Info[5] eq "ABS" and $Info[0]=~/\A0+\Z/)
444    { # 1272: 00000000     0 OBJECT  GLOBAL DEFAULT  ABS CXXABI_1.3
445        return ();
446    }
447    if(index($Info[2], "0x") == 0)
448    { # size == 0x3d158
449        $Info[2] = hex($Info[2]);
450    }
451    return @Info;
452}
453
454sub read_Symbols($)
455{
456    my $Lib_Path = $_[0];
457    my $Lib_Name = get_filename($Lib_Path);
458
459    my $Dynamic = ($Lib_Name=~/\.so(\.|\Z)/);
460    my $Dbg = ($Lib_Name=~/\.debug\Z/);
461
462    my $Readelf = "eu-readelf";
463    if(not check_Cmd($Readelf)) {
464        exitStatus("Not_Found", "can't find \"eu-readelf\"");
465    }
466
467    my $Cmd = $Readelf." -S \"$Lib_Path\" 2>\"$TMP_DIR/error\"";
468    foreach (split(/\n/, `$Cmd`))
469    {
470        if(/\[\s*(\d+)\]\s+([\w\.]+)/)
471        {
472            $SectionInfo{$1} = $2;
473        }
474    }
475
476    if($Dynamic)
477    { # dynamic library specifics
478        $Cmd = $Readelf." -d \"$Lib_Path\" 2>\"$TMP_DIR/error\"";
479        foreach (split(/\n/, `$Cmd`))
480        {
481            if(/NEEDED.+\[([^\[\]]+)\]/)
482            { # dependencies:
483              # 0x00000001 (NEEDED) Shared library: [libc.so.6]
484                $Library_Needed{$1} = 1;
485            }
486        }
487    }
488
489    my $ExtraPath = "";
490
491    if($ExtraInfo)
492    {
493        $ExtraPath = $ExtraInfo."/".$Lib_Name;
494        mkpath($ExtraPath);
495        $ExtraPath .= "/elf-info";
496    }
497
498    $Cmd = $Readelf." -s \"$Lib_Path\" 2>\"$TMP_DIR/error\"";
499
500    if($ExtraPath)
501    { # debug mode
502        # write to file
503        system($Cmd." >\"$ExtraPath\"");
504        open(LIB, $ExtraPath);
505    }
506    else
507    { # write to pipe
508        open(LIB, $Cmd." |");
509    }
510
511    my (%Symbol_Value, %Value_Symbol) = ();
512
513    my $symtab = undef; # indicates that we are processing 'symtab' section of 'readelf' output
514    while(<LIB>)
515    {
516        if($Dynamic and not $Dbg)
517        { # dynamic library specifics
518            if(defined $symtab)
519            {
520                if(index($_, "'.dynsym'")!=-1)
521                { # dynamic table
522                    $symtab = undef;
523                }
524                if(not $AllSymbols)
525                { # do nothing with symtab
526                    next;
527                }
528            }
529            elsif(index($_, "'.symtab'")!=-1)
530            { # symbol table
531                $symtab = 1;
532            }
533        }
534        if(my ($Value, $Size, $Type, $Bind, $Vis, $Ndx, $Symbol) = readline_ELF($_))
535        { # read ELF entry
536            if(not $symtab)
537            { # dynsym
538                if(skipSymbol($Symbol)) {
539                    next;
540                }
541
542                if($Ndx eq "UNDEF")
543                { # ignore interfaces that are imported from somewhere else
544                    $Library_UndefSymbol{$TargetName}{$Symbol} = 0;
545                    next;
546                }
547
548                if($Bind eq "LOCAL") {
549                    $Library_LocalSymbol{$TargetName}{$Symbol} = ($Type eq "OBJECT")?-$Size:1;
550                }
551                else {
552                    $Library_Symbol{$TargetName}{$Symbol} = ($Type eq "OBJECT")?-$Size:1;
553                }
554
555                $Symbol_Value{$Symbol} = $Value;
556                $Value_Symbol{$Value}{$Symbol} = 1;
557            }
558
559            foreach ($SectionInfo{$Ndx}, "")
560            {
561                my $Val = $Value;
562
563                $SymbolTable{$_}{$Val}{$Symbol} = 1;
564
565                if($Val=~s/\A[0]+//)
566                {
567                    if($Val eq "") {
568                        $Val = "0";
569                    }
570                    $SymbolTable{$_}{$Val}{$Symbol} = 1;
571                }
572            }
573        }
574    }
575    close(LIB);
576
577    my %Found = ();
578    foreach my $Symbol (keys(%{$Library_Symbol{$TargetName}}))
579    {
580        next if(index($Symbol,"\@")==-1);
581        if(my $Value = $Symbol_Value{$Symbol})
582        {
583            foreach my $Symbol_SameValue (keys(%{$Value_Symbol{$Value}}))
584            {
585                if($Symbol_SameValue ne $Symbol
586                and index($Symbol_SameValue,"\@")==-1)
587                {
588                    $SymVer{$Symbol_SameValue} = $Symbol;
589                    $Found{$Symbol} = 1;
590                    last;
591                }
592            }
593        }
594    }
595
596    # default
597    foreach my $Symbol (keys(%{$Library_Symbol{$TargetName}}))
598    {
599        next if(defined $Found{$Symbol});
600        next if(index($Symbol,"\@\@")==-1);
601
602        if($Symbol=~/\A([^\@]*)\@\@/
603        and not $SymVer{$1})
604        {
605            $SymVer{$1} = $Symbol;
606            $Found{$Symbol} = 1;
607        }
608    }
609
610    # non-default
611    foreach my $Symbol (keys(%{$Library_Symbol{$TargetName}}))
612    {
613        next if(defined $Found{$Symbol});
614        next if(index($Symbol,"\@")==-1);
615
616        if($Symbol=~/\A([^\@]*)\@([^\@]*)/
617        and not $SymVer{$1})
618        {
619            $SymVer{$1} = $Symbol;
620            $Found{$Symbol} = 1;
621        }
622    }
623}
624
625sub read_Alt_Info($)
626{
627    my $Path = $_[0];
628    my $Name = get_filename($Path);
629
630    my $Readelf = "eu-readelf";
631    if(not check_Cmd($Readelf)) {
632        exitStatus("Not_Found", "can't find \"$Readelf\" command");
633    }
634
635    printMsg("INFO", "Reading alternate debug-info");
636
637    my $ExtraPath = undef;
638
639    # lines info
640
641    if($ExtraInfo)
642    {
643        $ExtraPath = $ExtraInfo."/".$Name;
644        mkpath($ExtraPath);
645        $ExtraPath .= "/debug_line";
646    }
647
648    if($ExtraPath)
649    {
650        system("$Readelf -N --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\"");
651        open(SRC, $ExtraPath);
652    }
653    else {
654        open(SRC, "$Readelf -N --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" |");
655    }
656
657    my $Offset = undef;
658
659    while(<SRC>)
660    {
661        if(/(\d+)\s+\d+\s+\d+\s+\d+\s+([^ ]+)/)
662        {
663            my ($Num, $File) = ($1, $2);
664            chomp($File);
665
666            $SourceFile_Alt{0}{$Num} = $File;
667        }
668    }
669    close(SRC);
670
671    # debug info
672
673    if($ExtraInfo)
674    {
675        $ExtraPath = $ExtraInfo."/".$Name;
676        mkpath($ExtraPath);
677        $ExtraPath .= "/debug_info";
678    }
679
680    if($ExtraPath)
681    {
682        system("$Readelf -N --debug-dump=info \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\"");
683        open(INFO, $ExtraPath);
684    }
685    else {
686        open(INFO, "$Readelf -N --debug-dump=info \"$Path\" 2>\"$TMP_DIR/error\" |");
687    }
688
689    my $ID = undef;
690    my $Num = 0;
691
692    while(<INFO>)
693    {
694        if(index($_, "  ")==0)
695        {
696            if(defined $ID) {
697                $ImportedUnit{$ID}{$Num++} = $_;
698            }
699        }
700        elsif(index($_, " [")==0
701        and /\A \[\s*(\w+?)\](\s+)(\w+)/)
702        {
703            if($3 eq "partial_unit")
704            {
705                $ID = $1;
706                $Num = 0;
707                $ImportedUnit{$ID}{0} = $_;
708            }
709            elsif(length($2)==2)
710            { # not a partial_unit
711                $ID = undef;
712            }
713            elsif(defined $ID)
714            {
715                $ImportedDecl{$1} = $ID;
716                $ImportedUnit{$ID}{$Num++} = $_;
717            }
718        }
719    }
720}
721
722sub read_DWARF_Info($)
723{
724    my $Path = $_[0];
725
726    my $Dir = get_dirname($Path);
727    my $Name = get_filename($Path);
728
729    my $Readelf = "eu-readelf";
730    if(not check_Cmd($Readelf)) {
731        exitStatus("Not_Found", "can't find \"$Readelf\" command");
732    }
733
734    my $AddOpt = "";
735    if(not defined $AddrToName)
736    { # disable search of symbol names
737        $AddOpt .= " -N";
738    }
739
740    my $Sect = `$Readelf -S \"$Path\" 2>\"$TMP_DIR/error\"`;
741
742    if($Sect!~/\.debug_info/)
743    { # No DWARF info
744        return 0;
745    }
746
747    printMsg("INFO", "Reading debug-info");
748
749    my $ExtraPath = "";
750
751    # ELF header
752    if($ExtraInfo)
753    {
754        $ExtraPath = $ExtraInfo."/".$Name;
755        mkpath($ExtraPath);
756        $ExtraPath .= "/elf-header";
757    }
758
759    if($ExtraPath)
760    {
761        system($Readelf." -h \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\"");
762        open(HEADER, $ExtraPath);
763    }
764    else {
765        open(HEADER, $Readelf." -h \"$Path\" 2>\"$TMP_DIR/error\" |");
766    }
767
768    my %Header = ();
769    while(<HEADER>)
770    {
771        if(/\A\s*([\w ]+?)\:\s*(.+?)\Z/) {
772            $Header{$1} = $2;
773        }
774    }
775    close(HEADER);
776
777    $SYS_ARCH = $Header{"Machine"};
778
779    if($SYS_ARCH=~/80\d86/
780    or $SYS_ARCH=~/i\d86/)
781    { # i386, i586, etc.
782        $SYS_ARCH = "x86";
783    }
784
785    if($SYS_ARCH=~/amd64/i
786    or $SYS_ARCH=~/x86\-64/i)
787    { # amd64
788        $SYS_ARCH = "x86_64";
789    }
790
791    # source info
792    if($ExtraInfo)
793    {
794        $ExtraPath = $ExtraInfo."/".$Name;
795        mkpath($ExtraPath);
796        $ExtraPath .= "/debug_line";
797    }
798
799    if($ExtraPath)
800    {
801        system("$Readelf $AddOpt --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\"");
802        open(SRC, $ExtraPath);
803    }
804    else {
805        open(SRC, "$Readelf $AddOpt --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" |");
806    }
807
808    my $Offset = undef;
809
810    while(<SRC>)
811    {
812        if(/Table at offset (\w+)/)
813        {
814            $Offset = $1;
815        }
816        elsif(defined $Offset
817        and /(\d+)\s+\d+\s+\d+\s+\d+\s+([^ ]+)/)
818        {
819            my ($Num, $File) = ($1, $2);
820            chomp($File);
821
822            $SourceFile{$Offset}{$Num} = $File;
823        }
824    }
825    close(SRC);
826
827    # debug_loc
828    if($ExtraInfo)
829    {
830        $ExtraPath = $ExtraInfo."/".$Name;
831        mkpath($ExtraPath);
832        $ExtraPath .= "/debug_loc";
833    }
834
835    if($ExtraPath)
836    {
837        system("$Readelf $AddOpt --debug-dump=loc \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\"");
838        open(LOC, $ExtraPath);
839    }
840    else {
841        open(LOC, "$Readelf $AddOpt --debug-dump=loc \"$Path\" 2>\"$TMP_DIR/error\" |");
842    }
843
844    while(<LOC>)
845    {
846        if(/\[\s*(\w+)\].*\[\s*\w+\]\s*(.+)\Z/) {
847            $DebugLoc{$1} = $2;
848        }
849    }
850    close(LOC);
851
852    # dwarf
853    if($ExtraInfo)
854    {
855        $ExtraPath = $ExtraInfo."/".$Name;
856        mkpath($ExtraPath);
857        $ExtraPath .= "/debug_info";
858    }
859
860    my $INFO_fh;
861
862    if($Dir)
863    { # to find ".dwz" directory (Fedora)
864        chdir($Dir);
865    }
866    if($ExtraPath)
867    {
868        system("$Readelf $AddOpt --debug-dump=info \"$Name\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\"");
869        open($INFO_fh, $ExtraPath);
870    }
871    else {
872        open($INFO_fh, "$Readelf $AddOpt --debug-dump=info \"$Name\" 2>\"$TMP_DIR/error\" |");
873    }
874    chdir($ORIG_DIR);
875
876    read_DWARF_Dump($INFO_fh, 1);
877
878    close($INFO_fh);
879
880    if(my $Err = readFile("$TMP_DIR/error"))
881    { # eu-readelf: cannot get next DIE: invalid DWARF
882        if($Err=~/invalid DWARF/i) {
883            exitStatus("Invalid_DWARF", "invalid DWARF info");
884        }
885    }
886
887    return 1;
888}
889
890sub getSource($)
891{
892    my $ID = $_[0];
893
894    if(defined $DWARF_Info{$ID}{"decl_file"})
895    {
896        my $File = $DWARF_Info{$ID}{"decl_file"};
897        my $Unit = $DWARF_Info{$ID}{"Unit"};
898
899        my $Name = undef;
900
901        if($ID>=0) {
902            $Name = $SourceFile{$Unit}{$File};
903        }
904        else
905        { # imported
906            $Name = $SourceFile_Alt{0}{$File};
907        }
908
909        return $Name;
910    }
911
912    return undef;
913}
914
915sub read_DWARF_Dump($$)
916{
917    my ($FH, $Primary) = @_;
918
919    my %TypeUnit = ();
920    my %Post_Change = ();
921    my $TypeUnit_Sign = undef;
922    my $TypeUnit_Offset = undef;
923    my $Type_Offset = undef;
924
925    my $Shift_Enabled = 1;
926    my $ID_Shift = undef;
927
928    my $CUnit = undef;
929    my $CUnit_F = undef;
930
931    my $ID = undef;
932    my $Kind = undef;
933    my $NS = undef;
934
935    my $MAX_ID = undef;
936
937    my %Shift = map {$_=>1} (
938        "specification",
939        "type",
940        "sibling",
941        "object_pointer",
942        "containing_type",
943        "abstract_origin",
944        "import",
945        "signature"
946    );
947
948    my $Line = undef;
949    my $Import = undef;
950    my $Import_Num = 0;
951
952    my %UsedUnit;
953    my %UsedDecl;
954
955    my $ID_Pre = undef; # mem opt
956    my %Mangled_Subs = (); # mem opt
957    my %Simple_Types = ();
958    my %Type_Base = ();
959
960    my %SkipNode = (
961        "imported_declaration" => 1,
962        "imported_module" => 1
963    );
964
965    my %SkipAttr = (
966        "high_pc" => 1,
967        "frame_base" => 1,
968        "encoding" => 1
969    );
970
971    my %MarkByUnit = (
972        "member" => 1,
973        "subprogram" => 1,
974        "variable" => 1
975    );
976
977    my %Excess_IDs;
978    my %Delete_Src;
979
980    my $Lexical_Block = undef;
981    my $Inlined_Block = undef;
982    my $Subprogram_Block = undef;
983    my $Skip_Block = undef;
984
985    while(($Import and $Line = $ImportedUnit{$Import}{$Import_Num}) or $Line = <$FH>)
986    {
987        if(not defined $ImportedUnit{$Import}{$Import_Num})
988        {
989            $Import_Num = 0;
990            delete($ImportedUnit{$Import});
991            $Import = undef;
992        }
993
994        if($Import) {
995            $Import_Num++;
996        }
997        if($ID and $Line=~/\A\s*(\w+)\s*(.+?)\s*\Z/)
998        {
999
1000            if(defined $Skip_Block) {
1001                next;
1002            }
1003
1004            my ($Attr, $Val) = ($1, $2);
1005
1006            if($Kind eq "member"
1007            or $Kind eq "formal_parameter")
1008            {
1009                if($Attr eq "decl_file" or $Attr eq "decl_line")
1010                {
1011                    $Delete_Src{$ID} = 1;
1012                }
1013            }
1014            elsif($Kind eq "imported_unit")
1015            {
1016                if($Attr eq "import")
1017                {
1018                    if($Val=~/\(GNU_ref_alt\)\s*\[\s*(\w+?)\]/)
1019                    {
1020                        if(defined $ImportedUnit{$1})
1021                        {
1022                            $Import = $1;
1023                            $Import_Num = 0;
1024                            $UsedUnit{$Import} = 1;
1025                        }
1026                    }
1027                }
1028            }
1029
1030            if($Kind eq "member")
1031            {
1032                if($Attr eq "data_member_location")
1033                {
1034                    delete($DWARF_Info{$ID}{"Unit"});
1035                }
1036            }
1037
1038            if($Kind ne "structure_type")
1039            {
1040                if($Attr eq "sibling")
1041                {
1042                    next;
1043                }
1044            }
1045
1046            if($Attr eq "Type")
1047            {
1048                if($Line=~/Type\s+signature:\s*0x(\w+)/) {
1049                    $TypeUnit_Sign = $1;
1050                }
1051                if($Line=~/Type\s+offset:\s*0x(\w+)/) {
1052                    $Type_Offset = hex($1);
1053                }
1054                if($Line=~/Type\s+unit\s+at\s+offset\s+(\d+)/) {
1055                    $TypeUnit_Offset = $1;
1056                }
1057                next;
1058            }
1059            elsif(defined $SkipAttr{$Attr})
1060            { # unused
1061                next;
1062            }
1063
1064            if($Val=~/\A\s*\(([^()]*)\)\s*\[\s*(\w+)\]\s*\Z/)
1065            { # ref4, ref_udata, ref_addr, etc.
1066                $Val = hex($2);
1067
1068                if($1 eq "GNU_ref_alt")
1069                {
1070                    $Val = -$Val;
1071                    $UsedDecl{$2} = 1;
1072                }
1073            }
1074            elsif($Attr eq "name")
1075            {
1076                $Val=~s/\A\([^()]*\)\s*\"(.*)\"\Z/$1/;
1077            }
1078            elsif(index($Attr, "linkage_name")!=-1)
1079            {
1080                $Val=~s/\A\([^()]*\)\s*\"(.*)\"\Z/$1/;
1081                $Attr = "linkage_name";
1082            }
1083            elsif(index($Attr, "location")!=-1)
1084            {
1085                if($Val=~/\)\s*\Z/)
1086                { # value on the next line
1087                    if($Import) {
1088                        $Val .= $ImportedUnit{$Import}{$Import_Num}
1089                    }
1090                    else {
1091                        $Val .= <$FH>;
1092                    }
1093                }
1094
1095                if($Val=~/\A\(\w+\)\s*(-?)(\w+)\Z/)
1096                { # (data1) 1c
1097                    $Val = hex($2);
1098                    if($1) {
1099                        $Val = -$Val;
1100                    }
1101                }
1102                else
1103                {
1104                    if($Val=~/ (-?\d+)\Z/) {
1105                        $Val = $1;
1106                    }
1107                    else
1108                    {
1109                        if($Attr eq "location"
1110                        and $Kind eq "formal_parameter")
1111                        {
1112                            if($Val=~/location list\s+\[\s*(\w+)\]\Z/)
1113                            {
1114                                $Attr = "location_list";
1115                                $Val = $1;
1116                            }
1117                            elsif($Val=~/ reg(\d+)\Z/)
1118                            {
1119                                $Attr = "register";
1120                                $Val = $1;
1121                            }
1122                        }
1123                    }
1124                }
1125            }
1126            elsif($Attr eq "accessibility")
1127            {
1128                $Val=~s/\A\(.+?\)\s*//;
1129                $Val=~s/\s*\(.+?\)\Z//;
1130                if($Val eq "public")
1131                { # public by default
1132                    next;
1133                }
1134            }
1135            else
1136            {
1137                $Val=~s/\A\(\w+\)\s*//;
1138
1139                if(substr($Val, 0, 1) eq "{"
1140                and $Val=~/{(.+)}/)
1141                { # {ID}
1142                    $Val = $1;
1143                    $Post_Change{$ID} = 1;
1144                }
1145            }
1146
1147            if($Shift_Enabled and $ID_Shift)
1148            {
1149                if(defined $Shift{$Attr}
1150                and not $Post_Change{$ID}) {
1151                    $Val += $ID_Shift;
1152                }
1153
1154                $DWARF_Info{$ID}{"rID"} = $ID-$ID_Shift;
1155            }
1156
1157            if($Import or not $Primary)
1158            {
1159                if(defined $Shift{$Attr})
1160                {
1161                    $Val = -$Val;
1162                }
1163            }
1164
1165            $DWARF_Info{$ID}{$Attr} = "$Val";
1166
1167            if($Kind eq "compile_unit")
1168            {
1169                if($Attr eq "stmt_list") {
1170                    $CUnit = $Val;
1171                }
1172
1173                if(not defined $CUnit_F) {
1174                    $CUnit_F = $ID;
1175                }
1176            }
1177
1178            if($Kind eq "type_unit")
1179            {
1180                if($Attr eq "stmt_list") {
1181                    $CUnit = $Val;
1182                }
1183            }
1184
1185            if($Kind eq "partial_unit" and not $Import)
1186            { # support for dwz
1187                if($Attr eq "stmt_list") {
1188                    $CUnit = $Val;
1189                }
1190            }
1191        }
1192        elsif($Line=~/\A \[\s*(\w+)\](\s*)(\w+)/)
1193        {
1194            $ID = hex($1);
1195            $NS = length($2);
1196            $Kind = $3;
1197
1198            $Skip_Block = undef;
1199
1200            if(defined $SkipNode{$Kind})
1201            {
1202                $Skip_Block = 1;
1203                next;
1204            }
1205
1206            if($Kind eq "lexical_block")
1207            {
1208                $Lexical_Block = $NS;
1209                $Skip_Block = 1;
1210                next;
1211            }
1212            else
1213            {
1214                if(defined $Lexical_Block)
1215                {
1216                    if($NS>$Lexical_Block)
1217                    {
1218                        $Skip_Block = 1;
1219                        next;
1220                    }
1221                    else
1222                    { # end of lexical block
1223                        $Lexical_Block = undef;
1224                    }
1225                }
1226            }
1227
1228            if($Kind eq "inlined_subroutine")
1229            {
1230                $Inlined_Block = $NS;
1231                $Skip_Block = 1;
1232                next;
1233            }
1234            else
1235            {
1236                if(defined $Inlined_Block)
1237                {
1238                    if($NS>$Inlined_Block)
1239                    {
1240                        $Skip_Block = 1;
1241                        next;
1242                    }
1243                    else
1244                    { # end of inlined subroutine
1245                        $Inlined_Block = undef;
1246                    }
1247                }
1248            }
1249
1250            if($Kind eq "subprogram")
1251            {
1252                $Subprogram_Block = $NS;
1253            }
1254            else
1255            {
1256                if(defined $Subprogram_Block)
1257                {
1258                    if($NS>$Subprogram_Block)
1259                    {
1260                        if($Kind eq "variable")
1261                        { # temp variables
1262                            $Skip_Block = 1;
1263                            next;
1264                        }
1265                    }
1266                    else
1267                    { # end of subprogram block
1268                        $Subprogram_Block = undef;
1269                    }
1270                }
1271            }
1272
1273            if($Import or not $Primary)
1274            {
1275                $ID = -$ID;
1276            }
1277
1278            if($Shift_Enabled)
1279            {
1280                if($Kind eq "type_unit")
1281                {
1282                    if(not defined $ID_Shift)
1283                    {
1284                        if($ID_Shift<=$MAX_ID) {
1285                            $ID_Shift = $MAX_ID;
1286                        }
1287                        else {
1288                            $ID_Shift = 0;
1289                        }
1290                    }
1291                }
1292
1293                if($ID_Shift) {
1294                    $ID += $ID_Shift;
1295                }
1296            }
1297
1298            if(defined $TypeUnit_Sign)
1299            {
1300                if($Kind ne "type_unit"
1301                and $Kind ne "namespace")
1302                {
1303                    if($TypeUnit_Offset+$Type_Offset+$ID_Shift==$ID)
1304                    {
1305                        $TypeUnit{$TypeUnit_Sign} = "$ID";
1306                        $TypeUnit_Sign = undef;
1307                    }
1308                }
1309            }
1310
1311            if(defined $ID_Pre)
1312            {
1313                my $Kind_Pre = $DWARF_Info{$ID_Pre}{"Kind"};
1314
1315                if(my $Sp = $DWARF_Info{$ID_Pre}{"specification"})
1316                {
1317                    if(defined $Delete_Src{$Sp})
1318                    {
1319                        delete($Delete_Src{$Sp});
1320                    }
1321                }
1322
1323                if(defined $OptMem)
1324                { # optimize memory usage
1325                    if($Kind_Pre eq "subprogram")
1326                    {
1327                        my $Mangled = get_Mangled($ID_Pre);
1328
1329                        if($Mangled)
1330                        {
1331                            if(my $OLD_ID = $Mangled_Subs{$Mangled})
1332                            {
1333                                if(keys(%{$DWARF_Info{$OLD_ID}})==keys(%{$DWARF_Info{$ID_Pre}}))
1334                                {
1335                                    if(getSource($OLD_ID) eq getSource($ID_Pre))
1336                                    {
1337                                        $Excess_IDs{$ID_Pre} = 1;
1338                                    }
1339                                }
1340                            }
1341                            else {
1342                                $Mangled_Subs{$Mangled} = $ID_Pre;
1343                            }
1344                        }
1345
1346                        if(not $Excess_IDs{$ID_Pre})
1347                        {
1348                            if(my $Sp = $DWARF_Info{$ID_Pre}{"specification"})
1349                            {
1350                                delete($Excess_IDs{$Sp});
1351                            }
1352                            elsif(my $Orig = $DWARF_Info{$ID_Pre}{"abstract_origin"})
1353                            {
1354                                delete($Excess_IDs{$Orig});
1355                            }
1356                        }
1357                    }
1358                    elsif($NS==4)
1359                    { # simple types
1360                        if($Kind_Pre=~/typedef|base_type|structure_type/)
1361                        {
1362                            my $Name = $DWARF_Info{$ID_Pre}{"name"};
1363                            if(not $Name)
1364                            {
1365                                if(my $Sp = $DWARF_Info{$ID_Pre}{"specification"})
1366                                {
1367                                    $Name = $DWARF_Info{$Sp}{"name"};
1368                                }
1369                            }
1370
1371                            if($Name)
1372                            {
1373                                if(my $OLD_ID = $Simple_Types{$Name})
1374                                {
1375                                    if(keys(%{$DWARF_Info{$OLD_ID}})==keys(%{$DWARF_Info{$ID_Pre}}))
1376                                    {
1377                                        if(getSource($OLD_ID) eq getSource($ID_Pre))
1378                                        {
1379                                            $Excess_IDs{$ID_Pre} = 1;
1380                                            $Type_Base{$ID_Pre} = $OLD_ID;
1381                                        }
1382                                    }
1383                                }
1384                                else {
1385                                    $Simple_Types{$Name} = $ID_Pre;
1386                                }
1387                            }
1388                        }
1389                    }
1390                }
1391            }
1392
1393            $DWARF_Info{$ID}{"Kind"} = $Kind;
1394            $DWARF_Info{$ID}{"NS"} = $NS;
1395
1396            $ID_Pre = $ID;
1397
1398            if(defined $CUnit)
1399            {
1400                if(defined $MarkByUnit{$Kind}
1401                or defined $TypeType{$Kind}) {
1402                    $DWARF_Info{$ID}{"Unit"} = $CUnit;
1403                }
1404            }
1405
1406            if(not defined $ID_Shift) {
1407                $MAX_ID = $ID;
1408            }
1409        }
1410        elsif(not defined $SYS_WORD
1411        and $Line=~/Address\s*size:\s*(\d+)/i)
1412        {
1413            $SYS_WORD = $1;
1414        }
1415    }
1416
1417    %Mangled_Subs = ();
1418
1419    foreach my $ID (keys(%Post_Change))
1420    {
1421        if(my $Type = $DWARF_Info{$ID}{"type"})
1422        {
1423            if(my $To = $TypeUnit{$Type}) {
1424                $DWARF_Info{$ID}{"type"} = $To;
1425            }
1426        }
1427        if(my $Signature = $DWARF_Info{$ID}{"signature"})
1428        {
1429            if(my $To = $TypeUnit{$Signature}) {
1430                $DWARF_Info{$ID}{"signature"} = $To;
1431            }
1432        }
1433    }
1434
1435    %TypeUnit = ();
1436    %Post_Change = ();
1437
1438    if($Primary)
1439    {
1440        if(defined $CUnit_F)
1441        {
1442            if(my $Compiler= $DWARF_Info{$CUnit_F}{"producer"})
1443            {
1444                $Compiler=~s/\A\"//;
1445                $Compiler=~s/\"\Z//;
1446
1447                if($Compiler=~/GNU\s+(C|C\+\+)\s+(.+)\Z/)
1448                {
1449                    $SYS_GCCV = $2;
1450                    $SYS_GCCV=~s/\d+\s+\(.+\).*//; # 4.6.1 20110627 (Mandriva)
1451                }
1452                else {
1453                    $SYS_COMP = $Compiler;
1454                }
1455            }
1456            if(my $Lang = $DWARF_Info{$CUnit_F}{"language"})
1457            {
1458                $Lang=~s/\s*\(.+?\)\Z//;
1459                if($Lang=~/C\d/i) {
1460                    $LIB_LANG = "C";
1461                }
1462                elsif($Lang=~/C\+\+/i) {
1463                    $LIB_LANG = "C++";
1464                }
1465                else {
1466                    $LIB_LANG = $Lang;
1467                }
1468            }
1469        }
1470
1471        my %AddUnits = ();
1472
1473        foreach my $ID (keys(%UsedDecl))
1474        {
1475            if(my $U_ID = $ImportedDecl{$ID})
1476            {
1477                if(not $UsedUnit{$U_ID})
1478                {
1479                    $AddUnits{$U_ID} = 1;
1480                }
1481            }
1482        }
1483
1484        if(keys(%AddUnits))
1485        {
1486            my $ADD_DUMP = "";
1487
1488            foreach my $U_ID (sort {hex($a)<=>hex($b)} keys(%AddUnits))
1489            {
1490                foreach my $N (sort {int($a)<=>int($b)} keys(%{$ImportedUnit{$U_ID}}))
1491                {
1492                    $ADD_DUMP .= $ImportedUnit{$U_ID}{$N};
1493                }
1494            }
1495
1496            my $AddUnit_F = $TMP_DIR."/add_unit.dump";
1497
1498            writeFile($AddUnit_F, $ADD_DUMP);
1499
1500            my $FH_add;
1501            open($FH_add, $AddUnit_F);
1502            read_DWARF_Dump($FH_add, 0);
1503            close($FH_add);
1504
1505            unlink($AddUnit_F);
1506        }
1507
1508    }
1509
1510    # clean memory
1511    %ImportedUnit = ();
1512    %ImportedDecl = ();
1513    %UsedUnit = ();
1514    %UsedDecl = ();
1515
1516    foreach my $ID (keys(%Delete_Src))
1517    {
1518        delete($DWARF_Info{$ID}{"decl_file"});
1519        delete($DWARF_Info{$ID}{"decl_line"});
1520    }
1521
1522    %Delete_Src = ();
1523
1524    if(defined $OptMem)
1525    { # optimize memory usage
1526        foreach my $ID (sort {int($a)<=>int($b)} keys(%DWARF_Info))
1527        {
1528            foreach my $Attr ("type", "sibling", "specification")
1529            {
1530                if(defined $DWARF_Info{$ID}{$Attr})
1531                {
1532                    my $Val = $DWARF_Info{$ID}{$Attr};
1533                    if(defined $Type_Base{$Val})
1534                    {
1535                        $DWARF_Info{$ID}{$Attr} = $Type_Base{$Val};
1536                    }
1537                }
1538            }
1539        }
1540
1541        my $NS_Del = undef;
1542
1543        foreach my $ID (sort {int($a)<=>int($b)} keys(%DWARF_Info))
1544        {
1545            if($Excess_IDs{$ID})
1546            { # node
1547                $NS_Del = $DWARF_Info{$ID}{"NS"};
1548                delete($DWARF_Info{$ID});
1549            }
1550            else
1551            { # sub-node
1552                if(defined $NS_Del)
1553                {
1554                    if($DWARF_Info{$ID}{"NS"}<=$NS_Del)
1555                    { # other node
1556                        $NS_Del = undef;
1557                    }
1558                    else
1559                    { # subprogram/types internals
1560                        if($DWARF_Info{$ID}{"Kind"}!~/(typedef|_type)\Z/)
1561                        {
1562                            delete($DWARF_Info{$ID});
1563                        }
1564                        else {
1565                            $DWARF_Info{$ID}{"NS"} = $NS_Del;
1566                        }
1567                    }
1568                }
1569            }
1570        }
1571
1572        %Excess_IDs = ();
1573    }
1574}
1575
1576sub read_Vtables($)
1577{
1578    my $Path = $_[0];
1579
1580    my $Name = get_filename($Path);
1581    $Path = abs_path($Path);
1582
1583
1584    if(index($LIB_LANG, "C++")!=-1)
1585    {
1586        if(check_Cmd($VTABLE_DUMPER))
1587        {
1588            if(my $Version = `$VTABLE_DUMPER -dumpversion`)
1589            {
1590                if(cmpVersions($Version, $VTABLE_DUMPER_VERSION)<0)
1591                {
1592                    printMsg("ERROR", "the version of Vtable-Dumper should be $VTABLE_DUMPER_VERSION or newer");
1593                    return;
1594                }
1595            }
1596        }
1597        else
1598        {
1599            printMsg("ERROR", "cannot find \'$VTABLE_DUMPER\'");
1600            return;
1601        }
1602
1603        my $ExtraPath = $TMP_DIR."/v-tables";
1604
1605        if($ExtraInfo)
1606        {
1607            $ExtraPath = $ExtraInfo."/".$Name;
1608            mkpath($ExtraPath);
1609            $ExtraPath .= "/v-tables";
1610        }
1611
1612        system("$VTABLE_DUMPER \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\"");
1613
1614        my $Content = readFile($ExtraPath);
1615        foreach my $ClassInfo (split(/\n\n\n/, $Content))
1616        {
1617            if($ClassInfo=~/\AVtable\s+for\s+(.+)\n((.|\n)+)\Z/i)
1618            {
1619                my ($CName, $VTable) = ($1, $2);
1620                my @Entries = split(/\n/, $VTable);
1621                foreach (1 .. $#Entries)
1622                {
1623                    my $Entry = $Entries[$_];
1624                    if($Entry=~/\A(\d+)\s+(.+)\Z/) {
1625                        $VirtualTable{$CName}{$1} = $2;
1626                    }
1627                }
1628            }
1629        }
1630    }
1631}
1632
1633sub dump_ABI()
1634{
1635    printMsg("INFO", "Creating ABI dump");
1636
1637    my %ABI = (
1638        "TypeInfo" => \%TypeInfo,
1639        "SymbolInfo" => \%SymbolInfo,
1640        "Symbols" => \%Library_Symbol,
1641        "LocalSymbols" => \%Library_LocalSymbol,
1642        "UndefinedSymbols" => \%Library_UndefSymbol,
1643        "Needed" => \%Library_Needed,
1644        "SymbolVersion" => \%SymVer,
1645        "LibraryVersion" => $TargetVersion,
1646        "LibraryName" => $TargetName,
1647        "Language" => $LIB_LANG,
1648        "Headers" => \%HeadersInfo,
1649        "Sources" => \%SourcesInfo,
1650        "NameSpaces" => \%NestedNameSpaces,
1651        "Target" => "unix",
1652        "Arch" => $SYS_ARCH,
1653        "WordSize" => $SYS_WORD,
1654        "ABI_DUMP_VERSION" => $ABI_DUMP_VERSION,
1655        "ABI_DUMPER_VERSION" => $TOOL_VERSION,
1656    );
1657
1658    if($SYS_GCCV) {
1659        $ABI{"GccVersion"} = $SYS_GCCV;
1660    }
1661    else {
1662        $ABI{"Compiler"} = $SYS_COMP;
1663    }
1664
1665    my $ABI_DUMP = Dumper(\%ABI);
1666
1667    if($StdOut)
1668    { # --stdout option
1669        print STDOUT $ABI_DUMP;
1670    }
1671    else
1672    {
1673        mkpath(get_dirname($OutputDump));
1674
1675        open(DUMP, ">", $OutputDump) || die ("can't open file \'$OutputDump\': $!\n");
1676        print DUMP $ABI_DUMP;
1677        close(DUMP);
1678
1679        printMsg("INFO", "\nThe object ABI has been dumped to:\n  $OutputDump");
1680    }
1681}
1682
1683sub unmangleString($)
1684{
1685    my $Str = $_[0];
1686
1687    $Str=~s/\AN(.+)E\Z/$1/;
1688    while($Str=~s/\A(\d+)//)
1689    {
1690        if(length($Str)==$1) {
1691            last;
1692        }
1693
1694        $Str = substr($Str, $1, length($Str) - $1);
1695    }
1696
1697    return $Str;
1698}
1699
1700sub read_ABI()
1701{
1702    printMsg("INFO", "Extracting ABI information");
1703
1704    my %CurID = ();
1705
1706    my @IDs = sort {int($a) <=> int($b)} keys(%DWARF_Info);
1707
1708    if($AltDebugInfo) {
1709        @IDs = sort {$b>0 <=> $a>0} sort {abs(int($a)) <=> abs(int($b))} @IDs;
1710    }
1711
1712    foreach my $ID (@IDs)
1713    {
1714        $ID = "$ID";
1715
1716        my $Kind = $DWARF_Info{$ID}{"Kind"};
1717        my $NS = $DWARF_Info{$ID}{"NS"};
1718        my $Scope = $CurID{$NS-2};
1719
1720        if($Kind eq "typedef")
1721        {
1722            if($DWARF_Info{$Scope}{"Kind"} eq "subprogram")
1723            {
1724                $NS = $DWARF_Info{$Scope}{"NS"};
1725                $Scope = $CurID{$NS-2};
1726            }
1727        }
1728
1729        if($Kind ne "subprogram") {
1730            delete($DWARF_Info{$ID}{"NS"});
1731        }
1732
1733        my $IsType = ($Kind=~/(struct|structure|class|union|enumeration|subroutine|array)_type/);
1734
1735        if($IsType
1736        or $Kind eq "typedef"
1737        or $Kind eq "subprogram"
1738        or $Kind eq "variable"
1739        or $Kind eq "namespace")
1740        {
1741            if($Kind ne "variable"
1742            and $Kind ne "typedef")
1743            {
1744                $CurID{$NS} = $ID;
1745            }
1746
1747            if($Scope)
1748            {
1749                $NameSpace{$ID} = $Scope;
1750                if($Kind eq "subprogram"
1751                or $Kind eq "variable")
1752                {
1753                    if($DWARF_Info{$Scope}{"Kind"}=~/class|struct/)
1754                    {
1755                        $ClassMethods{$Scope}{$ID} = 1;
1756                        if(my $Sp = $DWARF_Info{$Scope}{"specification"}) {
1757                            $ClassMethods{$Sp}{$ID} = 1;
1758                        }
1759                    }
1760                }
1761            }
1762
1763            if(my $Spec = $DWARF_Info{$ID}{"specification"}) {
1764                $SpecElem{$Spec} = $ID;
1765            }
1766
1767            if(my $Orig = $DWARF_Info{$ID}{"abstract_origin"}) {
1768                $OrigElem{$Orig} = $ID;
1769            }
1770
1771            if($IsType)
1772            {
1773                if(not $DWARF_Info{$ID}{"name"}
1774                and $DWARF_Info{$ID}{"linkage_name"})
1775                {
1776                    $DWARF_Info{$ID}{"name"} = unmangleString($DWARF_Info{$ID}{"linkage_name"});
1777
1778                    # free memory
1779                    delete($DWARF_Info{$ID}{"linkage_name"});
1780                }
1781            }
1782        }
1783        elsif($Kind eq "member")
1784        {
1785            if($Scope)
1786            {
1787                $NameSpace{$ID} = $Scope;
1788
1789                if($DWARF_Info{$Scope}{"Kind"}=~/class|struct/
1790                and not defined $DWARF_Info{$ID}{"data_member_location"})
1791                { # variable (global data)
1792                    next;
1793                }
1794            }
1795
1796            $TypeMember{$Scope}{keys(%{$TypeMember{$Scope}})} = $ID;
1797        }
1798        elsif($Kind eq "enumerator")
1799        {
1800            $TypeMember{$Scope}{keys(%{$TypeMember{$Scope}})} = $ID;
1801        }
1802        elsif($Kind eq "inheritance")
1803        {
1804            my %In = (
1805                "id" => $DWARF_Info{$ID}{"type"},
1806                "access" => $DWARF_Info{$ID}{"accessibility"}
1807            );
1808            if(defined $DWARF_Info{$ID}{"virtuality"}) {
1809                $In{"virtual"} = 1;
1810            }
1811            $Inheritance{$Scope}{keys(%{$Inheritance{$Scope}})} = \%In;
1812
1813            # free memory
1814            delete($DWARF_Info{$ID});
1815        }
1816        elsif($Kind eq "formal_parameter")
1817        {
1818            $FuncParam{$Scope}{keys(%{$FuncParam{$Scope}})} = $ID;
1819        }
1820        elsif($Kind eq "unspecified_parameters")
1821        {
1822            $FuncParam{$Scope}{keys(%{$FuncParam{$Scope}})} = $ID;
1823            $DWARF_Info{$ID}{"type"} = "-1"; # "..."
1824        }
1825        elsif($Kind eq "subrange_type")
1826        {
1827            if((my $Bound = $DWARF_Info{$ID}{"upper_bound"}) ne "") {
1828                $ArrayCount{$Scope} = $Bound + 1;
1829            }
1830
1831            # free memory
1832            delete($DWARF_Info{$ID});
1833        }
1834    }
1835
1836    # register "void" type
1837    %{$TypeInfo{"1"}} = (
1838        "Name"=>"void",
1839        "Type"=>"Intrinsic"
1840    );
1841    $TName_Tid{"Intrinsic"}{"void"} = "1";
1842    $TName_Tids{"Intrinsic"}{"void"}{"1"} = 1;
1843    $Cache{"getTypeInfo"}{"1"} = 1;
1844
1845    # register "..." type
1846    %{$TypeInfo{"-1"}} = (
1847        "Name"=>"...",
1848        "Type"=>"Intrinsic"
1849    );
1850    $TName_Tid{"Intrinsic"}{"..."} = "-1";
1851    $TName_Tids{"Intrinsic"}{"..."}{"-1"} = 1;
1852    $Cache{"getTypeInfo"}{"-1"} = 1;
1853
1854    my @IDs = sort {int($a) <=> int($b)} keys(%DWARF_Info);
1855
1856    if($AltDebugInfo) {
1857        @IDs = sort {$b>0 <=> $a>0} sort {abs(int($a)) <=> abs(int($b))} @IDs;
1858    }
1859
1860    foreach my $ID (@IDs)
1861    {
1862        if(my $Kind = $DWARF_Info{$ID}{"Kind"})
1863        {
1864            if(defined $TypeType{$Kind}) {
1865                getTypeInfo($ID);
1866            }
1867        }
1868    }
1869
1870    foreach my $Tid (sort {int($a) <=> int($b)} keys(%TypeInfo))
1871    {
1872        my $Type = $TypeInfo{$Tid}{"Type"};
1873
1874        if(not defined $TypeInfo{$Tid}{"Memb"})
1875        {
1876            if($Type=~/Struct|Class|Union|Enum/)
1877            {
1878                if(my $Signature = $DWARF_Info{$Tid}{"signature"})
1879                {
1880                    if(defined $TypeInfo{$Signature})
1881                    {
1882                        foreach my $Attr (keys(%{$TypeInfo{$Signature}}))
1883                        {
1884                            if(not defined $TypeInfo{$Tid}{$Attr}) {
1885                                $TypeInfo{$Tid}{$Attr} = $TypeInfo{$Signature}{$Attr};
1886                            }
1887                        }
1888                    }
1889                }
1890            }
1891        }
1892    }
1893
1894    my %Incomplete = ();
1895    my %Incomplete_TN = ();
1896
1897    my @IDs = sort {int($a) <=> int($b)} keys(%TypeInfo);
1898
1899    if($AltDebugInfo) {
1900        @IDs = sort {$b>0 <=> $a>0} sort {abs(int($a)) <=> abs(int($b))} @IDs;
1901    }
1902
1903    foreach my $Tid (@IDs)
1904    {
1905        my $Name = $TypeInfo{$Tid}{"Name"};
1906        my $Type = $TypeInfo{$Tid}{"Type"};
1907
1908        if(not defined $SpecElem{$Tid}
1909        and not defined $Incomplete_TN{$Type}{$Name})
1910        {
1911            if(not defined $TypeInfo{$Tid}{"Size"})
1912            {
1913                if($Type=~/Struct|Class|Union|Enum/)
1914                {
1915                    $Incomplete{$Tid} = 1;
1916                }
1917            }
1918        }
1919
1920        $Incomplete_TN{$Type}{$Name} = 1;
1921    }
1922
1923    # free memory
1924    %Incomplete_TN = ();
1925
1926    foreach my $Tid (sort {int($a) <=> int($b)} keys(%Incomplete))
1927    {
1928        my $Name = $TypeInfo{$Tid}{"Name"};
1929        my $Type = $TypeInfo{$Tid}{"Type"};
1930
1931        my @Adv_IDs = sort {int($a) <=> int($b)} keys(%{$TName_Tids{$Type}{$Name}});
1932
1933        if($AltDebugInfo) {
1934            @Adv_IDs = sort {$b>0 <=> $a>0} sort {abs(int($a)) <=> abs(int($b))} @Adv_IDs;
1935        }
1936
1937        foreach my $Tid_Adv (@Adv_IDs)
1938        {
1939            if($Tid_Adv!=$Tid)
1940            {
1941                if(defined $SpecElem{$Tid_Adv}
1942                or defined $TypeInfo{$Tid_Adv}{"Size"})
1943                {
1944                    foreach my $Attr (keys(%{$TypeInfo{$Tid_Adv}}))
1945                    {
1946                        if(not defined $TypeInfo{$Tid}{$Attr})
1947                        {
1948                            if(ref($TypeInfo{$Tid_Adv}{$Attr}) eq "HASH") {
1949                                $TypeInfo{$Tid}{$Attr} = dclone($TypeInfo{$Tid_Adv}{$Attr});
1950                            }
1951                            else {
1952                                $TypeInfo{$Tid}{$Attr} = $TypeInfo{$Tid_Adv}{$Attr};
1953                            }
1954
1955                        }
1956                    }
1957                    last;
1958                }
1959            }
1960        }
1961    }
1962
1963    # free memory
1964    %Incomplete = ();
1965
1966    my %Delete = ();
1967
1968    foreach my $Tid (sort {int($a) <=> int($b)} keys(%TypeInfo))
1969    {
1970        if(defined $TypeInfo{$Tid}
1971        and $TypeInfo{$Tid}{"Type"} eq "Typedef")
1972        {
1973            my $TN = $TypeInfo{$Tid}{"Name"};
1974            my $TL = $TypeInfo{$Tid}{"Line"};
1975            my $NS = $TypeInfo{$Tid}{"NameSpace"};
1976
1977            if(my $BTid = $TypeInfo{$Tid}{"BaseType"})
1978            {
1979                if(defined $TypeInfo{$BTid}
1980                and $TypeInfo{$BTid}{"Name"}=~/\Aanon\-(\w+)\-/)
1981                {
1982                    %{$TypeInfo{$Tid}} = %{$TypeInfo{$BTid}};
1983                    $TypeInfo{$Tid}{"Name"} = $1." ".$TN;
1984                    $TypeInfo{$Tid}{"Line"} = $TL;
1985
1986                    my $Name = $TypeInfo{$Tid}{"Name"};
1987                    my $Type = $TypeInfo{$Tid}{"Type"};
1988
1989                    if(not defined $TName_Tid{$Type}{$Name}
1990                    or ($Tid>0 and $Tid<$TName_Tid{$Type}{$Name})
1991                    or ($Tid>0 and $TName_Tid{$Type}{$Name}<0)) {
1992                        $TName_Tid{$Type}{$Name} = $Tid;
1993                    }
1994                    $TName_Tids{$Type}{$Name}{$Tid} = 1;
1995
1996                    if($NS) {
1997                        $TypeInfo{$Tid}{"NameSpace"} = $NS;
1998                    }
1999                    $Delete{$BTid} = 1;
2000                }
2001            }
2002        }
2003    }
2004
2005    foreach my $Tid (keys(%Delete))
2006    {
2007        my $TN = $TypeInfo{$Tid}{"Name"};
2008        my $TT = $TypeInfo{$Tid}{"Type"};
2009
2010        delete($TName_Tid{$TT}{$TN});
2011        delete($TName_Tids{$TT}{$TN}{$Tid});
2012        if(my @IDs = sort {int($a) <=> int($b)} keys(%{$TName_Tids{$TT}{$TN}}))
2013        { # minimal ID
2014            $TName_Tid{$TT}{$TN} = $IDs[0];
2015        }
2016
2017        delete($TypeInfo{$Tid});
2018    }
2019
2020    # free memory
2021    # delete types info
2022    %Delete = ();
2023    foreach (keys(%DWARF_Info))
2024    {
2025        if(my $Kind = $DWARF_Info{$_}{"Kind"})
2026        {
2027            if(defined $TypeType{$Kind}) {
2028                delete($DWARF_Info{$_});
2029            }
2030        }
2031    }
2032
2033    foreach my $ID (sort {int($a) <=> int($b)} keys(%DWARF_Info))
2034    {
2035        if($ID<0)
2036        { # imported
2037            next;
2038        }
2039
2040        if($DWARF_Info{$ID}{"Kind"} eq "subprogram"
2041        or $DWARF_Info{$ID}{"Kind"} eq "variable")
2042        {
2043            getSymbolInfo($ID);
2044        }
2045    }
2046
2047    foreach my $ID (sort {int($a) <=> int($b)} keys(%SymbolInfo))
2048    {
2049        # add missed c-tors
2050        if($SymbolInfo{$ID}{"Constructor"})
2051        {
2052            if($SymbolInfo{$ID}{"MnglName"}=~/(C[1-2])([EI]).+/)
2053            {
2054                my ($K1, $K2) = ($1, $2);
2055                foreach ("C1", "C2")
2056                {
2057                    if($K1 ne $_)
2058                    {
2059                        my $Name = $SymbolInfo{$ID}{"MnglName"};
2060                        $Name=~s/$K1$K2/$_$K2/;
2061
2062                        if(not defined $Mangled_ID{$Name}) {
2063                            cloneSymbol($ID, $Name);
2064                        }
2065                    }
2066                }
2067            }
2068        }
2069
2070        # add missed d-tors
2071        if($SymbolInfo{$ID}{"Destructor"})
2072        {
2073            if($SymbolInfo{$ID}{"MnglName"}=~/(D[0-2])([EI]).+/)
2074            {
2075                my ($K1, $K2) = ($1, $2);
2076                foreach ("D0", "D1", "D2")
2077                {
2078                    if($K1 ne $_)
2079                    {
2080                        my $Name = $SymbolInfo{$ID}{"MnglName"};
2081                        $Name=~s/$K1$K2/$_$K2/;
2082
2083                        if(not defined $Mangled_ID{$Name}) {
2084                            cloneSymbol($ID, $Name);
2085                        }
2086                    }
2087                }
2088            }
2089        }
2090    }
2091
2092    foreach my $ID (sort {int($a) <=> int($b)} keys(%SymbolInfo))
2093    {
2094        if($LIB_LANG eq "C++")
2095        {
2096            if(not $SymbolInfo{$ID}{"MnglName"})
2097            {
2098                if($SymbolInfo{$ID}{"Artificial"}
2099                or index($SymbolInfo{$ID}{"ShortName"}, "~")==0)
2100                {
2101                    delete($SymbolInfo{$ID});
2102                    next;
2103                }
2104            }
2105        }
2106
2107        if($SymbolInfo{$ID}{"Class"}
2108        and not $SymbolInfo{$ID}{"Data"}
2109        and not $SymbolInfo{$ID}{"Constructor"}
2110        and not $SymbolInfo{$ID}{"Destructor"}
2111        and not $SymbolInfo{$ID}{"Virt"}
2112        and not $SymbolInfo{$ID}{"PureVirt"})
2113        {
2114            if(not defined $SymbolInfo{$ID}{"Param"}
2115            or $SymbolInfo{$ID}{"Param"}{0}{"name"} ne "this")
2116            {
2117                $SymbolInfo{$ID}{"Static"} = 1;
2118            }
2119        }
2120
2121        if(not $SymbolInfo{$ID}{"Return"})
2122        { # void
2123            if(not $SymbolInfo{$ID}{"Constructor"}
2124            and not $SymbolInfo{$ID}{"Destructor"})
2125            {
2126                $SymbolInfo{$ID}{"Return"} = "1";
2127            }
2128        }
2129
2130        if(defined $SymbolInfo{$ID}{"Source"})
2131        {
2132            if(not defined $SymbolInfo{$ID}{"Header"})
2133            {
2134                $SymbolInfo{$ID}{"Line"} = $SymbolInfo{$ID}{"SourceLine"};
2135                delete($SymbolInfo{$ID}{"SourceLine"});
2136            }
2137        }
2138
2139        my $S = selectSymbol($ID);
2140
2141        if($S==0)
2142        {
2143            if(defined $AllSymbols)
2144            {
2145                if($SymbolInfo{$ID}{"External"})
2146                {
2147                    $S = 1;
2148                }
2149                else
2150                { # local
2151                    if(defined $DumpStatic) {
2152                        $S = 1;
2153                    }
2154                }
2155            }
2156        }
2157
2158        if($S==0)
2159        {
2160            delete($SymbolInfo{$ID});
2161            next;
2162        }
2163        $SelectedSymbols{$ID} = $S;
2164
2165        delete($SymbolInfo{$ID}{"External"});
2166    }
2167}
2168
2169sub cloneSymbol($$)
2170{
2171    my ($ID, $Symbol) = @_;
2172
2173    my $nID = undef;
2174    if(not defined $SymbolInfo{$ID + 1}) {
2175        $nID = $ID + 1;
2176    }
2177    else {
2178        $nID = ++$GLOBAL_ID;
2179    }
2180    foreach my $Attr (keys(%{$SymbolInfo{$ID}}))
2181    {
2182        if(ref($SymbolInfo{$ID}{$Attr}) eq "HASH") {
2183            $SymbolInfo{$nID}{$Attr} = dclone($SymbolInfo{$ID}{$Attr});
2184        }
2185        else {
2186            $SymbolInfo{$nID}{$Attr} = $SymbolInfo{$ID}{$Attr};
2187        }
2188    }
2189    $SymbolInfo{$nID}{"MnglName"} = $Symbol;
2190}
2191
2192sub selectSymbol($)
2193{
2194    my $ID = $_[0];
2195
2196    my $MnglName = $SymbolInfo{$ID}{"MnglName"};
2197
2198    if(not $MnglName) {
2199        $MnglName = $SymbolInfo{$ID}{"ShortName"};
2200    }
2201
2202    my $Exp = 0;
2203
2204    if($Library_Symbol{$TargetName}{$MnglName}
2205    or $Library_Symbol{$TargetName}{$SymVer{$MnglName}})
2206    {
2207        $Exp = 1;
2208    }
2209
2210    if(my $Alias = $SymbolInfo{$ID}{"Alias"})
2211    {
2212        if($Library_Symbol{$TargetName}{$Alias}
2213        or $Library_Symbol{$TargetName}{$SymVer{$Alias}})
2214        {
2215            $Exp = 1;
2216        }
2217    }
2218
2219    if(not $Exp)
2220    {
2221        if(defined $Library_UndefSymbol{$TargetName}{$MnglName}
2222        or defined $Library_UndefSymbol{$TargetName}{$SymVer{$MnglName}})
2223        {
2224            return 0;
2225        }
2226
2227        if($SymbolInfo{$ID}{"Data"}
2228        or $SymbolInfo{$ID}{"InLine"}
2229        or $SymbolInfo{$ID}{"PureVirt"})
2230        {
2231            if(not $SymbolInfo{$ID}{"External"})
2232            { # skip static
2233                return 0;
2234            }
2235
2236            if(defined $BinOnly)
2237            { # data, inline, pure
2238                return 0;
2239            }
2240            elsif(not defined $SymbolInfo{$ID}{"Header"})
2241            { # defined in source files
2242                return 0;
2243            }
2244            else
2245            {
2246                return 2;
2247            }
2248        }
2249        else
2250        {
2251            return 0;
2252        }
2253    }
2254
2255    return 1;
2256}
2257
2258sub formatName($$)
2259{ # type name correction
2260    if(defined $Cache{"formatName"}{$_[1]}{$_[0]}) {
2261        return $Cache{"formatName"}{$_[1]}{$_[0]};
2262    }
2263
2264    my $N = $_[0];
2265
2266    if($_[1] ne "S")
2267    {
2268        $N=~s/\A[ ]+//g;
2269        $N=~s/[ ]+\Z//g;
2270        $N=~s/[ ]{2,}/ /g;
2271    }
2272
2273    $N=~s/[ ]*(\W)[ ]*/$1/g; # std::basic_string<char> const
2274
2275    $N=~s/\b(const|volatile) ([\w\:]+)([\*&,>]|\Z)/$2 $1$3/g; # "const void" to "void const"
2276
2277    $N=~s/\bvolatile const\b/const volatile/g;
2278
2279    $N=~s/\b(long long|short|long) unsigned\b/unsigned $1/g;
2280    $N=~s/\b(short|long) int\b/$1/g;
2281
2282    $N=~s/([\)\]])(const|volatile)\b/$1 $2/g;
2283
2284    while($N=~s/>>/> >/g) {};
2285
2286    if($_[1] eq "S")
2287    {
2288        if(index($N, "operator")!=-1) {
2289            $N=~s/\b(operator[ ]*)> >/$1>>/;
2290        }
2291    }
2292
2293    $N=~s/,/, /g;
2294
2295    return ($Cache{"formatName"}{$_[1]}{$_[0]} = $N);
2296}
2297
2298sub separate_Params($)
2299{
2300    my $Str = $_[0];
2301    my @Parts = ();
2302    my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 );
2303    my $Part = 0;
2304    foreach my $Pos (0 .. length($Str) - 1)
2305    {
2306        my $S = substr($Str, $Pos, 1);
2307        if(defined $B{$S}) {
2308            $B{$S} += 1;
2309        }
2310        if($S eq "," and
2311        $B{"("}==$B{")"} and $B{"<"}==$B{">"}) {
2312            $Part += 1;
2313        }
2314        else {
2315            $Parts[$Part] .= $S;
2316        }
2317    }
2318    # remove spaces
2319    foreach (@Parts)
2320    {
2321        s/\A //g;
2322        s/ \Z//g;
2323    }
2324    return @Parts;
2325}
2326
2327sub init_FuncType($$$)
2328{
2329    my ($TInfo, $FTid, $Type) = @_;
2330
2331    $TInfo->{"Type"} = $Type;
2332
2333    if($TInfo->{"Return"} = $DWARF_Info{$FTid}{"type"}) {
2334        getTypeInfo($TInfo->{"Return"});
2335    }
2336    else
2337    { # void
2338        $TInfo->{"Return"} = "1";
2339    }
2340    delete($TInfo->{"BaseType"});
2341
2342    my @Prms = ();
2343    my $PPos = 0;
2344    foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$FuncParam{$FTid}}))
2345    {
2346        my $ParamId = $FuncParam{$FTid}{$Pos};
2347        my %PInfo = %{$DWARF_Info{$ParamId}};
2348
2349        if(defined $PInfo{"artificial"})
2350        { # this
2351            next;
2352        }
2353
2354        if(my $PTypeId = $PInfo{"type"})
2355        {
2356            $TInfo->{"Param"}{$PPos}{"type"} = $PTypeId;
2357            getTypeInfo($PTypeId);
2358            push(@Prms, $TypeInfo{$PTypeId}{"Name"});
2359        }
2360
2361        $PPos += 1;
2362    }
2363
2364    $TInfo->{"Name"} = $TypeInfo{$TInfo->{"Return"}}{"Name"};
2365    if($Type eq "FuncPtr") {
2366        $TInfo->{"Name"} .= "(*)";
2367    }
2368    else {
2369        $TInfo->{"Name"} .= "()";
2370    }
2371    $TInfo->{"Name"} .= "(".join(",", @Prms).")";
2372}
2373
2374sub get_TParams($)
2375{
2376    my $Name = $_[0];
2377    if(my $Cent = find_center($Name, "<"))
2378    {
2379        my $TParams = substr($Name, $Cent);
2380        $TParams=~s/\A<|>\Z//g;
2381
2382        $TParams = simpleName($TParams);
2383
2384        my $Short = substr($Name, 0, $Cent);
2385
2386        my @Params = separate_Params($TParams);
2387        foreach my $Pos (0 .. $#Params)
2388        {
2389            my $Param = $Params[$Pos];
2390            if($Param=~/\A(.+>)(.*?)\Z/)
2391            {
2392                my ($Tm, $Suf) = ($1, $2);
2393                my ($Sh, @Prm) = get_TParams($Tm);
2394                $Param = $Sh."<".join(", ", @Prm).">".$Suf;
2395            }
2396            $Params[$Pos] = formatName($Param, "T");
2397        }
2398
2399        # default arguments
2400        if($Short eq "std::vector")
2401        {
2402            if($#Params==1)
2403            {
2404                if($Params[1] eq "std::allocator<".$Params[0].">")
2405                { # std::vector<T, std::allocator<T> >
2406                    splice(@Params, 1, 1);
2407                }
2408            }
2409        }
2410        elsif($Short eq "std::set")
2411        {
2412            if($#Params==2)
2413            {
2414                if($Params[1] eq "std::less<".$Params[0].">"
2415                and $Params[2] eq "std::allocator<".$Params[0].">")
2416                { # std::set<T, std::less<T>, std::allocator<T> >
2417                    splice(@Params, 1, 2);
2418                }
2419            }
2420        }
2421        elsif($Short eq "std::basic_string")
2422        {
2423            if($#Params==2)
2424            {
2425                if($Params[1] eq "std::char_traits<".$Params[0].">"
2426                and $Params[2] eq "std::allocator<".$Params[0].">")
2427                { # std::basic_string<T, std::char_traits<T>, std::allocator<T> >
2428                    splice(@Params, 1, 2);
2429                }
2430            }
2431        }
2432
2433        return ($Short, @Params);
2434    }
2435
2436    return $Name; # error
2437}
2438
2439sub getTypeInfo($)
2440{
2441    my $ID = $_[0];
2442    my $Kind = $DWARF_Info{$ID}{"Kind"};
2443
2444    if(defined $Cache{"getTypeInfo"}{$ID}) {
2445        return;
2446    }
2447
2448    if(my $N = $NameSpace{$ID})
2449    {
2450        if($DWARF_Info{$N}{"Kind"} eq "subprogram")
2451        { # local code
2452          # template instances are declared in the subprogram (constructor)
2453            my $Tmpl = 0;
2454            if(my $ObjP = $DWARF_Info{$N}{"object_pointer"})
2455            {
2456                while($DWARF_Info{$ObjP}{"type"}) {
2457                    $ObjP = $DWARF_Info{$ObjP}{"type"};
2458                }
2459                my $CName = $DWARF_Info{$ObjP}{"name"};
2460                $CName=~s/<.*//g;
2461                if($CName eq $DWARF_Info{$N}{"name"}) {
2462                    $Tmpl = 1;
2463                }
2464            }
2465            if(not $Tmpl)
2466            { # local types
2467                $LocalType{$ID} = 1;
2468            }
2469        }
2470        elsif($DWARF_Info{$N}{"Kind"} eq "lexical_block")
2471        { # local code
2472            return;
2473        }
2474    }
2475
2476    $Cache{"getTypeInfo"}{$ID} = 1;
2477
2478    my %TInfo = ();
2479
2480    $TInfo{"Type"} = $TypeType{$Kind};
2481
2482    if(not $TInfo{"Type"})
2483    {
2484        if($DWARF_Info{$ID}{"Kind"} eq "subroutine_type") {
2485            $TInfo{"Type"} = "Func";
2486        }
2487    }
2488
2489    if(defined $ClassMethods{$ID})
2490    {
2491        if($TInfo{"Type"} eq "Struct") {
2492            $TInfo{"Type"} = "Class";
2493        }
2494    }
2495
2496    if(my $BaseType = $DWARF_Info{$ID}{"type"})
2497    {
2498        $TInfo{"BaseType"} = "$BaseType";
2499
2500        if(defined $TypeType{$DWARF_Info{$BaseType}{"Kind"}})
2501        {
2502            getTypeInfo($TInfo{"BaseType"});
2503
2504            if(not defined $TypeInfo{$TInfo{"BaseType"}}
2505            or not $TypeInfo{$TInfo{"BaseType"}}{"Name"})
2506            { # local code
2507                delete($TypeInfo{$ID});
2508                return;
2509            }
2510        }
2511    }
2512    if($TInfo{"Type"} eq "Class") {
2513        $TInfo{"Copied"} = 1; # will be changed in getSymbolInfo()
2514    }
2515
2516    if(defined $TypeMember{$ID})
2517    {
2518        my $Unnamed = 0;
2519        foreach my $Pos (sort {int($a) <=> int($b)} keys(%{$TypeMember{$ID}}))
2520        {
2521            my $MemId = $TypeMember{$ID}{$Pos};
2522            my %MInfo = %{$DWARF_Info{$MemId}};
2523
2524            if(my $Name = $MInfo{"name"})
2525            {
2526                if(index($Name, "_vptr.")==0)
2527                { # v-table pointer
2528                    $Name="_vptr";
2529                }
2530                $TInfo{"Memb"}{$Pos}{"name"} = $Name;
2531            }
2532            else
2533            {
2534                $TInfo{"Memb"}{$Pos}{"name"} = "unnamed".$Unnamed;
2535                $Unnamed += 1;
2536            }
2537            if($TInfo{"Type"} eq "Enum") {
2538                $TInfo{"Memb"}{$Pos}{"value"} = $MInfo{"const_value"};
2539            }
2540            else
2541            {
2542                $TInfo{"Memb"}{$Pos}{"type"} = $MInfo{"type"};
2543                if(my $Access = $MInfo{"accessibility"}) {
2544                    $TInfo{"Memb"}{$Pos}{"access"} = $Access;
2545                }
2546                if($TInfo{"Type"} eq "Union") {
2547                    $TInfo{"Memb"}{$Pos}{"offset"} = "0";
2548                }
2549                elsif(defined $MInfo{"data_member_location"}) {
2550                    $TInfo{"Memb"}{$Pos}{"offset"} = $MInfo{"data_member_location"};
2551                }
2552            }
2553
2554            if((my $BitSize = $MInfo{"bit_size"}) ne "") {
2555                $TInfo{"Memb"}{$Pos}{"bitfield"} = $BitSize;
2556            }
2557        }
2558    }
2559
2560    if(my $Access = $DWARF_Info{$ID}{"accessibility"}) {
2561        $TInfo{ucfirst($Access)} = 1;
2562    }
2563
2564    if(my $Size = $DWARF_Info{$ID}{"byte_size"}) {
2565        $TInfo{"Size"} = $Size;
2566    }
2567
2568    setSource(\%TInfo, $ID);
2569
2570    if(not $DWARF_Info{$ID}{"name"}
2571    and my $Spec = $DWARF_Info{$ID}{"specification"}) {
2572        $DWARF_Info{$ID}{"name"} = $DWARF_Info{$Spec}{"name"};
2573    }
2574
2575    my $NS = $NameSpace{$ID};
2576    if(not $NS)
2577    {
2578        if(my $Sp = $DWARF_Info{$ID}{"specification"}) {
2579            $NS = $NameSpace{$Sp};
2580        }
2581    }
2582
2583    if($NS)
2584    {
2585        if($DWARF_Info{$NS}{"Kind"} eq "namespace")
2586        {
2587            if(my $NS_F = completeNS($ID))
2588            {
2589                $TInfo{"NameSpace"} = $NS_F;
2590            }
2591        }
2592        elsif($DWARF_Info{$NS}{"Kind"} eq "class_type"
2593        or $DWARF_Info{$NS}{"Kind"} eq "structure_type")
2594        { # class
2595            getTypeInfo($NS);
2596
2597            if(my $Sp = $SpecElem{$NS}) {
2598                getTypeInfo($Sp);
2599            }
2600
2601            if($TypeInfo{$NS}{"Name"})
2602            {
2603                $TInfo{"NameSpace"} = $TypeInfo{$NS}{"Name"};
2604                $TInfo{"NameSpace"}=~s/\Astruct //;
2605            }
2606        }
2607    }
2608
2609    if(my $Name = $DWARF_Info{$ID}{"name"})
2610    {
2611        $TInfo{"Name"} = $Name;
2612
2613        if($TInfo{"NameSpace"}) {
2614            $TInfo{"Name"} = $TInfo{"NameSpace"}."::".$TInfo{"Name"};
2615        }
2616
2617        if($TInfo{"Type"}=~/\A(Struct|Class)\Z/)
2618        {
2619            if(defined $VirtualTable{$TInfo{"Name"}}) {
2620                %{$TInfo{"VTable"}} = %{$VirtualTable{$TInfo{"Name"}}};
2621            }
2622        }
2623
2624        if($TInfo{"Type"}=~/\A(Struct|Enum|Union)\Z/) {
2625            $TInfo{"Name"} = lc($TInfo{"Type"})." ".$TInfo{"Name"};
2626        }
2627    }
2628
2629    if($TInfo{"Type"} eq "Pointer")
2630    {
2631        if($DWARF_Info{$TInfo{"BaseType"}}{"Kind"} eq "subroutine_type")
2632        {
2633            init_FuncType(\%TInfo, $TInfo{"BaseType"}, "FuncPtr");
2634        }
2635    }
2636    elsif($TInfo{"Type"}=~/Typedef|Const|Volatile/)
2637    {
2638        if($DWARF_Info{$TInfo{"BaseType"}}{"Kind"} eq "subroutine_type")
2639        {
2640            getTypeInfo($TInfo{"BaseType"});
2641        }
2642    }
2643    elsif($TInfo{"Type"} eq "Func")
2644    {
2645        init_FuncType(\%TInfo, $ID, "Func");
2646    }
2647    elsif($TInfo{"Type"} eq "Struct")
2648    {
2649        if(not $TInfo{"Name"}
2650        and my $Sb = $DWARF_Info{$ID}{"sibling"})
2651        {
2652            if($DWARF_Info{$Sb}{"Kind"} eq "subroutine_type"
2653            and defined $TInfo{"Memb"}
2654            and $TInfo{"Memb"}{0}{"name"} eq "__pfn")
2655            { # __pfn and __delta
2656                $TInfo{"Type"} = "MethodPtr";
2657
2658                my @Prms = ();
2659                my $PPos = 0;
2660                foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$FuncParam{$Sb}}))
2661                {
2662                    my $ParamId = $FuncParam{$Sb}{$Pos};
2663                    my %PInfo = %{$DWARF_Info{$ParamId}};
2664
2665                    if(defined $PInfo{"artificial"})
2666                    { # this
2667                        next;
2668                    }
2669
2670                    if(my $PTypeId = $PInfo{"type"})
2671                    {
2672                        $TInfo{"Param"}{$PPos}{"type"} = $PTypeId;
2673                        getTypeInfo($PTypeId);
2674                        push(@Prms, $TypeInfo{$PTypeId}{"Name"});
2675                    }
2676
2677                    $PPos += 1;
2678                }
2679
2680                if(my $ClassId = $DWARF_Info{$Sb}{"object_pointer"})
2681                {
2682                    while($DWARF_Info{$ClassId}{"type"}) {
2683                        $ClassId = $DWARF_Info{$ClassId}{"type"};
2684                    }
2685                    $TInfo{"Class"} = $ClassId;
2686                    getTypeInfo($TInfo{"Class"});
2687                }
2688
2689                if($TInfo{"Return"} = $DWARF_Info{$Sb}{"type"}) {
2690                    getTypeInfo($TInfo{"Return"});
2691                }
2692                else
2693                { # void
2694                    $TInfo{"Return"} = "1";
2695                }
2696
2697                $TInfo{"Name"} = $TypeInfo{$TInfo{"Return"}}{"Name"};
2698                $TInfo{"Name"} .= "(".$TypeInfo{$TInfo{"Class"}}{"Name"}."::*)";
2699                $TInfo{"Name"} .= "(".join(",", @Prms).")";
2700            }
2701        }
2702    }
2703    elsif($TInfo{"Type"} eq "FieldPtr")
2704    {
2705        $TInfo{"Return"} = $TInfo{"BaseType"};
2706        delete($TInfo{"BaseType"});
2707
2708        if(my $Class = $DWARF_Info{$ID}{"containing_type"})
2709        {
2710            $TInfo{"Class"} = $Class;
2711            getTypeInfo($TInfo{"Class"});
2712
2713            $TInfo{"Name"} = $TypeInfo{$TInfo{"Return"}}{"Name"}."(".$TypeInfo{$TInfo{"Class"}}{"Name"}."::*)";
2714        }
2715
2716        $TInfo{"Size"} = $SYS_WORD;
2717    }
2718
2719    foreach my $Pos (sort {int($a) <=> int($b)} keys(%{$Inheritance{$ID}}))
2720    {
2721        if(my $BaseId = $Inheritance{$ID}{$Pos}{"id"})
2722        {
2723            if(my $E = $SpecElem{$BaseId}) {
2724                $BaseId = $E;
2725            }
2726
2727            $TInfo{"Base"}{$BaseId}{"pos"} = "$Pos";
2728            if(my $Access = $Inheritance{$ID}{$Pos}{"access"}) {
2729                $TInfo{"Base"}{$BaseId}{"access"} = $Access;
2730            }
2731            if($Inheritance{$ID}{$Pos}{"virtual"}) {
2732                $TInfo{"Base"}{$BaseId}{"virtual"} = 1;
2733            }
2734
2735            $ClassChild{$BaseId}{$ID} = 1;
2736        }
2737    }
2738
2739    if($TInfo{"Type"} eq "Pointer")
2740    {
2741        if(not $TInfo{"BaseType"})
2742        {
2743            $TInfo{"Name"} = "void*";
2744            $TInfo{"BaseType"} = "1";
2745        }
2746    }
2747    if($TInfo{"Type"} eq "Const")
2748    {
2749        if(not $TInfo{"BaseType"})
2750        {
2751            $TInfo{"Name"} = "const void";
2752            $TInfo{"BaseType"} = "1";
2753        }
2754    }
2755    if($TInfo{"Type"} eq "Volatile")
2756    {
2757        if(not $TInfo{"BaseType"})
2758        {
2759            $TInfo{"Name"} = "volatile void";
2760            $TInfo{"BaseType"} = "1";
2761        }
2762    }
2763
2764    if(not $TInfo{"Name"})
2765    {
2766        my $ID_ = $ID;
2767        my $BaseID = undef;
2768        my $Name = "";
2769
2770        while($BaseID = $DWARF_Info{$ID_}{"type"})
2771        {
2772            my $Kind = $DWARF_Info{$ID_}{"Kind"};
2773            if(my $Q = $Qual{$TypeType{$Kind}})
2774            {
2775                $Name = $Q.$Name;
2776                if($Q=~/\A\w/) {
2777                    $Name = " ".$Name;
2778                }
2779            }
2780            if(my $BName = $TypeInfo{$BaseID}{"Name"})
2781            {
2782                $Name = $BName.$Name;
2783                last;
2784            }
2785            elsif(my $BName2 = $DWARF_Info{$BaseID}{"name"})
2786            {
2787                $Name = $BName2.$Name;
2788            }
2789            $ID_ = $BaseID;
2790        }
2791
2792        if($Name) {
2793            $TInfo{"Name"} = $Name;
2794        }
2795
2796        if($TInfo{"Type"} eq "Array")
2797        {
2798            if(my $Count = $ArrayCount{$ID})
2799            {
2800                $TInfo{"Name"} .= "[".$Count."]";
2801                if(my $BType = $TInfo{"BaseType"})
2802                {
2803                    if(my $BSize = $TypeInfo{$BType}{"Size"})
2804                    {
2805                        if(my $Size = $Count*$BSize)
2806                        {
2807                            $TInfo{"Size"} = "$Size";
2808                        }
2809                    }
2810                }
2811            }
2812            else
2813            {
2814                $TInfo{"Name"} .= "[]";
2815                $TInfo{"Size"} = $SYS_WORD;
2816            }
2817        }
2818    }
2819
2820    if(my $Bid = $TInfo{"BaseType"})
2821    {
2822        if(not $TInfo{"Size"}
2823        and $TypeInfo{$Bid}{"Size"}) {
2824            $TInfo{"Size"} = $TypeInfo{$Bid}{"Size"};
2825        }
2826    }
2827    if($TInfo{"Name"}) {
2828        $TInfo{"Name"} = formatName($TInfo{"Name"}, "T"); # simpleName()
2829    }
2830
2831    if($TInfo{"Name"}=~/>\Z/)
2832    {
2833        my ($Short, @TParams) = get_TParams($TInfo{"Name"});
2834
2835        if(@TParams)
2836        {
2837            delete($TInfo{"TParam"});
2838            foreach my $Pos (0 .. $#TParams) {
2839                $TInfo{"TParam"}{$Pos}{"name"} = $TParams[$Pos];
2840            }
2841            $TInfo{"Name"} = formatName($Short."<".join(", ", @TParams).">", "T");
2842        }
2843    }
2844
2845    if(not $TInfo{"Name"})
2846    {
2847        if($TInfo{"Type"}=~/\A(Class|Struct|Enum|Union)\Z/)
2848        {
2849            if($TInfo{"Header"}) {
2850                $TInfo{"Name"} = "anon-".lc($TInfo{"Type"})."-".$TInfo{"Header"}."-".$TInfo{"Line"};
2851            }
2852            elsif($TInfo{"Source"}) {
2853                $TInfo{"Name"} = "anon-".lc($TInfo{"Type"})."-".$TInfo{"Source"}."-".$TInfo{"SourceLine"};
2854            }
2855            else
2856            {
2857                if(not defined $TypeMember{$ID})
2858                {
2859                    if(not defined $ANON_TYPE{$TInfo{"Type"}})
2860                    {
2861                        printMsg("WARNING", "a \"".$TInfo{"Type"}."\" type with no attributes detected in the DWARF dump ($ID)");
2862                        $ANON_TYPE{$TInfo{"Type"}} = 1;
2863                    }
2864                    $TInfo{"Name"} = "anon-".lc($TInfo{"Type"});
2865                }
2866            }
2867
2868            if($TInfo{"Name"} and $TInfo{"NameSpace"}) {
2869                $TInfo{"Name"} = $TInfo{"NameSpace"}."::".$TInfo{"Name"};
2870            }
2871        }
2872    }
2873
2874    if($TInfo{"Name"})
2875    {
2876        if(not defined $TName_Tid{$TInfo{"Type"}}{$TInfo{"Name"}}
2877        or ($ID>0 and $ID<$TName_Tid{$TInfo{"Type"}}{$TInfo{"Name"}})
2878        or ($ID>0 and $TName_Tid{$TInfo{"Type"}}{$TInfo{"Name"}}<0))
2879        {
2880            $TName_Tid{$TInfo{"Type"}}{$TInfo{"Name"}} = "$ID";
2881        }
2882        $TName_Tids{$TInfo{"Type"}}{$TInfo{"Name"}}{$ID} = 1;
2883    }
2884
2885    if(defined $TInfo{"Source"})
2886    {
2887        if(not defined $TInfo{"Header"})
2888        {
2889            $TInfo{"Line"} = $TInfo{"SourceLine"};
2890            delete($TInfo{"SourceLine"});
2891        }
2892    }
2893
2894    foreach my $Attr (keys(%TInfo)) {
2895        $TypeInfo{$ID}{$Attr} = $TInfo{$Attr};
2896    }
2897
2898    if(my $BASE_ID = $DWARF_Info{$ID}{"specification"})
2899    {
2900        foreach my $Attr (keys(%{$TypeInfo{$BASE_ID}}))
2901        {
2902            if($Attr ne "Type") {
2903                $TypeInfo{$ID}{$Attr} = $TypeInfo{$BASE_ID}{$Attr};
2904            }
2905        }
2906
2907        foreach my $Attr (keys(%{$TypeInfo{$ID}})) {
2908            $TypeInfo{$BASE_ID}{$Attr} = $TypeInfo{$ID}{$Attr};
2909        }
2910
2911        $TypeSpec{$ID} = $BASE_ID;
2912    }
2913}
2914
2915sub setSource($$)
2916{
2917    my ($R, $ID) = @_;
2918
2919    my $File = $DWARF_Info{$ID}{"decl_file"};
2920    my $Line = $DWARF_Info{$ID}{"decl_line"};
2921
2922    my $Unit = $DWARF_Info{$ID}{"Unit"};
2923
2924    if(defined $File)
2925    {
2926        my $Name = undef;
2927
2928        if($ID>=0) {
2929            $Name = $SourceFile{$Unit}{$File};
2930        }
2931        else
2932        { # imported
2933            $Name = $SourceFile_Alt{0}{$File};
2934        }
2935
2936        if($Name=~/\.($HEADER_EXT)\Z/)
2937        { # header
2938            $R->{"Header"} = $Name;
2939            if(defined $Line) {
2940                $R->{"Line"} = $Line;
2941            }
2942        }
2943        elsif($Name ne "<built-in>")
2944        { # source
2945            $R->{"Source"} = $Name;
2946            if(defined $Line) {
2947                $R->{"SourceLine"} = $Line;
2948            }
2949        }
2950    }
2951}
2952
2953sub skipSymbol($)
2954{
2955    if($SkipCxx and not $STDCXX_TARGET)
2956    {
2957        if($_[0]=~/\A(_ZS|_ZNS|_ZNKS|_ZN9__gnu_cxx|_ZNK9__gnu_cxx|_ZTIS|_ZTSS)/)
2958        { # stdc++ symbols
2959            return 1;
2960        }
2961    }
2962    return 0;
2963}
2964
2965sub find_center($$)
2966{
2967    my ($Name, $Target) = @_;
2968    my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 );
2969    foreach my $Pos (0 .. length($Name)-1)
2970    {
2971        my $S = substr($Name, length($Name)-1-$Pos, 1);
2972        if(defined $B{$S}) {
2973            $B{$S}+=1;
2974        }
2975        if($S eq $Target)
2976        {
2977            if($B{"("}==$B{")"}
2978            and $B{"<"}==$B{">"}) {
2979                return length($Name)-1-$Pos;
2980            }
2981        }
2982    }
2983    return 0;
2984}
2985
2986sub isExternal($)
2987{
2988    my $ID = $_[0];
2989
2990    if($DWARF_Info{$ID}{"external"}) {
2991        return 1;
2992    }
2993    elsif(my $Spec = $DWARF_Info{$ID}{"specification"})
2994    {
2995        if($DWARF_Info{$Spec}{"external"}) {
2996            return 1;
2997        }
2998    }
2999
3000    return 0;
3001}
3002
3003sub symByAddr($)
3004{
3005    my $Loc = $_[0];
3006
3007    my ($Addr, $Sect) = ("", "");
3008    if($Loc=~/\+(.+)/)
3009    {
3010        $Addr = $1;
3011        if(not $Addr=~s/\A0x//)
3012        {
3013            $Addr=~s/\A00//;
3014        }
3015    }
3016    if($Loc=~/([\w\.]+)\+/) {
3017        $Sect = $1;
3018    }
3019
3020    if($Addr ne "")
3021    {
3022        foreach ($Sect, "")
3023        {
3024            if(defined $SymbolTable{$_}{$Addr})
3025            {
3026                if(my @Symbols = sort keys(%{$SymbolTable{$_}{$Addr}})) {
3027                    return $Symbols[0];
3028                }
3029            }
3030        }
3031    }
3032
3033    return undef;
3034}
3035
3036sub get_Mangled($)
3037{
3038    my $ID = $_[0];
3039
3040    if(not defined $AddrToName)
3041    {
3042        if(my $Link = $DWARF_Info{$ID}{"linkage_name"})
3043        {
3044            return $Link;
3045        }
3046    }
3047
3048    if(my $Low_Pc = $DWARF_Info{$ID}{"low_pc"})
3049    {
3050        if($Low_Pc=~/<([\w\@\.]+)>/) {
3051            return $1;
3052        }
3053        else
3054        {
3055            if(my $Symbol = symByAddr($Low_Pc)) {
3056                return $Symbol;
3057            }
3058        }
3059    }
3060
3061    if(my $Loc = $DWARF_Info{$ID}{"location"})
3062    {
3063        if($Loc=~/<([\w\@\.]+)>/) {
3064            return $1;
3065        }
3066        else
3067        {
3068            if(my $Symbol = symByAddr($Loc)) {
3069                return $Symbol;
3070            }
3071        }
3072    }
3073
3074    if(my $Link = $DWARF_Info{$ID}{"linkage_name"})
3075    {
3076        return $Link;
3077    }
3078
3079    return undef;
3080}
3081
3082sub completeNS($)
3083{
3084    my $ID = $_[0];
3085
3086    my $NS = undef;
3087    my $ID_ = $ID;
3088    my @NSs = ();
3089
3090    while($NS = $NameSpace{$ID_}
3091    or $NS = $NameSpace{$DWARF_Info{$ID_}{"specification"}})
3092    {
3093        if(my $N = $DWARF_Info{$NS}{"name"}) {
3094            push(@NSs, $N);
3095        }
3096        $ID_ = $NS;
3097    }
3098
3099    if(@NSs)
3100    {
3101        my $N = join("::", reverse(@NSs));
3102        $NestedNameSpaces{$N} = 1;
3103        return $N;
3104    }
3105
3106    return undef;
3107}
3108
3109sub getSymbolInfo($)
3110{
3111    my $ID = $_[0];
3112
3113    if(my $N = $NameSpace{$ID})
3114    {
3115        if($DWARF_Info{$N}{"Kind"} eq "lexical_block"
3116        or $DWARF_Info{$N}{"Kind"} eq "subprogram")
3117        { # local variables
3118            return;
3119        }
3120    }
3121
3122    if(my $Loc = $DWARF_Info{$ID}{"location"})
3123    {
3124        if($Loc=~/ reg\d+\Z/)
3125        { # local variables
3126            return;
3127        }
3128    }
3129
3130    my $ShortName = $DWARF_Info{$ID}{"name"};
3131    my $MnglName = get_Mangled($ID);
3132
3133    if(not $MnglName)
3134    {
3135        if(my $Sp = $SpecElem{$ID}) {
3136            $MnglName = get_Mangled($Sp);
3137        }
3138    }
3139
3140    if(not $MnglName)
3141    {
3142        if(index($ShortName, "<")!=-1)
3143        { # template
3144            return;
3145        }
3146        $MnglName = $ShortName;
3147    }
3148
3149    if(skipSymbol($MnglName)) {
3150        return;
3151    }
3152
3153    if(index($MnglName, "\@")!=-1) {
3154        $MnglName=~s/([\@]+.*?)\Z//;
3155    }
3156
3157    if(not $MnglName) {
3158        return;
3159    }
3160
3161    if(index($MnglName, ".")!=-1)
3162    { # foo.part.14
3163      # bar.isra.15
3164        return;
3165    }
3166
3167    if($MnglName=~/\W/)
3168    { # unmangled operators, etc.
3169        return;
3170    }
3171
3172    if($MnglName)
3173    {
3174        if(my $OLD_ID = $Mangled_ID{$MnglName})
3175        { # duplicates
3176            if(not defined $SymbolInfo{$OLD_ID}{"Header"}
3177            or not defined $SymbolInfo{$OLD_ID}{"Source"})
3178            {
3179                setSource($SymbolInfo{$OLD_ID}, $ID);
3180            }
3181
3182            if(defined $Checked_Spec{$MnglName}
3183            or not $DWARF_Info{$ID}{"specification"})
3184            {
3185                if(not defined $SpecElem{$ID}
3186                and not defined $OrigElem{$ID}) {
3187                    delete($DWARF_Info{$ID});
3188                }
3189                return;
3190            }
3191        }
3192    }
3193
3194    my %SInfo = ();
3195
3196    if($ShortName) {
3197        $SInfo{"ShortName"} = $ShortName;
3198    }
3199    $SInfo{"MnglName"} = $MnglName;
3200
3201    if($MnglName eq $ShortName)
3202    {
3203        delete($SInfo{"MnglName"});
3204        $MnglName = $ShortName;
3205    }
3206    elsif(index($MnglName, "_Z")!=0)
3207    {
3208        if($SInfo{"ShortName"})
3209        {
3210            $SInfo{"Alias"} = $SInfo{"ShortName"};
3211            $SInfo{"ShortName"} = $SInfo{"MnglName"};
3212        }
3213
3214        delete($SInfo{"MnglName"});
3215        $MnglName = $ShortName;
3216    }
3217
3218    if(isExternal($ID)) {
3219        $SInfo{"External"} = 1;
3220    }
3221
3222    if(my $Orig = $DWARF_Info{$ID}{"abstract_origin"})
3223    {
3224        if(isExternal($Orig)) {
3225            $SInfo{"External"} = 1;
3226        }
3227    }
3228
3229    if(index($MnglName, "_ZNVK")==0)
3230    {
3231        $SInfo{"Const"} = 1;
3232        $SInfo{"Volatile"} = 1;
3233    }
3234    elsif(index($MnglName, "_ZNV")==0) {
3235        $SInfo{"Volatile"} = 1;
3236    }
3237    elsif(index($MnglName, "_ZNK")==0) {
3238        $SInfo{"Const"} = 1;
3239    }
3240
3241    if($DWARF_Info{$ID}{"artificial"}) {
3242        $SInfo{"Artificial"} = 1;
3243    }
3244
3245    my ($C, $D) = ();
3246
3247    if($MnglName=~/C[1-2][EI].+/)
3248    {
3249        $C = 1;
3250        $SInfo{"Constructor"} = 1;
3251    }
3252
3253    if($MnglName=~/D[0-2][EI].+/)
3254    {
3255        $D = 1;
3256        $SInfo{"Destructor"} = 1;
3257    }
3258
3259    if($C or $D)
3260    {
3261        if(my $Orig = $DWARF_Info{$ID}{"abstract_origin"})
3262        {
3263            if(my $InLine = $DWARF_Info{$Orig}{"inline"})
3264            {
3265                if(index($InLine, "declared_not_inlined")==0)
3266                {
3267                    $SInfo{"InLine"} = 1;
3268                    $SInfo{"Artificial"} = 1;
3269                }
3270            }
3271
3272            setSource(\%SInfo, $Orig);
3273
3274            if(my $Spec = $DWARF_Info{$Orig}{"specification"})
3275            {
3276                setSource(\%SInfo, $Spec);
3277
3278                $SInfo{"ShortName"} = $DWARF_Info{$Spec}{"name"};
3279                if($D) {
3280                    $SInfo{"ShortName"}=~s/\A\~//g;
3281                }
3282
3283                if(my $Class = $NameSpace{$Spec}) {
3284                    $SInfo{"Class"} = $Class;
3285                }
3286
3287                if(my $Virt = $DWARF_Info{$Spec}{"virtuality"})
3288                {
3289                    if(index($Virt, "virtual")!=-1) {
3290                        $SInfo{"Virt"} = 1;
3291                    }
3292                }
3293
3294                if(my $Access = $DWARF_Info{$Spec}{"accessibility"}) {
3295                    $SInfo{ucfirst($Access)} = 1;
3296                }
3297
3298                # clean origin
3299                delete($SymbolInfo{$Spec});
3300            }
3301        }
3302    }
3303    else
3304    {
3305        if(my $InLine = $DWARF_Info{$ID}{"inline"})
3306        {
3307            if(index($InLine, "declared_inlined")==0) {
3308                $SInfo{"InLine"} = 1;
3309            }
3310        }
3311    }
3312
3313    if(defined $AddrToName)
3314    {
3315        if(not $SInfo{"Alias"}
3316        and not $SInfo{"Constructor"}
3317        and not $SInfo{"Destructor"})
3318        {
3319            if(my $Linkage = $DWARF_Info{$ID}{"linkage_name"})
3320            {
3321                if($Linkage ne $MnglName) {
3322                    $SInfo{"Alias"} = $Linkage;
3323                }
3324            }
3325        }
3326    }
3327
3328    if($DWARF_Info{$ID}{"Kind"} eq "variable")
3329    { # global data
3330        $SInfo{"Data"} = 1;
3331
3332        if(my $Spec = $DWARF_Info{$ID}{"specification"})
3333        {
3334            if($DWARF_Info{$Spec}{"Kind"} eq "member")
3335            {
3336                setSource(\%SInfo, $Spec);
3337                $SInfo{"ShortName"} = $DWARF_Info{$Spec}{"name"};
3338
3339                if(my $NSp = $NameSpace{$Spec})
3340                {
3341                    if($DWARF_Info{$NSp}{"Kind"} eq "namespace") {
3342                        $SInfo{"NameSpace"} = completeNS($Spec);
3343                    }
3344                    else {
3345                        $SInfo{"Class"} = $NSp;
3346                    }
3347                }
3348            }
3349        }
3350    }
3351
3352    if(my $Access = $DWARF_Info{$ID}{"accessibility"})
3353    {
3354        $SInfo{ucfirst($Access)} = 1;
3355    }
3356
3357    if(my $Class = $DWARF_Info{$ID}{"containing_type"})
3358    {
3359        $SInfo{"Class"} = $Class;
3360    }
3361    if(my $NS = $NameSpace{$ID})
3362    {
3363        if($DWARF_Info{$NS}{"Kind"} eq "namespace") {
3364            $SInfo{"NameSpace"} = completeNS($ID);
3365        }
3366        else {
3367            $SInfo{"Class"} = $NS;
3368        }
3369    }
3370
3371    if($SInfo{"Class"} and $MnglName
3372    and index($MnglName, "_Z")!=0)
3373    {
3374        return;
3375    }
3376
3377    if(my $Return = $DWARF_Info{$ID}{"type"})
3378    {
3379        $SInfo{"Return"} = $Return;
3380    }
3381    if(my $Spec = $DWARF_Info{$ID}{"specification"})
3382    {
3383        if(not $DWARF_Info{$ID}{"type"}) {
3384            $SInfo{"Return"} = $DWARF_Info{$Spec}{"type"};
3385        }
3386        if(my $Value = $DWARF_Info{$Spec}{"const_value"})
3387        {
3388            if($Value=~/ block:\s*(.*?)\Z/) {
3389                $Value = $1;
3390            }
3391            $SInfo{"Value"} = $Value;
3392        }
3393    }
3394
3395    if($SInfo{"ShortName"}=~/>\Z/)
3396    { # foo<T1, T2, ...>
3397        my ($Short, @TParams) = get_TParams($SInfo{"ShortName"});
3398        if(@TParams)
3399        {
3400            foreach my $Pos (0 .. $#TParams) {
3401                $SInfo{"TParam"}{$Pos}{"name"} = formatName($TParams[$Pos], "T");
3402            }
3403            # simplify short name
3404            $SInfo{"ShortName"} = $Short.formatName("<".join(", ", @TParams).">", "T");
3405        }
3406    }
3407    elsif($SInfo{"ShortName"}=~/\Aoperator (\w.*)\Z/)
3408    { # operator type<T1>::name
3409        $SInfo{"ShortName"} = "operator ".simpleName($1);
3410    }
3411
3412    if(my $Virt = $DWARF_Info{$ID}{"virtuality"})
3413    {
3414        if(index($Virt, "virtual")!=-1)
3415        {
3416            if($D) {
3417                $SInfo{"Virt"} = 1;
3418            }
3419            else {
3420                $SInfo{"PureVirt"} = 1;
3421            }
3422        }
3423
3424        if((my $VirtPos = $DWARF_Info{$ID}{"vtable_elem_location"}) ne "")
3425        {
3426            $SInfo{"VirtPos"} = $VirtPos;
3427        }
3428    }
3429
3430    setSource(\%SInfo, $ID);
3431
3432    my $PPos = 0;
3433
3434    foreach my $Pos (sort {int($a) <=> int($b)} keys(%{$FuncParam{$ID}}))
3435    {
3436        my $ParamId = $FuncParam{$ID}{$Pos};
3437        my $Offset = undef;
3438        my $Reg = undef;
3439
3440        if(my $Sp = $SpecElem{$ID})
3441        {
3442            if(defined $FuncParam{$Sp}) {
3443                $ParamId = $FuncParam{$Sp}{$Pos};
3444            }
3445        }
3446
3447        if((my $Loc = $DWARF_Info{$ParamId}{"location"}) ne "") {
3448            $Offset = $Loc;
3449        }
3450        elsif((my $R = $DWARF_Info{$ParamId}{"register"}) ne "") {
3451            $Reg = $RegName{$R};
3452        }
3453        elsif((my $LL = $DWARF_Info{$ParamId}{"location_list"}) ne "")
3454        {
3455            if(my $L = $DebugLoc{$LL})
3456            {
3457                if($L=~/reg(\d+)/) {
3458                    $Reg = $RegName{$1};
3459                }
3460                elsif($L=~/fbreg\s+(-?\w+)\Z/) {
3461                    $Offset = $1;
3462                }
3463            }
3464        }
3465
3466        if(my $Orig = $DWARF_Info{$ParamId}{"abstract_origin"}) {
3467            $ParamId = $Orig;
3468        }
3469
3470        my %PInfo = %{$DWARF_Info{$ParamId}};
3471
3472        if(defined $Offset) {
3473            $SInfo{"Param"}{$Pos}{"offset"} = $Offset;
3474        }
3475
3476        $SInfo{"Param"}{$Pos}{"type"} = $PInfo{"type"};
3477
3478        if(defined $PInfo{"name"}) {
3479            $SInfo{"Param"}{$Pos}{"name"} = $PInfo{"name"};
3480        }
3481        elsif($TypeInfo{$PInfo{"type"}}{"Name"} ne "...") {
3482            $SInfo{"Param"}{$Pos}{"name"} = "p".($PPos+1);
3483        }
3484        if(defined $Reg)
3485        { # FIXME: 0+8, 1+16, etc. (for partially distributed parameters)
3486            $SInfo{"Reg"}{$Pos} = $Reg;
3487        }
3488
3489        if($DWARF_Info{$ParamId}{"artificial"} and $Pos==0)
3490        {
3491            if($SInfo{"Param"}{$Pos}{"name"} eq "p1") {
3492                $SInfo{"Param"}{$Pos}{"name"} = "this";
3493            }
3494        }
3495
3496        if($SInfo{"Param"}{$Pos}{"name"} ne "this")
3497        { # this, p1, p2, etc.
3498            $PPos += 1;
3499        }
3500    }
3501
3502    if($SInfo{"Constructor"} and not $SInfo{"InLine"}
3503    and $SInfo{"Class"}) {
3504        delete($TypeInfo{$SInfo{"Class"}}{"Copied"});
3505    }
3506
3507    if(my $BASE_ID = $Mangled_ID{$MnglName})
3508    {
3509        $ID = $BASE_ID;
3510
3511        if(defined $SymbolInfo{$ID}{"PureVirt"})
3512        {
3513            delete($SymbolInfo{$ID}{"PureVirt"});
3514            $SymbolInfo{$ID}{"Virt"} = 1;
3515        }
3516    }
3517    $Mangled_ID{$MnglName} = $ID;
3518
3519    if($DWARF_Info{$ID}{"specification"}) {
3520        $Checked_Spec{$MnglName} = 1;
3521    }
3522
3523    foreach my $Attr (keys(%SInfo)) {
3524        $SymbolInfo{$ID}{$Attr} = $SInfo{$Attr};
3525    }
3526
3527    if($ID>$GLOBAL_ID) {
3528        $GLOBAL_ID = $ID;
3529    }
3530}
3531
3532sub getTypeIdByName($$)
3533{
3534    my ($Type, $Name) = @_;
3535    return $TName_Tid{$Type}{formatName($Name, "T")};
3536}
3537
3538sub getFirst($)
3539{
3540    my $Tid = $_[0];
3541    if(not $Tid) {
3542        return $Tid;
3543    }
3544
3545    if(defined $TypeSpec{$Tid}) {
3546        $Tid = $TypeSpec{$Tid};
3547    }
3548
3549    my $F = 0;
3550
3551    if(my $Name = $TypeInfo{$Tid}{"Name"})
3552    {
3553        my $Type = $TypeInfo{$Tid}{"Type"};
3554        if($Name=~s/\Astruct //)
3555        { # search for class or derived types (const, *, etc.)
3556            $F = 1;
3557        }
3558
3559        my $FTid = undef;
3560        if($F)
3561        {
3562            foreach my $Type ("Class", "Const", "Ref", "Pointer")
3563            {
3564                if($FTid = $TName_Tid{$Type}{$Name})
3565                {
3566                    if($FTid ne $Tid)
3567                    {
3568                        $MergedTypes{$Tid} = 1;
3569                    }
3570                    return "$FTid";
3571                }
3572            }
3573
3574            $Name = "struct ".$Name;
3575        }
3576
3577        if(not $FTid) {
3578            $FTid = $TName_Tid{$Type}{$Name};
3579        }
3580
3581        if($FTid) {
3582            return "$FTid";
3583        }
3584        printMsg("ERROR", "internal error (missed type id $Tid)");
3585    }
3586
3587    return $Tid;
3588}
3589
3590sub searchTypeID($)
3591{
3592    my $Name = $_[0];
3593
3594    my %Pr = map {$_=>1} (
3595        "Struct",
3596        "Union",
3597        "Enum"
3598    );
3599
3600    foreach my $Type ("Class", "Struct", "Union", "Enum", "Typedef", "Const",
3601    "Volatile", "Ref", "Pointer", "FuncPtr", "MethodPtr", "FieldPtr")
3602    {
3603        my $Tid = $TName_Tid{$Type}{$Name};
3604
3605        if(not $Tid)
3606        {
3607            my $P = "";
3608            if(defined $Pr{$Type})
3609            {
3610                $P = lc($Type)." ";
3611            }
3612
3613            $Tid = $TName_Tid{$Type}{$P.$Name}
3614        }
3615        if($Tid) {
3616            return $Tid;
3617        }
3618    }
3619    return undef;
3620}
3621
3622sub remove_Unused()
3623{ # remove unused data types from the ABI dump
3624    %HeadersInfo = ();
3625    %SourcesInfo = ();
3626
3627    my (%SelectedHeaders, %SelectedSources) = ();
3628
3629    foreach my $ID (sort {int($a)<=>int($b)} keys(%SymbolInfo))
3630    {
3631        if($SelectedSymbols{$ID}==2)
3632        { # data, inline, pure
3633            next;
3634        }
3635
3636        register_SymbolUsage($ID);
3637
3638        if(my $H = $SymbolInfo{$ID}{"Header"}) {
3639            $SelectedHeaders{$H} = 1;
3640        }
3641        if(my $S = $SymbolInfo{$ID}{"Source"}) {
3642            $SelectedSources{$S} = 1;
3643        }
3644    }
3645
3646    foreach my $ID (sort {int($a)<=>int($b)} keys(%SymbolInfo))
3647    {
3648        if($SelectedSymbols{$ID}==2)
3649        { # data, inline, pure
3650            my $Save = 0;
3651            if(my $Class = $SymbolInfo{$ID}{"Class"})
3652            {
3653                if(defined $UsedType{$Class}) {
3654                    $Save = 1;
3655                }
3656                else
3657                {
3658                    foreach (keys(%{$ClassChild{$Class}}))
3659                    {
3660                        if(defined $UsedType{$_})
3661                        {
3662                            $Save = 1;
3663                            last;
3664                        }
3665                    }
3666                }
3667            }
3668            if(my $Header = $SymbolInfo{$ID}{"Header"})
3669            {
3670                if(defined $SelectedHeaders{$Header}) {
3671                    $Save = 1;
3672                }
3673            }
3674            if(my $Source = $SymbolInfo{$ID}{"Source"})
3675            {
3676                if(defined $SelectedSources{$Source}) {
3677                    $Save = 1;
3678                }
3679            }
3680            if($Save) {
3681                register_SymbolUsage($ID);
3682            }
3683            else {
3684                delete($SymbolInfo{$ID});
3685            }
3686        }
3687    }
3688
3689    if(defined $AllTypes)
3690    {
3691        # register all data types (except anon structs and unions)
3692        foreach my $Tid (keys(%TypeInfo))
3693        {
3694            if(defined $LocalType{$Tid})
3695            { # except local code
3696                next;
3697            }
3698            if($TypeInfo{$Tid}{"Type"} eq "Enum"
3699            or index($TypeInfo{$Tid}{"Name"}, "anon-")!=0) {
3700                register_TypeUsage($Tid);
3701            }
3702        }
3703
3704        # remove unused anons (except enums)
3705        foreach my $Tid (keys(%TypeInfo))
3706        {
3707            if(not $UsedType{$Tid})
3708            {
3709                if($TypeInfo{$Tid}{"Type"} ne "Enum")
3710                {
3711                    if(index($TypeInfo{$Tid}{"Name"}, "anon-")==0) {
3712                        delete($TypeInfo{$Tid});
3713                    }
3714                }
3715            }
3716        }
3717
3718        # remove duplicates
3719        foreach my $Tid (keys(%TypeInfo))
3720        {
3721            my $Name = $TypeInfo{$Tid}{"Name"};
3722            my $Type = $TypeInfo{$Tid}{"Type"};
3723
3724            if($TName_Tid{$Type}{$Name} ne $Tid) {
3725                delete($TypeInfo{$Tid});
3726            }
3727        }
3728    }
3729    else
3730    {
3731        foreach my $Tid (keys(%TypeInfo))
3732        { # remove unused types
3733            if(not $UsedType{$Tid}) {
3734                delete($TypeInfo{$Tid});
3735            }
3736        }
3737    }
3738
3739    foreach my $Tid (keys(%MergedTypes)) {
3740        delete($TypeInfo{$Tid});
3741    }
3742
3743    foreach my $Tid (keys(%LocalType))
3744    {
3745        if(not $UsedType{$Tid}) {
3746            delete($TypeInfo{$Tid});
3747        }
3748    }
3749
3750    # clean memory
3751    %MergedTypes = ();
3752    %LocalType = ();
3753
3754    # completeness
3755    foreach my $Tid (keys(%TypeInfo)) {
3756        check_Completeness($TypeInfo{$Tid});
3757    }
3758
3759    foreach my $Sid (keys(%SymbolInfo)) {
3760        check_Completeness($SymbolInfo{$Sid});
3761    }
3762
3763    # clean memory
3764    %UsedType = ();
3765}
3766
3767sub simpleName($)
3768{
3769    my $N = $_[0];
3770
3771    if(index($N, "std::basic_string")!=-1)
3772    {
3773        $N=~s/std::basic_string<char, std::char_traits<char>, std::allocator<char> >/std::string /g;
3774        $N=~s/std::basic_string<char>/std::string /g;
3775    }
3776
3777    return formatName($N, "T");
3778}
3779
3780sub register_SymbolUsage($)
3781{
3782    my $InfoId = $_[0];
3783
3784    my %FuncInfo = %{$SymbolInfo{$InfoId}};
3785
3786    if(my $S = $FuncInfo{"Source"}) {
3787        $SourcesInfo{$S} = 1;
3788    }
3789    if(my $H = $FuncInfo{"Header"}) {
3790        $HeadersInfo{$H} = 1;
3791    }
3792    if(my $RTid = getFirst($FuncInfo{"Return"}))
3793    {
3794        register_TypeUsage($RTid);
3795        $SymbolInfo{$InfoId}{"Return"} = $RTid;
3796    }
3797    if(my $FCid = getFirst($FuncInfo{"Class"}))
3798    {
3799        register_TypeUsage($FCid);
3800        $SymbolInfo{$InfoId}{"Class"} = $FCid;
3801
3802        if(my $ThisId = getTypeIdByName("Const", $TypeInfo{$FCid}{"Name"}."*const"))
3803        { # register "this" pointer
3804            register_TypeUsage($ThisId);
3805        }
3806        if(my $ThisId_C = getTypeIdByName("Const", $TypeInfo{$FCid}{"Name"}." const*const"))
3807        { # register "this" pointer (const method)
3808            register_TypeUsage($ThisId_C);
3809        }
3810    }
3811    foreach my $PPos (keys(%{$FuncInfo{"Param"}}))
3812    {
3813        if(my $PTid = getFirst($FuncInfo{"Param"}{$PPos}{"type"}))
3814        {
3815            register_TypeUsage($PTid);
3816            $SymbolInfo{$InfoId}{"Param"}{$PPos}{"type"} = $PTid;
3817        }
3818    }
3819    foreach my $TPos (keys(%{$FuncInfo{"TParam"}}))
3820    {
3821        my $TPName = $FuncInfo{"TParam"}{$TPos}{"name"};
3822        if(my $TTid = searchTypeID($TPName))
3823        {
3824            if(my $FTTid = getFirst($TTid)) {
3825                register_TypeUsage($FTTid);
3826            }
3827        }
3828    }
3829}
3830
3831sub register_TypeUsage($)
3832{
3833    my $TypeId = $_[0];
3834    if(not $TypeId) {
3835        return 0;
3836    }
3837    if($UsedType{$TypeId})
3838    { # already registered
3839        return 1;
3840    }
3841    my %TInfo = %{$TypeInfo{$TypeId}};
3842
3843    if(my $S = $TInfo{"Source"}) {
3844        $SourcesInfo{$S} = 1;
3845    }
3846    if(my $H = $TInfo{"Header"}) {
3847        $HeadersInfo{$H} = 1;
3848    }
3849
3850    if($TInfo{"Type"})
3851    {
3852        if(my $NS = $TInfo{"NameSpace"})
3853        {
3854            if(my $NSTid = searchTypeID($NS))
3855            {
3856                if(my $FNSTid = getFirst($NSTid)) {
3857                    register_TypeUsage($FNSTid);
3858                }
3859            }
3860        }
3861
3862        if($TInfo{"Type"}=~/\A(Struct|Union|Class|FuncPtr|Func|MethodPtr|FieldPtr|Enum)\Z/)
3863        {
3864            $UsedType{$TypeId} = 1;
3865            if($TInfo{"Type"}=~/\A(Struct|Class)\Z/)
3866            {
3867                foreach my $BaseId (keys(%{$TInfo{"Base"}}))
3868                { # register base classes
3869                    if(my $FBaseId = getFirst($BaseId))
3870                    {
3871                        register_TypeUsage($FBaseId);
3872                        if($FBaseId ne $BaseId)
3873                        {
3874                            %{$TypeInfo{$TypeId}{"Base"}{$FBaseId}} = %{$TypeInfo{$TypeId}{"Base"}{$BaseId}};
3875                            delete($TypeInfo{$TypeId}{"Base"}{$BaseId});
3876                        }
3877                    }
3878                }
3879                foreach my $TPos (keys(%{$TInfo{"TParam"}}))
3880                {
3881                    my $TPName = $TInfo{"TParam"}{$TPos}{"name"};
3882                    if(my $TTid = searchTypeID($TPName))
3883                    {
3884                        if(my $FTTid = getFirst($TTid)) {
3885                            register_TypeUsage($FTTid);
3886                        }
3887                    }
3888                }
3889            }
3890            foreach my $Memb_Pos (keys(%{$TInfo{"Memb"}}))
3891            {
3892                if(my $MTid = getFirst($TInfo{"Memb"}{$Memb_Pos}{"type"}))
3893                {
3894                    register_TypeUsage($MTid);
3895                    $TypeInfo{$TypeId}{"Memb"}{$Memb_Pos}{"type"} = $MTid;
3896                }
3897            }
3898            if($TInfo{"Type"} eq "FuncPtr"
3899            or $TInfo{"Type"} eq "MethodPtr"
3900            or $TInfo{"Type"} eq "Func")
3901            {
3902                if(my $RTid = getFirst($TInfo{"Return"}))
3903                {
3904                    register_TypeUsage($RTid);
3905                    $TypeInfo{$TypeId}{"Return"} = $RTid;
3906                }
3907                foreach my $Memb_Pos (keys(%{$TInfo{"Param"}}))
3908                {
3909                    if(my $MTid = getFirst($TInfo{"Param"}{$Memb_Pos}{"type"}))
3910                    {
3911                        register_TypeUsage($MTid);
3912                        $TypeInfo{$TypeId}{"Param"}{$Memb_Pos}{"type"} = $MTid;
3913                    }
3914                }
3915            }
3916            if($TInfo{"Type"} eq "FieldPtr")
3917            {
3918                if(my $RTid = getFirst($TInfo{"Return"}))
3919                {
3920                    register_TypeUsage($RTid);
3921                    $TypeInfo{$TypeId}{"Return"} = $RTid;
3922                }
3923                if(my $CTid = getFirst($TInfo{"Class"}))
3924                {
3925                    register_TypeUsage($CTid);
3926                    $TypeInfo{$TypeId}{"Class"} = $CTid;
3927                }
3928            }
3929            if($TInfo{"Type"} eq "MethodPtr")
3930            {
3931                if(my $CTid = getFirst($TInfo{"Class"}))
3932                {
3933                    register_TypeUsage($CTid);
3934                    $TypeInfo{$TypeId}{"Class"} = $CTid;
3935                }
3936            }
3937            return 1;
3938        }
3939        elsif($TInfo{"Type"}=~/\A(Const|ConstVolatile|Volatile|Pointer|Ref|Restrict|Array|Typedef)\Z/)
3940        {
3941            $UsedType{$TypeId} = 1;
3942            if(my $BTid = getFirst($TInfo{"BaseType"}))
3943            {
3944                register_TypeUsage($BTid);
3945                $TypeInfo{$TypeId}{"BaseType"} = $BTid;
3946            }
3947            return 1;
3948        }
3949        elsif($TInfo{"Type"} eq "Intrinsic")
3950        {
3951            $UsedType{$TypeId} = 1;
3952            return 1;
3953        }
3954    }
3955    return 0;
3956}
3957
3958my %CheckedType = ();
3959
3960sub check_Completeness($)
3961{
3962    my $Info = $_[0];
3963
3964    # data types
3965    if(defined $Info->{"Memb"})
3966    {
3967        foreach my $Pos (keys(%{$Info->{"Memb"}}))
3968        {
3969            if(defined $Info->{"Memb"}{$Pos}{"type"}) {
3970                check_TypeInfo($Info->{"Memb"}{$Pos}{"type"});
3971            }
3972        }
3973    }
3974    if(defined $Info->{"Base"})
3975    {
3976        foreach my $Bid (keys(%{$Info->{"Base"}})) {
3977            check_TypeInfo($Bid);
3978        }
3979    }
3980    if(defined $Info->{"BaseType"}) {
3981        check_TypeInfo($Info->{"BaseType"});
3982    }
3983    if(defined $Info->{"TParam"})
3984    {
3985        foreach my $Pos (keys(%{$Info->{"TParam"}}))
3986        {
3987            my $TName = $Info->{"TParam"}{$Pos}{"name"};
3988            if($TName=~/\A(true|false|\d.*)\Z/) {
3989                next;
3990            }
3991
3992            if(my $Tid = searchTypeID($TName)) {
3993                check_TypeInfo($Tid);
3994            }
3995            else
3996            {
3997                if(defined $Loud) {
3998                    printMsg("WARNING", "missed type $TName");
3999                }
4000            }
4001        }
4002    }
4003
4004    # symbols
4005    if(defined $Info->{"Param"})
4006    {
4007        foreach my $Pos (keys(%{$Info->{"Param"}}))
4008        {
4009            if(defined $Info->{"Param"}{$Pos}{"type"}) {
4010                check_TypeInfo($Info->{"Param"}{$Pos}{"type"});
4011            }
4012        }
4013    }
4014    if(defined $Info->{"Return"}) {
4015        check_TypeInfo($Info->{"Return"});
4016    }
4017    if(defined $Info->{"Class"}) {
4018        check_TypeInfo($Info->{"Class"});
4019    }
4020}
4021
4022sub check_TypeInfo($)
4023{
4024    my $Tid = $_[0];
4025
4026    if(defined $CheckedType{$Tid}) {
4027        return;
4028    }
4029    $CheckedType{$Tid} = 1;
4030
4031    if(defined $TypeInfo{$Tid})
4032    {
4033        if(not $TypeInfo{$Tid}{"Name"}) {
4034            printMsg("ERROR", "missed type name ($Tid)");
4035        }
4036        check_Completeness($TypeInfo{$Tid});
4037    }
4038    else {
4039        printMsg("ERROR", "missed type id $Tid");
4040    }
4041}
4042
4043sub init_Registers()
4044{
4045    if($SYS_ARCH eq "x86")
4046    {
4047        %RegName = (
4048        # integer registers
4049        # 32 bits
4050            "0"=>"eax",
4051            "1"=>"ecx",
4052            "2"=>"edx",
4053            "3"=>"ebx",
4054            "4"=>"esp",
4055            "5"=>"ebp",
4056            "6"=>"esi",
4057            "7"=>"edi",
4058            "8"=>"eip",
4059            "9"=>"eflags",
4060            "10"=>"trapno",
4061        # FPU-control registers
4062        # 16 bits
4063            "37"=>"fctrl",
4064            "38"=>"fstat",
4065        # 32 bits
4066            "39"=>"mxcsr",
4067        # MMX registers
4068        # 64 bits
4069            "29"=>"mm0",
4070            "30"=>"mm1",
4071            "31"=>"mm2",
4072            "32"=>"mm3",
4073            "33"=>"mm4",
4074            "34"=>"mm5",
4075            "35"=>"mm6",
4076            "36"=>"mm7",
4077        # SSE registers
4078        # 128 bits
4079            "21"=>"xmm0",
4080            "22"=>"xmm1",
4081            "23"=>"xmm2",
4082            "24"=>"xmm3",
4083            "25"=>"xmm4",
4084            "26"=>"xmm5",
4085            "27"=>"xmm6",
4086            "28"=>"xmm7",
4087        # segment registers
4088        # 16 bits
4089            "40"=>"es",
4090            "41"=>"cs",
4091            "42"=>"ss",
4092            "43"=>"ds",
4093            "44"=>"fs",
4094            "45"=>"gs",
4095        # x87 registers
4096        # 80 bits
4097            "11"=>"st0",
4098            "12"=>"st1",
4099            "13"=>"st2",
4100            "14"=>"st3",
4101            "15"=>"st4",
4102            "16"=>"st5",
4103            "17"=>"st6",
4104            "18"=>"st7"
4105        );
4106    }
4107    elsif($SYS_ARCH eq "x86_64")
4108    {
4109        %RegName = (
4110        # integer registers
4111        # 64 bits
4112            "1"=>"rdx",
4113            "2"=>"rcx",
4114            "3"=>"rbx",
4115            "4"=>"rsi",
4116            "5"=>"rdi",
4117            "6"=>"rbp",
4118            "7"=>"rsp",
4119            "8"=>"r8",
4120            "9"=>"r9",
4121            "10"=>"r10",
4122            "11"=>"r11",
4123            "12"=>"r12",
4124            "13"=>"r13",
4125            "14"=>"r14",
4126            "15"=>"r15",
4127            "16"=>"rip",
4128            "49"=>"rflags",
4129        # MMX registers
4130        # 64 bits
4131            "41"=>"mm0",
4132            "42"=>"mm1",
4133            "43"=>"mm2",
4134            "44"=>"mm3",
4135            "45"=>"mm4",
4136            "46"=>"mm5",
4137            "47"=>"mm6",
4138            "48"=>"mm7",
4139        # SSE registers
4140        # 128 bits
4141            "17"=>"xmm0",
4142            "18"=>"xmm1",
4143            "19"=>"xmm2",
4144            "20"=>"xmm3",
4145            "21"=>"xmm4",
4146            "22"=>"xmm5",
4147            "23"=>"xmm6",
4148            "24"=>"xmm7",
4149            "25"=>"xmm8",
4150            "26"=>"xmm9",
4151            "27"=>"xmm10",
4152            "28"=>"xmm11",
4153            "29"=>"xmm12",
4154            "30"=>"xmm13",
4155            "31"=>"xmm14",
4156            "32"=>"xmm15",
4157        # control registers
4158        # 64 bits
4159            "62"=>"tr",
4160            "63"=>"ldtr",
4161            "64"=>"mxcsr",
4162        # 16 bits
4163            "65"=>"fcw",
4164            "66"=>"fsw",
4165        # segment registers
4166        # 16 bits
4167            "50"=>"es",
4168            "51"=>"cs",
4169            "52"=>"ss",
4170            "53"=>"ds",
4171            "54"=>"fs",
4172            "55"=>"gs",
4173        # 64 bits
4174            "58"=>"fs.base",
4175            "59"=>"gs.base",
4176        # x87 registers
4177        # 80 bits
4178            "33"=>"st0",
4179            "34"=>"st1",
4180            "35"=>"st2",
4181            "36"=>"st3",
4182            "37"=>"st4",
4183            "38"=>"st5",
4184            "39"=>"st6",
4185            "40"=>"st7"
4186        );
4187    }
4188    elsif($SYS_ARCH eq "arm")
4189    {
4190        %RegName = (
4191        # integer registers
4192        # 32-bit
4193            "0"=>"r0",
4194            "1"=>"r1",
4195            "2"=>"r2",
4196            "3"=>"r3",
4197            "4"=>"r4",
4198            "5"=>"r5",
4199            "6"=>"r6",
4200            "7"=>"r7",
4201            "8"=>"r8",
4202            "9"=>"r9",
4203            "10"=>"r10",
4204            "11"=>"r11",
4205            "12"=>"r12",
4206            "13"=>"r13",
4207            "14"=>"r14",
4208            "15"=>"r15"
4209        );
4210    }
4211}
4212
4213sub dump_sorting($)
4214{
4215    my $Hash = $_[0];
4216    return [] if(not $Hash);
4217    my @Keys = keys(%{$Hash});
4218    return [] if($#Keys<0);
4219    if($Keys[0]=~/\A\d+\Z/)
4220    { # numbers
4221        return [sort {int($a)<=>int($b)} @Keys];
4222    }
4223    else
4224    { # strings
4225        return [sort {$a cmp $b} @Keys];
4226    }
4227}
4228
4229sub scenario()
4230{
4231    if($Help)
4232    {
4233        HELP_MESSAGE();
4234        exit(0);
4235    }
4236    if($ShowVersion)
4237    {
4238        printMsg("INFO", "ABI Dumper $TOOL_VERSION\nCopyright (C) 2015 Andrey Ponomarenko's ABI Laboratory\nLicense: LGPL or GPL <http://www.gnu.org/licenses/>\nThis program is free software: you can redistribute it and/or modify it.\n\nWritten by Andrey Ponomarenko.");
4239        exit(0);
4240    }
4241    if($DumpVersion)
4242    {
4243        printMsg("INFO", $TOOL_VERSION);
4244        exit(0);
4245    }
4246
4247    $Data::Dumper::Sortkeys = 1;
4248
4249    if($SortDump) {
4250        $Data::Dumper::Sortkeys = \&dump_sorting;
4251    }
4252
4253    if(defined $Compare)
4254    {
4255        my $P1 = $ARGV[0];
4256        my $P2 = $ARGV[1];
4257
4258        if(not $P1) {
4259            exitStatus("Error", "arguments are not specified");
4260        }
4261        elsif(not -e $P1) {
4262            exitStatus("Access_Error", "can't access \'$P1\'");
4263        }
4264
4265        if(not $P2) {
4266            exitStatus("Error", "second argument is not specified");
4267        }
4268        elsif(not -e $P2) {
4269            exitStatus("Access_Error", "can't access \'$P2\'");
4270        }
4271
4272        my %ABI = ();
4273
4274        $ABI{1} = eval(readFile($P1));
4275        $ABI{2} = eval(readFile($P2));
4276
4277        my %SymInfo = ();
4278
4279        foreach (1, 2)
4280        {
4281            foreach my $ID (keys(%{$ABI{$_}->{"SymbolInfo"}}))
4282            {
4283                my $Info = $ABI{$_}->{"SymbolInfo"}{$ID};
4284
4285                if(my $MnglName = $Info->{"MnglName"}) {
4286                    $SymInfo{$_}{$MnglName} = $Info;
4287                }
4288                elsif(my $ShortName = $Info->{"MnglName"}) {
4289                    $SymInfo{$_}{$ShortName} = $Info;
4290                }
4291            }
4292        }
4293
4294        foreach my $Symbol (sort keys(%{$SymInfo{1}}))
4295        {
4296            if(not defined $SymInfo{2}{$Symbol}) {
4297                printMsg("INFO", "Removed $Symbol");
4298            }
4299        }
4300
4301        foreach my $Symbol (sort keys(%{$SymInfo{2}}))
4302        {
4303            if(not defined $SymInfo{1}{$Symbol}) {
4304                printMsg("INFO", "Added $Symbol");
4305            }
4306        }
4307
4308        exit(0);
4309    }
4310
4311    if(not $TargetVersion) {
4312        printMsg("WARNING", "module version is not specified (-lver NUM)");
4313    }
4314
4315    if($FullDump)
4316    {
4317        $AllTypes = 1;
4318        $AllSymbols = 1;
4319    }
4320
4321    if(not $OutputDump) {
4322        $OutputDump = "./ABI.dump";
4323    }
4324
4325    if(not @ARGV) {
4326        exitStatus("Error", "object path is not specified");
4327    }
4328
4329    foreach my $Obj (@ARGV)
4330    {
4331        if(not -e $Obj) {
4332            exitStatus("Access_Error", "can't access \'$Obj\'");
4333        }
4334    }
4335
4336    if($AltDebugInfo)
4337    {
4338        if(not -e $AltDebugInfo) {
4339            exitStatus("Access_Error", "can't access \'$AltDebugInfo\'");
4340        }
4341    }
4342    else
4343    {
4344        my $Readelf = "eu-readelf";
4345        if(not check_Cmd($Readelf)) {
4346            exitStatus("Not_Found", "can't find \"$Readelf\" command");
4347        }
4348        foreach my $Obj (@ARGV)
4349        {
4350            my $Sect = `$Readelf -S \"$Obj\" 2>\"$TMP_DIR/error\"`;
4351
4352            if($Sect=~/\.debug_info/)
4353            {
4354                if($Sect=~/\.gnu_debugaltlink/)
4355                {
4356                    my $Str = `$Readelf --strings=.gnu_debugaltlink \"$Obj\" 2>\"$TMP_DIR/error\"`;
4357
4358                    if($Str=~/0\]\s*(.+)/)
4359                    {
4360                        my $AltObj_R = get_dirname($Obj)."/".$1;
4361
4362                        my $AltObj = $AltObj_R;
4363
4364                        while($AltObj=~s/\/[^\/]+\/\.\.\//\//){};
4365
4366                        if(-e $AltObj)
4367                        {
4368                            printMsg("INFO", "Set alternate debug-info file to \'$AltObj\' (use -alt option to change it)");
4369                            $AltDebugInfo = $AltObj;
4370                        }
4371                        else
4372                        {
4373                            printMsg("WARNING", "can't access \'$AltObj_R\'");
4374                        }
4375                    }
4376                }
4377                last;
4378            }
4379        }
4380    }
4381
4382    if($AltDebugInfo) {
4383        read_Alt_Info($AltDebugInfo);
4384    }
4385
4386    if($ExtraInfo)
4387    {
4388        mkpath($ExtraInfo);
4389        $ExtraInfo = abs_path($ExtraInfo);
4390    }
4391
4392    my $Res = 0;
4393
4394    foreach my $Obj (@ARGV)
4395    {
4396        $TargetName = get_filename(realpath($Obj));
4397        $TargetName=~s/\.debug\Z//; # nouveau.ko.debug
4398
4399        if(index($TargetName, "libstdc++.so")==0) {
4400            $STDCXX_TARGET = 1;
4401        }
4402
4403        read_Symbols($Obj);
4404        $Res += read_DWARF_Info($Obj);
4405        read_Vtables($Obj);
4406    }
4407
4408    if(not $Res) {
4409        exitStatus("No_DWARF", "can't find debug info in object(s)");
4410    }
4411
4412    init_Registers();
4413    read_ABI();
4414
4415    # clean memory
4416    %DWARF_Info = ();
4417
4418    remove_Unused();
4419
4420    # clean memory
4421    %SourceFile = ();
4422    %SourceFile_Alt = ();
4423    %DebugLoc = ();
4424    %TName_Tid = ();
4425    %TName_Tids = ();
4426
4427    %TypeMember = ();
4428    %ArrayCount = ();
4429    %FuncParam = ();
4430    %Inheritance = ();
4431    %NameSpace = ();
4432    %SpecElem = ();
4433    %OrigElem = ();
4434    %ClassMethods = ();
4435    %TypeSpec = ();
4436    %ClassChild = ();
4437
4438    %VirtualTable = ();
4439    %SymbolTable = ();
4440
4441    dump_ABI();
4442
4443    exit(0);
4444}
4445
4446scenario();
4447