xref: /petsc/lib/petsc/bin/maint/abi-compliance-checker/modules/Internals/ABIDump.pm (revision 375d23e45bc7f27f90f326da17aaea364b3bd8d3)
1###########################################################################
2# A module to create ABI dump from AST tree
3#
4# Copyright (C) 2015-2018 Andrey Ponomarenko's ABI Laboratory
5#
6# Written by Andrey Ponomarenko
7#
8# This library is free software; you can redistribute it and/or
9# modify it under the terms of the GNU Lesser General Public
10# License as published by the Free Software Foundation; either
11# version 2.1 of the License, or (at your option) any later version.
12#
13# This library is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16# Lesser General Public License for more details.
17#
18# You should have received a copy of the GNU Lesser General Public
19# License along with this library; if not, write to the Free Software
20# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21# MA  02110-1301 USA
22###########################################################################
23use strict;
24
25loadModule("ElfTools");
26loadModule("TUDump");
27loadModule("GccAst");
28
29my %Cache;
30
31my %RegisteredObj;
32my %RegisteredObj_Short;
33my %RegisteredSoname;
34my %RegisteredObj_Dir;
35my %CheckedDyLib;
36my %KnownLibs;
37my %CheckedArch;
38my @RecurLib;
39
40sub createABIDump($)
41{
42    my $LVer = $_[0];
43
44    if($In::Opt{"CheckHeadersOnly"}) {
45        $In::ABI{$LVer}{"Language"} = "C++";
46    }
47    else
48    {
49        readLibs($LVer);
50
51        if(not keys(%{$In::ABI{$LVer}{"SymLib"}})) {
52            exitStatus("Error", "the set of public symbols in library(ies) is empty");
53        }
54    }
55
56    if($In::Opt{"TargetArch"}) {
57        $In::ABI{$LVer}{"Arch"} = $In::Opt{"TargetArch"};
58    }
59    else {
60        $In::ABI{$LVer}{"Arch"} = getArch_GCC($LVer);
61    }
62
63    $In::ABI{$LVer}{"WordSize"} = detectWordSize($LVer);
64
65    $In::ABI{$LVer}{"LibraryVersion"} = $In::Desc{$LVer}{"Version"};
66    $In::ABI{$LVer}{"LibraryName"} = $In::Opt{"TargetLib"};
67
68    if(not $In::ABI{$LVer}{"Language"}) {
69        $In::ABI{$LVer}{"Language"} = "C";
70    }
71
72    if($In::Opt{"UserLang"}) {
73        $In::ABI{$LVer}{"Language"} = $In::Opt{"UserLang"};
74    }
75
76    $In::ABI{$LVer}{"GccVersion"} = $In::Opt{"GccVer"};
77
78    printMsg("INFO", "Checking header(s) ".$In::Desc{$LVer}{"Version"}." ...");
79    my $TUDump = createTUDump($LVer);
80
81    if($In::Opt{"Debug"})
82    { # debug mode
83        copy($TUDump, getDebugDir($LVer)."/translation-unit-dump.txt");
84    }
85
86    readGccAst($LVer, $TUDump);
87
88    if($In::Opt{"DebugMangling"})
89    {
90        if($In::ABI{$LVer}{"Language"} eq "C++")
91        {
92            debugMangling($LVer);
93        }
94    }
95
96    delete($In::ABI{$LVer}{"EnumConstants"});
97    delete($In::ABI{$LVer}{"ClassVTable_Content"});
98    delete($In::ABI{$LVer}{"WeakSymbols"});
99
100    cleanDump($LVer);
101
102    if(not keys(%{$In::ABI{$LVer}{"SymbolInfo"}}))
103    { # check if created dump is valid
104        if(not $In::Opt{"ExtendedCheck"})
105        {
106            if($In::Opt{"CheckHeadersOnly"}) {
107                exitStatus("Empty_Set", "the set of public symbols is empty");
108            }
109            else {
110                exitStatus("Empty_Intersection", "the sets of public symbols in headers and libraries have empty intersection");
111            }
112        }
113    }
114
115    foreach my $HPath (keys(%{$In::Desc{$LVer}{"RegHeader"}})) {
116        $In::ABI{$LVer}{"Headers"}{getFilename($HPath)} = 1;
117    }
118
119    foreach my $InfoId (keys(%{$In::ABI{$LVer}{"SymbolInfo"}}))
120    {
121        if(my $MnglName = $In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"MnglName"})
122        {
123            if(my $Unmangled = getUnmangled($MnglName, $LVer)) {
124                $In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"Unmangled"} = $Unmangled;
125            }
126        }
127    }
128
129    my %CompilerConstants = (); # built-in GCC constants
130    my $CRef = $In::ABI{$LVer}{"Constants"};
131    foreach my $Name (keys(%{$CRef}))
132    {
133        if(not defined $CRef->{$Name}{"Header"})
134        {
135            $CompilerConstants{$Name} = $CRef->{$Name}{"Value"};
136            delete($CRef->{$Name});
137        }
138    }
139    $In::ABI{$LVer}{"CompilerConstants"} = \%CompilerConstants;
140
141    if($In::Opt{"ExtendedCheck"})
142    { # --ext option
143        $In::ABI{$LVer}{"Mode"} = "Extended";
144    }
145    if($In::Opt{"BinOnly"})
146    { # --binary
147        $In::ABI{$LVer}{"BinOnly"} = 1;
148    }
149    if($In::Opt{"ExtraDump"})
150    { # --extra-dump
151        $In::ABI{$LVer}{"Extra"} = 1;
152    }
153
154    $In::ABI{$LVer}{"Target"} = $In::Opt{"Target"};
155}
156
157sub readSymbols($)
158{
159    my $LVer = $_[0];
160
161    my @LibPaths = getSOPaths($LVer);
162    if($#LibPaths==-1) {
163        exitStatus("Error", "library objects are not found");
164    }
165
166    foreach my $LibPath (@LibPaths) {
167        readSymbols_Lib($LVer, $LibPath, 0, "+Weak", 1, 1);
168    }
169
170    if($In::Opt{"CheckUndefined"})
171    {
172        my %UndefinedLibs = ();
173
174        my @Libs = (keys(%{$In::ABI{$LVer}{"Symbols"}}), keys(%{$In::ABI{$LVer}{"DepSymbols"}}));
175
176        foreach my $LibName (sort @Libs)
177        {
178            if(defined $In::ABI{$LVer}{"UndefinedSymbols"}{$LibName})
179            {
180                foreach my $Symbol (keys(%{$In::ABI{$LVer}{"UndefinedSymbols"}{$LibName}}))
181                {
182                    if($In::ABI{$LVer}{"SymLib"}{$Symbol}
183                    or $In::ABI{$LVer}{"DepSymLib"}{$Symbol})
184                    { # exported by target library
185                        next;
186                    }
187                    if(index($Symbol, '@')!=-1)
188                    { # exported default symbol version (@@)
189                        $Symbol=~s/\@/\@\@/;
190                        if($In::ABI{$LVer}{"SymLib"}{$Symbol}
191                        or $In::ABI{$LVer}{"DepSymLib"}{$Symbol}) {
192                            next;
193                        }
194                    }
195                    foreach my $Path (find_SymbolLibs($LVer, $Symbol)) {
196                        $UndefinedLibs{$Path} = 1;
197                    }
198                }
199            }
200        }
201
202        if(my @Paths = sort keys(%UndefinedLibs))
203        {
204            my $LibString = "";
205            my %Dirs = ();
206            foreach (@Paths)
207            {
208                $KnownLibs{$_} = 1;
209                my ($Dir, $Name) = sepPath($_);
210
211                if(not grep {$Dir eq $_} (@{$In::Opt{"SysPaths"}{"lib"}})) {
212                    $Dirs{escapeArg($Dir)} = 1;
213                }
214
215                $Name = libPart($Name, "name");
216                $Name=~s/\Alib//;
217
218                $LibString .= " -l$Name";
219            }
220
221            foreach my $Dir (sort {$b cmp $a} keys(%Dirs))
222            {
223                $LibString = " -L".escapeArg($Dir).$LibString;
224            }
225
226            if($In::Opt{"ExtraInfo"}) {
227                writeFile($In::Opt{"ExtraInfo"}."/libs-string", $LibString);
228            }
229        }
230    }
231
232    if($In::Opt{"ExtraInfo"}) {
233        writeFile($In::Opt{"ExtraInfo"}."/lib-paths", join("\n", sort keys(%KnownLibs)));
234    }
235}
236
237sub readSymbols_Lib($$$$$$)
238{
239    my ($LVer, $Lib_Path, $IsNeededLib, $Weak, $Deps, $Vers) = @_;
240
241    my $Real_Path = realpath_F($Lib_Path);
242
243    if(not $Real_Path)
244    { # broken link
245        return ();
246    }
247
248    my $Lib_Name = getFilename($Real_Path);
249    my $LExt = $In::Opt{"Ext"};
250
251    if($In::Opt{"ExtraInfo"})
252    {
253        $KnownLibs{$Real_Path} = 1;
254        $KnownLibs{$Lib_Path} = 1; # links
255    }
256
257    if($IsNeededLib)
258    {
259        if($CheckedDyLib{$LVer}{$Lib_Name}) {
260            return ();
261        }
262    }
263    if($#RecurLib>=1 or isCyclical(\@RecurLib, $Lib_Name)) {
264        return ();
265    }
266    $CheckedDyLib{$LVer}{$Lib_Name} = 1;
267
268    my $TmpDir = $In::Opt{"Tmp"};
269
270    push(@RecurLib, $Lib_Name);
271    my (%Value_Interface, %Interface_Value, %NeededLib) = ();
272    my $Lib_ShortName = libPart($Lib_Name, "name+ext");
273
274    if(not $IsNeededLib)
275    { # special cases: libstdc++ and libc
276        if(my $ShortName = libPart($Lib_Name, "short"))
277        {
278            if($ShortName eq "libstdc++"
279            or $ShortName eq "libc++")
280            { # libstdc++.so.6
281                $In::Opt{"StdcxxTesting"} = 1;
282            }
283            elsif($ShortName eq "libc")
284            { # libc-2.11.3.so
285                $In::Opt{"GlibcTesting"} = 1;
286            }
287        }
288    }
289    my $DebugPath = "";
290    if($In::Opt{"Debug"} and not $In::Opt{"DumpSystem"})
291    { # debug mode
292        $DebugPath = getDebugDir($LVer)."/libs/".getFilename($Lib_Path).".txt";
293        mkpath(getDirname($DebugPath));
294    }
295    if($In::Opt{"Target"} eq "macos")
296    { # Mac OS X: *.dylib, *.a
297        my $NM = getCmdPath("nm");
298        if(not $NM) {
299            exitStatus("Not_Found", "can't find \"nm\"");
300        }
301        $NM .= " -g \"$Lib_Path\" 2>\"$TmpDir/null\"";
302        if($DebugPath)
303        { # debug mode
304          # write to file
305            system($NM." >\"$DebugPath\"");
306            open(LIB, $DebugPath);
307        }
308        else
309        { # write to pipe
310            open(LIB, $NM." |");
311        }
312        while(<LIB>)
313        {
314            if($In::Opt{"CheckUndefined"})
315            {
316                if(not $IsNeededLib)
317                {
318                    if(/ U _([\w\$]+)\s*\Z/)
319                    {
320                        $In::ABI{$LVer}{"UndefinedSymbols"}{$Lib_Name}{$1} = 0;
321                        next;
322                    }
323                }
324            }
325
326            if(/ [STD] _([\w\$]+)\s*\Z/)
327            {
328                my $Symbol = $1;
329                if($IsNeededLib)
330                {
331                    if(not defined $RegisteredObj_Short{$LVer}{$Lib_ShortName})
332                    {
333                        $In::ABI{$LVer}{"DepSymLib"}{$Symbol} = $Lib_Name;
334                        $In::ABI{$LVer}{"DepSymbols"}{$Lib_Name}{$Symbol} = 1;
335                    }
336                }
337                else
338                {
339                    $In::ABI{$LVer}{"SymLib"}{$Symbol} = $Lib_Name;
340                    $In::ABI{$LVer}{"Symbols"}{$Lib_Name}{$Symbol} = 1;
341                    if($In::ABI{$LVer}{"Language"} ne "C++")
342                    {
343                        if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) {
344                            $In::ABI{$LVer}{"Language"} = "C++";
345                        }
346                    }
347                }
348            }
349        }
350        close(LIB);
351
352        if($Deps)
353        {
354            if(not $In::Opt{"UseStaticLibs"})
355            { # dependencies
356                my $OtoolCmd = getCmdPath("otool");
357                if(not $OtoolCmd) {
358                    exitStatus("Not_Found", "can't find \"otool\"");
359                }
360
361                open(LIB, "$OtoolCmd -L \"$Lib_Path\" 2>\"$TmpDir/null\" |");
362                while(<LIB>)
363                {
364                    if(/\s*([\/\\].+\.$LExt)\s*/
365                    and $1 ne $Lib_Path) {
366                        $NeededLib{$1} = 1;
367                    }
368                }
369                close(LIB);
370            }
371        }
372    }
373    elsif($In::Opt{"Target"} eq "windows")
374    { # Windows *.dll, *.lib
375        my $DumpBinCmd = getCmdPath("dumpbin");
376        if(not $DumpBinCmd) {
377            exitStatus("Not_Found", "can't find \"dumpbin\"");
378        }
379        $DumpBinCmd .= " /EXPORTS \"".$Lib_Path."\" 2>$TmpDir/null";
380        if($DebugPath)
381        { # debug mode
382          # write to file
383            system($DumpBinCmd." >\"$DebugPath\"");
384            open(LIB, $DebugPath);
385        }
386        else
387        { # write to pipe
388            open(LIB, $DumpBinCmd." |");
389        }
390        while(<LIB>)
391        {
392            my $Symbol = undef;
393            if($In::Opt{"UseStaticLibs"})
394            {
395                if(/\A\s{10,}(\d+\s+|)([_\w\?\@]+)(\s*\Z|\s+)/i)
396                {
397                    # 16 IID_ISecurityInformation
398                    # ??_7TestBaseClass@api@@6B@ (const api::TestBaseClass::`vftable')
399                    $Symbol = $2;
400                }
401            }
402            else
403            { # Dll
404                # 1197 4AC 0000A620 SetThreadStackGuarantee
405                # 1198 4AD          SetThreadToken (forwarded to ...)
406                # 3368 _o2i_ECPublicKey
407                # 1 0 00005B30 ??0?N = ... (with pdb)
408                if(/\A\s*\d+\s+[a-f\d]+\s+[a-f\d]+\s+([\w\?\@]+)\s*(?:=.+)?\Z/i
409                or /\A\s*\d+\s+[a-f\d]+\s+([\w\?\@]+)\s*\(\s*forwarded\s+/
410                or /\A\s*\d+\s+_([\w\?\@]+)\s*(?:=.+)?\Z/)
411                { # dynamic, static and forwarded symbols
412                    $Symbol = $1;
413                }
414            }
415
416            if($Symbol)
417            {
418                if($IsNeededLib)
419                {
420                    if(not defined $RegisteredObj_Short{$LVer}{$Lib_ShortName})
421                    {
422                        $In::ABI{$LVer}{"DepSymLib"}{$Symbol} = $Lib_Name;
423                        $In::ABI{$LVer}{"DepSymbols"}{$Lib_Name}{$Symbol} = 1;
424                    }
425                }
426                else
427                {
428                    $In::ABI{$LVer}{"SymLib"}{$Symbol} = $Lib_Name;
429                    $In::ABI{$LVer}{"Symbols"}{$Lib_Name}{$Symbol} = 1;
430                    if($In::ABI{$LVer}{"Language"} ne "C++")
431                    {
432                        if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) {
433                            $In::ABI{$LVer}{"Language"} = "C++";
434                        }
435                    }
436                }
437            }
438        }
439        close(LIB);
440
441        if($Deps)
442        {
443            if(not $In::Opt{"UseStaticLibs"})
444            { # dependencies
445                open(LIB, "$DumpBinCmd /DEPENDENTS \"$Lib_Path\" 2>\"$TmpDir/null\" |");
446                while(<LIB>)
447                {
448                    if(/\s*([^\s]+?\.$LExt)\s*/i
449                    and $1 ne $Lib_Path) {
450                        $NeededLib{pathFmt($1)} = 1;
451                    }
452                }
453                close(LIB);
454            }
455        }
456    }
457    else
458    { # Unix; *.so, *.a
459      # Symbian: *.dso, *.lib
460        my $ReadelfCmd = getCmdPath("readelf");
461        if(not $ReadelfCmd) {
462            exitStatus("Not_Found", "can't find \"readelf\"");
463        }
464        my $Cmd = $ReadelfCmd." -Ws \"$Lib_Path\" 2>\"$TmpDir/null\"";
465        if($DebugPath)
466        { # debug mode
467          # write to file
468            system($Cmd." >\"$DebugPath\"");
469            open(LIB, $DebugPath);
470        }
471        else
472        { # write to pipe
473            open(LIB, $Cmd." |");
474        }
475        my $symtab = undef; # indicates that we are processing 'symtab' section of 'readelf' output
476        while(<LIB>)
477        {
478            if(not $In::Opt{"UseStaticLibs"})
479            { # dynamic library specifics
480                if(defined $symtab)
481                {
482                    if(index($_, "'.dynsym'")!=-1)
483                    { # dynamic table
484                        $symtab = undef;
485                    }
486                    # do nothing with symtab
487                    next;
488                }
489                elsif(index($_, "'.symtab'")!=-1)
490                { # symbol table
491                    $symtab = 1;
492                    next;
493                }
494            }
495            if(my ($Value, $Size, $Type, $Bind, $Vis, $Ndx, $Symbol) = readline_ELF($_))
496            { # read ELF entry
497                if($Ndx eq "UND")
498                { # ignore interfaces that are imported from somewhere else
499                    if($In::Opt{"CheckUndefined"})
500                    {
501                        if(not $IsNeededLib) {
502                            $In::ABI{$LVer}{"UndefinedSymbols"}{$Lib_Name}{$Symbol} = 0;
503                        }
504                    }
505                    next;
506                }
507                if($Bind eq "WEAK")
508                {
509                    $In::ABI{$LVer}{"WeakSymbols"}{$Symbol} = 1;
510                    if($Weak eq "-Weak")
511                    { # skip WEAK symbols
512                        next;
513                    }
514                }
515                if($IsNeededLib)
516                {
517                    if(not defined $RegisteredObj_Short{$LVer}{$Lib_ShortName})
518                    {
519                        $In::ABI{$LVer}{"DepSymLib"}{$Symbol} = $Lib_Name;
520                        $In::ABI{$LVer}{"DepSymbols"}{$Lib_Name}{$Symbol} = ($Type eq "OBJECT")?-$Size:1;
521                    }
522                }
523                else
524                {
525                    $In::ABI{$LVer}{"SymLib"}{$Symbol} = $Lib_Name;
526                    $In::ABI{$LVer}{"Symbols"}{$Lib_Name}{$Symbol} = ($Type eq "OBJECT")?-$Size:1;
527                    if($Vers)
528                    {
529                        if($LExt eq "so")
530                        { # value
531                            $Interface_Value{$LVer}{$Symbol} = $Value;
532                            $Value_Interface{$LVer}{$Value}{$Symbol} = 1;
533                        }
534                    }
535                    if($In::ABI{$LVer}{"Language"} ne "C++")
536                    {
537                        if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) {
538                            $In::ABI{$LVer}{"Language"} = "C++";
539                        }
540                    }
541                }
542            }
543        }
544        close(LIB);
545
546        if($Deps and not $In::Opt{"UseStaticLibs"})
547        { # dynamic library specifics
548            $Cmd = $ReadelfCmd." -Wd \"$Lib_Path\" 2>\"$TmpDir/null\"";
549            open(LIB, $Cmd." |");
550
551            while(<LIB>)
552            {
553                if(/NEEDED.+\[([^\[\]]+)\]/)
554                { # dependencies:
555                  # 0x00000001 (NEEDED) Shared library: [libc.so.6]
556                    $NeededLib{$1} = 1;
557                }
558            }
559
560            close(LIB);
561        }
562    }
563    if($Vers)
564    {
565        if(not $IsNeededLib and $LExt eq "so")
566        { # get symbol versions
567            my %Found = ();
568
569            # by value
570            foreach my $Symbol (sort keys(%{$In::ABI{$LVer}{"Symbols"}{$Lib_Name}}))
571            {
572                next if(index($Symbol, '@')==-1);
573                if(my $Value = $Interface_Value{$LVer}{$Symbol})
574                {
575                    foreach my $Symbol_SameValue (sort keys(%{$Value_Interface{$LVer}{$Value}}))
576                    {
577                        if($Symbol_SameValue ne $Symbol
578                        and index($Symbol_SameValue, '@')==-1)
579                        {
580                            $In::ABI{$LVer}{"SymbolVersion"}{$Symbol_SameValue} = $Symbol;
581                            $Found{$Symbol} = 1;
582
583                            if(index($Symbol, '@@')==-1) {
584                                last;
585                            }
586                        }
587                    }
588                }
589            }
590
591            # default
592            foreach my $Symbol (keys(%{$In::ABI{$LVer}{"Symbols"}{$Lib_Name}}))
593            {
594                next if(defined $Found{$Symbol});
595                next if(index($Symbol, '@@')==-1);
596
597                if($Symbol=~/\A([^\@]*)\@\@/
598                and not $In::ABI{$LVer}{"SymbolVersion"}{$1})
599                {
600                    $In::ABI{$LVer}{"SymbolVersion"}{$1} = $Symbol;
601                    $Found{$Symbol} = 1;
602                }
603            }
604
605            # non-default
606            foreach my $Symbol (keys(%{$In::ABI{$LVer}{"Symbols"}{$Lib_Name}}))
607            {
608                next if(defined $Found{$Symbol});
609                next if(index($Symbol, '@')==-1);
610
611                if($Symbol=~/\A([^\@]*)\@([^\@]*)/
612                and not $In::ABI{$LVer}{"SymbolVersion"}{$1})
613                {
614                    $In::ABI{$LVer}{"SymbolVersion"}{$1} = $Symbol;
615                    $Found{$Symbol} = 1;
616                }
617            }
618        }
619    }
620    if($Deps)
621    {
622        foreach my $DyLib (sort keys(%NeededLib))
623        {
624            if($In::Opt{"ExtraDump"}) {
625                $In::ABI{$LVer}{"Needed"}{$Lib_Name}{getFilename($DyLib)} = 1;
626            }
627
628            if(my $DepPath = getLibPath($LVer, $DyLib))
629            {
630                if(not $CheckedDyLib{$LVer}{getFilename($DepPath)}) {
631                    readSymbols_Lib($LVer, $DepPath, 1, "+Weak", $Deps, $Vers);
632                }
633            }
634        }
635    }
636    pop(@RecurLib);
637    return $In::ABI{$LVer}{"Symbols"};
638}
639
640sub readSymbols_App($)
641{
642    my $Path = $_[0];
643
644    my $TmpDir = $In::Opt{"Tmp"};
645
646    my @Imported = ();
647    if($In::Opt{"Target"} eq "macos")
648    {
649        my $NM = getCmdPath("nm");
650        if(not $NM) {
651            exitStatus("Not_Found", "can't find \"nm\"");
652        }
653        open(APP, "$NM -g \"$Path\" 2>\"$TmpDir/null\" |");
654        while(<APP>)
655        {
656            if(/ U _([\w\$]+)\s*\Z/) {
657                push(@Imported, $1);
658            }
659        }
660        close(APP);
661    }
662    elsif($In::Opt{"Target"} eq "windows")
663    {
664        my $DumpBinCmd = getCmdPath("dumpbin");
665        if(not $DumpBinCmd) {
666            exitStatus("Not_Found", "can't find \"dumpbin.exe\"");
667        }
668        open(APP, "$DumpBinCmd /IMPORTS \"$Path\" 2>\"$TmpDir/null\" |");
669        while(<APP>)
670        {
671            if(/\s*\w+\s+\w+\s+\w+\s+([\w\?\@]+)\s*/) {
672                push(@Imported, $1);
673            }
674        }
675        close(APP);
676    }
677    else
678    {
679        my $ReadelfCmd = getCmdPath("readelf");
680        if(not $ReadelfCmd) {
681            exitStatus("Not_Found", "can't find \"readelf\"");
682        }
683        open(APP, "$ReadelfCmd -Ws \"$Path\" 2>\"$TmpDir/null\" |");
684        my $symtab = undef; # indicates that we are processing 'symtab' section of 'readelf' output
685        while(<APP>)
686        {
687            if(defined $symtab)
688            { # do nothing with symtab
689                if(index($_, "'.dynsym'")!=-1)
690                { # dynamic table
691                    $symtab = undef;
692                }
693            }
694            elsif(index($_, "'.symtab'")!=-1)
695            { # symbol table
696                $symtab = 1;
697            }
698            elsif(my @Info = readline_ELF($_))
699            {
700                my ($Ndx, $Symbol) = ($Info[5], $Info[6]);
701                if($Ndx eq "UND")
702                { # only imported symbols
703                    push(@Imported, $Symbol);
704                }
705            }
706        }
707        close(APP);
708    }
709    return @Imported;
710}
711
712sub cleanDump($)
713{ # clean data
714    my $LVer = $_[0];
715    foreach my $InfoId (keys(%{$In::ABI{$LVer}{"SymbolInfo"}}))
716    {
717        if(not keys(%{$In::ABI{$LVer}{"SymbolInfo"}{$InfoId}}))
718        {
719            delete($In::ABI{$LVer}{"SymbolInfo"}{$InfoId});
720            next;
721        }
722        my $MnglName = $In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"MnglName"};
723        if(not $MnglName)
724        {
725            delete($In::ABI{$LVer}{"SymbolInfo"}{$InfoId});
726            next;
727        }
728        my $ShortName = $In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"ShortName"};
729        if(not $ShortName)
730        {
731            delete($In::ABI{$LVer}{"SymbolInfo"}{$InfoId});
732            next;
733        }
734        if($MnglName eq $ShortName)
735        { # remove duplicate data
736            delete($In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"MnglName"});
737        }
738        if(not keys(%{$In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"Param"}})) {
739            delete($In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"Param"});
740        }
741        if(not keys(%{$In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"TParam"}})) {
742            delete($In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"TParam"});
743        }
744        delete($In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"Type"});
745    }
746    foreach my $Tid (keys(%{$In::ABI{$LVer}{"TypeInfo"}}))
747    {
748        if(not keys(%{$In::ABI{$LVer}{"TypeInfo"}{$Tid}}))
749        {
750            delete($In::ABI{$LVer}{"TypeInfo"}{$Tid});
751            next;
752        }
753        delete($In::ABI{$LVer}{"TypeInfo"}{$Tid}{"Tid"});
754        foreach my $Attr ("Header", "Line", "Size", "NameSpace")
755        {
756            if(not $In::ABI{$LVer}{"TypeInfo"}{$Tid}{$Attr}) {
757                delete($In::ABI{$LVer}{"TypeInfo"}{$Tid}{$Attr});
758            }
759        }
760        if(not keys(%{$In::ABI{$LVer}{"TypeInfo"}{$Tid}{"TParam"}})) {
761            delete($In::ABI{$LVer}{"TypeInfo"}{$Tid}{"TParam"});
762        }
763    }
764}
765
766sub readLibs($)
767{
768    my $LVer = $_[0];
769
770    if($In::Opt{"Target"} eq "windows")
771    { # dumpbin.exe will crash
772        # without VS Environment
773        checkWin32Env();
774    }
775
776    readSymbols($LVer);
777
778    translateSymbols(keys(%{$In::ABI{$LVer}{"SymLib"}}), $LVer);
779    translateSymbols(keys(%{$In::ABI{$LVer}{"DepSymLib"}}), $LVer);
780}
781
782sub getSOPaths($)
783{
784    my $LVer = $_[0];
785    my @Paths = ();
786    foreach my $P (keys(%{$In::Desc{$LVer}{"Libs"}}))
787    {
788        my @Found = getSOPaths_Dir(getAbsPath($P), $LVer);
789        foreach (@Found) {
790            push(@Paths, $_);
791        }
792    }
793    return sort @Paths;
794}
795
796sub getSOPaths_Dir($$)
797{
798    my ($Path, $LVer) = @_;
799    if(skipLib($Path, $LVer)) {
800        return ();
801    }
802
803    my $LExt = $In::Opt{"Ext"};
804
805    if(-f $Path)
806    {
807        if(not libPart($Path, "name")) {
808            exitStatus("Error", "incorrect format of library (should be *.$LExt): \'$Path\'");
809        }
810        registerObject($Path, $LVer);
811        registerObject_Dir(getDirname($Path), $LVer);
812        return ($Path);
813    }
814    elsif(-d $Path)
815    {
816        $Path=~s/[\/\\]+\Z//g;
817        my %Libs = ();
818        if(my $TN = $In::Opt{"TargetLib"}
819        and grep { $Path eq $_ } @{$In::Opt{"SysPaths"}{"lib"}})
820        { # you have specified /usr/lib as the search directory (<libs>) in the XML descriptor
821          # and the real name of the library by -l option (bz2, stdc++, Xaw, ...)
822            foreach my $P (cmdFind($Path,"","*".escapeArg($TN)."*.$LExt*",2))
823            { # all files and symlinks that match the name of a library
824                if(getFilename($P)=~/\A(|lib)\Q$TN\E[\d\-]*\.$LExt[\d\.]*\Z/i)
825                {
826                    registerObject($P, $LVer);
827                    $Libs{realpath_F($P)} = 1;
828                }
829            }
830        }
831        else
832        { # search for all files and symlinks
833            foreach my $P (findLibs($Path,"",""))
834            {
835                next if(ignorePath($P));
836                next if(skipLib($P, $LVer));
837                registerObject($P, $LVer);
838                $Libs{realpath_F($P)} = 1;
839            }
840            if($In::Opt{"OS"} eq "macos")
841            { # shared libraries on macOS may have no extension
842                foreach my $P (cmdFind($Path,"f"))
843                {
844                    next if(ignorePath($P));
845                    next if(skipLib($P, $LVer));
846                    if(getFilename($P)!~/\./ and -B $P
847                    and cmdFile($P)=~/(shared|dynamic)\s+library/i)
848                    {
849                        registerObject($P, $LVer);
850                        $Libs{realpath_F($P)} = 1;
851                    }
852                }
853            }
854        }
855        return keys(%Libs);
856    }
857
858    return ();
859}
860
861sub registerObject_Dir($$)
862{
863    my ($Dir, $LVer) = @_;
864    if(grep {$_ eq $Dir} @{$In::Opt{"SysPaths"}{"lib"}})
865    { # system directory
866        return;
867    }
868    if($RegisteredObj_Dir{$LVer}{$Dir})
869    { # already registered
870        return;
871    }
872    foreach my $Path (findLibs($Dir,"",1))
873    {
874        if(ignorePath($Path)) {
875            next;
876        }
877        if(skipLib($Path, $LVer)) {
878            next;
879        }
880        registerObject($Path, $LVer);
881    }
882    $RegisteredObj_Dir{$LVer}{$Dir} = 1;
883}
884
885sub registerObject($$)
886{
887    my ($Path, $LVer) = @_;
888
889    my $Name = getFilename($Path);
890    $RegisteredObj{$LVer}{$Name} = $Path;
891    if($In::Opt{"Target"}=~/linux|bsd|gnu|solaris/i)
892    {
893        if(my $SONAME = getSONAME($Path)) {
894            $RegisteredSoname{$LVer}{$SONAME} = $Path;
895        }
896    }
897    if(my $Short = libPart($Name, "name+ext")) {
898        $RegisteredObj_Short{$LVer}{$Short} = $Path;
899    }
900
901    if(not $CheckedArch{$LVer} and -f $Path)
902    {
903        if(my $ObjArch = getArch_Object($Path))
904        {
905            if($ObjArch ne getArch_GCC($LVer))
906            { # translation unit dump generated by the GCC compiler should correspond to input objects
907                $CheckedArch{$LVer} = 1;
908                printMsg("WARNING", "the architectures of input objects and the used GCC compiler are not equal, please change the compiler by --gcc-path=PATH option.");
909            }
910        }
911    }
912}
913
914sub remove_Unused($$)
915{ # remove unused data types from the ABI dump
916    my ($LVer, $Kind) = @_;
917
918    my %UsedType = ();
919
920    foreach my $InfoId (sort {$a<=>$b} keys(%{$In::ABI{$LVer}{"SymbolInfo"}}))
921    {
922        registerSymbolUsage($InfoId, \%UsedType, $LVer);
923    }
924    foreach my $Tid (sort {$a<=>$b} keys(%{$In::ABI{$LVer}{"TypeInfo"}}))
925    {
926        if($UsedType{$Tid})
927        { # All & Extended
928            next;
929        }
930
931        if($Kind eq "Extended")
932        {
933            if(pickType($Tid, $LVer))
934            {
935                my %Tree = ();
936                registerTypeUsage($Tid, \%Tree, $LVer);
937
938                my $Tmpl = 0;
939                foreach (sort {$a<=>$b} keys(%Tree))
940                {
941                    if(defined $In::ABI{$LVer}{"TypeInfo"}{$_}{"Template"}
942                    or $In::ABI{$LVer}{"TypeInfo"}{$_}{"Type"} eq "TemplateParam")
943                    {
944                        $Tmpl = 1;
945                        last;
946                    }
947                }
948                if(not $Tmpl)
949                {
950                    foreach (keys(%Tree)) {
951                        $UsedType{$_} = 1;
952                    }
953                }
954            }
955        }
956    }
957
958    my %Delete = ();
959
960    foreach my $Tid (sort {$a<=>$b} keys(%{$In::ABI{$LVer}{"TypeInfo"}}))
961    { # remove unused types
962        if($UsedType{$Tid})
963        { # All & Extended
964            next;
965        }
966
967        if($Kind eq "Extra")
968        {
969            my %Tree = ();
970            registerTypeUsage($Tid, \%Tree, $LVer);
971
972            foreach (sort {$a<=>$b} keys(%Tree))
973            {
974                if(defined $In::ABI{$LVer}{"TypeInfo"}{$_}{"Template"}
975                or $In::ABI{$LVer}{"TypeInfo"}{$_}{"Type"} eq "TemplateParam")
976                {
977                    $Delete{$Tid} = 1;
978                    last;
979                }
980            }
981        }
982        else
983        {
984            # remove type
985            delete($In::ABI{$LVer}{"TypeInfo"}{$Tid});
986        }
987    }
988
989    if($Kind eq "Extra")
990    { # remove duplicates
991        foreach my $Tid (sort {$a<=>$b} keys(%{$In::ABI{$LVer}{"TypeInfo"}}))
992        {
993            if($UsedType{$Tid})
994            { # All & Extended
995                next;
996            }
997
998            my $Name = $In::ABI{$LVer}{"TypeInfo"}{$Tid}{"Name"};
999
1000            if($In::ABI{$LVer}{"TName_Tid"}{$Name} ne $Tid) {
1001                delete($In::ABI{$LVer}{"TypeInfo"}{$Tid});
1002            }
1003        }
1004    }
1005
1006    foreach my $Tid (keys(%Delete))
1007    {
1008        delete($In::ABI{$LVer}{"TypeInfo"}{$Tid});
1009    }
1010}
1011
1012sub getFirst($$)
1013{
1014    my ($Tid, $LVer) = @_;
1015    if(not $Tid) {
1016        return $Tid;
1017    }
1018
1019    if(my $Name = $In::ABI{$LVer}{"TypeInfo"}{$Tid}{"Name"})
1020    {
1021        if($In::ABI{$LVer}{"TName_Tid"}{$Name}) {
1022            return $In::ABI{$LVer}{"TName_Tid"}{$Name};
1023        }
1024    }
1025
1026    return $Tid;
1027}
1028
1029sub registerSymbolUsage($$$)
1030{
1031    my ($InfoId, $UsedType, $LVer) = @_;
1032
1033    my %FuncInfo = %{$In::ABI{$LVer}{"SymbolInfo"}{$InfoId}};
1034    if(my $RTid = getFirst($FuncInfo{"Return"}, $LVer))
1035    {
1036        registerTypeUsage($RTid, $UsedType, $LVer);
1037        $In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"Return"} = $RTid;
1038    }
1039    if(my $FCid = getFirst($FuncInfo{"Class"}, $LVer))
1040    {
1041        registerTypeUsage($FCid, $UsedType, $LVer);
1042        $In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"Class"} = $FCid;
1043
1044        if(my $ThisId = getTypeIdByName($In::ABI{$LVer}{"TypeInfo"}{$FCid}{"Name"}."*const", $LVer))
1045        { # register "this" pointer
1046            registerTypeUsage($ThisId, $UsedType, $LVer);
1047        }
1048        if(my $ThisId_C = getTypeIdByName($In::ABI{$LVer}{"TypeInfo"}{$FCid}{"Name"}." const*const", $LVer))
1049        { # register "this" pointer (const method)
1050            registerTypeUsage($ThisId_C, $UsedType, $LVer);
1051        }
1052    }
1053    foreach my $PPos (sort {$a<=>$b} keys(%{$FuncInfo{"Param"}}))
1054    {
1055        if(my $PTid = getFirst($FuncInfo{"Param"}{$PPos}{"type"}, $LVer))
1056        {
1057            registerTypeUsage($PTid, $UsedType, $LVer);
1058            $FuncInfo{"Param"}{$PPos}{"type"} = $PTid;
1059        }
1060    }
1061    foreach my $TPos (sort {$a<=>$b} keys(%{$FuncInfo{"TParam"}}))
1062    {
1063        my $TPName = $FuncInfo{"TParam"}{$TPos}{"name"};
1064        if(my $TTid = $In::ABI{$LVer}{"TName_Tid"}{$TPName}) {
1065            registerTypeUsage($TTid, $UsedType, $LVer);
1066        }
1067    }
1068}
1069
1070sub registerTypeUsage($$$)
1071{
1072    my ($TypeId, $UsedType, $LVer) = @_;
1073    if(not $TypeId) {
1074        return;
1075    }
1076    if($UsedType->{$TypeId})
1077    { # already registered
1078        return;
1079    }
1080
1081    my %TInfo = getType($TypeId, $LVer);
1082    if($TInfo{"Type"})
1083    {
1084        if(my $NS = $TInfo{"NameSpace"})
1085        {
1086            if(my $NSTid = $In::ABI{$LVer}{"TName_Tid"}{$NS}) {
1087                registerTypeUsage($NSTid, $UsedType, $LVer);
1088            }
1089        }
1090
1091        if($TInfo{"Type"}=~/\A(Struct|Union|Class|FuncPtr|Func|MethodPtr|FieldPtr|Enum)\Z/)
1092        {
1093            $UsedType->{$TypeId} = 1;
1094            if($TInfo{"Type"}=~/\A(Struct|Class)\Z/)
1095            {
1096                foreach my $BaseId (sort {$a<=>$b} keys(%{$TInfo{"Base"}})) {
1097                    registerTypeUsage($BaseId, $UsedType, $LVer);
1098                }
1099                foreach my $TPos (sort {$a<=>$b} keys(%{$TInfo{"TParam"}}))
1100                {
1101                    my $TPName = $TInfo{"TParam"}{$TPos}{"name"};
1102                    if(my $TTid = $In::ABI{$LVer}{"TName_Tid"}{$TPName}) {
1103                        registerTypeUsage($TTid, $UsedType, $LVer);
1104                    }
1105                }
1106            }
1107            foreach my $Memb_Pos (sort {$a<=>$b} keys(%{$TInfo{"Memb"}}))
1108            {
1109                if(my $MTid = getFirst($TInfo{"Memb"}{$Memb_Pos}{"type"}, $LVer))
1110                {
1111                    registerTypeUsage($MTid, $UsedType, $LVer);
1112                    $TInfo{"Memb"}{$Memb_Pos}{"type"} = $MTid;
1113                }
1114            }
1115            if($TInfo{"Type"} eq "FuncPtr"
1116            or $TInfo{"Type"} eq "MethodPtr"
1117            or $TInfo{"Type"} eq "Func")
1118            {
1119                if(my $RTid = $TInfo{"Return"}) {
1120                    registerTypeUsage($RTid, $UsedType, $LVer);
1121                }
1122                foreach my $PPos (sort {$a<=>$b} keys(%{$TInfo{"Param"}}))
1123                {
1124                    if(my $PTid = $TInfo{"Param"}{$PPos}{"type"}) {
1125                        registerTypeUsage($PTid, $UsedType, $LVer);
1126                    }
1127                }
1128            }
1129            if($TInfo{"Type"} eq "FieldPtr")
1130            {
1131                if(my $RTid = $TInfo{"Return"}) {
1132                    registerTypeUsage($RTid, $UsedType, $LVer);
1133                }
1134                if(my $CTid = $TInfo{"Class"}) {
1135                    registerTypeUsage($CTid, $UsedType, $LVer);
1136                }
1137            }
1138            if($TInfo{"Type"} eq "MethodPtr")
1139            {
1140                if(my $CTid = $TInfo{"Class"}) {
1141                    registerTypeUsage($CTid, $UsedType, $LVer);
1142                }
1143            }
1144        }
1145        elsif($TInfo{"Type"}=~/\A(Const|ConstVolatile|Volatile|Pointer|Ref|Restrict|Array|Typedef)\Z/)
1146        {
1147            $UsedType->{$TypeId} = 1;
1148            if(my $BTid = getFirst($TInfo{"BaseType"}, $LVer))
1149            {
1150                registerTypeUsage($BTid, $UsedType, $LVer);
1151                $In::ABI{$LVer}{"TypeInfo"}{$TypeId}{"BaseType"} = $BTid;
1152            }
1153        }
1154        else
1155        { # Intrinsic, TemplateParam, TypeName, SizeOf, etc.
1156            $UsedType->{$TypeId} = 1;
1157        }
1158    }
1159}
1160
1161sub detectWordSize($)
1162{
1163    my $LVer = $_[0];
1164
1165    my $Size = undef;
1166
1167    # speed up detection
1168    if(my $Arch = $In::ABI{$LVer}{"Arch"})
1169    {
1170        if($Arch=~/\A(x86_64|s390x|ppc64|ia64|alpha)\Z/) {
1171            $Size = "8";
1172        }
1173        elsif($Arch=~/\A(x86|s390|ppc32)\Z/) {
1174            $Size = "4";
1175        }
1176    }
1177
1178    if(my $GccPath = $In::Opt{"GccPath"})
1179    {
1180        my $TmpDir = $In::Opt{"Tmp"};
1181        writeFile("$TmpDir/empty.h", "");
1182
1183        my $Cmd = $GccPath." -E -dD empty.h";
1184        if(my $Opts = getGccOptions($LVer))
1185        { # user-defined options
1186            $Cmd .= " ".$Opts;
1187        }
1188
1189        chdir($TmpDir);
1190        my $Defines = `$Cmd`;
1191        chdir($In::Opt{"OrigDir"});
1192
1193        unlink("$TmpDir/empty.h");
1194
1195        if($Defines=~/ __SIZEOF_POINTER__\s+(\d+)/)
1196        { # GCC 4
1197            $Size = $1;
1198        }
1199        elsif($Defines=~/ __PTRDIFF_TYPE__\s+(\w+)/)
1200        { # GCC 3
1201            my $PTRDIFF = $1;
1202            if($PTRDIFF=~/long/) {
1203                $Size = "8";
1204            }
1205            else {
1206                $Size = "4";
1207            }
1208        }
1209    }
1210
1211    if(not $Size) {
1212        exitStatus("Error", "can't check WORD size");
1213    }
1214
1215    return $Size;
1216}
1217
1218sub getLibPath($$)
1219{
1220    my ($LVer, $Name) = @_;
1221    if(defined $Cache{"getLibPath"}{$LVer}{$Name}) {
1222        return $Cache{"getLibPath"}{$LVer}{$Name};
1223    }
1224    return ($Cache{"getLibPath"}{$LVer}{$Name} = getLibPath_I($LVer, $Name));
1225}
1226
1227sub getLibPath_I($$)
1228{
1229    my ($LVer, $Name) = @_;
1230    if(isAbsPath($Name))
1231    {
1232        if(-f $Name)
1233        { # absolute path
1234            return $Name;
1235        }
1236        else
1237        { # broken
1238            return "";
1239        }
1240    }
1241    if(defined $RegisteredObj{$LVer}{$Name})
1242    { # registered paths
1243        return $RegisteredObj{$LVer}{$Name};
1244    }
1245    if(defined $RegisteredSoname{$LVer}{$Name})
1246    { # registered paths
1247        return $RegisteredSoname{$LVer}{$Name};
1248    }
1249    if(my $DefaultPath = $In::Opt{"LibDefaultPath"}{$Name})
1250    { # ldconfig default paths
1251        return $DefaultPath;
1252    }
1253    foreach my $Dir (@{$In::Opt{"DefaultLibPaths"}}, @{$In::Opt{"SysPaths"}{"lib"}})
1254    { # search in default linker directories
1255      # and then in all system paths
1256        if(-f $Dir."/".$Name) {
1257            return join_P($Dir,$Name);
1258        }
1259    }
1260
1261    checkSystemFiles();
1262
1263    if(my @AllObjects = keys(%{$In::Opt{"SystemObjects"}{$Name}})) {
1264        return $AllObjects[0];
1265    }
1266    if(my $ShortName = libPart($Name, "name+ext"))
1267    {
1268        if($ShortName ne $Name)
1269        { # FIXME: check this case
1270            if(my $Path = getLibPath($LVer, $ShortName)) {
1271                return $Path;
1272            }
1273        }
1274    }
1275    # can't find
1276    return "";
1277}
1278
1279my %Prefix_Lib_Map=(
1280 # symbols for autodetecting library dependencies (by prefix)
1281    "pthread_" => ["libpthread"],
1282    "g_" => ["libglib-2.0", "libgobject-2.0", "libgio-2.0"],
1283    "cairo_" => ["libcairo"],
1284    "gtk_" => ["libgtk-x11-2.0"],
1285    "atk_" => ["libatk-1.0"],
1286    "gdk_" => ["libgdk-x11-2.0"],
1287    "gl" => ["libGL"],
1288    "glu" => ["libGLU"],
1289    "popt" => ["libpopt"],
1290    "Py" => ["libpython"],
1291    "jpeg_" => ["libjpeg"],
1292    "BZ2_" => ["libbz2"],
1293    "Fc" => ["libfontconfig"],
1294    "Xft" => ["libXft"],
1295    "SSL_" => ["libssl"],
1296    "sem_" => ["libpthread"],
1297    "snd_" => ["libasound"],
1298    "art_" => ["libart_lgpl_2"],
1299    "dbus_g" => ["libdbus-glib-1"],
1300    "GOMP_" => ["libgomp"],
1301    "omp_" => ["libgomp"],
1302    "cms" => ["liblcms"]
1303);
1304
1305my %Pattern_Lib_Map=(
1306    "SL[a-z]" => ["libslang"]
1307);
1308
1309my %Symbol_Lib_Map=(
1310 # symbols for autodetecting library dependencies (by name)
1311    "pow" => "libm",
1312    "fmod" => "libm",
1313    "sin" => "libm",
1314    "floor" => "libm",
1315    "cos" => "libm",
1316    "dlopen" => "libdl",
1317    "deflate" => "libz",
1318    "inflate" => "libz",
1319    "move_panel" => "libpanel",
1320    "XOpenDisplay" => "libX11",
1321    "resize_term" => "libncurses",
1322    "clock_gettime" => "librt",
1323    "crypt" => "libcrypt"
1324);
1325
1326sub find_SymbolLibs($$)
1327{
1328    my ($LVer, $Symbol) = @_;
1329
1330    if(index($Symbol, "g_")==0 and $Symbol=~/[A-Z]/)
1331    { # debug symbols
1332        return ();
1333    }
1334
1335    my $LibExt = $In::Opt{"Ext"};
1336
1337    my %Paths = ();
1338
1339    if(my $LibName = $Symbol_Lib_Map{$Symbol})
1340    {
1341        if(my $Path = getLibPath($LVer, $LibName.".".$LibExt)) {
1342            $Paths{$Path} = 1;
1343        }
1344    }
1345
1346    if(my $SymbolPrefix = getPrefix($Symbol))
1347    {
1348        if(defined $Cache{"find_SymbolLibs"}{$SymbolPrefix}) {
1349            return @{$Cache{"find_SymbolLibs"}{$SymbolPrefix}};
1350        }
1351
1352        if(not keys(%Paths))
1353        {
1354            if(defined $Prefix_Lib_Map{$SymbolPrefix})
1355            {
1356                foreach my $LibName (@{$Prefix_Lib_Map{$SymbolPrefix}})
1357                {
1358                    if(my $Path = getLibPath($LVer, $LibName.".".$LibExt)) {
1359                        $Paths{$Path} = 1;
1360                    }
1361                }
1362            }
1363        }
1364
1365        if(not keys(%Paths))
1366        {
1367            foreach my $Prefix (sort keys(%Pattern_Lib_Map))
1368            {
1369                if($Symbol=~/\A$Prefix/)
1370                {
1371                    foreach my $LibName (@{$Pattern_Lib_Map{$Prefix}})
1372                    {
1373                        if(my $Path = getLibPath($LVer, $LibName.".".$LibExt)) {
1374                            $Paths{$Path} = 1;
1375                        }
1376                    }
1377                }
1378            }
1379        }
1380
1381        if(not keys(%Paths))
1382        {
1383            if($SymbolPrefix)
1384            { # try to find a library by symbol prefix
1385                if($SymbolPrefix eq "inotify" and
1386                index($Symbol, "\@GLIBC")!=-1)
1387                {
1388                    if(my $Path = getLibPath($LVer, "libc.$LibExt")) {
1389                        $Paths{$Path} = 1;
1390                    }
1391                }
1392                else
1393                {
1394                    if(my $Path = getLibPathPrefix($LVer, $SymbolPrefix)) {
1395                        $Paths{$Path} = 1;
1396                    }
1397                }
1398            }
1399        }
1400
1401        if(my @Paths = keys(%Paths)) {
1402            $Cache{"find_SymbolLibs"}{$SymbolPrefix} = \@Paths;
1403        }
1404    }
1405    return keys(%Paths);
1406}
1407
1408sub getLibPathPrefix($$)
1409{
1410    my ($LVer, $Prefix) = @_;
1411    my $LibExt = $In::Opt{"Ext"};
1412
1413    $Prefix = lc($Prefix);
1414    $Prefix=~s/[_]+\Z//g;
1415
1416    foreach ("-2", "2", "-1", "1", "")
1417    { # libgnome-2.so
1418      # libxml2.so
1419      # libdbus-1.so
1420        if(my $Path = getLibPath($LVer, "lib".$Prefix.$_.".".$LibExt)) {
1421            return $Path;
1422        }
1423    }
1424    return "";
1425}
1426
1427return 1;
1428