xref: /petsc/lib/petsc/bin/maint/abi-compliance-checker/abi-compliance-checker.pl (revision e8b6250908b962c387f7ab2e7b38caaa661b5fa1) !
1#!/usr/bin/perl
2########################################################################
3# ABI Compliance Checker (ABICC) 2.3
4# A tool for checking backward compatibility of a C/C++ library API
5#
6# Copyright (C) 2009-2011 Institute for System Programming, RAS
7# Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies)
8# Copyright (C) 2012-2019 Andrey Ponomarenko's ABI Laboratory
9#
10# Written by Andrey Ponomarenko
11#
12# PLATFORMS
13# =========
14#  Linux, FreeBSD, Solaris, Mac OS X, MS Windows, Symbian, Haiku
15#
16# REQUIREMENTS
17# ============
18#  Linux
19#    - G++ (3.0 or newer)
20#    - GNU Binutils (readelf, c++filt, objdump)
21#    - Perl 5
22#    - Ctags
23#    - ABI Dumper >= 1.1
24#
25#  Mac OS X
26#    - Xcode (g++, c++filt, otool, nm)
27#    - Ctags
28#
29#  MS Windows
30#    - MinGW (3.0 or newer)
31#    - MS Visual C++ (dumpbin, undname, cl)
32#    - Active Perl 5 (5.8 or newer)
33#    - Sigcheck v2.52 or newer
34#    - GnuWin Zip and UnZip
35#    - Ctags (Exuberant or Universal)
36#    - Add tool locations to the PATH environment variable
37#    - Run vcvars64.bat (C:\Microsoft Visual Studio 9.0\VC\bin\)
38#
39# This library is free software; you can redistribute it and/or
40# modify it under the terms of the GNU Lesser General Public
41# License as published by the Free Software Foundation; either
42# version 2.1 of the License, or (at your option) any later version.
43#
44# This library is distributed in the hope that it will be useful,
45# but WITHOUT ANY WARRANTY; without even the implied warranty of
46# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
47# Lesser General Public License for more details.
48#
49# You should have received a copy of the GNU Lesser General Public
50# License along with this library; if not, write to the Free Software
51# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
52# MA  02110-1301 USA
53########################################################################
54use Getopt::Long;
55Getopt::Long::Configure ("posix_default", "no_ignore_case");
56use File::Path qw(mkpath rmtree);
57use File::Temp qw(tempdir);
58use File::Copy qw(copy);
59use File::Basename qw(dirname);
60use Cwd qw(abs_path cwd);
61use Data::Dumper;
62
63my $TOOL_VERSION = "2.3";
64my $ABI_DUMP_VERSION = "3.5";
65my $ABI_DUMP_VERSION_MIN = "3.5";
66
67my $XML_REPORT_VERSION = "1.2";
68my $XML_ABI_DUMP_VERSION = "1.2";
69
70# Internal modules
71my $MODULES_DIR = getModules();
72push(@INC, dirname($MODULES_DIR));
73
74# Basic modules
75my %LoadedModules = ();
76loadModule("Basic");
77loadModule("Input");
78loadModule("Path");
79loadModule("Logging");
80loadModule("Utils");
81loadModule("TypeAttr");
82loadModule("Filter");
83loadModule("SysFiles");
84loadModule("Descriptor");
85loadModule("Mangling");
86
87# Rules DB
88my %RULES_PATH = (
89    "Binary" => $MODULES_DIR."/RulesBin.xml",
90    "Source" => $MODULES_DIR."/RulesSrc.xml");
91
92my $BYTE = 8;
93my $CmdName = getFilename($0);
94
95my %HomePage = (
96    "Dev"=>"https://github.com/lvc/abi-compliance-checker",
97    "Doc"=>"https://lvc.github.io/abi-compliance-checker/"
98);
99
100my $ShortUsage = "ABI Compliance Checker (ABICC) $TOOL_VERSION
101A tool for checking backward compatibility of a C/C++ library API
102Copyright (C) 2019 Andrey Ponomarenko's ABI Laboratory
103License: GNU LGPL 2.1
104
105Usage: $CmdName [options]
106Example: $CmdName -l NAME -old ABI-0.dump -new ABI-1.dump
107
108ABI-0.dump and ABI-1.dump are ABI dumps generated
109by the ABI Dumper or ABICC tools.
110
111More info: $CmdName --help\n";
112
113if($#ARGV==-1)
114{
115    printMsg("INFO", $ShortUsage);
116    exit(0);
117}
118
119GetOptions(
120  "h|help!" => \$In::Opt{"Help"},
121  "i|info!" => \$In::Opt{"InfoMsg"},
122  "v|version!" => \$In::Opt{"ShowVersion"},
123  "dumpversion!" => \$In::Opt{"DumpVersion"},
124# General
125  "l|lib|library=s" => \$In::Opt{"TargetLib"},
126  "d1|old|o=s" => \$In::Desc{1}{"Path"},
127  "d2|new|n=s" => \$In::Desc{2}{"Path"},
128  "dump|dump-abi|dump_abi=s" => \$In::Opt{"DumpABI"},
129  "d|f|filter=s" => \$In::Opt{"FilterPath"},
130# Extra
131  "debug!" => \$In::Opt{"Debug"},
132  "debug-mangling!" => \$In::Opt{"DebugMangling"},
133  "ext|extended!" => \$In::Opt{"ExtendedCheck"},
134  "static|static-libs!" => \$In::Opt{"UseStaticLibs"},
135  "gcc-path|cross-gcc=s" => \$In::Opt{"CrossGcc"},
136  "gcc-prefix|cross-prefix=s" => \$In::Opt{"CrossPrefix"},
137  "gcc-options=s" => \$In::Opt{"GccOptions"},
138  "count-symbols=s" => \$In::Opt{"CountSymbols"},
139  "use-dumps!" => \$In::Opt{"UseDumps"},
140  "xml!" => \$In::Opt{"UseXML"},
141  "app|application=s" => \$In::Opt{"AppPath"},
142  "headers-only!" => \$In::Opt{"CheckHeadersOnly"},
143  "v1|vnum1|version1=s" => \$In::Desc{1}{"TargetVersion"},
144  "v2|vnum2|version2=s" => \$In::Desc{2}{"TargetVersion"},
145  "relpath1=s" => \$In::Desc{1}{"RelativeDirectory"},
146  "relpath2=s" => \$In::Desc{2}{"RelativeDirectory"},
147# Test
148  "test!" => \$In::Opt{"TestTool"},
149  "test-dump!" => \$In::Opt{"TestDump"},
150  "test-abi-dumper!" => \$In::Opt{"TestABIDumper"},
151# Report
152  "s|strict!" => \$In::Opt{"StrictCompat"},
153  "binary|bin|abi!" => \$In::Opt{"BinOnly"},
154  "source|src|api!" => \$In::Opt{"SrcOnly"},
155  "warn-newsym!" => \$In::Opt{"WarnNewSym"},
156# Report path
157  "report-path=s" => \$In::Opt{"OutputReportPath"},
158  "bin-report-path=s" => \$In::Opt{"BinReportPath"},
159  "src-report-path=s" => \$In::Opt{"SrcReportPath"},
160# Report format
161  "show-retval!" => \$In::Opt{"ShowRetVal"},
162  "stdout!" => \$In::Opt{"StdOut"},
163  "report-format=s" => \$In::Opt{"ReportFormat"},
164  "old-style!" => \$In::Opt{"OldStyle"},
165  "title=s" => \$In::Opt{"TargetTitle"},
166  "component=s" => \$In::Opt{"TargetComponent"},
167  "p|params=s" => \$In::Opt{"ParamNamesPath"},
168  "limit-affected|affected-limit=s" => \$In::Opt{"AffectLimit"},
169  "all-affected!" => \$In::Opt{"AllAffected"},
170  "list-affected!" => \$In::Opt{"ListAffected"},
171# ABI dump
172  "dump-path=s" => \$In::Opt{"OutputDumpPath"},
173  "dump-format=s" => \$In::Opt{"DumpFormat"},
174  "check!" => \$In::Opt{"CheckInfo"},
175  "extra-info=s" => \$In::Opt{"ExtraInfo"},
176  "extra-dump!" => \$In::Opt{"ExtraDump"},
177  "relpath=s" => \$In::Desc{1}{"RelativeDirectory"},
178  "vnum=s" => \$In::Desc{1}{"TargetVersion"},
179  "sort!" => \$In::Opt{"SortDump"},
180# Filter symbols and types
181  "symbols-list=s" => \$In::Opt{"SymbolsListPath"},
182  "types-list=s" => \$In::Opt{"TypesListPath"},
183  "skip-symbols=s" => \$In::Opt{"SkipSymbolsListPath"},
184  "skip-types=s" => \$In::Opt{"SkipTypesListPath"},
185  "skip-internal-symbols|skip-internal=s" => \$In::Opt{"SkipInternalSymbols"},
186  "skip-internal-types=s" => \$In::Opt{"SkipInternalTypes"},
187  "keep-cxx!" => \$In::Opt{"KeepCxx"},
188  "keep-reserved!" => \$In::Opt{"KeepReserved"},
189# Filter header files
190  "skip-headers=s" => \$In::Opt{"SkipHeadersPath"},
191  "headers-list=s" => \$In::Opt{"TargetHeadersPath"},
192  "header=s" => \$In::Opt{"TargetHeader"},
193  "nostdinc!" => \$In::Opt{"NoStdInc"},
194  "tolerance=s" => \$In::Opt{"Tolerance"},
195  "tolerant!" => \$In::Opt{"Tolerant"},
196  "skip-unidentified!" => \$In::Opt{"SkipUnidentified"},
197# Filter rules
198  "skip-typedef-uncover!" => \$In::Opt{"SkipTypedefUncover"},
199  "check-private-abi!" => \$In::Opt{"CheckPrivateABI"},
200  "disable-constants-check!" => \$In::Opt{"DisableConstantsCheck"},
201  "skip-added-constants!" => \$In::Opt{"SkipAddedConstants"},
202  "skip-removed-constants!" => \$In::Opt{"SkipRemovedConstants"},
203# Other
204  "lang=s" => \$In::Opt{"UserLang"},
205  "arch=s" => \$In::Opt{"TargetArch"},
206  "mingw-compatible!" => \$In::Opt{"MinGWCompat"},
207  "cxx-incompatible|cpp-incompatible!" => \$In::Opt{"CxxIncompat"},
208  "cpp-compatible!" => \$In::Opt{"CxxCompat"},
209  "quick!" => \$In::Opt{"Quick"},
210  "force!" => \$In::Opt{"Force"},
211# OS analysis
212  "dump-system=s" => \$In::Opt{"DumpSystem"},
213  "cmp-systems!" => \$In::Opt{"CmpSystems"},
214  "sysroot=s" => \$In::Opt{"SystemRoot"},
215  "sysinfo=s" => \$In::Opt{"TargetSysInfo"},
216  "libs-list=s" => \$In::Opt{"TargetLibsPath"},
217# Logging
218  "log-path=s" => \$In::Opt{"LoggingPath"},
219  "log1-path=s" => \$In::Desc{1}{"OutputLogPath"},
220  "log2-path=s" => \$In::Desc{2}{"OutputLogPath"},
221  "logging-mode=s" => \$In::Opt{"LogMode"},
222  "q|quiet!" => \$In::Opt{"Quiet"}
223) or errMsg();
224
225sub errMsg()
226{
227    printMsg("INFO", "\n".$ShortUsage);
228    exit(getErrorCode("Error"));
229}
230
231# Default log path
232$In::Opt{"DefaultLog"} = "logs/run.log";
233
234my $HelpMessage = "
235NAME:
236  ABI Compliance Checker ($CmdName)
237  Check backward compatibility of a C/C++ library API
238
239DESCRIPTION:
240  ABI Compliance Checker (ABICC) is a tool for checking backward binary
241  compatibility and backward source compatibility of a C/C++ library API.
242
243  The tool analyzes changes in API and ABI (ABI=API+compiler ABI) that may
244  break binary compatibility and/or source compatibility: changes in calling
245  stack, v-table changes, removed symbols, renamed fields, etc.
246
247  Binary incompatibility may result in crashing or incorrect behavior of
248  applications built with an old version of a library if they run on a new
249  one. Source incompatibility may result in recompilation errors with a new
250  library version.
251
252  The tool can create and compare ABI dumps for header files and shared
253  objects of a library. The ABI dump for a library can also be created by
254  the ABI Dumper tool (https://github.com/lvc/abi-dumper) if shared objects
255  include debug-info.
256
257  The tool is intended for developers of software libraries and maintainers
258  of operating systems who are interested in ensuring backward compatibility,
259  i.e. allow old applications to run or to be recompiled with newer library
260  versions.
261
262  Also the tool can be used by ISVs for checking applications portability to
263  new library versions. Found issues can be taken into account when adapting
264  the application to a new library version.
265
266  This tool is free software: you can redistribute it and/or modify it
267  under the terms of the GNU LGPL 2.1.
268
269USAGE #1 (WITH ABI DUMPER):
270
271  1. Library should be compiled with \"-g -Og\" GCC options
272     to contain DWARF debug info
273
274  2. Create ABI dumps for both library versions
275     using the ABI Dumper (https://github.com/lvc/abi-dumper) tool:
276
277       abi-dumper OLD.so -o ABI-0.dump -lver 0
278       abi-dumper NEW.so -o ABI-1.dump -lver 1
279
280  3. You can filter public ABI with the help of
281     additional -public-headers option of the ABI Dumper tool.
282
283  4. Compare ABI dumps to create report:
284
285       abi-compliance-checker -l NAME -old ABI-0.dump -new ABI-1.dump
286
287USAGE #2 (ORIGINAL):
288
289  1. Create XML-descriptors for two versions
290     of a library (OLD.xml and NEW.xml):
291
292       <version>
293           1.0
294       </version>
295
296       <headers>
297           /path/to/headers/
298       </headers>
299
300       <libs>
301           /path/to/libraries/
302       </libs>
303
304  2. Compare Xml-descriptors to create report:
305
306       abi-compliance-checker -lib NAME -old OLD.xml -new NEW.xml
307
308USAGE #3 (CREATE ABI DUMPS):
309
310  1. Create XML-descriptors for two versions
311     of a library (OLD.xml and NEW.xml):
312
313       <version>
314           1.0
315       </version>
316
317       <headers>
318           /path/to/headers/
319       </headers>
320
321       <libs>
322           /path/to/libraries/
323       </libs>
324
325  2. Create ABI dumps:
326
327       abi-compliance-checker -lib NAME -dump OLD.xml -dump-path ./ABI-0.dump
328       abi-compliance-checker -lib NAME -dump NEW.xml -dump-path ./ABI-1.dump
329
330  3. Compare ABI dumps to create report:
331
332       abi-compliance-checker -l NAME -old ABI-0.dump -new ABI-1.dump
333
334INFO OPTIONS:
335  -h|-help
336      Print this help.
337
338  -i|-info
339      Print complete info including all options.
340
341  -v|-version
342      Print version information.
343
344  -dumpversion
345      Print the tool version ($TOOL_VERSION) and don't do anything else.
346
347GENERAL OPTIONS:
348  -l|-library NAME
349      Any name of the library.
350
351  -old|-d1 PATH
352      Descriptor of the 1st (old) library version.
353      It may be one of the following:
354
355         1. ABI dump generated by the ABI Dumper tool
356         2. XML-descriptor (*.xml file):
357
358              <version>
359                  1.0
360              </version>
361
362              <headers>
363                  /path1/to/header(s)/
364                  /path2/to/header(s)/
365                   ...
366              </headers>
367
368              <libs>
369                  /path1/to/library(ies)/
370                  /path2/to/library(ies)/
371                   ...
372              </libs>
373
374                 ...
375
376         3. ABI dump generated by -dump option
377         4. Directory with headers and libraries
378         5. Single header file
379
380      If you are using 4-5 descriptor types then you should
381      specify version numbers with -v1 and -v2 options.
382
383      For more information, please see:
384        https://lvc.github.io/abi-compliance-checker/Xml-Descriptor.html
385
386  -new|-d2 PATH
387      Descriptor of the 2nd (new) library version.
388
389  -dump PATH
390      Create library ABI dump for the input XML descriptor. You can
391      transfer it anywhere and pass instead of the descriptor. Also
392      it can be used for debugging the tool.
393
394  -filter PATH
395      A path to XML descriptor with skip_* rules to filter
396      analyzed symbols in the report.
397";
398
399sub helpMsg() {
400    printMsg("INFO", $HelpMessage."
401MORE OPTIONS:
402     $CmdName --info\n");
403}
404
405sub infoMsg()
406{
407    printMsg("INFO", "$HelpMessage
408EXTRA OPTIONS:
409  -debug
410      Debugging mode. Print debug info on the screen. Save intermediate
411      analysis stages in the debug directory:
412          debug/LIB_NAME/VERSION/
413
414      Also consider using -dump option for debugging the tool.
415
416  -ext|-extended
417      If your library A is supposed to be used by other library B and you
418      want to control the ABI of B, then you should enable this option. The
419      tool will check for changes in all data types, even if they are not
420      used by any function in the library A. Such data types are not part
421      of the A library ABI, but may be a part of the ABI of the B library.
422
423      The short scheme is:
424        app C (broken) -> lib B (broken ABI) -> lib A (stable ABI)
425
426  -static
427      Check static libraries instead of the shared ones. The <libs> section
428      of the XML-descriptor should point to static libraries location.
429
430  -gcc-path PATH
431      Path to the cross GCC compiler to use instead of the usual (host) GCC.
432
433  -gcc-prefix PREFIX
434      GCC toolchain prefix.
435
436  -gcc-options OPTS
437      Additional compiler options.
438
439  -count-symbols PATH
440      Count total public symbols in the ABI dump.
441
442  -use-dumps
443      Make dumps for two versions of a library and compare dumps. This should
444      increase the performance of the tool and decrease the system memory usage.
445
446  -xml
447      Alias for: --report-format=xml or --dump-format=xml
448
449  -app|-application PATH
450      This option allows one to specify the application that should be checked
451      for portability to the new library version.
452
453  -headers-only
454      Check header files without libraries. It is easy to run, but may
455      provide a low quality compatibility report with false positives and
456      without detecting of added/removed symbols.
457
458  -v1|-vnum1 NUM
459      Specify 1st library version outside the descriptor. This option is needed
460      if you have preferred an alternative descriptor type (see -d1 option).
461
462      In general case you should specify it in the XML-descriptor:
463          <version>
464              VERSION
465          </version>
466
467  -v2|-vnum2 NUM
468      Specify 2nd library version outside the descriptor.
469
470  -relpath1 PATH
471      Replace {RELPATH} macros to PATH in the 1st XML-descriptor (-d1).
472
473  -relpath2 PATH
474      Replace {RELPATH} macros to PATH in the 2nd XML-descriptor (-d2).
475
476TEST OPTIONS:
477  -test
478      Run internal tests. Create two binary incompatible versions of a sample
479      library and run the tool to check them for compatibility. This option
480      allows one to check if the tool works correctly in the current environment.
481
482  -test-dump
483      Test ability to create, read and compare ABI dumps.
484
485  -test-abi-dumper
486      Compare ABI dumps created by the ABI Dumper tool.
487
488REPORT OPTIONS:
489  -binary|-bin|-abi
490      Show binary compatibility problems only.
491      Generate report to:
492        compat_reports/LIB_NAME/V1_to_V2/abi_compat_report.html
493
494  -source|-src|-api
495      Show source compatibility problems only.
496      Generate report to:
497        compat_reports/LIB_NAME/V1_to_V2/src_compat_report.html
498
499  -s|-strict
500      Treat all compatibility warnings as problems. Add a number of \"Low\"
501      severity problems to the return value of the tool.
502
503  -warn-newsym
504      Treat new symbols as problems, because that breaks semantic versioning.
505      For more info please visit https://semver.org/
506
507REPORT PATH OPTIONS:
508  -report-path PATH
509      Path to compatibility report.
510      Default:
511          compat_reports/LIB_NAME/V1_to_V2/compat_report.html
512
513  -bin-report-path PATH
514      Path to binary compatibility report.
515      Default:
516          compat_reports/LIB_NAME/V1_to_V2/abi_compat_report.html
517
518  -src-report-path PATH
519      Path to source compatibility report.
520      Default:
521          compat_reports/LIB_NAME/V1_to_V2/src_compat_report.html
522
523REPORT FORMAT OPTIONS:
524  -show-retval
525      Show the symbol's return type in the report.
526
527  -stdout
528      Print analysis results (compatibility reports and ABI dumps) to stdout
529      instead of creating a file. This would allow piping data to other programs.
530
531  -report-format FMT
532      Change format of compatibility report.
533      Formats:
534        htm - HTML format (default)
535        xml - XML format
536
537  -old-style
538      Generate old-style report.
539
540  -title NAME
541      Change library name in the report title to NAME. By default
542      will be displayed a name specified by -l option.
543
544  -component NAME
545      The component name in the title and summary of the HTML report.
546      Default:
547          library
548
549  -p|-params PATH
550      Path to file with the function parameter names. It can be used
551      for improving report view if the library header files have no
552      parameter names. File format:
553
554            func1;param1;param2;param3 ...
555            func2;param1;param2;param3 ...
556             ...
557
558  -limit-affected LIMIT
559      The maximum number of affected symbols listed under the description
560      of the changed type in the report.
561
562  -list-affected
563      Generate file with the list of incompatible
564      symbols beside the HTML compatibility report.
565      Use 'c++filt \@file' command from GNU binutils
566      to unmangle C++ symbols in the generated file.
567      Default names:
568          abi_affected.txt
569          src_affected.txt
570
571ABI DUMP OPTIONS:
572  -dump-path PATH
573      Specify a *.dump file path where to generate an ABI dump.
574      Default:
575          abi_dumps/LIB_NAME/VERSION/ABI.dump
576
577  -dump-format FMT
578      Change format of ABI dump.
579      Formats:
580        perl - Data::Dumper format (default)
581        xml - XML format
582
583  -check
584      Check completeness of the ABI dump.
585
586  -extra-info DIR
587      Dump extra info to DIR.
588
589  -extra-dump
590      Create extended ABI dump containing all symbols
591      from the translation unit.
592
593  -relpath PATH
594      Replace {RELPATH} macros to PATH in the XML-descriptor used
595      for dumping the library ABI (see -dump option).
596
597  -vnum NUM
598      Specify the library version in the generated ABI dump. The <version> section
599      of the input XML descriptor will be overwritten in this case.
600
601  -sort
602      Enable sorting of data in ABI dumps.
603
604FILTER SYMBOLS OPTIONS:
605  -symbols-list PATH
606      This option allows one to specify a file with a list of symbols (mangled
607      names in C++) that should be checked. Other symbols will not be checked.
608
609  -types-list PATH
610      This option allows one to specify a file with a list of types that should
611      be checked. Other types will not be checked.
612
613  -skip-symbols PATH
614      The list of symbols that should not be checked.
615
616  -skip-types PATH
617      The list of types that should not be checked.
618
619  -skip-internal-symbols PATTERN
620      Do not check symbols matched by the regular expression.
621
622  -skip-internal-types PATTERN
623      Do not check types matched by the regular expression.
624      It's matched against full qualified type names (e.g. 'struct xyz::Name<T>').
625      It has to match any part of type name.
626
627  -keep-cxx
628      Check _ZS*, _ZNS* and _ZNKS* symbols.
629
630  -keep-reserved
631      Report changes in reserved fields.
632
633FILTER HEADERS OPTIONS:
634  -skip-headers PATH
635      The file with the list of header files, that should not be checked.
636
637  -headers-list PATH
638      The file with a list of headers, that should be checked/dumped.
639
640  -header NAME
641      Check/Dump ABI of this header only.
642
643  -nostdinc
644      Do not search in GCC standard system directories for header files.
645
646      -tolerance LEVEL
647      Apply a set of heuristics to successfully compile input
648      header files. You can enable several tolerance levels by
649      joining them into one string (e.g. 13, 124, etc.).
650      Levels:
651          1 - skip non-Linux headers (e.g. win32_*.h, etc.)
652          2 - skip internal headers (e.g. *_p.h, impl/*.h, etc.)
653          3 - skip headers that include non-Linux headers
654          4 - skip headers included by others
655
656  -tolerant
657      Enable highest tolerance level [1234].
658
659  -skip-unidentified
660      Skip header files in 'headers' and 'include_preamble' sections
661      of the XML descriptor that cannot be found. This is useful if
662      you are trying to use the same descriptor for different targets.
663
664FILTER RULES OPTIONS:
665  -skip-typedef-uncover
666      Do not report a problem if type is covered or
667      uncovered by typedef (useful for broken debug info).
668
669  -check-private-abi
670      Check data types from the private part of the ABI when
671      comparing ABI dumps created by the ABI Dumper tool with
672      use of the -public-headers option.
673
674      Requires ABI Dumper >= 0.99.14
675
676  -disable-constants-check
677      Do not check for changes in constants.
678
679  -skip-added-constants
680      Do not detect added constants.
681
682  -skip-removed-constants
683      Do not detect removed constants.
684
685OTHER OPTIONS:
686  -lang LANG
687      Set library language (C or C++). You can use this option if the tool
688      cannot auto-detect a language. This option may be useful for checking
689      C-library headers (--lang=C) in --headers-only or --extended modes.
690
691  -arch ARCH
692      Set library architecture (x86, x86_64, ia64, arm, ppc32, ppc64, s390,
693      ect.). The option is useful if the tool cannot detect correct architecture
694      of the input objects.
695
696  -mingw-compatible
697      If input header files are compatible with the MinGW GCC compiler,
698      then you can tell the tool about this and speedup the analysis.
699
700  -cxx-incompatible
701      Set this option if input C header files use C++ keywords. The tool
702      will try to replace such keywords at preprocessor stage and replace
703      them back in the final TU dump.
704
705  -cpp-compatible
706      Do nothing.
707
708  -quick
709      Quick analysis. Disable check of some template instances.
710
711  -force
712      Try to enable this option if the tool checked not all
713      types and symbols in header files.
714
715OS ANALYSIS OPTIONS:
716  -dump-system NAME -sysroot DIR
717      Find all the shared libraries and header files in DIR directory,
718      create XML descriptors and make ABI dumps for each library. The result
719      set of ABI dumps can be compared (--cmp-systems) with the other one
720      created for other version of operating system in order to check them for
721      compatibility. Do not forget to specify -cross-gcc option if your target
722      system requires some specific version of GCC compiler (different from
723      the host GCC). The system ABI dump will be generated to:
724          sys_dumps/NAME/ARCH
725
726  -dump-system DESCRIPTOR.xml
727      The same as the previous option but takes an XML descriptor of the target
728      system as input, where you should describe it:
729
730          /* Primary sections */
731
732          <name>
733              /* Name of the system */
734          </name>
735
736          <headers>
737              /* The list of paths to header files and/or
738                 directories with header files, one per line */
739          </headers>
740
741          <libs>
742              /* The list of paths to shared libraries and/or
743                 directories with shared libraries, one per line */
744          </libs>
745
746          /* Optional sections */
747
748          <search_headers>
749              /* List of directories to be searched
750                 for header files to automatically
751                 generate include paths, one per line */
752          </search_headers>
753
754          <search_libs>
755              /* List of directories to be searched
756                 for shared libraries to resolve
757                 dependencies, one per line */
758          </search_libs>
759
760          <tools>
761              /* List of directories with tools used
762                 for analysis (GCC toolchain), one per line */
763          </tools>
764
765          <cross_prefix>
766              /* GCC toolchain prefix.
767                 Examples:
768                     arm-linux-gnueabi
769                     arm-none-symbianelf */
770          </cross_prefix>
771
772          <gcc_options>
773              /* Additional GCC options, one per line */
774          </gcc_options>
775
776  -cmp-systems -d1 sys_dumps/NAME1/ARCH -d2 sys_dumps/NAME2/ARCH
777      Compare two ABI dumps of a system. Create compatibility reports for
778      each system library and the common HTML report including the summary
779      of test results for all checked libraries.
780
781      Summary report will be generated to:
782          sys_compat_reports/NAME1_to_NAME2/ARCH
783
784  -sysroot DIR
785      Specify the alternative root directory. The tool will search for include
786      paths in the DIR/usr/include and DIR/usr/lib directories.
787
788  -sysinfo DIR
789      This option should be used with -dump-system option to dump
790      ABI of operating systems and configure the dumping process.
791
792  -libs-list PATH
793      The file with a list of libraries, that should be dumped by
794      the -dump-system option or should be checked by the -cmp-systems option.
795
796LOGGING OPTIONS:
797  -log-path PATH
798      Log path for all messages.
799      Default:
800          logs/LIB_NAME/VERSION/log.txt
801
802  -log1-path PATH
803      Log path for 1st version of a library.
804      Default:
805          logs/LIB_NAME/V1/log.txt
806
807  -log2-path PATH
808      Log path for 2nd version of a library.
809      Default:
810          logs/LIB_NAME/V2/log.txt
811
812  -logging-mode MODE
813      Change logging mode.
814      Modes:
815        w - overwrite old logs (default)
816        a - append old logs
817        n - do not write any logs
818
819  -q|-quiet
820      Print all messages to the file instead of stdout and stderr.
821      Default path (can be changed by -log-path option):
822          ".$In::Opt{"DefaultLog"}."
823
824REPORT PATH:
825    Compatibility report will be generated to:
826        compat_reports/LIB_NAME/V1_to_V2/compat_report.html
827
828LOG PATH:
829    Log will be generated to:
830        logs/LIB_NAME/V1/log.txt
831        logs/LIB_NAME/V2/log.txt
832
833EXIT CODES:
834    0 - Compatible. The tool has run without any errors.
835    non-zero - Incompatible or the tool has run with errors.
836
837MORE INFO:
838    ".$HomePage{"Doc"}."
839    ".$HomePage{"Dev"}."\n\n");
840}
841
842# Aliases
843my (%SymbolInfo, %TypeInfo, %TName_Tid, %Constants) = ();
844
845# Global
846my %Cache;
847my $TOP_REF = "<a class='top_ref' href='#Top'>to the top</a>";
848my %RESULT;
849
850# Counter
851my %CheckedTypes;
852my %CheckedSymbols;
853
854# Classes
855my %VirtualTable;
856my %VirtualTable_Model;
857my %VTableClass;
858my %AllocableClass;
859my %ClassMethods;
860my %ClassNames;
861my %OverriddenMethods;
862
863# Symbols
864my %Func_ShortName;
865my %AddSymbolParams;
866my %GlobalDataObject;
867
868# Merging
869my %CompSign;
870my %AddedInt;
871my %RemovedInt;
872my %AddedInt_Virt;
873my %RemovedInt_Virt;
874my %VirtualReplacement;
875my %ChangedTypedef;
876my %CompatRules;
877my %IncompleteRules;
878my %UnknownRules;
879my %VTableChanged_M;
880my %ReturnedClass;
881my %ParamClass;
882my %SourceAlternative;
883my %SourceAlternative_B;
884my %SourceReplacement;
885
886# Extra
887my %ExtendedSymbols;
888
889#Report
890my %TypeChanges;
891
892#Speedup
893my %TypeProblemsIndex;
894
895# Calling Conventions
896my %UseConv_Real = (
897  1=>{ "R"=>0, "P"=>0 },
898  2=>{ "R"=>0, "P"=>0 }
899);
900
901# ABI Dump
902my %UsedDump;
903
904# Recursion locks
905my @RecurTypes;
906my @RecurTypes_Diff;
907my @RecurConstant;
908
909# Problem descriptions
910my %CompatProblems;
911my %CompatProblems_Constants;
912my %TotalAffected;
913
914# Reports
915my $ContentID = 1;
916my $ContentSpanStart = "<span class=\"section\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
917my $ContentSpanStart_Affected = "<span class=\"sect_aff\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
918my $ContentSpanStart_Info = "<span class=\"sect_info\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
919my $ContentSpanEnd = "</span>\n";
920my $ContentDivStart = "<div id=\"CONTENT_ID\" style=\"display:none;\">\n";
921my $ContentDivEnd = "</div>\n";
922my $Content_Counter = 0;
923
924my %Severity_Val=(
925    "High"=>3,
926    "Medium"=>2,
927    "Low"=>1,
928    "Safe"=>-1
929);
930
931sub getModules()
932{
933    my $TOOL_DIR = dirname($0);
934    if(not $TOOL_DIR)
935    { # patch for MS Windows
936        $TOOL_DIR = ".";
937    }
938    my @SEARCH_DIRS = (
939        # tool's directory
940        abs_path($TOOL_DIR),
941        # relative path to modules
942        abs_path($TOOL_DIR)."/../share/abi-compliance-checker",
943        # install path
944        'MODULES_INSTALL_PATH'
945    );
946    foreach my $DIR (@SEARCH_DIRS)
947    {
948        if($DIR!~/\A(\/|\w+:[\/\\])/)
949        { # relative path
950            $DIR = abs_path($TOOL_DIR)."/".$DIR;
951        }
952        if(-d $DIR."/modules") {
953            return $DIR."/modules";
954        }
955    }
956
957    print STDERR "ERROR: can't find modules (Did you installed the tool by 'make install' command?)\n";
958    exit(9); # Module_Error
959}
960
961sub loadModule($)
962{
963    my $Name = $_[0];
964    if(defined $LoadedModules{$Name}) {
965        return;
966    }
967    my $Path = $MODULES_DIR."/Internals/$Name.pm";
968    if(not -f $Path)
969    {
970        print STDERR "can't access \'$Path\'\n";
971        exit(2);
972    }
973    require $Path;
974    $LoadedModules{$Name} = 1;
975}
976
977sub readModule($$)
978{
979    my ($Module, $Name) = @_;
980    my $Path = $MODULES_DIR."/Internals/$Module/".$Name;
981    if(not -f $Path) {
982        exitStatus("Module_Error", "can't access \'$Path\'");
983    }
984    return readFile($Path);
985}
986
987sub selectSymbolNs($$)
988{
989    my ($Symbol, $LVer) = @_;
990
991    my $NS = $CompSign{$LVer}{$Symbol}{"NameSpace"};
992    if(not $NS)
993    {
994        if(my $Class = $CompSign{$LVer}{$Symbol}{"Class"}) {
995            $NS = $TypeInfo{$LVer}{$Class}{"NameSpace"};
996        }
997    }
998    if($NS)
999    {
1000        if(defined $In::ABI{$LVer}{"NameSpaces"}{$NS}) {
1001            return $NS;
1002        }
1003        else
1004        {
1005            while($NS=~s/::[^:]+\Z//)
1006            {
1007                if(defined $In::ABI{$LVer}{"NameSpaces"}{$NS}) {
1008                    return $NS;
1009                }
1010            }
1011        }
1012    }
1013
1014    return "";
1015}
1016
1017sub selectTypeNs($$)
1018{
1019    my ($TypeName, $LVer) = @_;
1020
1021    my $Tid = $TName_Tid{$LVer}{$TypeName};
1022
1023    if(my $NS = $TypeInfo{$LVer}{$Tid}{"NameSpace"})
1024    {
1025        if(defined $In::ABI{$LVer}{"NameSpaces"}{$NS}) {
1026            return $NS;
1027        }
1028        else
1029        {
1030            while($NS=~s/::[^:]+\Z//)
1031            {
1032                if(defined $In::ABI{$LVer}{"NameSpaces"}{$NS}) {
1033                    return $NS;
1034                }
1035            }
1036        }
1037    }
1038    return "";
1039}
1040
1041sub getChargeLevel($$)
1042{
1043    my ($Symbol, $LVer) = @_;
1044
1045    if(defined $CompSign{$LVer}{$Symbol}
1046    and $CompSign{$LVer}{$Symbol}{"ShortName"})
1047    {
1048        if($CompSign{$LVer}{$Symbol}{"Constructor"})
1049        {
1050            if($Symbol=~/C([1-4])[EI]/)
1051            { # [in-charge]
1052              # [not-in-charge]
1053                return "[C".$1."]";
1054            }
1055        }
1056        elsif($CompSign{$LVer}{$Symbol}{"Destructor"})
1057        {
1058            if($Symbol=~/D([0-4])[EI]/)
1059            { # [in-charge]
1060              # [not-in-charge]
1061              # [in-charge-deleting]
1062                return "[D".$1."]";
1063            }
1064        }
1065    }
1066
1067    return undef;
1068}
1069
1070sub blackName($)
1071{
1072    my $N = $_[0];
1073    return "<span class='iname_b'>".$N."</span>";
1074}
1075
1076sub highLight_ItalicColor($$)
1077{
1078    my ($Symbol, $LVer) = @_;
1079    return getSignature($Symbol, $LVer, "Full|HTML|Italic|Color");
1080}
1081
1082sub getSignature($$$)
1083{
1084    my ($Symbol, $LVer, $Kind) = @_;
1085    if($Cache{"getSignature"}{$LVer}{$Symbol}{$Kind}) {
1086        return $Cache{"getSignature"}{$LVer}{$Symbol}{$Kind};
1087    }
1088
1089    # settings
1090    my ($Html, $Simple, $Italic, $Color, $Full, $ShowClass, $ShowName,
1091    $ShowParams, $ShowQuals, $ShowAttr, $Desc, $Target) = ();
1092
1093    if($Kind=~/HTML/) {
1094        $Html = 1;
1095    }
1096    if($Kind=~/Simple/) {
1097        $Simple = 1;
1098    }
1099    if($Kind=~/Italic/) {
1100        $Italic = 1;
1101    }
1102    if($Kind=~/Color/) {
1103        $Color = 1;
1104    }
1105
1106    if($Kind=~/Full/) {
1107        $Full = 1;
1108    }
1109    if($Kind=~/Class/) {
1110        $ShowClass = 1;
1111    }
1112    if($Kind=~/Name/) {
1113        $ShowName = 1;
1114    }
1115    if($Kind=~/Param/) {
1116        $ShowParams = 1;
1117    }
1118    if($Kind=~/Qual/) {
1119        $ShowQuals = 1;
1120    }
1121    if($Kind=~/Attr/) {
1122        $ShowAttr = 1;
1123    }
1124    if($Kind=~/Desc/) {
1125        $Desc = 1;
1126    }
1127
1128    if($Kind=~/Target=(\d+)/) {
1129        $Target = $1;
1130    }
1131
1132    if($Full)
1133    {
1134        $ShowName = 1;
1135        $ShowClass = 1;
1136    }
1137
1138    my ($MnglName, $VSpec, $SVer) = symbolParts($Symbol);
1139
1140    if(index($Symbol, "_ZTV")==0)
1141    {
1142        if(my $ClassId = $CompSign{$LVer}{$Symbol}{"Class"})
1143        {
1144            my $ClassName = $TypeInfo{$LVer}{$ClassId}{"Name"};
1145            $ClassName=~s/\bstruct //g;
1146
1147            if($Html) {
1148                return "vtable for ".specChars($ClassName)." <span class='attr'>[data]</span>";
1149            }
1150
1151            return "vtable for $ClassName [data]";
1152        }
1153        else
1154        { # failure
1155            return undef;
1156        }
1157    }
1158
1159    my $Mngl = (index($Symbol, "_Z")==0 or index($Symbol, "?")==0);
1160
1161    my $Signature = "";
1162    if($ShowName)
1163    {
1164        my $ShortName = $CompSign{$LVer}{$Symbol}{"ShortName"};
1165
1166        if(not $Mngl and defined $CompSign{$LVer}{$Symbol}{"Alias"})
1167        { # alias symbol
1168            $ShortName = $MnglName;
1169        }
1170
1171        if($Html) {
1172            $ShortName = specChars($ShortName);
1173        }
1174
1175        $Signature .= $ShortName;
1176
1177        my $ClassId = $CompSign{$LVer}{$Symbol}{"Class"};
1178
1179        if($Mngl or $ClassId)
1180        {
1181            if($CompSign{$LVer}{$Symbol}{"Destructor"}) {
1182                $Signature = "~".$Signature;
1183            }
1184
1185            if($ShowClass)
1186            {
1187                if($ClassId)
1188                {
1189                    my $Class = $TypeInfo{$LVer}{$ClassId}{"Name"};
1190                    $Class=~s/\bstruct //g;
1191
1192                    if($Html) {
1193                        $Class = specChars($Class);
1194                    }
1195
1196                    $Signature = $Class."::".$Signature;
1197                }
1198                elsif(my $NameSpace = $CompSign{$LVer}{$Symbol}{"NameSpace"}) {
1199                    $Signature = $NameSpace."::".$Signature;
1200                }
1201            }
1202        }
1203    }
1204
1205    my @Params = ();
1206    if(defined $CompSign{$LVer}{$Symbol}{"Param"})
1207    {
1208        foreach my $PPos (sort {$a<=>$b} keys(%{$CompSign{$LVer}{$Symbol}{"Param"}}))
1209        {
1210            my $PTid = $CompSign{$LVer}{$Symbol}{"Param"}{$PPos}{"type"};
1211            if(not $PTid) {
1212                next;
1213            }
1214
1215            if(my $PTName = $TypeInfo{$LVer}{$PTid}{"Name"})
1216            {
1217                foreach my $Typedef (keys(%ChangedTypedef))
1218                {
1219                    if(index($PTName, $Typedef)!=-1)
1220                    {
1221                        if($PTName=~/\b\Q$Typedef\E\b/)
1222                        {
1223                            $PTName = uncoverTypedefs($PTName, $LVer);
1224                            last;
1225                        }
1226                    }
1227                }
1228
1229                $PTName=~s/\(kind=\d+\)//g;
1230
1231                if($Html) {
1232                    $PTName = specChars($PTName);
1233                }
1234
1235                my $PName = $CompSign{$LVer}{$Symbol}{"Param"}{$PPos}{"name"};
1236                if($Mngl and ($PName eq "this" or $PName eq "__in_chrg" or $PName eq "__vtt_parm"))
1237                { # do NOT show first hidded "this"-parameter
1238                    next;
1239                }
1240
1241                if($PName and ($Full or $ShowParams))
1242                {
1243                    if($Simple) {
1244                        $PName = "<i>$PName</i>";
1245                    }
1246                    elsif($Html)
1247                    {
1248                        if(defined $Target
1249                        and $Target==adjustParamPos($PPos, $Symbol, $LVer)) {
1250                            $PName = "<span class='fp'>$PName</span>";
1251                        }
1252                        elsif($Color) {
1253                            $PName = "<span class='color_p'>$PName</span>";
1254                        }
1255                        elsif($Italic) {
1256                            $PName = "<i>$PName</i>";
1257                        }
1258                    }
1259
1260                    push(@Params, createMemDecl($PTName, $PName));
1261                }
1262                else {
1263                    push(@Params, $PTName);
1264                }
1265            }
1266        }
1267    }
1268
1269    if($Simple) {
1270        $Signature = "<b>".$Signature."</b>";
1271    }
1272
1273    if($CompSign{$LVer}{$Symbol}{"Data"})
1274    {
1275        $Signature .= " [data]";
1276    }
1277    else
1278    {
1279        if($Full or $ShowAttr)
1280        {
1281            if($Mngl)
1282            {
1283                if(my $ChargeLevel = getChargeLevel($Symbol, $LVer)) {
1284                    $Signature .= " ".$ChargeLevel;
1285                }
1286            }
1287        }
1288
1289        if($Html and not $Simple)
1290        {
1291            $Signature .= "&#160;";
1292
1293            if($Desc) {
1294                $Signature .= "<span class='sym_pd'>";
1295            }
1296            else {
1297                $Signature .= "<span class='sym_p'>";
1298            }
1299            if(@Params)
1300            {
1301                foreach my $Pos (0 .. $#Params)
1302                {
1303                    my $Name = "";
1304
1305                    if($Pos==0) {
1306                        $Name .= "(&#160;";
1307                    }
1308
1309                    $Name .= $Params[$Pos];
1310
1311                    $Name = "<span>".$Name."</span>";
1312
1313                    if($Pos==$#Params) {
1314                        $Name .= "&#160;)";
1315                    }
1316                    else {
1317                        $Name .= ", ";
1318                    }
1319
1320                    $Signature .= $Name;
1321                }
1322            }
1323            else {
1324                $Signature .= "(&#160;)";
1325            }
1326            $Signature .= "</span>";
1327        }
1328        else
1329        {
1330            if(@Params) {
1331                $Signature .= " ( ".join(", ", @Params)." )";
1332            }
1333            else {
1334                $Signature .= " ( )";
1335            }
1336        }
1337
1338
1339        if($Full or $ShowQuals)
1340        {
1341            if($CompSign{$LVer}{$Symbol}{"Const"}
1342            or $Symbol=~/\A_ZN(V|)K/) {
1343                $Signature .= " const";
1344            }
1345
1346            if($CompSign{$LVer}{$Symbol}{"Volatile"}
1347            or $Symbol=~/\A_ZN(K|)V/) {
1348                $Signature .= " volatile";
1349            }
1350        }
1351
1352        if($Full or $ShowAttr)
1353        {
1354            if($CompSign{$LVer}{$Symbol}{"Static"}
1355            and $Mngl) {
1356                $Signature .= " [static]";
1357            }
1358        }
1359    }
1360
1361    if($Full)
1362    {
1363        if(defined $In::Opt{"ShowRetVal"})
1364        {
1365            if(my $ReturnId = $CompSign{$LVer}{$Symbol}{"Return"})
1366            {
1367                my $RName = $TypeInfo{$LVer}{$ReturnId}{"Name"};
1368                if($Simple) {
1369                    $Signature .= " <b>:</b> ".specChars($RName);
1370                }
1371                elsif($Html) {
1372                    $Signature .= "<span class='sym_p nowrap'> &#160;<b>:</b>&#160;&#160;".specChars($RName)."</span>";
1373                }
1374                else {
1375                    $Signature .= " : ".$RName;
1376                }
1377            }
1378        }
1379
1380        if($SVer)
1381        {
1382            if($Html) {
1383                $Signature .= "<span class='sym_ver'>&#160;$VSpec&#160;$SVer</span>";
1384            }
1385            else {
1386                $Signature .= $VSpec.$SVer;
1387            }
1388        }
1389    }
1390
1391    if($Html) {
1392        $Signature=~s!(\[C\d\]|\[D\d\]|\[static\]|\[data\])!<span class='attr'>$1</span>!g;
1393    }
1394
1395    if($Simple) {
1396        $Signature=~s/\[\]/\[ \]/g;
1397    }
1398    elsif($Html)
1399    {
1400        $Signature=~s!\[\]![&#160;]!g;
1401        $Signature=~s!operator=!operator&#160;=!g;
1402    }
1403
1404    return ($Cache{"getSignature"}{$LVer}{$Symbol}{$Kind} = $Signature);
1405}
1406
1407sub createMemDecl($$)
1408{
1409    my ($TName, $Member) = @_;
1410    if($TName=~/\([\*]+\)/)
1411    {
1412        $TName=~s/\(([\*]+)\)/\($1$Member\)/;
1413        return $TName;
1414    }
1415    else
1416    {
1417        my @ArraySizes = ();
1418        while($TName=~s/(\[[^\[\]]*\])\Z//) {
1419            push(@ArraySizes, $1);
1420        }
1421        return $TName." ".$Member.join("", @ArraySizes);
1422    }
1423}
1424
1425sub prepareSymbols($)
1426{
1427    my $LVer = $_[0];
1428
1429    if(not keys(%{$SymbolInfo{$LVer}}))
1430    { # check if input is valid
1431        if(not $In::Opt{"ExtendedCheck"})
1432        {
1433            if($In::Opt{"CheckHeadersOnly"}) {
1434                exitStatus("Empty_Set", "the set of public symbols is empty");
1435            }
1436            else {
1437                exitStatus("Empty_Intersection", "the sets of public symbols in headers and libraries have empty intersection");
1438            }
1439        }
1440    }
1441
1442    foreach my $InfoId (sort {$b<=>$a} keys(%{$SymbolInfo{$LVer}}))
1443    {
1444        my $MnglName = $SymbolInfo{$LVer}{$InfoId}{"MnglName"};
1445        my $ShortName = $SymbolInfo{$LVer}{$InfoId}{"ShortName"};
1446
1447        if(not $MnglName)
1448        {
1449            $SymbolInfo{$LVer}{$InfoId}{"MnglName"} = $ShortName;
1450            $MnglName = $ShortName;
1451        }
1452
1453        # symbol and its symlink have same signatures
1454        if(my $SVer = $In::ABI{$LVer}{"SymbolVersion"}{$MnglName}) {
1455            $CompSign{$LVer}{$SVer} = $SymbolInfo{$LVer}{$InfoId};
1456        }
1457
1458        if(my $Alias = $SymbolInfo{$LVer}{$InfoId}{"Alias"})
1459        {
1460            $CompSign{$LVer}{$Alias} = $SymbolInfo{$LVer}{$InfoId};
1461
1462            if(my $SAVer = $In::ABI{$LVer}{"SymbolVersion"}{$Alias}) {
1463                $CompSign{$LVer}{$SAVer} = $SymbolInfo{$LVer}{$InfoId};
1464            }
1465        }
1466
1467        if(defined $CompSign{$LVer}{$MnglName})
1468        { # NOTE: duplicated entries in the ABI Dump
1469            if(defined $SymbolInfo{$LVer}{$InfoId}{"Param"})
1470            {
1471                if($SymbolInfo{$LVer}{$InfoId}{"Param"}{0}{"name"} eq "p1") {
1472                    next;
1473                }
1474            }
1475        }
1476
1477        if(not $CompSign{$LVer}{$MnglName}{"MnglName"})
1478        { # NOTE: global data may enter here twice
1479            $CompSign{$LVer}{$MnglName} = $SymbolInfo{$LVer}{$InfoId};
1480        }
1481
1482        if(not defined $SymbolInfo{$LVer}{$InfoId}{"Unmangled"})
1483        {
1484            if($MnglName eq $ShortName) {
1485                $SymbolInfo{$LVer}{$InfoId}{"Unmangled"} = $ShortName;
1486            }
1487            else {
1488                $SymbolInfo{$LVer}{$InfoId}{"Unmangled"} = getSignature($MnglName, $LVer, "Class|Name|Qual");
1489            }
1490        }
1491    }
1492
1493    if($In::ABI{$LVer}{"Language"} eq "C++"
1494    and getCmdPath("c++filt"))
1495    {
1496        my @VTables = ();
1497        foreach my $S (keys(%{$In::ABI{$LVer}{"SymLib"}}))
1498        {
1499            if(index($S, "_ZTV")==0) {
1500                push(@VTables, $S);
1501            }
1502        }
1503        translateSymbols(@VTables, $LVer);
1504    }
1505
1506    if($In::Opt{"ExtendedCheck"}) {
1507        addExtension($LVer);
1508    }
1509
1510    foreach my $Symbol (keys(%{$CompSign{$LVer}}))
1511    { # detect allocable classes with public exported constructors
1512      # or classes with auto-generated or inline-only constructors
1513      # and other temp info
1514        if(my $ClassId = $CompSign{$LVer}{$Symbol}{"Class"})
1515        {
1516            my $ClassName = $TypeInfo{$LVer}{$ClassId}{"Name"};
1517            if($CompSign{$LVer}{$Symbol}{"Constructor"}
1518            and not $CompSign{$LVer}{$Symbol}{"InLine"})
1519            { # Class() { ... } will not be exported
1520                if(not $CompSign{$LVer}{$Symbol}{"Private"})
1521                {
1522                    if($In::Opt{"CheckHeadersOnly"} or linkSymbol($Symbol, $LVer, "-Deps")) {
1523                        $AllocableClass{$LVer}{$ClassName} = 1;
1524                    }
1525                }
1526            }
1527            if(not $CompSign{$LVer}{$Symbol}{"Private"})
1528            { # all imported class methods
1529                if(symbolFilter($Symbol, $CompSign{$LVer}{$Symbol}, "Affected", "Binary", $LVer))
1530                {
1531                    if($In::Opt{"CheckHeadersOnly"})
1532                    {
1533                        if(not $CompSign{$LVer}{$Symbol}{"InLine"}
1534                        or $CompSign{$LVer}{$Symbol}{"Virt"})
1535                        { # all symbols except non-virtual inline
1536                            $ClassMethods{"Binary"}{$LVer}{$ClassName}{$Symbol} = 1;
1537                        }
1538                    }
1539                    else {
1540                        $ClassMethods{"Binary"}{$LVer}{$ClassName}{$Symbol} = 1;
1541                    }
1542                }
1543                if(symbolFilter($Symbol, $CompSign{$LVer}{$Symbol}, "Affected", "Source", $LVer)) {
1544                    $ClassMethods{"Source"}{$LVer}{$ClassName}{$Symbol} = 1;
1545                }
1546            }
1547            $ClassNames{$LVer}{$ClassName} = 1;
1548        }
1549        if(my $RetId = $CompSign{$LVer}{$Symbol}{"Return"})
1550        {
1551            my %Base = getBaseType($RetId, $LVer);
1552            if(defined $Base{"Type"}
1553            and $Base{"Type"}=~/Struct|Class/)
1554            {
1555                my $Name = $TypeInfo{$LVer}{$Base{"Tid"}}{"Name"};
1556                if($Name=~/<([^<>\s]+)>/)
1557                {
1558                    if(my $Tid = getTypeIdByName($1, $LVer)) {
1559                        $ReturnedClass{$LVer}{$Tid} = 1;
1560                    }
1561                }
1562                else {
1563                    $ReturnedClass{$LVer}{$Base{"Tid"}} = 1;
1564                }
1565            }
1566        }
1567        foreach my $Num (keys(%{$CompSign{$LVer}{$Symbol}{"Param"}}))
1568        {
1569            my $PId = $CompSign{$LVer}{$Symbol}{"Param"}{$Num}{"type"};
1570            if(getPLevel($PId, $LVer)>=1)
1571            {
1572                if(my %Base = getBaseType($PId, $LVer))
1573                {
1574                    if($Base{"Type"}=~/Struct|Class/)
1575                    {
1576                        $ParamClass{$LVer}{$Base{"Tid"}}{$Symbol} = 1;
1577                        foreach my $SubId (getSubClasses($Base{"Tid"}, $LVer, 1))
1578                        { # mark all derived classes
1579                            $ParamClass{$LVer}{$SubId}{$Symbol} = 1;
1580                        }
1581                    }
1582                }
1583            }
1584        }
1585
1586        # mapping {short name => symbols}
1587        $Func_ShortName{$LVer}{$CompSign{$LVer}{$Symbol}{"ShortName"}}{$Symbol} = 1;
1588    }
1589
1590    foreach my $ClassName (keys(%{$In::ABI{$LVer}{"ClassVTable"}}))
1591    {
1592        my $MnglName = $In::ABI{$LVer}{"ClassVTable"}{$ClassName};
1593
1594        if(my $ClassId = $TName_Tid{$LVer}{$ClassName})
1595        {
1596            if(my $H = $TypeInfo{$LVer}{$ClassId}{"Header"}) {
1597                $CompSign{$LVer}{$MnglName}{"Header"} = $H;
1598            }
1599            if(my $S = $TypeInfo{$LVer}{$ClassId}{"Source"}) {
1600                $CompSign{$LVer}{$MnglName}{"Source"} = $S;
1601            }
1602            $CompSign{$LVer}{$MnglName}{"Class"} = $ClassId;
1603            $CompSign{$LVer}{$MnglName}{"Unmangled"} = getSignature($MnglName, $LVer, "Class|Name");
1604        }
1605
1606        $VTableClass{$LVer}{$MnglName} = $ClassName;
1607    }
1608
1609    # types
1610    foreach my $TypeId (keys(%{$TypeInfo{$LVer}}))
1611    {
1612        if(my $TName = $TypeInfo{$LVer}{$TypeId}{"Name"})
1613        {
1614            if(defined $TypeInfo{$LVer}{$TypeId}{"VTable"}) {
1615                $ClassNames{$LVer}{$TName} = 1;
1616            }
1617            if(defined $TypeInfo{$LVer}{$TypeId}{"Base"})
1618            {
1619                $ClassNames{$LVer}{$TName} = 1;
1620                foreach my $Bid (keys(%{$TypeInfo{$LVer}{$TypeId}{"Base"}}))
1621                {
1622                    if(my $BName = $TypeInfo{$LVer}{$Bid}{"Name"}) {
1623                        $ClassNames{$LVer}{$BName} = 1;
1624                    }
1625                }
1626            }
1627        }
1628    }
1629}
1630
1631sub addExtension($)
1632{
1633    my $LVer = $_[0];
1634    foreach my $Tid (sort {$a<=>$b} keys(%{$TypeInfo{$LVer}}))
1635    {
1636        if(pickType($Tid, $LVer))
1637        {
1638            my $TName = $TypeInfo{$LVer}{$Tid}{"Name"};
1639            $TName=~s/\A(struct|union|class|enum) //;
1640            my $Symbol = "external_func_".$TName;
1641            $Symbol=~s/\W+/_/g;
1642
1643            %{$CompSign{$LVer}{$Symbol}} = (
1644                "Header" => "extended.h",
1645                "ShortName" => $Symbol,
1646                "MnglName" => $Symbol,
1647                "Param" => { 0 => { "type"=>$Tid, "name"=>"p1" } }
1648            );
1649
1650            $ExtendedSymbols{$Symbol} = 1;
1651            $CheckedSymbols{"Binary"}{$Symbol} = 1;
1652            $CheckedSymbols{"Source"}{$Symbol} = 1;
1653        }
1654    }
1655
1656    $ExtendedSymbols{"external_func_0"} = 1;
1657    $CheckedSymbols{"Binary"}{"external_func_0"} = 1;
1658    $CheckedSymbols{"Source"}{"external_func_0"} = 1;
1659}
1660
1661sub findMethod($$$)
1662{
1663    my ($VirtFunc, $ClassId, $LVer) = @_;
1664    foreach my $BaseClass_Id (keys(%{$TypeInfo{$LVer}{$ClassId}{"Base"}}))
1665    {
1666        if(my $VirtMethodInClass = findMethod_Class($VirtFunc, $BaseClass_Id, $LVer)) {
1667            return $VirtMethodInClass;
1668        }
1669        elsif(my $VirtMethodInBaseClasses = findMethod($VirtFunc, $BaseClass_Id, $LVer)) {
1670            return $VirtMethodInBaseClasses;
1671        }
1672    }
1673    return undef;
1674}
1675
1676sub findMethod_Class($$$)
1677{
1678    my ($VirtFunc, $ClassId, $LVer) = @_;
1679
1680    my $ClassName = $TypeInfo{$LVer}{$ClassId}{"Name"};
1681    if(not defined $VirtualTable{$LVer}{$ClassName}) {
1682        return undef;
1683    }
1684    my $TargetSuffix = getSignature($VirtFunc, $LVer, "Qual");
1685    my $TargetShortName = $CompSign{$LVer}{$VirtFunc}{"ShortName"};
1686
1687    foreach my $Candidate (keys(%{$VirtualTable{$LVer}{$ClassName}}))
1688    { # search for interface with the same parameters suffix (overridden)
1689        if($TargetSuffix eq getSignature($Candidate, $LVer, "Qual"))
1690        {
1691            if($CompSign{$LVer}{$VirtFunc}{"Destructor"})
1692            {
1693                if($CompSign{$LVer}{$Candidate}{"Destructor"})
1694                {
1695                    if(($VirtFunc=~/D0E/ and $Candidate=~/D0E/)
1696                    or ($VirtFunc=~/D1E/ and $Candidate=~/D1E/)
1697                    or ($VirtFunc=~/D2E/ and $Candidate=~/D2E/)) {
1698                        return $Candidate;
1699                    }
1700                }
1701            }
1702            else
1703            {
1704                if($TargetShortName eq $CompSign{$LVer}{$Candidate}{"ShortName"}) {
1705                    return $Candidate;
1706                }
1707            }
1708        }
1709    }
1710    return undef;
1711}
1712
1713sub registerVTable($)
1714{
1715    my $LVer = $_[0];
1716    foreach my $Symbol (keys(%{$CompSign{$LVer}}))
1717    {
1718        if($CompSign{$LVer}{$Symbol}{"Virt"}
1719        or $CompSign{$LVer}{$Symbol}{"PureVirt"})
1720        {
1721            my $ClassName = $TypeInfo{$LVer}{$CompSign{$LVer}{$Symbol}{"Class"}}{"Name"};
1722            if(not $In::Opt{"StdcxxTesting"} and $ClassName=~/\A(std::|__cxxabi)/) {
1723                next;
1724            }
1725            if($CompSign{$LVer}{$Symbol}{"Destructor"}
1726            and $Symbol=~/D2E/)
1727            { # pure virtual D2-destructors are marked as "virt" in the dump
1728              # virtual D2-destructors are NOT marked as "virt" in the dump
1729              # both destructors are not presented in the v-table
1730                next;
1731            }
1732            my ($MnglName, $VersionSpec, $SymbolVersion) = symbolParts($Symbol);
1733            $VirtualTable{$LVer}{$ClassName}{$MnglName} = 1;
1734        }
1735    }
1736}
1737
1738sub registerOverriding($)
1739{
1740    my $LVer = $_[0];
1741    my @Classes = keys(%{$VirtualTable{$LVer}});
1742    @Classes = sort {$TName_Tid{$LVer}{$a}<=>$TName_Tid{$LVer}{$b}} @Classes;
1743    foreach my $ClassName (@Classes)
1744    {
1745        foreach my $VirtFunc (keys(%{$VirtualTable{$LVer}{$ClassName}}))
1746        {
1747            if($CompSign{$LVer}{$VirtFunc}{"PureVirt"})
1748            { # pure virtuals
1749                next;
1750            }
1751            my $ClassId = $TName_Tid{$LVer}{$ClassName};
1752            if(my $Overridden = findMethod($VirtFunc, $ClassId, $LVer))
1753            {
1754                if($CompSign{$LVer}{$Overridden}{"Virt"}
1755                or $CompSign{$LVer}{$Overridden}{"PureVirt"})
1756                { # both overridden virtual methods
1757                  # and implemented pure virtual methods
1758                    $CompSign{$LVer}{$VirtFunc}{"Override"} = $Overridden;
1759                    $OverriddenMethods{$LVer}{$Overridden}{$VirtFunc} = 1;
1760
1761                    # remove from v-table model
1762                    delete($VirtualTable{$LVer}{$ClassName}{$VirtFunc});
1763                }
1764            }
1765        }
1766        if(not keys(%{$VirtualTable{$LVer}{$ClassName}})) {
1767            delete($VirtualTable{$LVer}{$ClassName});
1768        }
1769    }
1770}
1771
1772sub setVirtFuncPositions($)
1773{
1774    my $LVer = $_[0];
1775    foreach my $ClassName (keys(%{$VirtualTable{$LVer}}))
1776    {
1777        my ($Num, $Rel) = (1, 0);
1778
1779        if(my @Funcs = sort keys(%{$VirtualTable{$LVer}{$ClassName}}))
1780        {
1781            if($UsedDump{$LVer}{"DWARF"}) {
1782                @Funcs = sort {$CompSign{$LVer}{$a}{"VirtPos"}<=>$CompSign{$LVer}{$b}{"VirtPos"}} @Funcs;
1783            }
1784            else {
1785                @Funcs = sort {$CompSign{$LVer}{$a}{"Line"}<=>$CompSign{$LVer}{$b}{"Line"}} @Funcs;
1786            }
1787            foreach my $VirtFunc (@Funcs)
1788            {
1789                if($UsedDump{$LVer}{"DWARF"})
1790                {
1791                    if(defined $CompSign{$LVer}{$VirtFunc}{"VirtPos"}) {
1792                        $VirtualTable{$LVer}{$ClassName}{$VirtFunc} = $CompSign{$LVer}{$VirtFunc}{"VirtPos"};
1793                    }
1794                    else {
1795                        $VirtualTable{$LVer}{$ClassName}{$VirtFunc} = 1;
1796                    }
1797                }
1798                else {
1799                    $VirtualTable{$LVer}{$ClassName}{$VirtFunc} = $Num++;
1800                }
1801
1802                # set relative positions
1803                if(defined $VirtualTable{1}{$ClassName} and defined $VirtualTable{1}{$ClassName}{$VirtFunc}
1804                and defined $VirtualTable{2}{$ClassName} and defined $VirtualTable{2}{$ClassName}{$VirtFunc})
1805                { # relative position excluding added and removed virtual functions
1806                    if(not $CompSign{1}{$VirtFunc}{"Override"}
1807                    and not $CompSign{2}{$VirtFunc}{"Override"}) {
1808                        $CompSign{$LVer}{$VirtFunc}{"RelPos"} = $Rel++;
1809                    }
1810                }
1811            }
1812        }
1813    }
1814    foreach my $ClassName (keys(%{$ClassNames{$LVer}}))
1815    {
1816        my $AbsNum = 1;
1817        foreach my $VirtFunc (getVTable_Model($ClassName, $LVer)) {
1818            $VirtualTable_Model{$LVer}{$ClassName}{$VirtFunc} = $AbsNum++;
1819        }
1820    }
1821}
1822
1823sub getVTable_Model($$)
1824{ # return an ordered list of v-table elements
1825    my ($ClassName, $LVer) = @_;
1826
1827    my $ClassId = $TName_Tid{$LVer}{$ClassName};
1828    my @Bases = getBaseClasses($ClassId, $LVer, 1);
1829    my @Elements = ();
1830    foreach my $BaseId (@Bases, $ClassId)
1831    {
1832        if(my $BName = $TypeInfo{$LVer}{$BaseId}{"Name"})
1833        {
1834            if(defined $VirtualTable{$LVer}{$BName})
1835            {
1836                my @VFuncs = sort keys(%{$VirtualTable{$LVer}{$BName}});
1837                if($UsedDump{$LVer}{"DWARF"}) {
1838                    @VFuncs = sort {$CompSign{$LVer}{$a}{"VirtPos"} <=> $CompSign{$LVer}{$b}{"VirtPos"}} @VFuncs;
1839                }
1840                else {
1841                    @VFuncs = sort {$CompSign{$LVer}{$a}{"Line"} <=> $CompSign{$LVer}{$b}{"Line"}} @VFuncs;
1842                }
1843
1844                foreach my $VFunc (@VFuncs) {
1845                    push(@Elements, $VFunc);
1846                }
1847            }
1848        }
1849    }
1850    return @Elements;
1851}
1852
1853sub getVShift($$)
1854{
1855    my ($ClassId, $LVer) = @_;
1856    my @Bases = getBaseClasses($ClassId, $LVer, 1);
1857    my $VShift = 0;
1858    foreach my $BaseId (@Bases)
1859    {
1860        if(my $BName = $TypeInfo{$LVer}{$BaseId}{"Name"})
1861        {
1862            if(defined $VirtualTable{$LVer}{$BName}) {
1863                $VShift+=keys(%{$VirtualTable{$LVer}{$BName}});
1864            }
1865        }
1866    }
1867    return $VShift;
1868}
1869
1870sub getShift($$)
1871{
1872    my ($ClassId, $LVer) = @_;
1873    my @Bases = getBaseClasses($ClassId, $LVer, 0);
1874    my $Shift = 0;
1875    foreach my $BaseId (@Bases)
1876    {
1877        if(my $Size = $TypeInfo{$LVer}{$BaseId}{"Size"})
1878        {
1879            if($Size!=1)
1880            { # not empty base class
1881                $Shift+=$Size;
1882            }
1883        }
1884    }
1885    return $Shift;
1886}
1887
1888sub getVTable_Size($$)
1889{ # number of v-table elements
1890    my ($ClassName, $LVer) = @_;
1891    my $Size = 0;
1892    # three approaches
1893    if(not $Size)
1894    { # real size
1895        if(my %VTable = getVTable_Real($ClassName, $LVer)) {
1896            $Size = keys(%VTable);
1897        }
1898    }
1899    if(not $Size)
1900    { # shared library symbol size
1901        if(my $VTSym = $In::ABI{$LVer}{"ClassVTable"}{$ClassName})
1902        {
1903            if($Size = getSymbolSize($VTSym, $LVer)) {
1904                $Size /= $In::ABI{$LVer}{"WordSize"};
1905            }
1906        }
1907    }
1908    if(not $Size)
1909    { # model size
1910        if(defined $VirtualTable_Model{$LVer}{$ClassName}) {
1911            $Size = keys(%{$VirtualTable_Model{$LVer}{$ClassName}}) + 2;
1912        }
1913    }
1914    return $Size;
1915}
1916
1917sub isLeafClass($$)
1918{
1919    my ($ClassId, $LVer) = @_;
1920    if(not defined $In::ABI{$LVer}{"Class_SubClasses"}{$ClassId}
1921    or not keys(%{$In::ABI{$LVer}{"Class_SubClasses"}{$ClassId}})) {
1922        return 1;
1923    }
1924
1925    return 0;
1926}
1927
1928sub havePubFields($)
1929{ # check structured type for public fields
1930    return isAccessible($_[0], {}, 0, -1);
1931}
1932
1933sub isAccessible($$$$)
1934{ # check interval in structured type for public fields
1935    my ($TypePtr, $Skip, $Start, $End) = @_;
1936    if(not $TypePtr) {
1937        return 0;
1938    }
1939    if($End==-1) {
1940        $End = keys(%{$TypePtr->{"Memb"}})-1;
1941    }
1942    foreach my $MemPos (sort {$a<=>$b} keys(%{$TypePtr->{"Memb"}}))
1943    {
1944        if($Skip and $Skip->{$MemPos})
1945        { # skip removed/added fields
1946            next;
1947        }
1948        if(int($MemPos)>=$Start and int($MemPos)<=$End)
1949        {
1950            if(isPublic($TypePtr, $MemPos)) {
1951                return ($MemPos+1);
1952            }
1953        }
1954    }
1955    return 0;
1956}
1957
1958sub isPublic($$)
1959{
1960    my ($TypePtr, $FieldPos) = @_;
1961
1962    return 0 if(not $TypePtr);
1963    return 0 if(not defined $TypePtr->{"Memb"}{$FieldPos});
1964    return 0 if(not defined $TypePtr->{"Memb"}{$FieldPos}{"name"});
1965
1966    my $Access = $TypePtr->{"Memb"}{$FieldPos}{"access"};
1967    if($Access eq "private")
1968    { # by access in C++ language
1969        return 0;
1970    }
1971
1972    # by name in C language
1973    # TODO: add other methods to detect private members
1974    my $MName = $TypePtr->{"Memb"}{$FieldPos}{"name"};
1975    if($MName=~/priv|abidata|parent_object|impl/i)
1976    { # C-styled private data
1977        return 0;
1978    }
1979    if(lc($MName) eq "abi")
1980    { # ABI information/reserved field
1981        return 0;
1982    }
1983    if(isReserved($MName))
1984    { # reserved fields
1985        return 0;
1986    }
1987
1988    return 1;
1989}
1990
1991sub getVTable_Real($$)
1992{
1993    my ($ClassName, $LVer) = @_;
1994    if(my $ClassId = $TName_Tid{$LVer}{$ClassName})
1995    {
1996        my %Type = getType($ClassId, $LVer);
1997        if(defined $Type{"VTable"}) {
1998            return %{$Type{"VTable"}};
1999        }
2000    }
2001    return ();
2002}
2003
2004sub cmpVTables($)
2005{
2006    my $ClassName = $_[0];
2007    my $Res = cmpVTables_Real($ClassName, 1);
2008    if($Res==-1) {
2009        $Res = cmpVTables_Model($ClassName);
2010    }
2011    return $Res;
2012}
2013
2014sub cmpVTables_Model($)
2015{
2016    my $ClassName = $_[0];
2017    foreach my $Symbol (keys(%{$VirtualTable_Model{1}{$ClassName}}))
2018    {
2019        if(not defined $VirtualTable_Model{2}{$ClassName}{$Symbol}) {
2020            return 1;
2021        }
2022    }
2023    return 0;
2024}
2025
2026sub cmpVTables_Real($$)
2027{
2028    my ($ClassName, $Strong) = @_;
2029    if(defined $Cache{"cmpVTables_Real"}{$Strong}{$ClassName}) {
2030        return $Cache{"cmpVTables_Real"}{$Strong}{$ClassName};
2031    }
2032    my %VTable_Old = getVTable_Real($ClassName, 1);
2033    my %VTable_New = getVTable_Real($ClassName, 2);
2034    if(not %VTable_Old or not %VTable_New)
2035    { # old ABI dumps
2036        return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = -1);
2037    }
2038    my %Indexes = map {$_=>1} (keys(%VTable_Old), keys(%VTable_New));
2039    foreach my $Offset (sort {$a<=>$b} keys(%Indexes))
2040    {
2041        if(not defined $VTable_Old{$Offset})
2042        { # v-table v.1 < v-table v.2
2043            return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = $Strong);
2044        }
2045        my $Entry1 = $VTable_Old{$Offset};
2046        if(not defined $VTable_New{$Offset})
2047        { # v-table v.1 > v-table v.2
2048            return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = ($Strong or $Entry1!~/__cxa_pure_virtual/));
2049        }
2050        my $Entry2 = $VTable_New{$Offset};
2051
2052        $Entry1 = simpleVEntry($Entry1);
2053        $Entry2 = simpleVEntry($Entry2);
2054
2055        if($Entry1=~/ 0x/ or $Entry2=~/ 0x/)
2056        { # NOTE: problem with vtable-dumper
2057            next;
2058        }
2059
2060        if($Entry1 ne $Entry2)
2061        { # register as changed
2062            if($Entry1=~/::([^:]+)\Z/)
2063            {
2064                my $M1 = $1;
2065                if($Entry2=~/::([^:]+)\Z/)
2066                {
2067                    my $M2 = $1;
2068                    if($M1 eq $M2)
2069                    { # overridden
2070                        next;
2071                    }
2072                }
2073            }
2074            if($In::ABI{1}{"GccVersion"} ne $In::ABI{2}{"GccVersion"})
2075            {
2076                if($Entry1=~/\A\-(0x|\d+)/ and $Entry2=~/\A\-(0x|\d+)/)
2077                {
2078                    # GCC 4.6.1: -0x00000000000000010
2079                    # GCC 4.7.0: -16
2080                    next;
2081                }
2082            }
2083            return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = 1);
2084        }
2085    }
2086    return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = 0);
2087}
2088
2089sub mergeVTables($)
2090{ # merging v-tables without diagnostics
2091    my $Level = $_[0];
2092    foreach my $ClassName (keys(%{$VirtualTable{1}}))
2093    {
2094        my $ClassId = $TName_Tid{1}{$ClassName};
2095        if(isPrivateABI($ClassId, 1)) {
2096            next;
2097        }
2098
2099        if($VTableChanged_M{$ClassName})
2100        { # already registered
2101            next;
2102        }
2103        if(cmpVTables_Real($ClassName, 0)==1)
2104        {
2105            my @Affected = (keys(%{$ClassMethods{$Level}{1}{$ClassName}}));
2106            foreach my $Symbol (@Affected)
2107            {
2108                %{$CompatProblems{$Level}{$Symbol}{"Virtual_Table_Changed_Unknown"}{$ClassName}}=(
2109                    "Type_Name"=>$ClassName,
2110                    "Target"=>$ClassName);
2111            }
2112        }
2113    }
2114}
2115
2116sub mergeBases($)
2117{
2118    my $Level = $_[0];
2119    foreach my $ClassName (sort keys(%{$ClassNames{1}}))
2120    { # detect added and removed virtual functions
2121        my $ClassId = $TName_Tid{1}{$ClassName};
2122        next if(not $ClassId);
2123
2124        if(isPrivateABI($ClassId, 1)) {
2125            next;
2126        }
2127
2128        if(defined $VirtualTable{2}{$ClassName})
2129        {
2130            foreach my $Symbol (keys(%{$VirtualTable{2}{$ClassName}}))
2131            {
2132                if($TName_Tid{1}{$ClassName}
2133                and not defined $VirtualTable{1}{$ClassName}{$Symbol})
2134                { # added to v-table
2135                    if(defined $CompSign{1}{$Symbol})
2136                    {
2137                        if($CompSign{1}{$Symbol}{"Virt"})
2138                        { # override some method in v.1
2139                            next;
2140                        }
2141                    }
2142                    else
2143                    {
2144                        if(linkSymbol($Symbol, 1, "+Deps")) {
2145                            next;
2146                        }
2147                    }
2148
2149                    $AddedInt_Virt{$Level}{$ClassName}{$Symbol} = 1;
2150                }
2151            }
2152        }
2153        if(defined $VirtualTable{1}{$ClassName})
2154        {
2155            foreach my $Symbol (keys(%{$VirtualTable{1}{$ClassName}}))
2156            {
2157                if($TName_Tid{2}{$ClassName}
2158                and not defined $VirtualTable{2}{$ClassName}{$Symbol})
2159                { # removed from v-table
2160                    if(defined $CompSign{2}{$Symbol})
2161                    {
2162                        if($CompSign{2}{$Symbol}{"Virt"})
2163                        { # override some method in v.2
2164                            next;
2165                        }
2166                    }
2167                    else
2168                    {
2169                        if(linkSymbol($Symbol, 2, "+Deps")) {
2170                            next;
2171                        }
2172                    }
2173
2174                    $RemovedInt_Virt{$Level}{$ClassName}{$Symbol} = 1;
2175                }
2176            }
2177        }
2178
2179        if($Level eq "Binary")
2180        { # Binary-level
2181            my %Class_Type = getType($ClassId, 1);
2182            foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$Level}{$ClassName}}))
2183            { # check replacements, including pure virtual methods
2184                my $AddedPos = $VirtualTable{2}{$ClassName}{$AddedVFunc};
2185                foreach my $RemovedVFunc (keys(%{$RemovedInt_Virt{$Level}{$ClassName}}))
2186                {
2187                    my $RemovedPos = $VirtualTable{1}{$ClassName}{$RemovedVFunc};
2188                    if($AddedPos==$RemovedPos)
2189                    {
2190                        $VirtualReplacement{$AddedVFunc} = $RemovedVFunc;
2191                        $VirtualReplacement{$RemovedVFunc} = $AddedVFunc;
2192                        last; # other methods will be reported as "added" or "removed"
2193                    }
2194                }
2195                if(my $RemovedVFunc = $VirtualReplacement{$AddedVFunc})
2196                {
2197                    if(lc($AddedVFunc) eq lc($RemovedVFunc))
2198                    { # skip: DomUi => DomUI parameter (Qt 4.2.3 to 4.3.0)
2199                        next;
2200                    }
2201
2202                    my $ProblemType = "Virtual_Replacement";
2203                    my @Affected = ();
2204
2205                    if($CompSign{1}{$RemovedVFunc}{"PureVirt"})
2206                    { # pure methods
2207                        if(not isUsedClass($ClassId, 1, $Level))
2208                        { # not a parameter of some exported method
2209                            next;
2210                        }
2211                        $ProblemType = "Pure_Virtual_Replacement";
2212
2213                        # affected all methods (both virtual and non-virtual ones)
2214                        @Affected = (keys(%{$ClassMethods{$Level}{1}{$ClassName}}));
2215                    }
2216                    else {
2217                        @Affected = ($RemovedVFunc);
2218                    }
2219
2220                    push(@Affected, keys(%{$OverriddenMethods{1}{$RemovedVFunc}}));
2221
2222                    $VTableChanged_M{$ClassName} = 1;
2223
2224                    foreach my $AffectedInt (@Affected)
2225                    {
2226                        if($CompSign{1}{$AffectedInt}{"PureVirt"})
2227                        { # affected exported methods only
2228                            next;
2229                        }
2230                        if(not symbolFilter($AffectedInt, $CompSign{1}{$AffectedInt}, "Affected", $Level, 1)) {
2231                            next;
2232                        }
2233                        %{$CompatProblems{$Level}{$AffectedInt}{$ProblemType}{$AddedVFunc}}=(
2234                            "Type_Name"=>$Class_Type{"Name"},
2235                            "Target"=>$AddedVFunc,
2236                            "Old_Value"=>$RemovedVFunc);
2237                    }
2238                }
2239            }
2240        }
2241    }
2242
2243    foreach my $ClassName (sort keys(%{$ClassNames{1}}))
2244    {
2245        my $ClassId_Old = $TName_Tid{1}{$ClassName};
2246        next if(not $ClassId_Old);
2247
2248        if(isPrivateABI($ClassId_Old, 1)) {
2249            next;
2250        }
2251
2252        if(not isCreatable($ClassId_Old, 1))
2253        { # skip classes without public constructors (including auto-generated)
2254          # example: class has only a private exported or private inline constructor
2255            next;
2256        }
2257
2258        my %Class_Old = getType($ClassId_Old, 1);
2259        my $ClassId_New = $TName_Tid{2}{$ClassName};
2260        if(not $ClassId_New) {
2261            next;
2262        }
2263        my %Class_New = getType($ClassId_New, 2);
2264        if($Class_New{"Type"}!~/Class|Struct/)
2265        { # became typedef
2266            if($Level eq "Binary") {
2267                next;
2268            }
2269            if($Level eq "Source")
2270            {
2271                %Class_New = getPureType($ClassId_New, 2);
2272                if($Class_New{"Type"}!~/Class|Struct/) {
2273                    next;
2274                }
2275                $ClassId_New = $Class_New{"Tid"};
2276            }
2277        }
2278
2279        if(not $Class_New{"Size"} or not $Class_Old{"Size"})
2280        { # incomplete info in the ABI dump
2281            next;
2282        }
2283
2284        if($Level eq "Binary" and cmpVTables_Real($ClassName, 1)!=0)
2285        {
2286            foreach my $Symbol (sort keys(%{$RemovedInt_Virt{$Level}{$ClassName}}))
2287            {
2288                if($VirtualReplacement{$Symbol}) {
2289                    next;
2290                }
2291
2292                if(symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected", $Level, 1))
2293                {
2294                    my $ProblemType = "Removed_Virtual_Method";
2295                    if($CompSign{1}{$Symbol}{"PureVirt"}) {
2296                        $ProblemType = "Removed_Pure_Virtual_Method";
2297                    }
2298
2299                    %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{getSignature($Symbol, 1, "Class|Name|Qual")}}=(
2300                        "Type_Name"=>$ClassName,
2301                        "Target"=>$Symbol);
2302                }
2303
2304                $VTableChanged_M{$ClassName} = 1;
2305                foreach my $SubId (getSubClasses($ClassId_Old, 1, 1))
2306                {
2307                    if(my $SubName = $TypeInfo{1}{$SubId}{"Name"}) {
2308                        $VTableChanged_M{$SubName} = 1;
2309                    }
2310                }
2311            }
2312        }
2313
2314        if(index($ClassName, ">")!=-1)
2315        { # skip affected template instances
2316            next;
2317        }
2318
2319        my @Bases_Old = sort {$Class_Old{"Base"}{$a}{"pos"}<=>$Class_Old{"Base"}{$b}{"pos"}} keys(%{$Class_Old{"Base"}});
2320        my @Bases_New = sort {$Class_New{"Base"}{$a}{"pos"}<=>$Class_New{"Base"}{$b}{"pos"}} keys(%{$Class_New{"Base"}});
2321
2322        my %Tr_Old = map {$TypeInfo{1}{$_}{"Name"} => uncoverTypedefs($TypeInfo{1}{$_}{"Name"}, 1)} @Bases_Old;
2323        my %Tr_New = map {$TypeInfo{2}{$_}{"Name"} => uncoverTypedefs($TypeInfo{2}{$_}{"Name"}, 2)} @Bases_New;
2324
2325        my ($BNum1, $BNum2) = (1, 1);
2326        my %BasePos_Old = map {$Tr_Old{$TypeInfo{1}{$_}{"Name"}} => $BNum1++} @Bases_Old;
2327        my %BasePos_New = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $BNum2++} @Bases_New;
2328        my %ShortBase_Old = map {getShortClass($_, 1) => 1} @Bases_Old;
2329        my %ShortBase_New = map {getShortClass($_, 2) => 1} @Bases_New;
2330        my $Shift_Old = getShift($ClassId_Old, 1);
2331        my $Shift_New = getShift($ClassId_New, 2);
2332        my %BaseId_New = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $_} @Bases_New;
2333        my @StableBases_Old = ();
2334        foreach my $BaseId (@Bases_Old)
2335        {
2336            my $BaseName = $TypeInfo{1}{$BaseId}{"Name"};
2337            if($BasePos_New{$Tr_Old{$BaseName}}) {
2338                push(@StableBases_Old, $BaseId);
2339            }
2340            elsif(not $ShortBase_New{$Tr_Old{$BaseName}}
2341            and not $ShortBase_New{getShortClass($BaseId, 1)})
2342            { # removed base
2343              # excluding namespace::SomeClass to SomeClass renaming
2344                my $ProblemKind = "Removed_Base_Class";
2345                if($Level eq "Binary")
2346                { # Binary-level
2347                    if($Shift_Old ne $Shift_New)
2348                    { # affected fields
2349                        if(havePubFields(\%Class_Old)) {
2350                            $ProblemKind .= "_And_Shift";
2351                        }
2352                        elsif($Class_Old{"Size"} ne $Class_New{"Size"}) {
2353                            $ProblemKind .= "_And_Size";
2354                        }
2355                    }
2356                    if(keys(%{$VirtualTable_Model{1}{$BaseName}})
2357                    and cmpVTables($ClassName)==1)
2358                    { # affected v-table
2359                        $ProblemKind .= "_And_VTable";
2360                        $VTableChanged_M{$ClassName} = 1;
2361                    }
2362                }
2363                my @Affected = keys(%{$ClassMethods{$Level}{1}{$ClassName}});
2364                foreach my $SubId (getSubClasses($ClassId_Old, 1, 1))
2365                {
2366                    if(my $SubName = $TypeInfo{1}{$SubId}{"Name"})
2367                    {
2368                        push(@Affected, keys(%{$ClassMethods{$Level}{1}{$SubName}}));
2369                        if($ProblemKind=~/VTable/) {
2370                            $VTableChanged_M{$SubName} = 1;
2371                        }
2372                    }
2373                }
2374                foreach my $Interface (@Affected)
2375                {
2376                    if(symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1))
2377                    {
2378                        %{$CompatProblems{$Level}{$Interface}{$ProblemKind}{"this"}}=(
2379                            "Type_Name"=>$ClassName,
2380                            "Target"=>$BaseName,
2381                            "Old_Size"=>$Class_Old{"Size"}*$BYTE,
2382                            "New_Size"=>$Class_New{"Size"}*$BYTE,
2383                            "Shift"=>abs($Shift_New-$Shift_Old));
2384                    }
2385                }
2386            }
2387        }
2388        my @StableBases_New = ();
2389        foreach my $BaseId (@Bases_New)
2390        {
2391            my $BaseName = $TypeInfo{2}{$BaseId}{"Name"};
2392            if($BasePos_Old{$Tr_New{$BaseName}}) {
2393                push(@StableBases_New, $BaseId);
2394            }
2395            elsif(not $ShortBase_Old{$Tr_New{$BaseName}}
2396            and not $ShortBase_Old{getShortClass($BaseId, 2)})
2397            { # added base
2398              # excluding namespace::SomeClass to SomeClass renaming
2399                my $ProblemKind = "Added_Base_Class";
2400                if($Level eq "Binary")
2401                { # Binary-level
2402                    if($Shift_Old ne $Shift_New)
2403                    { # affected fields
2404                        if(havePubFields(\%Class_Old)) {
2405                            $ProblemKind .= "_And_Shift";
2406                        }
2407                        elsif($Class_Old{"Size"} ne $Class_New{"Size"}) {
2408                            $ProblemKind .= "_And_Size";
2409                        }
2410                    }
2411                    if(keys(%{$VirtualTable_Model{2}{$BaseName}})
2412                    and cmpVTables($ClassName)==1)
2413                    { # affected v-table
2414                        $ProblemKind .= "_And_VTable";
2415                        $VTableChanged_M{$ClassName} = 1;
2416                    }
2417                }
2418                my @Affected = keys(%{$ClassMethods{$Level}{1}{$ClassName}});
2419                foreach my $SubId (getSubClasses($ClassId_Old, 1, 1))
2420                {
2421                    if(my $SubName = $TypeInfo{1}{$SubId}{"Name"})
2422                    {
2423                        push(@Affected, keys(%{$ClassMethods{$Level}{1}{$SubName}}));
2424                        if($ProblemKind=~/VTable/) {
2425                            $VTableChanged_M{$SubName} = 1;
2426                        }
2427                    }
2428                }
2429                foreach my $Interface (@Affected)
2430                {
2431                    if(symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1))
2432                    {
2433                        %{$CompatProblems{$Level}{$Interface}{$ProblemKind}{"this"}}=(
2434                            "Type_Name"=>$ClassName,
2435                            "Target"=>$BaseName,
2436                            "Old_Size"=>$Class_Old{"Size"}*$BYTE,
2437                            "New_Size"=>$Class_New{"Size"}*$BYTE,
2438                            "Shift"=>abs($Shift_New-$Shift_Old));
2439                    }
2440                }
2441            }
2442        }
2443        if($Level eq "Binary")
2444        { # Binary-level
2445            ($BNum1, $BNum2) = (1, 1);
2446            my %BaseRelPos_Old = map {$Tr_Old{$TypeInfo{1}{$_}{"Name"}} => $BNum1++} @StableBases_Old;
2447            my %BaseRelPos_New = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $BNum2++} @StableBases_New;
2448            foreach my $BaseId (@Bases_Old)
2449            {
2450                my $BaseName = $TypeInfo{1}{$BaseId}{"Name"};
2451                if(my $NewPos = $BaseRelPos_New{$Tr_Old{$BaseName}})
2452                {
2453                    my $BaseNewId = $BaseId_New{$Tr_Old{$BaseName}};
2454                    my $OldPos = $BaseRelPos_Old{$Tr_Old{$BaseName}};
2455                    if($NewPos!=$OldPos)
2456                    { # changed position of the base class
2457                        foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}}))
2458                        {
2459                            if(symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1))
2460                            {
2461                                %{$CompatProblems{$Level}{$Interface}{"Base_Class_Position"}{"this"}}=(
2462                                    "Type_Name"=>$ClassName,
2463                                    "Target"=>$BaseName,
2464                                    "Old_Value"=>$OldPos-1,
2465                                    "New_Value"=>$NewPos-1);
2466                            }
2467                        }
2468                    }
2469                    if($Class_Old{"Base"}{$BaseId}{"virtual"}
2470                    and not $Class_New{"Base"}{$BaseNewId}{"virtual"})
2471                    { # became non-virtual base
2472                        foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}}))
2473                        {
2474                            if(symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1))
2475                            {
2476                                %{$CompatProblems{$Level}{$Interface}{"Base_Class_Became_Non_Virtually_Inherited"}{"this->".$BaseName}}=(
2477                                    "Type_Name"=>$ClassName,
2478                                    "Target"=>$BaseName  );
2479                            }
2480                        }
2481                    }
2482                    elsif(not $Class_Old{"Base"}{$BaseId}{"virtual"}
2483                    and $Class_New{"Base"}{$BaseNewId}{"virtual"})
2484                    { # became virtual base
2485                        foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}}))
2486                        {
2487                            if(not symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1)) {
2488                                next;
2489                            }
2490                            %{$CompatProblems{$Level}{$Interface}{"Base_Class_Became_Virtually_Inherited"}{"this->".$BaseName}}=(
2491                                "Type_Name"=>$ClassName,
2492                                "Target"=>$BaseName  );
2493                        }
2494                    }
2495                }
2496            }
2497            # detect size changes in base classes
2498            if($Shift_Old!=$Shift_New)
2499            { # size of allocable class
2500                foreach my $BaseId (@StableBases_Old)
2501                { # search for changed base
2502                    my %BaseType = getType($BaseId, 1);
2503                    my $Size_Old = $TypeInfo{1}{$BaseId}{"Size"};
2504                    my $Size_New = $TypeInfo{2}{$BaseId_New{$Tr_Old{$BaseType{"Name"}}}}{"Size"};
2505                    if($Size_Old ne $Size_New
2506                    and $Size_Old and $Size_New)
2507                    {
2508                        my $ProblemType = undef;
2509                        if(isCopyingClass($BaseId, 1)) {
2510                            $ProblemType = "Size_Of_Copying_Class";
2511                        }
2512                        elsif($AllocableClass{1}{$BaseType{"Name"}})
2513                        {
2514                            if($Size_New>$Size_Old)
2515                            { # increased size
2516                                $ProblemType = "Size_Of_Allocable_Class_Increased";
2517                            }
2518                            else
2519                            { # decreased size
2520                                $ProblemType = "Size_Of_Allocable_Class_Decreased";
2521                                if(not havePubFields(\%Class_Old))
2522                                { # affected class has no public members
2523                                    next;
2524                                }
2525                            }
2526                        }
2527                        next if(not $ProblemType);
2528                        foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}}))
2529                        { # base class size changes affecting current class
2530                            if(not symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1)) {
2531                                next;
2532                            }
2533                            %{$CompatProblems{$Level}{$Interface}{$ProblemType}{"this->".$BaseType{"Name"}}}=(
2534                                "Type_Name"=>$BaseType{"Name"},
2535                                "Target"=>$BaseType{"Name"},
2536                                "Old_Size"=>$Size_Old*$BYTE,
2537                                "New_Size"=>$Size_New*$BYTE  );
2538                        }
2539                    }
2540                }
2541            }
2542
2543            if(defined $VirtualTable_Model{1}{$ClassName}
2544            and cmpVTables_Real($ClassName, 1)==1
2545            and my @VFunctions = keys(%{$VirtualTable_Model{1}{$ClassName}}))
2546            { # compare virtual tables size in base classes
2547                my $VShift_Old = getVShift($ClassId_Old, 1);
2548                my $VShift_New = getVShift($ClassId_New, 2);
2549                if($VShift_Old ne $VShift_New)
2550                { # changes in the base class or changes in the list of base classes
2551                    my @AllBases_Old = getBaseClasses($ClassId_Old, 1, 1);
2552                    my @AllBases_New = getBaseClasses($ClassId_New, 2, 1);
2553                    ($BNum1, $BNum2) = (1, 1);
2554                    my %StableBase = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $_} @AllBases_New;
2555                    foreach my $BaseId (@AllBases_Old)
2556                    {
2557                        my %BaseType = getType($BaseId, 1);
2558                        if(not $StableBase{$Tr_Old{$BaseType{"Name"}}})
2559                        { # lost base
2560                            next;
2561                        }
2562                        my $VSize_Old = getVTable_Size($BaseType{"Name"}, 1);
2563                        my $VSize_New = getVTable_Size($BaseType{"Name"}, 2);
2564                        if($VSize_Old!=$VSize_New)
2565                        {
2566                            foreach my $Symbol (@VFunctions)
2567                            { # TODO: affected non-virtual methods?
2568                                if(not defined $VirtualTable_Model{2}{$ClassName}{$Symbol})
2569                                { # Removed_Virtual_Method, will be registered in mergeVirtualTables()
2570                                    next;
2571                                }
2572
2573                                if($VirtualTable_Model{2}{$ClassName}{$Symbol}-$VirtualTable_Model{1}{$ClassName}{$Symbol}==0)
2574                                { # skip interfaces that have not changed the absolute virtual position
2575                                    next;
2576                                }
2577
2578                                if(not symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected", $Level, 1)) {
2579                                    next;
2580                                }
2581
2582                                $VTableChanged_M{$BaseType{"Name"}} = 1;
2583                                $VTableChanged_M{$ClassName} = 1;
2584
2585                                foreach my $VirtFunc (keys(%{$AddedInt_Virt{$Level}{$BaseType{"Name"}}}))
2586                                { # the reason of the layout change: added virtual functions
2587                                    next if($VirtualReplacement{$VirtFunc});
2588                                    my $ProblemType = "Added_Virtual_Method";
2589                                    if($CompSign{2}{$VirtFunc}{"PureVirt"}) {
2590                                        $ProblemType = "Added_Pure_Virtual_Method";
2591                                    }
2592                                    %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{getSignature($VirtFunc, 2, "Class|Name|Qual")}}=(
2593                                        "Type_Name"=>$BaseType{"Name"},
2594                                        "Target"=>$VirtFunc  );
2595                                }
2596
2597                                foreach my $VirtFunc (keys(%{$RemovedInt_Virt{$Level}{$BaseType{"Name"}}}))
2598                                { # the reason of the layout change: removed virtual functions
2599                                    next if($VirtualReplacement{$VirtFunc});
2600                                    my $ProblemType = "Removed_Virtual_Method";
2601                                    if($CompSign{1}{$VirtFunc}{"PureVirt"}) {
2602                                        $ProblemType = "Removed_Pure_Virtual_Method";
2603                                    }
2604                                    %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{getSignature($VirtFunc, 1, "Class|Name|Qual")}}=(
2605                                        "Type_Name"=>$BaseType{"Name"},
2606                                        "Target"=>$VirtFunc  );
2607                                }
2608                            }
2609                        }
2610                    }
2611                }
2612            }
2613        }
2614    }
2615}
2616
2617sub isCreatable($$)
2618{
2619    my ($ClassId, $LVer) = @_;
2620    if($AllocableClass{$LVer}{$TypeInfo{$LVer}{$ClassId}{"Name"}}
2621    or isCopyingClass($ClassId, $LVer)) {
2622        return 1;
2623    }
2624    if(keys(%{$In::ABI{$LVer}{"Class_SubClasses"}{$ClassId}}))
2625    { # Fix for incomplete data: if this class has
2626      # a base class then it should also has a constructor
2627        return 1;
2628    }
2629    if($ReturnedClass{$LVer}{$ClassId})
2630    { # returned by some method of this class
2631      # or any other class
2632        return 1;
2633    }
2634    return 0;
2635}
2636
2637sub isUsedClass($$$)
2638{
2639    my ($ClassId, $LVer, $Level) = @_;
2640    if(keys(%{$ParamClass{$LVer}{$ClassId}}))
2641    { # parameter of some exported method
2642        return 1;
2643    }
2644    my $CName = $TypeInfo{$LVer}{$ClassId}{"Name"};
2645    if(keys(%{$ClassMethods{$Level}{$LVer}{$CName}}))
2646    { # method from target class
2647        return 1;
2648    }
2649    return 0;
2650}
2651
2652sub mergeVirtualTables($$)
2653{ # check for changes in the virtual table
2654    my ($Interface, $Level) = @_;
2655    # affected methods:
2656    #  - virtual
2657    #  - pure-virtual
2658    #  - non-virtual
2659    if($CompSign{1}{$Interface}{"Data"})
2660    { # global data is not affected
2661        return;
2662    }
2663    my $Class_Id = $CompSign{1}{$Interface}{"Class"};
2664    if(not $Class_Id) {
2665        return;
2666    }
2667    my $CName = $TypeInfo{1}{$Class_Id}{"Name"};
2668    if(cmpVTables_Real($CName, 1)==0)
2669    { # no changes
2670        return;
2671    }
2672    $CheckedTypes{$Level}{$CName} = 1;
2673    if($Level eq "Binary")
2674    { # Binary-level
2675        if($CompSign{1}{$Interface}{"PureVirt"}
2676        and not isUsedClass($Class_Id, 1, $Level))
2677        { # pure virtuals should not be affected
2678          # if there are no exported methods using this class
2679            return;
2680        }
2681    }
2682    foreach my $Func (keys(%{$VirtualTable{1}{$CName}}))
2683    {
2684        if(defined $VirtualTable{2}{$CName}{$Func}
2685        and defined $CompSign{2}{$Func})
2686        {
2687            if(not $CompSign{1}{$Func}{"PureVirt"}
2688            and $CompSign{2}{$Func}{"PureVirt"})
2689            { # became pure virtual
2690                %{$CompatProblems{$Level}{$Interface}{"Virtual_Method_Became_Pure"}{$Func}}=(
2691                    "Type_Name"=>$CName,
2692                    "Target"=>$Func  );
2693                $VTableChanged_M{$CName} = 1;
2694            }
2695            elsif($CompSign{1}{$Func}{"PureVirt"}
2696            and not $CompSign{2}{$Func}{"PureVirt"})
2697            { # became non-pure virtual
2698                %{$CompatProblems{$Level}{$Interface}{"Virtual_Method_Became_Non_Pure"}{$Func}}=(
2699                    "Type_Name"=>$CName,
2700                    "Target"=>$Func  );
2701                $VTableChanged_M{$CName} = 1;
2702            }
2703        }
2704    }
2705    if($Level eq "Binary")
2706    { # Binary-level
2707        # check virtual table structure
2708        foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$Level}{$CName}}))
2709        {
2710            next if($Interface eq $AddedVFunc);
2711            next if($VirtualReplacement{$AddedVFunc});
2712            my $VPos_Added = $VirtualTable{2}{$CName}{$AddedVFunc};
2713            if($CompSign{2}{$AddedVFunc}{"PureVirt"})
2714            { # pure virtual methods affect all others (virtual and non-virtual)
2715                %{$CompatProblems{$Level}{$Interface}{"Added_Pure_Virtual_Method"}{$AddedVFunc}}=(
2716                    "Type_Name"=>$CName,
2717                    "Target"=>$AddedVFunc  );
2718                $VTableChanged_M{$CName} = 1;
2719            }
2720            elsif(not defined $VirtualTable{1}{$CName}
2721            or $VPos_Added>keys(%{$VirtualTable{1}{$CName}}))
2722            { # added virtual function at the end of v-table
2723                if(not keys(%{$VirtualTable_Model{1}{$CName}}))
2724                { # became polymorphous class, added v-table pointer
2725                    %{$CompatProblems{$Level}{$Interface}{"Added_First_Virtual_Method"}{$AddedVFunc}}=(
2726                        "Type_Name"=>$CName,
2727                        "Target"=>$AddedVFunc  );
2728                    $VTableChanged_M{$CName} = 1;
2729                }
2730                else
2731                {
2732                    my $VSize_Old = getVTable_Size($CName, 1);
2733                    my $VSize_New = getVTable_Size($CName, 2);
2734                    next if($VSize_Old==$VSize_New); # exception: register as removed and added virtual method
2735                    if(isCopyingClass($Class_Id, 1))
2736                    { # class has no constructors and v-table will be copied by applications, this may affect all methods
2737                        my $ProblemType = "Added_Virtual_Method";
2738                        if(isLeafClass($Class_Id, 1)) {
2739                            $ProblemType = "Added_Virtual_Method_At_End_Of_Leaf_Copying_Class";
2740                        }
2741                        %{$CompatProblems{$Level}{$Interface}{$ProblemType}{getSignature($AddedVFunc, 2, "Class|Name|Qual")}}=(
2742                            "Type_Name"=>$CName,
2743                            "Target"=>$AddedVFunc  );
2744                        $VTableChanged_M{$CName} = 1;
2745                    }
2746                    else
2747                    {
2748                        my $ProblemType = "Added_Virtual_Method";
2749                        if(isLeafClass($Class_Id, 1)) {
2750                            $ProblemType = "Added_Virtual_Method_At_End_Of_Leaf_Allocable_Class";
2751                        }
2752                        %{$CompatProblems{$Level}{$Interface}{$ProblemType}{getSignature($AddedVFunc, 2, "Class|Name|Qual")}}=(
2753                            "Type_Name"=>$CName,
2754                            "Target"=>$AddedVFunc  );
2755                        $VTableChanged_M{$CName} = 1;
2756                    }
2757                }
2758            }
2759            elsif($CompSign{1}{$Interface}{"Virt"}
2760            or $CompSign{1}{$Interface}{"PureVirt"})
2761            {
2762                if(defined $VirtualTable{1}{$CName}
2763                and defined $VirtualTable{2}{$CName})
2764                {
2765                    my $VPos_Old = $VirtualTable{1}{$CName}{$Interface};
2766                    my $VPos_New = $VirtualTable{2}{$CName}{$Interface};
2767
2768                    if($VPos_Added<=$VPos_Old and $VPos_Old!=$VPos_New)
2769                    {
2770                        my @Affected = ($Interface, keys(%{$OverriddenMethods{1}{$Interface}}));
2771                        foreach my $ASymbol (@Affected)
2772                        {
2773                            if(not $CompSign{1}{$ASymbol}{"PureVirt"})
2774                            {
2775                                if(not symbolFilter($ASymbol, $CompSign{1}{$ASymbol}, "Affected", $Level, 1)) {
2776                                    next;
2777                                }
2778                            }
2779                            %{$CompatProblems{$Level}{$ASymbol}{"Added_Virtual_Method"}{getSignature($AddedVFunc, 2, "Class|Name|Qual")}}=(
2780                                "Type_Name"=>$CName,
2781                                "Target"=>$AddedVFunc  );
2782                            $VTableChanged_M{$TypeInfo{1}{$CompSign{1}{$ASymbol}{"Class"}}{"Name"}} = 1;
2783                        }
2784                    }
2785                }
2786            }
2787            else {
2788                # safe
2789            }
2790        }
2791
2792        foreach my $RemovedVFunc (sort keys(%{$RemovedInt_Virt{$Level}{$CName}}))
2793        {
2794            next if($VirtualReplacement{$RemovedVFunc});
2795            if($RemovedVFunc eq $Interface
2796            and $CompSign{1}{$RemovedVFunc}{"PureVirt"})
2797            { # This case is for removed virtual methods
2798              # implemented in both versions of a library
2799                next;
2800            }
2801
2802            if(not keys(%{$VirtualTable_Model{2}{$CName}}))
2803            { # became non-polymorphous class, removed v-table pointer
2804                %{$CompatProblems{$Level}{$Interface}{"Removed_Last_Virtual_Method"}{getSignature($RemovedVFunc, 1, "Class|Name|Qual")}}=(
2805                    "Type_Name"=>$CName,
2806                    "Target"=>$RemovedVFunc);
2807                $VTableChanged_M{$CName} = 1;
2808            }
2809            elsif($CompSign{1}{$Interface}{"Virt"}
2810            or $CompSign{1}{$Interface}{"PureVirt"})
2811            {
2812                if(defined $VirtualTable{1}{$CName}
2813                and defined $VirtualTable{2}{$CName})
2814                {
2815                    if(not defined $VirtualTable{1}{$CName}{$Interface}) {
2816                        next;
2817                    }
2818                    my $VPos_New = -1;
2819                    if(defined $VirtualTable{2}{$CName}{$Interface})
2820                    {
2821                        $VPos_New = $VirtualTable{2}{$CName}{$Interface};
2822                    }
2823                    else
2824                    {
2825                        if($Interface ne $RemovedVFunc) {
2826                            next;
2827                        }
2828                    }
2829                    my $VPos_Removed = $VirtualTable{1}{$CName}{$RemovedVFunc};
2830                    my $VPos_Old = $VirtualTable{1}{$CName}{$Interface};
2831                    if($VPos_Removed<=$VPos_Old and $VPos_Old!=$VPos_New)
2832                    {
2833                        my @Affected = ($Interface, keys(%{$OverriddenMethods{1}{$Interface}}));
2834                        foreach my $ASymbol (@Affected)
2835                        {
2836                            if(not $CompSign{1}{$ASymbol}{"PureVirt"})
2837                            {
2838                                if(not symbolFilter($ASymbol, $CompSign{1}{$ASymbol}, "Affected", $Level, 1)) {
2839                                    next;
2840                                }
2841                            }
2842                            my $ProblemType = "Removed_Virtual_Method";
2843                            if($CompSign{1}{$RemovedVFunc}{"PureVirt"}) {
2844                                $ProblemType = "Removed_Pure_Virtual_Method";
2845                            }
2846
2847                            %{$CompatProblems{$Level}{$ASymbol}{$ProblemType}{getSignature($RemovedVFunc, 1, "Class|Name|Qual")}}=(
2848                                "Type_Name"=>$CName,
2849                                "Target"=>$RemovedVFunc);
2850                            $VTableChanged_M{$TypeInfo{1}{$CompSign{1}{$ASymbol}{"Class"}}{"Name"}} = 1;
2851                        }
2852                    }
2853                }
2854            }
2855        }
2856    }
2857    else
2858    { # Source-level
2859        foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$Level}{$CName}}))
2860        {
2861            next if($Interface eq $AddedVFunc);
2862            if($CompSign{2}{$AddedVFunc}{"PureVirt"})
2863            {
2864                %{$CompatProblems{$Level}{$Interface}{"Added_Pure_Virtual_Method"}{$AddedVFunc}}=(
2865                    "Type_Name"=>$CName,
2866                    "Target"=>$AddedVFunc  );
2867            }
2868        }
2869        foreach my $RemovedVFunc (keys(%{$RemovedInt_Virt{$Level}{$CName}}))
2870        {
2871            if($CompSign{1}{$RemovedVFunc}{"PureVirt"})
2872            {
2873                %{$CompatProblems{$Level}{$Interface}{"Removed_Pure_Virtual_Method"}{$RemovedVFunc}}=(
2874                    "Type_Name"=>$CName,
2875                    "Target"=>$RemovedVFunc  );
2876            }
2877        }
2878    }
2879}
2880
2881sub findMemPairByName($$)
2882{
2883    my ($Mem, $PairType) = @_;
2884    $Mem=~s/\A[_]+|[_]+\Z//g;
2885    foreach my $Pair (sort {$a<=>$b} keys(%{$PairType->{"Memb"}}))
2886    {
2887        if(defined $PairType->{"Memb"}{$Pair})
2888        {
2889            my $Name = $PairType->{"Memb"}{$Pair}{"name"};
2890
2891            if(index($Name, "_")!=-1) {
2892                $Name=~s/\A[_]+|[_]+\Z//g;
2893            }
2894
2895            if($Name eq $Mem) {
2896                return $Pair;
2897            }
2898        }
2899    }
2900    return "lost";
2901}
2902
2903sub findMemPairByVal($$)
2904{
2905    my ($Member_Value, $Pair_Type) = @_;
2906    foreach my $MemberPair_Pos (sort {$a<=>$b} keys(%{$Pair_Type->{"Memb"}}))
2907    {
2908        if(defined $Pair_Type->{"Memb"}{$MemberPair_Pos}
2909        and $Pair_Type->{"Memb"}{$MemberPair_Pos}{"value"} eq $Member_Value) {
2910            return $MemberPair_Pos;
2911        }
2912    }
2913    return "lost";
2914}
2915
2916sub isRenamed($$$$$)
2917{
2918    my ($MemPos, $Type1, $V1, $Type2, $V2) = @_;
2919    my $Member_Name = $Type1->{"Memb"}{$MemPos}{"name"};
2920    my $MemberType_Id = $Type1->{"Memb"}{$MemPos}{"type"};
2921    my %MemberType_Pure = getPureType($MemberType_Id, $V1);
2922    if(not defined $Type2->{"Memb"}{$MemPos}) {
2923        return "";
2924    }
2925    my $PairType_Id = $Type2->{"Memb"}{$MemPos}{"type"};
2926    my %PairType_Pure = getPureType($PairType_Id, $V2);
2927
2928    my $Pair_Name = $Type2->{"Memb"}{$MemPos}{"name"};
2929    my $MemberPair_Pos_Rev = ($Member_Name eq $Pair_Name)?$MemPos:findMemPairByName($Pair_Name, $Type1);
2930    if($MemberPair_Pos_Rev eq "lost")
2931    {
2932        if($MemberType_Pure{"Name"} eq $PairType_Pure{"Name"})
2933        { # base type match
2934            return $Pair_Name;
2935        }
2936        if($TypeInfo{$V1}{$MemberType_Id}{"Name"} eq $TypeInfo{$V2}{$PairType_Id}{"Name"})
2937        { # exact type match
2938            return $Pair_Name;
2939        }
2940        if($MemberType_Pure{"Size"} eq $PairType_Pure{"Size"})
2941        { # size match
2942            return $Pair_Name;
2943        }
2944        if(isReserved($Pair_Name))
2945        { # reserved fields
2946            return $Pair_Name;
2947        }
2948    }
2949    return "";
2950}
2951
2952sub isLastElem($$)
2953{
2954    my ($Pos, $TypeRef) = @_;
2955    my $Name = $TypeRef->{"Memb"}{$Pos}{"name"};
2956    if($Name=~/last|count|max|total|num/i)
2957    { # GST_LEVEL_COUNT, GST_RTSP_ELAST
2958        return 1;
2959    }
2960    elsif($Name=~/END|NLIMITS\Z/)
2961    { # __RLIMIT_NLIMITS
2962        return 1;
2963    }
2964    elsif($Name=~/\AN[A-Z](.+)[a-z]+s\Z/
2965    and $Pos+1==keys(%{$TypeRef->{"Memb"}}))
2966    { # NImageFormats, NColorRoles
2967        return 1;
2968    }
2969    return 0;
2970}
2971
2972sub nonComparable($$)
2973{
2974    my ($T1, $T2) = @_;
2975
2976    my $N1 = $T1->{"Name"};
2977    my $N2 = $T2->{"Name"};
2978
2979    $N1=~s/\A(struct|union|enum) //;
2980    $N2=~s/\A(struct|union|enum) //;
2981
2982    if($N1 ne $N2 and $N2!~/\A\Q$N1\E_v\d+\Z/
2983    and not isAnon($N1)
2984    and not isAnon($N2))
2985    { # different names
2986      # NOTE: compare versioned types (type vs type_v30)
2987        if($T1->{"Type"} ne "Pointer"
2988        or $T2->{"Type"} ne "Pointer")
2989        { # compare base types
2990            return 1;
2991        }
2992
2993        if($N1!~/\Avoid\s*\*/
2994        and $N2=~/\Avoid\s*\*/)
2995        {
2996            return 1;
2997        }
2998    }
2999    elsif($T1->{"Type"} ne $T2->{"Type"})
3000    { # different types
3001        if($T1->{"Type"} eq "Class"
3002        and $T2->{"Type"} eq "Struct")
3003        { # "class" to "struct"
3004            return 0;
3005        }
3006        elsif($T2->{"Type"} eq "Class"
3007        and $T1->{"Type"} eq "Struct")
3008        { # "struct" to "class"
3009            return 0;
3010        }
3011        else
3012        { # "class" to "enum"
3013          # "union" to "class"
3014          #  ...
3015            return 1;
3016        }
3017    }
3018    return 0;
3019}
3020
3021sub mergeTypes($$$)
3022{
3023    my ($Type1_Id, $Type2_Id, $Level) = @_;
3024    return {} if(not $Type1_Id or not $Type2_Id);
3025
3026    if(defined $Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id})
3027    { # already merged
3028        return $Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id};
3029    }
3030
3031    my %Type1 = getType($Type1_Id, 1);
3032    my %Type2 = getType($Type2_Id, 2);
3033    if(not $Type1{"Name"} or not $Type2{"Name"}) {
3034        return {};
3035    }
3036
3037    my %Type1_Pure = getPureType($Type1_Id, 1);
3038    my %Type2_Pure = getPureType($Type2_Id, 2);
3039
3040    if(defined $UsedDump{1}{"DWARF"})
3041    {
3042        if($Type1_Pure{"Name"} eq "__unknown__"
3043        or $Type2_Pure{"Name"} eq "__unknown__")
3044        { # Error ABI dump
3045            return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = {});
3046        }
3047    }
3048
3049    if(isPrivateABI($Type1_Id, 1)) {
3050        return {};
3051    }
3052
3053    if($Type1{"Type"}=~/Intrinsic|Class|Struct|Union|Enum|Ptr|Typedef/) {
3054        $CheckedTypes{$Level}{$Type1{"Name"}} = 1;
3055    }
3056
3057    if($Type1_Pure{"Type"}=~/Intrinsic|Class|Struct|Union|Enum|Ptr|Typedef/) {
3058        $CheckedTypes{$Level}{$Type1_Pure{"Name"}} = 1;
3059    }
3060
3061    my %SubProblems = ();
3062
3063    if($Type1_Pure{"Name"} eq $Type2_Pure{"Name"})
3064    {
3065        if($Type1_Pure{"Type"}=~/Struct|Union/
3066        and $Type2_Pure{"Type"}=~/Struct|Union/)
3067        {
3068            if(isOpaque(\%Type2_Pure) and not isOpaque(\%Type1_Pure))
3069            {
3070                if(not defined $UsedDump{1}{"DWARF"}
3071                and not defined $UsedDump{2}{"DWARF"})
3072                {
3073                    %{$SubProblems{"Type_Became_Opaque"}{$Type1_Pure{"Name"}}}=(
3074                        "Target"=>$Type1_Pure{"Name"},
3075                        "Type_Name"=>$Type1_Pure{"Name"}  );
3076                }
3077
3078                return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems);
3079            }
3080        }
3081    }
3082
3083    if($Type1_Pure{"Size"} eq ""
3084    or $Type2_Pure{"Size"} eq "")
3085    { # NOTE: size of struct may be 0 bytes
3086        if($Type1_Pure{"Type"}=~/Class|Struct|Union/)
3087        { # including a case when "class Class { ... };" changed to "class Class;"
3088            if(not defined $Type1_Pure{"Memb"} or not defined $Type2_Pure{"Memb"}
3089            or index($Type1_Pure{"Name"}, "<")==-1 or index($Type2_Pure{"Name"}, "<")==-1)
3090            { # NOTE: template instances have no size
3091                return {};
3092            }
3093        }
3094    }
3095
3096    if(isRecurType($Type1_Pure{"Tid"}, $Type2_Pure{"Tid"}, \@RecurTypes))
3097    { # skip recursive declarations
3098        return {};
3099    }
3100    return {} if(not $Type1_Pure{"Name"} or not $Type2_Pure{"Name"});
3101    return {} if($In::Desc{1}{"SkipTypes"}{$Type1_Pure{"Name"}});
3102    return {} if($In::Desc{1}{"SkipTypes"}{$Type1{"Name"}});
3103
3104    if($Type1_Pure{"Type"}=~/Class|Struct|Union|Enum|Typedef/
3105    and not isTargetType($Type1_Pure{"Tid"}, 1)) {
3106        return {};
3107    }
3108
3109    my %Typedef_1 = goToFirst($Type1{"Tid"}, 1, "Typedef");
3110    my %Typedef_2 = goToFirst($Type2{"Tid"}, 2, "Typedef");
3111
3112    if(%Typedef_1 and %Typedef_2
3113    and $Typedef_1{"Type"} eq "Typedef" and $Typedef_2{"Type"} eq "Typedef"
3114    and $Typedef_1{"Name"} eq $Typedef_2{"Name"})
3115    {
3116        my %Base_1 = getOneStepBaseType($Typedef_1{"Tid"}, 1);
3117        my %Base_2 = getOneStepBaseType($Typedef_2{"Tid"}, 2);
3118        if($Base_1{"Name"} ne $Base_2{"Name"})
3119        {
3120            if($In::ABI{1}{"GccVersion"} ne $In::ABI{2}{"GccVersion"}
3121            or $In::Opt{"SkipTypedefUncover"})
3122            { # different GCC versions or different dumps
3123                $Base_1{"Name"} = uncoverTypedefs($Base_1{"Name"}, 1);
3124                $Base_2{"Name"} = uncoverTypedefs($Base_2{"Name"}, 2);
3125                # std::__va_list and __va_list
3126                $Base_1{"Name"}=~s/\A(\w+::)+//;
3127                $Base_2{"Name"}=~s/\A(\w+::)+//;
3128                $Base_1{"Name"} = formatName($Base_1{"Name"}, "T");
3129                $Base_2{"Name"} = formatName($Base_2{"Name"}, "T");
3130            }
3131        }
3132        if($Base_1{"Name"}!~/anon\-/ and $Base_2{"Name"}!~/anon\-/
3133        and $Base_1{"Name"} ne $Base_2{"Name"})
3134        {
3135            if($Level eq "Binary"
3136            and $Type1{"Size"} and $Type2{"Size"}
3137            and $Type1{"Size"} ne $Type2{"Size"})
3138            {
3139                %{$SubProblems{"DataType_Size"}{$Typedef_1{"Name"}}}=(
3140                    "Target"=>$Typedef_1{"Name"},
3141                    "Type_Name"=>$Typedef_1{"Name"},
3142                    "Old_Size"=>$Type1{"Size"}*$BYTE,
3143                    "New_Size"=>$Type2{"Size"}*$BYTE  );
3144            }
3145            my %Base1_Pure = getPureType($Base_1{"Tid"}, 1);
3146            my %Base2_Pure = getPureType($Base_2{"Tid"}, 2);
3147
3148            if(defined $UsedDump{1}{"DWARF"})
3149            {
3150                if($Base1_Pure{"Name"}=~/\b__unknown__\b/
3151                or $Base2_Pure{"Name"}=~/\b__unknown__\b/)
3152                { # Error ABI dump
3153                    return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = {});
3154                }
3155            }
3156
3157            if(tNameLock($Base_1{"Tid"}, $Base_2{"Tid"}))
3158            {
3159                if(diffTypes($Base1_Pure{"Tid"}, $Base2_Pure{"Tid"}, $Level))
3160                {
3161                    %{$SubProblems{"Typedef_BaseType_Format"}{$Typedef_1{"Name"}}}=(
3162                        "Target"=>$Typedef_1{"Name"},
3163                        "Type_Name"=>$Typedef_1{"Name"},
3164                        "Old_Value"=>$Base_1{"Name"},
3165                        "New_Value"=>$Base_2{"Name"}  );
3166                }
3167                else
3168                {
3169                    %{$SubProblems{"Typedef_BaseType"}{$Typedef_1{"Name"}}}=(
3170                        "Target"=>$Typedef_1{"Name"},
3171                        "Type_Name"=>$Typedef_1{"Name"},
3172                        "Old_Value"=>$Base_1{"Name"},
3173                        "New_Value"=>$Base_2{"Name"}  );
3174                }
3175            }
3176        }
3177    }
3178    if(nonComparable(\%Type1_Pure, \%Type2_Pure))
3179    { # different types (reported in detectTypeChange(...))
3180        my $TT1 = $Type1_Pure{"Type"};
3181        my $TT2 = $Type2_Pure{"Type"};
3182
3183        if($TT1 ne $TT2
3184        and $TT1!~/Intrinsic|Pointer|Ref|Typedef/)
3185        { # different type of the type
3186            my $Short1 = $Type1_Pure{"Name"};
3187            my $Short2 = $Type2_Pure{"Name"};
3188
3189            $Short1=~s/\A\Q$TT1\E //ig;
3190            $Short2=~s/\A\Q$TT2\E //ig;
3191
3192            if($Short1 eq $Short2)
3193            {
3194                %{$SubProblems{"DataType_Type"}{$Type1_Pure{"Name"}}}=(
3195                    "Target"=>$Type1_Pure{"Name"},
3196                    "Type_Name"=>$Type1_Pure{"Name"},
3197                    "Old_Value"=>lc($Type1_Pure{"Type"}),
3198                    "New_Value"=>lc($Type2_Pure{"Type"})  );
3199            }
3200        }
3201        return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems);
3202    }
3203
3204    pushType($Type1_Pure{"Tid"}, $Type2_Pure{"Tid"}, \@RecurTypes);
3205
3206    if(($Type1_Pure{"Name"} eq $Type2_Pure{"Name"}
3207    or (isAnon($Type1_Pure{"Name"}) and isAnon($Type2_Pure{"Name"})))
3208    and $Type1_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
3209    { # checking size
3210        if($Level eq "Binary"
3211        and $Type1_Pure{"Size"} and $Type2_Pure{"Size"}
3212        and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
3213        {
3214            my $ProblemKind = "DataType_Size";
3215            if($Type1_Pure{"Type"} eq "Class"
3216            and keys(%{$ClassMethods{$Level}{1}{$Type1_Pure{"Name"}}}))
3217            {
3218                if(isCopyingClass($Type1_Pure{"Tid"}, 1)) {
3219                    $ProblemKind = "Size_Of_Copying_Class";
3220                }
3221                elsif($AllocableClass{1}{$Type1_Pure{"Name"}})
3222                {
3223                    if(int($Type2_Pure{"Size"})>int($Type1_Pure{"Size"})) {
3224                        $ProblemKind = "Size_Of_Allocable_Class_Increased";
3225                    }
3226                    else
3227                    {
3228                        # descreased size of allocable class
3229                        # it has no special effects
3230                    }
3231                }
3232            }
3233            %{$SubProblems{$ProblemKind}{$Type1_Pure{"Name"}}}=(
3234                "Target"=>$Type1_Pure{"Name"},
3235                "Type_Name"=>$Type1_Pure{"Name"},
3236                "Old_Size"=>$Type1_Pure{"Size"}*$BYTE,
3237                "New_Size"=>$Type2_Pure{"Size"}*$BYTE);
3238        }
3239    }
3240    if(defined $Type1_Pure{"BaseType"}
3241    and defined $Type2_Pure{"BaseType"})
3242    { # checking base types
3243        my $Sub_SubProblems = mergeTypes($Type1_Pure{"BaseType"}, $Type2_Pure{"BaseType"}, $Level);
3244        foreach my $Sub_SubProblemType (keys(%{$Sub_SubProblems}))
3245        {
3246            foreach my $Sub_SubLocation (keys(%{$Sub_SubProblems->{$Sub_SubProblemType}})) {
3247                $SubProblems{$Sub_SubProblemType}{$Sub_SubLocation} = $Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation};
3248            }
3249        }
3250    }
3251    my (%AddedField, %RemovedField, %RenamedField, %RenamedField_Rev, %RelatedField, %RelatedField_Rev) = ();
3252    my %NameToPosA = map {$Type1_Pure{"Memb"}{$_}{"name"}=>$_} keys(%{$Type1_Pure{"Memb"}});
3253    my %NameToPosB = map {$Type2_Pure{"Memb"}{$_}{"name"}=>$_} keys(%{$Type2_Pure{"Memb"}});
3254    foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type1_Pure{"Memb"}}))
3255    { # detect removed and renamed fields
3256        my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"};
3257        next if(not $Member_Name);
3258        my $MemberPair_Pos = (defined $Type2_Pure{"Memb"}{$Member_Pos} and $Type2_Pure{"Memb"}{$Member_Pos}{"name"} eq $Member_Name)?$Member_Pos:findMemPairByName($Member_Name, \%Type2_Pure);
3259        if($MemberPair_Pos eq "lost")
3260        {
3261            if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
3262            {
3263                if(my $RenamedTo = isRenamed($Member_Pos, \%Type1_Pure, 1, \%Type2_Pure, 2))
3264                { # renamed
3265                    $RenamedField{$Member_Pos} = $RenamedTo;
3266                    $RenamedField_Rev{$NameToPosB{$RenamedTo}} = $Member_Name;
3267                }
3268                else
3269                { # removed
3270                    $RemovedField{$Member_Pos} = 1;
3271                }
3272            }
3273            elsif($Type1_Pure{"Type"} eq "Enum")
3274            {
3275                my $Member_Value1 = $Type1_Pure{"Memb"}{$Member_Pos}{"value"};
3276                next if($Member_Value1 eq "");
3277                $MemberPair_Pos = findMemPairByVal($Member_Value1, \%Type2_Pure);
3278                if($MemberPair_Pos ne "lost")
3279                { # renamed
3280                    my $RenamedTo = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"name"};
3281                    my $MemberPair_Pos_Rev = findMemPairByName($RenamedTo, \%Type1_Pure);
3282                    if($MemberPair_Pos_Rev eq "lost")
3283                    {
3284                        $RenamedField{$Member_Pos} = $RenamedTo;
3285                        $RenamedField_Rev{$NameToPosB{$RenamedTo}} = $Member_Name;
3286                    }
3287                    else {
3288                        $RemovedField{$Member_Pos} = 1;
3289                    }
3290                }
3291                else
3292                { # removed
3293                    $RemovedField{$Member_Pos} = 1;
3294                }
3295            }
3296        }
3297        else
3298        { # related
3299            $RelatedField{$Member_Pos} = $MemberPair_Pos;
3300            $RelatedField_Rev{$MemberPair_Pos} = $Member_Pos;
3301        }
3302    }
3303    foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type2_Pure{"Memb"}}))
3304    { # detect added fields
3305        my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"};
3306        next if(not $Member_Name);
3307        my $MemberPair_Pos = (defined $Type1_Pure{"Memb"}{$Member_Pos} and $Type1_Pure{"Memb"}{$Member_Pos}{"name"} eq $Member_Name)?$Member_Pos:findMemPairByName($Member_Name, \%Type1_Pure);
3308        if($MemberPair_Pos eq "lost")
3309        {
3310            if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union|Enum)\Z/)
3311            {
3312                if(not $RenamedField_Rev{$Member_Pos})
3313                { # added
3314                    $AddedField{$Member_Pos}=1;
3315                }
3316            }
3317        }
3318    }
3319    if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/)
3320    { # detect moved fields
3321        my (%RelPos, %RelPosName, %AbsPos) = ();
3322        my $Pos = 0;
3323        foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type1_Pure{"Memb"}}))
3324        { # relative positions in 1st version
3325            my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"};
3326            next if(not $Member_Name);
3327            if(not $RemovedField{$Member_Pos})
3328            { # old type without removed fields
3329                $RelPos{1}{$Member_Name} = $Pos;
3330                $RelPosName{1}{$Pos} = $Member_Name;
3331                $AbsPos{1}{$Pos++} = $Member_Pos;
3332            }
3333        }
3334        $Pos = 0;
3335        foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type2_Pure{"Memb"}}))
3336        { # relative positions in 2nd version
3337            my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"};
3338            next if(not $Member_Name);
3339            if(not $AddedField{$Member_Pos})
3340            { # new type without added fields
3341                $RelPos{2}{$Member_Name} = $Pos;
3342                $RelPosName{2}{$Pos} = $Member_Name;
3343                $AbsPos{2}{$Pos++} = $Member_Pos;
3344            }
3345        }
3346        foreach my $Member_Name (keys(%{$RelPos{1}}))
3347        {
3348            my $RPos1 = $RelPos{1}{$Member_Name};
3349            my $AbsPos1 = $NameToPosA{$Member_Name};
3350            my $Member_Name2 = $Member_Name;
3351            if(my $RenamedTo = $RenamedField{$AbsPos1})
3352            { # renamed
3353                $Member_Name2 = $RenamedTo;
3354            }
3355            my $RPos2 = $RelPos{2}{$Member_Name2};
3356            if($RPos2 ne "" and $RPos1 ne $RPos2)
3357            { # different relative positions
3358                my $AbsPos2 = $NameToPosB{$Member_Name2};
3359                if($AbsPos1 ne $AbsPos2)
3360                { # different absolute positions
3361                    my $ProblemType = "Moved_Field";
3362                    if(not isPublic(\%Type1_Pure, $AbsPos1))
3363                    { # may change layout and size of type
3364                        if($Level eq "Source") {
3365                            next;
3366                        }
3367                        $ProblemType = "Moved_Private_Field";
3368                    }
3369                    if($Level eq "Binary"
3370                    and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
3371                    { # affected size
3372                        my $MemSize1 = $TypeInfo{1}{$Type1_Pure{"Memb"}{$AbsPos1}{"type"}}{"Size"};
3373                        my $MovedAbsPos = $AbsPos{1}{$RPos2};
3374                        my $MemSize2 = $TypeInfo{1}{$Type1_Pure{"Memb"}{$MovedAbsPos}{"type"}}{"Size"};
3375                        if($MemSize1 ne $MemSize2) {
3376                            $ProblemType .= "_And_Size";
3377                        }
3378                    }
3379                    if($ProblemType eq "Moved_Private_Field") {
3380                        next;
3381                    }
3382                    %{$SubProblems{$ProblemType}{$Member_Name}}=(
3383                        "Target"=>$Member_Name,
3384                        "Type_Name"=>$Type1_Pure{"Name"},
3385                        "Old_Value"=>$RPos1,
3386                        "New_Value"=>$RPos2 );
3387                }
3388            }
3389        }
3390    }
3391    foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type1_Pure{"Memb"}}))
3392    { # check older fields, public and private
3393        my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"};
3394        next if(not $Member_Name);
3395        next if($Member_Name eq "_vptr");
3396        if(my $RenamedTo = $RenamedField{$Member_Pos})
3397        { # renamed
3398            if(defined $Constants{2}{$Member_Name})
3399            {
3400                if($Constants{2}{$Member_Name}{"Value"} eq $RenamedTo)
3401                { # define OLD NEW
3402                    next; # Safe
3403                }
3404            }
3405
3406            if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
3407            {
3408                if(isPublic(\%Type1_Pure, $Member_Pos))
3409                {
3410                    %{$SubProblems{"Renamed_Field"}{$Member_Name}}=(
3411                        "Target"=>$Member_Name,
3412                        "Type_Name"=>$Type1_Pure{"Name"},
3413                        "Old_Value"=>$Member_Name,
3414                        "New_Value"=>$RenamedTo  );
3415                }
3416                elsif(isReserved($Member_Name))
3417                {
3418                    %{$SubProblems{"Used_Reserved_Field"}{$Member_Name}}=(
3419                        "Target"=>$Member_Name,
3420                        "Type_Name"=>$Type1_Pure{"Name"},
3421                        "Old_Value"=>$Member_Name,
3422                        "New_Value"=>$RenamedTo  );
3423                }
3424            }
3425            elsif($Type1_Pure{"Type"} eq "Enum")
3426            {
3427                %{$SubProblems{"Enum_Member_Name"}{$Type1_Pure{"Memb"}{$Member_Pos}{"value"}}}=(
3428                    "Target"=>$Type1_Pure{"Memb"}{$Member_Pos}{"value"},
3429                    "Type_Name"=>$Type1_Pure{"Name"},
3430                    "Old_Value"=>$Member_Name,
3431                    "New_Value"=>$RenamedTo  );
3432            }
3433        }
3434        elsif($RemovedField{$Member_Pos})
3435        { # removed
3436            if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/)
3437            {
3438                my $ProblemType = "Removed_Field";
3439                if(not isPublic(\%Type1_Pure, $Member_Pos)
3440                or isUnnamed($Member_Name))
3441                {
3442                    if($Level eq "Source") {
3443                        next;
3444                    }
3445                    $ProblemType = "Removed_Private_Field";
3446                }
3447                if($Level eq "Binary"
3448                and not isMemPadded($Member_Pos, -1, \%Type1_Pure, \%RemovedField, 1))
3449                {
3450                    if(my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1))
3451                    { # affected fields
3452                        if(getOffset($MNum-1, \%Type1_Pure, 1)!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, 2))
3453                        { # changed offset
3454                            $ProblemType .= "_And_Layout";
3455                        }
3456                    }
3457                    if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
3458                    { # affected size
3459                        $ProblemType .= "_And_Size";
3460                    }
3461                }
3462                if($ProblemType eq "Removed_Private_Field") {
3463                    next;
3464                }
3465                %{$SubProblems{$ProblemType}{$Member_Name}}=(
3466                    "Target"=>$Member_Name,
3467                    "Type_Name"=>$Type1_Pure{"Name"}  );
3468            }
3469            elsif($Type2_Pure{"Type"} eq "Union")
3470            {
3471                if($Level eq "Binary"
3472                and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
3473                {
3474                    %{$SubProblems{"Removed_Union_Field_And_Size"}{$Member_Name}}=(
3475                        "Target"=>$Member_Name,
3476                        "Type_Name"=>$Type1_Pure{"Name"}  );
3477                }
3478                else
3479                {
3480                    %{$SubProblems{"Removed_Union_Field"}{$Member_Name}}=(
3481                        "Target"=>$Member_Name,
3482                        "Type_Name"=>$Type1_Pure{"Name"}  );
3483                }
3484            }
3485            elsif($Type1_Pure{"Type"} eq "Enum")
3486            {
3487                %{$SubProblems{"Enum_Member_Removed"}{$Member_Name}}=(
3488                    "Target"=>$Member_Name,
3489                    "Type_Name"=>$Type1_Pure{"Name"},
3490                    "Old_Value"=>$Member_Name  );
3491            }
3492        }
3493        else
3494        { # changed
3495            my $MemberPair_Pos = $RelatedField{$Member_Pos};
3496            if($Type1_Pure{"Type"} eq "Enum")
3497            {
3498                my $Member_Value1 = $Type1_Pure{"Memb"}{$Member_Pos}{"value"};
3499                next if($Member_Value1 eq "");
3500                my $Member_Value2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"value"};
3501                next if($Member_Value2 eq "");
3502                if($Member_Value1 ne $Member_Value2)
3503                {
3504                    my $ProblemType = "Enum_Member_Value";
3505                    if(isLastElem($Member_Pos, \%Type1_Pure)) {
3506                        $ProblemType = "Enum_Last_Member_Value";
3507                    }
3508                    if($In::Desc{1}{"SkipConstants"}{$Member_Name}) {
3509                        $ProblemType = "Enum_Private_Member_Value";
3510                    }
3511                    %{$SubProblems{$ProblemType}{$Member_Name}}=(
3512                        "Target"=>$Member_Name,
3513                        "Type_Name"=>$Type1_Pure{"Name"},
3514                        "Old_Value"=>$Member_Value1,
3515                        "New_Value"=>$Member_Value2  );
3516                }
3517            }
3518            elsif($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
3519            {
3520                my $Access1 = $Type1_Pure{"Memb"}{$Member_Pos}{"access"};
3521                my $Access2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"access"};
3522
3523                if($Access1 ne "private"
3524                and $Access2 eq "private")
3525                {
3526                    %{$SubProblems{"Field_Became_Private"}{$Member_Name}}=(
3527                        "Target"=>$Member_Name,
3528                        "Type_Name"=>$Type1_Pure{"Name"});
3529                }
3530                elsif($Access1 ne "protected"
3531                and $Access1 ne "private"
3532                and $Access2 eq "protected")
3533                {
3534                    %{$SubProblems{"Field_Became_Protected"}{$Member_Name}}=(
3535                        "Target"=>$Member_Name,
3536                        "Type_Name"=>$Type1_Pure{"Name"});
3537                }
3538
3539                my $MemberType1_Id = $Type1_Pure{"Memb"}{$Member_Pos}{"type"};
3540                my $MemberType2_Id = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"type"};
3541                my $SizeV1 = $TypeInfo{1}{$MemberType1_Id}{"Size"}*$BYTE;
3542                if(my $BSize1 = $Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"}) {
3543                    $SizeV1 = $BSize1;
3544                }
3545                my $SizeV2 = $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE;
3546                if(my $BSize2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"}) {
3547                    $SizeV2 = $BSize2;
3548                }
3549
3550                my $MemberType1_Name = $TypeInfo{1}{$MemberType1_Id}{"Name"};
3551                my $MemberType2_Name = $TypeInfo{2}{$MemberType2_Id}{"Name"};
3552
3553                if($Level eq "Binary"
3554                and $SizeV1 ne "" and $SizeV2 ne ""
3555                and $SizeV1 ne $SizeV2)
3556                {
3557                    if($MemberType1_Name eq $MemberType2_Name or (isAnon($MemberType1_Name) and isAnon($MemberType2_Name))
3558                    or ($Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"} and $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"}))
3559                    { # field size change (including anon-structures and unions)
3560                      # - same types
3561                      # - unnamed types
3562                      # - bitfields
3563                        my $ProblemType = "Field_Size";
3564                        if(not isPublic(\%Type1_Pure, $Member_Pos)
3565                        or isUnnamed($Member_Name))
3566                        { # should not be accessed by applications, goes to "Low Severity"
3567                          # example: "abidata" members in GStreamer types
3568                            $ProblemType = "Private_".$ProblemType;
3569                        }
3570                        if(not isMemPadded($Member_Pos, $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE, \%Type1_Pure, \%RemovedField, 1))
3571                        { # check an effect
3572                            if($Type2_Pure{"Type"} ne "Union"
3573                            and my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1))
3574                            { # public fields after the current
3575                                if(getOffset($MNum-1, \%Type1_Pure, 1)!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, 2))
3576                                { # changed offset
3577                                    $ProblemType .= "_And_Layout";
3578                                }
3579                            }
3580                            if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) {
3581                                $ProblemType .= "_And_Type_Size";
3582                            }
3583                        }
3584                        if($ProblemType eq "Private_Field_Size")
3585                        { # private field size with no effect
3586                        }
3587                        if($ProblemType eq "Field_Size")
3588                        {
3589                            if($Type1_Pure{"Type"}=~/Union|Struct/ and $SizeV1<$SizeV2)
3590                            { # Low severity
3591                                $ProblemType = "Struct_Field_Size_Increased";
3592                            }
3593                        }
3594                        if($ProblemType)
3595                        { # register a problem
3596                            %{$SubProblems{$ProblemType}{$Member_Name}}=(
3597                                "Target"=>$Member_Name,
3598                                "Type_Name"=>$Type1_Pure{"Name"},
3599                                "Old_Size"=>$SizeV1,
3600                                "New_Size"=>$SizeV2);
3601                        }
3602                    }
3603                }
3604                if($Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"}
3605                or $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"})
3606                { # do NOT check bitfield type changes
3607                    next;
3608                }
3609
3610                if(not $Type1_Pure{"Memb"}{$Member_Pos}{"mutable"}
3611                and $Type2_Pure{"Memb"}{$MemberPair_Pos}{"mutable"})
3612                {
3613                    %{$SubProblems{"Field_Became_Mutable"}{$Member_Name}}=(
3614                        "Target"=>$Member_Name,
3615                        "Type_Name"=>$Type1_Pure{"Name"});
3616                }
3617                elsif($Type1_Pure{"Memb"}{$Member_Pos}{"mutable"}
3618                and not $Type2_Pure{"Memb"}{$MemberPair_Pos}{"mutable"})
3619                {
3620                    %{$SubProblems{"Field_Became_Non_Mutable"}{$Member_Name}}=(
3621                        "Target"=>$Member_Name,
3622                        "Type_Name"=>$Type1_Pure{"Name"});
3623                }
3624
3625                my %Sub_SubChanges = detectTypeChange($MemberType1_Id, $MemberType2_Id, "Field", $Level);
3626                foreach my $ProblemType (keys(%Sub_SubChanges))
3627                {
3628                    my $Old_Value = $Sub_SubChanges{$ProblemType}{"Old_Value"};
3629                    my $New_Value = $Sub_SubChanges{$ProblemType}{"New_Value"};
3630
3631                    # quals
3632                    if($ProblemType eq "Field_Type"
3633                    or $ProblemType eq "Field_Type_And_Size"
3634                    or $ProblemType eq "Field_Type_Format")
3635                    {
3636                        if(addedQual($Old_Value, $New_Value, "volatile")) {
3637                            %{$Sub_SubChanges{"Field_Became_Volatile"}} = %{$Sub_SubChanges{$ProblemType}};
3638                        }
3639                        elsif(removedQual($Old_Value, $New_Value, "volatile")) {
3640                            %{$Sub_SubChanges{"Field_Became_Non_Volatile"}} = %{$Sub_SubChanges{$ProblemType}};
3641                        }
3642
3643                        if(my $RA = addedQual($Old_Value, $New_Value, "const"))
3644                        {
3645                            if($RA==2) {
3646                                %{$Sub_SubChanges{"Field_Added_Const"}} = %{$Sub_SubChanges{$ProblemType}};
3647                            }
3648                            else {
3649                                %{$Sub_SubChanges{"Field_Became_Const"}} = %{$Sub_SubChanges{$ProblemType}};
3650                            }
3651                        }
3652                        elsif(my $RR = removedQual($Old_Value, $New_Value, "const"))
3653                        {
3654                            if($RR==2) {
3655                                %{$Sub_SubChanges{"Field_Removed_Const"}} = %{$Sub_SubChanges{$ProblemType}};
3656                            }
3657                            else {
3658                                %{$Sub_SubChanges{"Field_Became_Non_Const"}} = %{$Sub_SubChanges{$ProblemType}};
3659                            }
3660                        }
3661                    }
3662                }
3663
3664                if($Level eq "Source")
3665                {
3666                    foreach my $ProblemType (keys(%Sub_SubChanges))
3667                    {
3668                        my $Old_Value = $Sub_SubChanges{$ProblemType}{"Old_Value"};
3669                        my $New_Value = $Sub_SubChanges{$ProblemType}{"New_Value"};
3670
3671                        if($ProblemType eq "Field_Type")
3672                        {
3673                            if(cmpBTypes($Old_Value, $New_Value, 1, 2)) {
3674                                delete($Sub_SubChanges{$ProblemType});
3675                            }
3676                        }
3677                    }
3678                }
3679
3680                foreach my $ProblemType (keys(%Sub_SubChanges))
3681                {
3682                    my $ProblemType_Init = $ProblemType;
3683                    if($ProblemType eq "Field_Type_And_Size")
3684                    { # Binary
3685                        if(not isPublic(\%Type1_Pure, $Member_Pos)
3686                        or isUnnamed($Member_Name)) {
3687                            $ProblemType = "Private_".$ProblemType;
3688                        }
3689                        if(not isMemPadded($Member_Pos, $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE, \%Type1_Pure, \%RemovedField, 1))
3690                        { # check an effect
3691                            if($Type2_Pure{"Type"} ne "Union"
3692                            and my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1))
3693                            { # public fields after the current
3694                                if(getOffset($MNum-1, \%Type1_Pure, 1)!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, 2))
3695                                { # changed offset
3696                                    $ProblemType .= "_And_Layout";
3697                                }
3698                            }
3699                            if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) {
3700                                $ProblemType .= "_And_Type_Size";
3701                            }
3702                        }
3703                    }
3704                    else
3705                    {
3706                        # TODO: Private_Field_Type rule?
3707
3708                        if(not isPublic(\%Type1_Pure, $Member_Pos)
3709                        or isUnnamed($Member_Name)) {
3710                            next;
3711                        }
3712                    }
3713                    if($ProblemType eq "Private_Field_Type_And_Size")
3714                    { # private field change with no effect
3715                    }
3716                    %{$SubProblems{$ProblemType}{$Member_Name}}=(
3717                        "Target"=>$Member_Name,
3718                        "Type_Name"=>$Type1_Pure{"Name"});
3719
3720                    foreach my $Attr (keys(%{$Sub_SubChanges{$ProblemType_Init}}))
3721                    { # other properties
3722                        $SubProblems{$ProblemType}{$Member_Name}{$Attr} = $Sub_SubChanges{$ProblemType_Init}{$Attr};
3723                    }
3724                }
3725                if(not isPublic(\%Type1_Pure, $Member_Pos))
3726                { # do NOT check internal type changes
3727                    next;
3728                }
3729                if($MemberType1_Id and $MemberType2_Id)
3730                { # checking member type changes
3731                    my $Sub_SubProblems = mergeTypes($MemberType1_Id, $MemberType2_Id, $Level);
3732
3733                    my %DupProblems = ();
3734
3735                    foreach my $Sub_SubProblemType (sort keys(%{$Sub_SubProblems}))
3736                    {
3737                        foreach my $Sub_SubLocation (sort {length($a)<=>length($b)} sort keys(%{$Sub_SubProblems->{$Sub_SubProblemType}}))
3738                        {
3739                            if(not defined $In::Opt{"AllAffected"})
3740                            {
3741                                if(defined $DupProblems{$Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}}) {
3742                                    next;
3743                                }
3744                            }
3745
3746                            my $NewLocation = ($Sub_SubLocation)?$Member_Name."->".$Sub_SubLocation:$Member_Name;
3747                            $SubProblems{$Sub_SubProblemType}{$NewLocation} = $Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation};
3748
3749                            if(not defined $In::Opt{"AllAffected"})
3750                            {
3751                                $DupProblems{$Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}} = 1;
3752                            }
3753                        }
3754                    }
3755
3756                    %DupProblems = ();
3757                }
3758            }
3759        }
3760    }
3761    foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type2_Pure{"Memb"}}))
3762    { # checking added members, public and private
3763        my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"};
3764        next if(not $Member_Name);
3765        next if($Member_Name eq "_vptr");
3766        if($AddedField{$Member_Pos})
3767        { # added
3768            if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/)
3769            {
3770                my $ProblemType = "Added_Field";
3771                if(not isPublic(\%Type2_Pure, $Member_Pos)
3772                or isUnnamed($Member_Name))
3773                {
3774                    if($Level eq "Source") {
3775                        next;
3776                    }
3777                    $ProblemType = "Added_Private_Field";
3778                }
3779                if($Level eq "Binary"
3780                and not isMemPadded($Member_Pos, -1, \%Type2_Pure, \%AddedField, 2))
3781                {
3782                    if(my $MNum = isAccessible(\%Type2_Pure, \%AddedField, $Member_Pos, -1))
3783                    { # public fields after the current
3784                        if(getOffset($MNum-1, \%Type2_Pure, 2)!=getOffset($RelatedField_Rev{$MNum-1}, \%Type1_Pure, 1))
3785                        { # changed offset
3786                            $ProblemType .= "_And_Layout";
3787                        }
3788                    }
3789                    if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) {
3790                        $ProblemType .= "_And_Size";
3791                    }
3792                }
3793                if($ProblemType eq "Added_Private_Field")
3794                { # skip added private fields
3795                    next;
3796                }
3797                %{$SubProblems{$ProblemType}{$Member_Name}}=(
3798                    "Target"=>$Member_Name,
3799                    "Type_Name"=>$Type1_Pure{"Name"});
3800            }
3801            elsif($Type2_Pure{"Type"} eq "Union")
3802            {
3803                if($Level eq "Binary"
3804                and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
3805                {
3806                    %{$SubProblems{"Added_Union_Field_And_Size"}{$Member_Name}}=(
3807                        "Target"=>$Member_Name,
3808                        "Type_Name"=>$Type1_Pure{"Name"});
3809                }
3810                else
3811                {
3812                    %{$SubProblems{"Added_Union_Field"}{$Member_Name}}=(
3813                        "Target"=>$Member_Name,
3814                        "Type_Name"=>$Type1_Pure{"Name"});
3815                }
3816            }
3817            elsif($Type2_Pure{"Type"} eq "Enum")
3818            {
3819                my $Member_Value = $Type2_Pure{"Memb"}{$Member_Pos}{"value"};
3820                next if($Member_Value eq "");
3821                %{$SubProblems{"Added_Enum_Member"}{$Member_Name}}=(
3822                    "Target"=>$Member_Name,
3823                    "Type_Name"=>$Type2_Pure{"Name"},
3824                    "New_Value"=>$Member_Value);
3825            }
3826        }
3827    }
3828
3829    if($Type1_Pure{"Type"} eq "FuncPtr")
3830    {
3831        foreach my $PPos (sort {$a<=>$b} keys(%{$Type1_Pure{"Param"}}))
3832        {
3833            if(not defined $Type2_Pure{"Param"}{$PPos}) {
3834                next;
3835            }
3836
3837            my $PT1 = $Type1_Pure{"Param"}{$PPos}{"type"};
3838            my $PT2 = $Type2_Pure{"Param"}{$PPos}{"type"};
3839
3840            my $PName = "p".$PPos;
3841
3842            my $FP_SubProblems = mergeTypes($PT1, $PT2, $Level);
3843            my %DupProblems = ();
3844
3845            foreach my $FP_SubProblemType (keys(%{$FP_SubProblems}))
3846            {
3847                foreach my $FP_SubLocation (keys(%{$FP_SubProblems->{$FP_SubProblemType}}))
3848                {
3849                    if(not defined $In::Opt{"AllAffected"})
3850                    {
3851                        if(defined $DupProblems{$FP_SubProblems->{$FP_SubProblemType}{$FP_SubLocation}}) {
3852                            next;
3853                        }
3854                    }
3855
3856                    my $NewLocation = ($FP_SubLocation)?$PName."->".$FP_SubLocation:$PName;
3857                    $SubProblems{$FP_SubProblemType}{$NewLocation} = $FP_SubProblems->{$FP_SubProblemType}{$FP_SubLocation};
3858
3859                    if(not defined $In::Opt{"AllAffected"})
3860                    {
3861                        $DupProblems{$FP_SubProblems->{$FP_SubProblemType}{$FP_SubLocation}} = 1;
3862                    }
3863                }
3864            }
3865
3866            %DupProblems = ();
3867        }
3868    }
3869
3870    pop(@RecurTypes);
3871    return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems);
3872}
3873
3874sub isUnnamed($) {
3875    return $_[0]=~/\Aunnamed\d+\Z/;
3876}
3877
3878sub detectAdded($)
3879{
3880    my $Level = $_[0];
3881    foreach my $Symbol (keys(%{$In::ABI{2}{"SymLib"}}))
3882    {
3883        if(linkSymbol($Symbol, 1, "+Deps"))
3884        { # linker can find a new symbol
3885          # in the old-version library
3886          # So, it's not a new symbol
3887            next;
3888        }
3889        if(my $VSym = $In::ABI{2}{"SymbolVersion"}{$Symbol}
3890        and index($Symbol,"\@")==-1) {
3891            next;
3892        }
3893        $AddedInt{$Level}{$Symbol} = 1;
3894    }
3895}
3896
3897sub detectRemoved($)
3898{
3899    my $Level = $_[0];
3900    foreach my $Symbol (keys(%{$In::ABI{1}{"SymLib"}}))
3901    {
3902        if(linkSymbol($Symbol, 2, "+Deps"))
3903        { # linker can find an old symbol
3904          # in the new-version library
3905            next;
3906        }
3907        if(my $VSym = $In::ABI{1}{"SymbolVersion"}{$Symbol}
3908        and index($Symbol,"\@")==-1) {
3909            next;
3910        }
3911        $RemovedInt{$Level}{$Symbol} = 1;
3912    }
3913}
3914
3915sub checkVtable($$$)
3916{
3917    my ($Level, $Symbol, $V) = @_;
3918
3919    # skip v-tables for templates, that should not be imported by applications
3920    if(my $CName = $VTableClass{$V}{$Symbol})
3921    {
3922        if(index($CName, "<")!=-1) {
3923            return 0;
3924        }
3925
3926        if(not keys(%{$ClassMethods{$Level}{$V}{$CName}}))
3927        { # do not show vtables for "private" classes
3928          # use case: vtable for QDragManager (Qt 4.5.3 to 4.6.0) became HIDDEN symbol
3929            return 0;
3930        }
3931    }
3932
3933    if($In::Desc{$V}{"SkipSymbols"}{$Symbol})
3934    { # user defined symbols to ignore
3935        return 0;
3936    }
3937
3938    return 1;
3939}
3940
3941sub mergeLibs($)
3942{
3943    my $Level = $_[0];
3944    foreach my $Symbol (sort keys(%{$AddedInt{$Level}}))
3945    { # checking added symbols
3946        next if(not $CompSign{2}{$Symbol}{"Header"} and not $CompSign{2}{$Symbol}{"Source"});
3947
3948        if(index($Symbol, "_ZTV")==0)
3949        {
3950            if(not checkVtable($Level, $Symbol, 2)) {
3951                next;
3952            }
3953        }
3954        else {
3955            next if(not symbolFilter($Symbol, $CompSign{2}{$Symbol}, "Affected + InlineVirt", $Level, 2));
3956        }
3957
3958        if($CompSign{2}{$Symbol}{"PureVirt"})
3959        { # symbols for pure virtual methods cannot be called by clients
3960            next;
3961        }
3962
3963        %{$CompatProblems{$Level}{$Symbol}{"Added_Symbol"}{""}} = ();
3964    }
3965    foreach my $Symbol (sort keys(%{$RemovedInt{$Level}}))
3966    { # checking removed symbols
3967        next if(not $CompSign{1}{$Symbol}{"Header"} and not $CompSign{1}{$Symbol}{"Source"});
3968
3969        if(index($Symbol, "_ZTV")==0)
3970        {
3971            if(not checkVtable($Level, $Symbol, 1)) {
3972                next;
3973            }
3974        }
3975        else {
3976            next if(not symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected + InlineVirt", $Level, 1));
3977        }
3978
3979        if($CompSign{1}{$Symbol}{"PureVirt"})
3980        { # symbols for pure virtual methods cannot be called by clients
3981            next;
3982        }
3983
3984        %{$CompatProblems{$Level}{$Symbol}{"Removed_Symbol"}{""}} = ();
3985    }
3986}
3987
3988sub detectAdded_H($)
3989{
3990    my $Level = $_[0];
3991    foreach my $Symbol (sort keys(%{$CompSign{2}}))
3992    {
3993        if($Level eq "Source")
3994        { # remove symbol version
3995            my ($SN, $SS, $SV) = symbolParts($Symbol);
3996            $Symbol=$SN;
3997
3998            if($CompSign{2}{$Symbol}{"Artificial"})
3999            { # skip artificial constructors
4000                next;
4001            }
4002        }
4003
4004        if(not $CompSign{2}{$Symbol}{"Header"}
4005        and not $CompSign{2}{$Symbol}{"Source"}) {
4006            next;
4007        }
4008
4009        if(not $CompSign{2}{$Symbol}{"MnglName"}) {
4010            next;
4011        }
4012
4013        if($ExtendedSymbols{$Symbol}) {
4014            next;
4015        }
4016
4017        if(not $CompSign{2}{$Symbol}{"Virt"}
4018        and not $CompSign{2}{$Symbol}{"PureVirt"}
4019        and $CompSign{2}{$Symbol}{"InLine"})
4020        { # sunch symbols are not properly detected
4021            next;
4022        }
4023
4024        if(not defined $CompSign{1}{$Symbol}
4025        or not $CompSign{1}{$Symbol}{"MnglName"})
4026        {
4027            if($UsedDump{2}{"SrcBin"})
4028            {
4029                if($UsedDump{1}{"BinOnly"})
4030                { # support for different ABI dumps
4031                    if(not $CompSign{2}{$Symbol}{"Virt"}
4032                    and not $CompSign{2}{$Symbol}{"PureVirt"})
4033                    {
4034                        if($In::Opt{"CheckHeadersOnly"})
4035                        {
4036                            if(my $Lang = $CompSign{2}{$Symbol}{"Lang"})
4037                            {
4038                                if($Lang eq "C")
4039                                { # support for old ABI dumps: missed extern "C" functions
4040                                    next;
4041                                }
4042                            }
4043                        }
4044                        else
4045                        {
4046                            if(not linkSymbol($Symbol, 2, "-Deps"))
4047                            { # skip added inline symbols and const global data
4048                                next;
4049                            }
4050                        }
4051                    }
4052                }
4053            }
4054            $AddedInt{$Level}{$Symbol} = 1;
4055        }
4056    }
4057}
4058
4059sub detectRemoved_H($)
4060{
4061    my $Level = $_[0];
4062    foreach my $Symbol (sort keys(%{$CompSign{1}}))
4063    {
4064        my $ISymbol = $Symbol;
4065
4066        if($Level eq "Source")
4067        { # remove symbol version
4068            my ($SN, $SS, $SV) = symbolParts($Symbol);
4069            $Symbol = $SN;
4070        }
4071
4072        if(not $CompSign{1}{$Symbol}{"Header"}
4073        and not $CompSign{1}{$Symbol}{"Source"}) {
4074            next;
4075        }
4076
4077        if(not $CompSign{1}{$Symbol}{"MnglName"}) {
4078            next;
4079        }
4080
4081        if($ExtendedSymbols{$Symbol}) {
4082            next;
4083        }
4084
4085        if(not $CompSign{1}{$Symbol}{"Virt"}
4086        and not $CompSign{1}{$Symbol}{"PureVirt"}
4087        and $CompSign{1}{$Symbol}{"InLine"})
4088        { # sunch symbols are not properly detected
4089            next;
4090        }
4091
4092        if(not defined $CompSign{2}{$Symbol}
4093        or not $CompSign{2}{$Symbol}{"MnglName"})
4094        {
4095            if(defined $UsedDump{1}{"DWARF"}
4096            and defined $UsedDump{2}{"DWARF"}
4097            and $Level eq "Source")
4098            { # not present in debug-info,
4099              # but present in dynsym
4100                if(linkSymbol($Symbol, 2, "-Deps")) {
4101                    next;
4102                }
4103
4104                if($ISymbol ne $Symbol)
4105                {
4106                    if(linkSymbol($ISymbol, 2, "-Deps")) {
4107                        next;
4108                    }
4109                }
4110
4111                if(my $SVer = $In::ABI{2}{"SymbolVersion"}{$Symbol})
4112                {
4113                    if(linkSymbol($SVer, 2, "-Deps")) {
4114                        next;
4115                    }
4116                }
4117
4118                if(my $Alias = $CompSign{1}{$ISymbol}{"Alias"})
4119                {
4120                    if(linkSymbol($Alias, 2, "-Deps")) {
4121                        next;
4122                    }
4123
4124                    if(my $SAVer = $In::ABI{2}{"SymbolVersion"}{$Alias})
4125                    {
4126                        if(linkSymbol($SAVer, 2, "-Deps")) {
4127                            next;
4128                        }
4129                    }
4130                }
4131            }
4132            if($UsedDump{1}{"SrcBin"})
4133            {
4134                if($UsedDump{2}{"BinOnly"})
4135                { # support for different ABI dumps
4136                    if(not $CompSign{1}{$Symbol}{"Virt"}
4137                    and not $CompSign{1}{$Symbol}{"PureVirt"})
4138                    {
4139                        if($In::Opt{"CheckHeadersOnly"})
4140                        { # skip all removed symbols
4141                            if(my $Lang = $CompSign{1}{$Symbol}{"Lang"})
4142                            {
4143                                if($Lang eq "C")
4144                                { # support for old ABI dumps: missed extern "C" functions
4145                                    next;
4146                                }
4147                            }
4148                        }
4149                        else
4150                        {
4151                            if(not linkSymbol($Symbol, 1, "-Deps"))
4152                            { # skip removed inline symbols
4153                                next;
4154                            }
4155                        }
4156                    }
4157                }
4158            }
4159
4160            if(not $CompSign{1}{$Symbol}{"Class"})
4161            {
4162                if(my $Short = $CompSign{1}{$Symbol}{"ShortName"})
4163                {
4164                    if(defined $Constants{2}{$Short})
4165                    {
4166                        my $Val = $Constants{2}{$Short}{"Value"};
4167                        if(defined $Func_ShortName{2}{$Val})
4168                        { # old name defined to new
4169                            next;
4170                        }
4171                    }
4172                }
4173
4174            }
4175            $RemovedInt{$Level}{$Symbol} = 1;
4176            if($Level eq "Source")
4177            { # search for a source-compatible equivalent
4178                setAlternative($Symbol, $Level);
4179            }
4180        }
4181    }
4182}
4183
4184sub mergeHeaders($)
4185{
4186    my $Level = $_[0];
4187    foreach my $Symbol (sort keys(%{$AddedInt{$Level}}))
4188    { # checking added symbols
4189        next if($CompSign{2}{$Symbol}{"PureVirt"});
4190        next if(not symbolFilter($Symbol, $CompSign{2}{$Symbol}, "Affected", $Level, 2));
4191        if($Level eq "Binary")
4192        {
4193            if($CompSign{2}{$Symbol}{"InLine"})
4194            {
4195                if(not $CompSign{2}{$Symbol}{"Virt"})
4196                { # skip inline non-virtual functions
4197                    next;
4198                }
4199            }
4200        }
4201        else
4202        { # Source
4203            if($SourceAlternative_B{$Symbol}) {
4204                next;
4205            }
4206        }
4207        %{$CompatProblems{$Level}{$Symbol}{"Added_Symbol"}{""}}=();
4208    }
4209    foreach my $Symbol (sort keys(%{$RemovedInt{$Level}}))
4210    { # checking removed symbols
4211        next if($CompSign{1}{$Symbol}{"PureVirt"});
4212        next if(not symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected", $Level, 1));
4213        if($Level eq "Binary")
4214        {
4215            if($CompSign{1}{$Symbol}{"InLine"})
4216            {
4217                if(not $CompSign{1}{$Symbol}{"Virt"})
4218                { # skip inline non-virtual functions
4219                    next;
4220                }
4221            }
4222        }
4223        else
4224        { # Source
4225            if(my $Alt = $SourceAlternative{$Symbol})
4226            {
4227                if(defined $CompSign{1}{$Alt}
4228                and $CompSign{1}{$Symbol}{"Const"})
4229                {
4230                    my $Cid = $CompSign{1}{$Symbol}{"Class"};
4231                    %{$CompatProblems{$Level}{$Symbol}{"Removed_Const_Overload"}{"this"}}=(
4232                        "Type_Name"=>$TypeInfo{1}{$Cid}{"Name"},
4233                        "Target"=>$Alt);
4234                }
4235                else
4236                { # do NOT show removed symbol
4237                    next;
4238                }
4239            }
4240        }
4241        %{$CompatProblems{$Level}{$Symbol}{"Removed_Symbol"}{""}}=();
4242    }
4243}
4244
4245sub addParamNames($)
4246{
4247    my $LibraryVersion = $_[0];
4248
4249    if(not keys(%AddSymbolParams)) {
4250        return;
4251    }
4252
4253    my $SecondVersion = $LibraryVersion==1?2:1;
4254    foreach my $Interface (sort keys(%{$CompSign{$LibraryVersion}}))
4255    {
4256        if(not keys(%{$AddSymbolParams{$Interface}})) {
4257            next;
4258        }
4259
4260        foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{$LibraryVersion}{$Interface}{"Param"}}))
4261        { # add absent parameter names
4262            my $ParamName = $CompSign{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"};
4263            if($ParamName=~/\Ap\d+\Z/ and my $NewParamName = $AddSymbolParams{$Interface}{$ParamPos})
4264            { # names from the external file
4265                if(defined $CompSign{$SecondVersion}{$Interface}
4266                and defined $CompSign{$SecondVersion}{$Interface}{"Param"}{$ParamPos})
4267                {
4268                    if($CompSign{$SecondVersion}{$Interface}{"Param"}{$ParamPos}{"name"}=~/\Ap\d+\Z/) {
4269                        $CompSign{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"} = $NewParamName;
4270                    }
4271                }
4272                else {
4273                    $CompSign{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"} = $NewParamName;
4274                }
4275            }
4276        }
4277    }
4278}
4279
4280sub detectChangedTypedefs()
4281{ # detect changed typedefs to show
4282  # correct function signatures
4283    foreach my $Typedef (keys(%{$In::ABI{1}{"TypedefBase"}}))
4284    {
4285        if(not $Typedef) {
4286            next;
4287        }
4288
4289        my $BName1 = $In::ABI{1}{"TypedefBase"}{$Typedef};
4290        if(not $BName1 or isAnon($BName1)) {
4291            next;
4292        }
4293
4294        my $BName2 = $In::ABI{2}{"TypedefBase"}{$Typedef};
4295        if(not $BName2 or isAnon($BName2)) {
4296            next;
4297        }
4298
4299        $BName1 = uncoverTypedefs($BName1, 1);
4300        $BName2 = uncoverTypedefs($BName2, 2);
4301
4302        if($BName1 ne $BName2) {
4303            $ChangedTypedef{$Typedef} = 1;
4304        }
4305    }
4306}
4307
4308sub symbolPrefix($$)
4309{
4310    my ($Symbol, $LVer) = @_;
4311    my $ShortName = $CompSign{$LVer}{$Symbol}{"ShortName"};
4312    if(my $ClassId = $CompSign{$LVer}{$Symbol}{"Class"})
4313    { # methods
4314        $ShortName = $TypeInfo{$LVer}{$ClassId}{"Name"}."::".$ShortName;
4315    }
4316    return $ShortName;
4317}
4318
4319sub setAlternative($)
4320{
4321    my $Symbol = $_[0];
4322    my $PSymbol = $Symbol;
4323    if(not defined $CompSign{2}{$PSymbol}
4324    or (not $CompSign{2}{$PSymbol}{"MnglName"}
4325    and not $CompSign{2}{$PSymbol}{"ShortName"}))
4326    { # search for a pair
4327        if(my $ShortName = $CompSign{1}{$PSymbol}{"ShortName"})
4328        {
4329            if($CompSign{1}{$PSymbol}{"Data"})
4330            {
4331                if($PSymbol=~s/L(\d+$ShortName(E)\Z)/$1/
4332                or $PSymbol=~s/(\d+$ShortName(E)\Z)/L$1/)
4333                {
4334                    if(defined $CompSign{2}{$PSymbol}
4335                    and $CompSign{2}{$PSymbol}{"MnglName"})
4336                    {
4337                        $SourceAlternative{$Symbol} = $PSymbol;
4338                        $SourceAlternative_B{$PSymbol} = $Symbol;
4339                        if(not defined $CompSign{1}{$PSymbol}
4340                        or not $CompSign{1}{$PSymbol}{"MnglName"}) {
4341                            $SourceReplacement{$Symbol} = $PSymbol;
4342                        }
4343                    }
4344                }
4345            }
4346            else
4347            {
4348                foreach my $Sp ("KV", "VK", "K", "V")
4349                {
4350                    if($PSymbol=~s/\A_ZN$Sp/_ZN/
4351                    or $PSymbol=~s/\A_ZN/_ZN$Sp/)
4352                    {
4353                        if(defined $CompSign{2}{$PSymbol}
4354                        and $CompSign{2}{$PSymbol}{"MnglName"})
4355                        {
4356                            $SourceAlternative{$Symbol} = $PSymbol;
4357                            $SourceAlternative_B{$PSymbol} = $Symbol;
4358                            if(not defined $CompSign{1}{$PSymbol}
4359                            or not $CompSign{1}{$PSymbol}{"MnglName"}) {
4360                                $SourceReplacement{$Symbol} = $PSymbol;
4361                            }
4362                        }
4363                    }
4364                    $PSymbol = $Symbol;
4365                }
4366            }
4367        }
4368    }
4369    return "";
4370}
4371
4372sub getSymKind($$)
4373{
4374    my ($Symbol, $LVer) = @_;
4375    if($CompSign{$LVer}{$Symbol}{"Data"})
4376    {
4377        return "Global_Data";
4378    }
4379    elsif($CompSign{$LVer}{$Symbol}{"Class"})
4380    {
4381        return "Method";
4382    }
4383    return "Function";
4384}
4385
4386sub isConstData($$)
4387{
4388    my ($Symbol, $LVer) = @_;
4389
4390    my $Return = $CompSign{$LVer}{$Symbol}{"Return"};
4391    my $RTName = uncoverTypedefs($TypeInfo{$LVer}{$Return}{"Name"}, $LVer);
4392
4393    return ($RTName=~/\bconst\Z/);
4394}
4395
4396sub getConstDataSym($$)
4397{
4398    my ($Symbol, $LVer) = @_;
4399
4400    my $Short = $CompSign{$LVer}{$Symbol}{"ShortName"};
4401    $Symbol=~s/(\d+$Short)/L$1/;
4402    return $Symbol;
4403}
4404
4405sub getNonConstDataSym($$)
4406{
4407    my ($Symbol, $LVer) = @_;
4408
4409    my $Short = $CompSign{$LVer}{$Symbol}{"ShortName"};
4410    $Symbol=~s/L(\d+$Short)/$1/;
4411    return $Symbol;
4412}
4413
4414sub mergeSymbols($)
4415{
4416    my $Level = $_[0];
4417    my %SubProblems = ();
4418
4419    mergeBases($Level);
4420
4421    my %AddedOverloads = ();
4422    foreach my $Symbol (sort keys(%{$AddedInt{$Level}}))
4423    { # check all added exported symbols
4424        if(not $CompSign{2}{$Symbol}{"Header"}
4425        and not $CompSign{2}{$Symbol}{"Source"}) {
4426            next;
4427        }
4428        if(defined $CompSign{1}{$Symbol}
4429        and ($CompSign{1}{$Symbol}{"Header"} or $CompSign{1}{$Symbol}{"Source"}))
4430        { # double-check added symbol
4431            next;
4432        }
4433        if($Symbol=~/\A(_Z|\?)/)
4434        { # C++
4435            $AddedOverloads{symbolPrefix($Symbol, 2)}{getSignature($Symbol, 2, "Qual")} = $Symbol;
4436        }
4437        if(my $OverriddenMethod = $CompSign{2}{$Symbol}{"Override"})
4438        { # register virtual overridings
4439            my $Cid = $CompSign{2}{$Symbol}{"Class"};
4440            my $AffectedClass_Name = $TypeInfo{2}{$Cid}{"Name"};
4441            if(defined $CompSign{1}{$OverriddenMethod} and $CompSign{1}{$OverriddenMethod}{"Virt"}
4442            and not $CompSign{1}{$OverriddenMethod}{"Private"})
4443            {
4444                if($TName_Tid{1}{$AffectedClass_Name})
4445                { # class should exist in previous version
4446                    if(not isCopyingClass($TName_Tid{1}{$AffectedClass_Name}, 1))
4447                    { # old v-table is NOT copied by old applications
4448                        if(symbolFilter($OverriddenMethod, $CompSign{1}{$OverriddenMethod}, "Affected", $Level, 1))
4449                        {
4450                            %{$CompatProblems{$Level}{$OverriddenMethod}{"Overridden_Virtual_Method"}{$Symbol}}=(
4451                                "Type_Name"=>$AffectedClass_Name,
4452                                "Target"=>$Symbol,
4453                                "Old_Value"=>$OverriddenMethod,
4454                                "New_Value"=>$Symbol);
4455                        }
4456                    }
4457                }
4458            }
4459        }
4460    }
4461    foreach my $Symbol (sort keys(%{$RemovedInt{$Level}}))
4462    { # check all removed exported symbols
4463        if(not $CompSign{1}{$Symbol}{"Header"}
4464        and not $CompSign{1}{$Symbol}{"Source"}) {
4465            next;
4466        }
4467        if(defined $CompSign{2}{$Symbol}
4468        and ($CompSign{2}{$Symbol}{"Header"} or $CompSign{2}{$Symbol}{"Source"}))
4469        { # double-check removed symbol
4470            next;
4471        }
4472        if(not symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected", $Level, 1)) {
4473            next;
4474        }
4475
4476        $CheckedSymbols{$Level}{$Symbol} = 1;
4477
4478        if(my $OverriddenMethod = $CompSign{1}{$Symbol}{"Override"})
4479        { # register virtual overridings
4480            my $Cid = $CompSign{1}{$Symbol}{"Class"};
4481            my $AffectedClass_Name = $TypeInfo{1}{$Cid}{"Name"};
4482            if(defined $CompSign{2}{$OverriddenMethod}
4483            and $CompSign{2}{$OverriddenMethod}{"Virt"})
4484            {
4485                if($TName_Tid{2}{$AffectedClass_Name})
4486                { # class should exist in newer version
4487                    if(not isCopyingClass($CompSign{1}{$Symbol}{"Class"}, 1))
4488                    { # old v-table is NOT copied by old applications
4489                        %{$CompatProblems{$Level}{$Symbol}{"Overridden_Virtual_Method_B"}{$OverriddenMethod}}=(
4490                            "Type_Name"=>$AffectedClass_Name,
4491                            "Target"=>$OverriddenMethod,
4492                            "Old_Value"=>$Symbol,
4493                            "New_Value"=>$OverriddenMethod);
4494                    }
4495                }
4496            }
4497        }
4498
4499        if($Level eq "Binary"
4500        and $In::Opt{"Target"} eq "windows")
4501        { # register the reason of symbol name change
4502            if(defined $CompSign{1}{$Symbol}{"Unmangled"}
4503            and my $NewSym = getMangled_MSVC($CompSign{1}{$Symbol}{"Unmangled"}, 2))
4504            {
4505                if($AddedInt{$Level}{$NewSym})
4506                {
4507                    if($CompSign{1}{$Symbol}{"Static"} ne $CompSign{2}{$NewSym}{"Static"})
4508                    {
4509                        if($CompSign{2}{$NewSym}{"Static"})
4510                        {
4511                            %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Static"}{$Symbol}}=(
4512                                "Target"=>$Symbol,
4513                                "Old_Value"=>$Symbol,
4514                                "New_Value"=>$NewSym  );
4515                        }
4516                        else
4517                        {
4518                            %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Non_Static"}{$Symbol}}=(
4519                                "Target"=>$Symbol,
4520                                "Old_Value"=>$Symbol,
4521                                "New_Value"=>$NewSym  );
4522                        }
4523                    }
4524                    if($CompSign{1}{$Symbol}{"Virt"} ne $CompSign{2}{$NewSym}{"Virt"})
4525                    {
4526                        if($CompSign{2}{$NewSym}{"Virt"})
4527                        {
4528                            %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Virtual"}{$Symbol}}=(
4529                                "Target"=>$Symbol,
4530                                "Old_Value"=>$Symbol,
4531                                "New_Value"=>$NewSym  );
4532                        }
4533                        else
4534                        {
4535                            %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Non_Virtual"}{$Symbol}}=(
4536                                "Target"=>$Symbol,
4537                                "Old_Value"=>$Symbol,
4538                                "New_Value"=>$NewSym  );
4539                        }
4540                    }
4541                    my $RTId1 = $CompSign{1}{$Symbol}{"Return"};
4542                    my $RTId2 = $CompSign{2}{$NewSym}{"Return"};
4543                    my $RTName1 = $TypeInfo{1}{$RTId1}{"Name"};
4544                    my $RTName2 = $TypeInfo{2}{$RTId2}{"Name"};
4545                    if($RTName1 ne $RTName2)
4546                    {
4547                        my $ProblemType = "Symbol_Changed_Return";
4548                        if($CompSign{1}{$Symbol}{"Data"}) {
4549                            $ProblemType = "Global_Data_Symbol_Changed_Type";
4550                        }
4551                        %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{$Symbol}}=(
4552                            "Target"=>$Symbol,
4553                            "Old_Type"=>$RTName1,
4554                            "New_Type"=>$RTName2,
4555                            "Old_Value"=>$Symbol,
4556                            "New_Value"=>$NewSym  );
4557                    }
4558                }
4559            }
4560        }
4561
4562        if($Symbol=~/\A(_Z|\?)/)
4563        { # C++
4564            my $Prefix = symbolPrefix($Symbol, 1);
4565            if(my @Overloads = sort keys(%{$AddedOverloads{$Prefix}})
4566            and not $AddedOverloads{$Prefix}{getSignature($Symbol, 1, "Qual")})
4567            { # changed signature: params, "const"-qualifier
4568                my $NewSym = $AddedOverloads{$Prefix}{$Overloads[0]};
4569                if($CompSign{1}{$Symbol}{"Constructor"})
4570                {
4571                    if($Symbol=~/(C[1-2][EI])/)
4572                    {
4573                        my $CtorType = $1;
4574                        $NewSym=~s/(C[1-2][EI])/$CtorType/g;
4575                    }
4576                }
4577                elsif($CompSign{1}{$Symbol}{"Destructor"})
4578                {
4579                    if($Symbol=~/(D[0-2][EI])/)
4580                    {
4581                        my $DtorType = $1;
4582                        $NewSym=~s/(D[0-2][EI])/$DtorType/g;
4583                    }
4584                }
4585                my $NS1 = $CompSign{1}{$Symbol}{"NameSpace"};
4586                my $NS2 = $CompSign{2}{$NewSym}{"NameSpace"};
4587                if((not $NS1 and not $NS2) or ($NS1 and $NS2 and $NS1 eq $NS2))
4588                { # from the same class and namespace
4589                    if($CompSign{1}{$Symbol}{"Const"}
4590                    and not $CompSign{2}{$NewSym}{"Const"})
4591                    { # "const" to non-"const"
4592                        %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Const"}{$Symbol}}=(
4593                            "Type_Name"=>$TypeInfo{1}{$CompSign{1}{$Symbol}{"Class"}}{"Name"},
4594                            "Target"=>$Symbol,
4595                            "New_Signature"=>$NewSym,
4596                            "Old_Value"=>$Symbol,
4597                            "New_Value"=>$NewSym  );
4598                    }
4599                    elsif(not $CompSign{1}{$Symbol}{"Const"}
4600                    and $CompSign{2}{$NewSym}{"Const"})
4601                    { # non-"const" to "const"
4602                        %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Const"}{$Symbol}}=(
4603                            "Target"=>$Symbol,
4604                            "New_Signature"=>$NewSym,
4605                            "Old_Value"=>$Symbol,
4606                            "New_Value"=>$NewSym  );
4607                    }
4608                    if($CompSign{1}{$Symbol}{"Volatile"}
4609                    and not $CompSign{2}{$NewSym}{"Volatile"})
4610                    { # "volatile" to non-"volatile"
4611
4612                        %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Volatile"}{$Symbol}}=(
4613                            "Target"=>$Symbol,
4614                            "New_Signature"=>$NewSym,
4615                            "Old_Value"=>$Symbol,
4616                            "New_Value"=>$NewSym  );
4617                    }
4618                    elsif(not $CompSign{1}{$Symbol}{"Volatile"}
4619                    and $CompSign{2}{$NewSym}{"Volatile"})
4620                    { # non-"volatile" to "volatile"
4621                        %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Volatile"}{$Symbol}}=(
4622                            "Target"=>$Symbol,
4623                            "New_Signature"=>$NewSym,
4624                            "Old_Value"=>$Symbol,
4625                            "New_Value"=>$NewSym  );
4626                    }
4627                    if(getSignature($Symbol, 1, "Param") ne getSignature($NewSym, 2, "Param"))
4628                    { # params list
4629                        %{$CompatProblems{$Level}{$Symbol}{"Symbol_Changed_Parameters"}{$Symbol}}=(
4630                            "Target"=>$Symbol,
4631                            "New_Signature"=>$NewSym,
4632                            "Old_Value"=>$Symbol,
4633                            "New_Value"=>$NewSym  );
4634                    }
4635                }
4636            }
4637        }
4638    }
4639
4640    foreach my $Symbol (sort keys(%{$CompSign{1}}))
4641    { # checking symbols
4642        my ($SN, $SS, $SV) = symbolParts($Symbol);
4643        if($Level eq "Source")
4644        { # remove symbol version
4645            $Symbol = $SN;
4646        }
4647        else
4648        { # Binary
4649            if(not $SV)
4650            { # symbol without version
4651                if(my $VSym = $In::ABI{1}{"SymbolVersion"}{$Symbol})
4652                { # the symbol is linked with versioned symbol
4653                    if($CompSign{2}{$VSym}{"MnglName"})
4654                    { # show report for symbol@ver only
4655                        next;
4656                    }
4657                    elsif(not linkSymbol($VSym, 2, "-Deps"))
4658                    { # changed version: sym@v1 to sym@v2
4659                      # do NOT show report for symbol
4660                        next;
4661                    }
4662                }
4663            }
4664        }
4665        my $PSymbol = $Symbol;
4666        if($Level eq "Source"
4667        and my $S = $SourceReplacement{$Symbol})
4668        { # take a source-compatible replacement function
4669            $PSymbol = $S;
4670        }
4671        if($CompSign{1}{$Symbol}{"Data"}
4672        and not defined $CompSign{2}{$Symbol})
4673        {
4674            if(isConstData($Symbol, 1))
4675            {
4676                if(my $NonConstSymbol = getNonConstDataSym($Symbol, 1))
4677                {
4678                    if($CompSign{2}{$NonConstSymbol}) {
4679                        $PSymbol = $NonConstSymbol;
4680                    }
4681                }
4682            }
4683            else
4684            {
4685                if(my $ConstSymbol = getConstDataSym($Symbol, 1))
4686                {
4687                    if($CompSign{2}{$ConstSymbol}) {
4688                        $PSymbol = $ConstSymbol;
4689                    }
4690                }
4691            }
4692        }
4693
4694        if($CompSign{1}{$Symbol}{"Private"})
4695        { # private symbols
4696            next;
4697        }
4698        if(not defined $CompSign{1}{$Symbol}
4699        or not defined $CompSign{2}{$PSymbol})
4700        { # no info
4701            next;
4702        }
4703        if(not $CompSign{1}{$Symbol}{"MnglName"}
4704        or not $CompSign{2}{$PSymbol}{"MnglName"})
4705        { # no mangled name
4706            next;
4707        }
4708        if((not $CompSign{1}{$Symbol}{"Header"} and not $CompSign{1}{$Symbol}{"Source"})
4709        or (not $CompSign{2}{$PSymbol}{"Header"} and not $CompSign{2}{$PSymbol}{"Source"}))
4710        { # without a header or source
4711            next;
4712        }
4713
4714        if(not $CompSign{1}{$Symbol}{"PureVirt"}
4715        and $CompSign{2}{$PSymbol}{"PureVirt"})
4716        { # became pure
4717            next;
4718        }
4719        if($CompSign{1}{$Symbol}{"PureVirt"}
4720        and not $CompSign{2}{$PSymbol}{"PureVirt"})
4721        { # became non-pure
4722            next;
4723        }
4724
4725        if(not symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected + InlineVirt", $Level, 1))
4726        { # exported, target, inline virtual and pure virtual
4727            next;
4728        }
4729
4730        if($CompSign{1}{$Symbol}{"Data"}
4731        and $CompSign{2}{$PSymbol}{"Data"})
4732        {
4733            my $Value1 = $CompSign{1}{$Symbol}{"Value"};
4734            my $Value2 = $CompSign{2}{$PSymbol}{"Value"};
4735            if(defined $Value1)
4736            {
4737                $Value1 = showVal($Value1, $CompSign{1}{$Symbol}{"Return"}, 1);
4738                if(defined $Value2)
4739                {
4740                    $Value2 = showVal($Value2, $CompSign{2}{$PSymbol}{"Return"}, 2);
4741                    if($Value1 ne $Value2)
4742                    {
4743                        %{$CompatProblems{$Level}{$Symbol}{"Global_Data_Value_Changed"}{""}}=(
4744                            "Old_Value"=>$Value1,
4745                            "New_Value"=>$Value2,
4746                            "Target"=>$Symbol  );
4747                    }
4748                }
4749            }
4750        }
4751
4752        if($CompSign{2}{$PSymbol}{"Private"})
4753        {
4754            %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Private"}{""}}=(
4755                "Target"=>$PSymbol  );
4756        }
4757        elsif(not $CompSign{1}{$Symbol}{"Protected"}
4758        and $CompSign{2}{$PSymbol}{"Protected"})
4759        {
4760            %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Protected"}{""}}=(
4761                "Target"=>$PSymbol  );
4762        }
4763        elsif($CompSign{1}{$Symbol}{"Protected"}
4764        and not $CompSign{2}{$PSymbol}{"Protected"})
4765        {
4766            %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Public"}{""}}=(
4767                "Target"=>$PSymbol  );
4768        }
4769
4770        # checking virtual table
4771        mergeVirtualTables($Symbol, $Level);
4772
4773        if($In::Opt{"CompileError"})
4774        { # if some errors occurred at the compiling stage
4775          # then some false positives can be skipped here
4776            if(not $CompSign{1}{$Symbol}{"Data"} and $CompSign{2}{$PSymbol}{"Data"}
4777            and not $GlobalDataObject{2}{$Symbol})
4778            { # missed information about parameters in newer version
4779                next;
4780            }
4781            if($CompSign{1}{$Symbol}{"Data"} and not $GlobalDataObject{1}{$Symbol}
4782            and not $CompSign{2}{$PSymbol}{"Data"})
4783            { # missed information about parameters in older version
4784                next;
4785            }
4786        }
4787        my ($MnglName, $VersionSpec, $SymbolVersion) = symbolParts($Symbol);
4788
4789        # check attributes
4790        if($CompSign{2}{$PSymbol}{"Static"}
4791        and not $CompSign{1}{$Symbol}{"Static"} and $Symbol=~/\A(_Z|\?)/)
4792        {
4793            %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Static"}{""}}=(
4794                "Target"=>$Symbol
4795            );
4796        }
4797        elsif(not $CompSign{2}{$PSymbol}{"Static"}
4798        and $CompSign{1}{$Symbol}{"Static"} and $Symbol=~/\A(_Z|\?)/)
4799        {
4800            %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Static"}{""}}=(
4801                "Target"=>$Symbol
4802            );
4803        }
4804        if(($CompSign{1}{$Symbol}{"Virt"} and $CompSign{2}{$PSymbol}{"Virt"})
4805        or ($CompSign{1}{$Symbol}{"PureVirt"} and $CompSign{2}{$PSymbol}{"PureVirt"}))
4806        { # relative position of virtual and pure virtual methods
4807            if($Level eq "Binary")
4808            {
4809                if(defined $CompSign{1}{$Symbol}{"RelPos"} and defined $CompSign{2}{$PSymbol}{"RelPos"}
4810                and $CompSign{1}{$Symbol}{"RelPos"}!=$CompSign{2}{$PSymbol}{"RelPos"})
4811                { # top-level virtual methods only
4812                    my $Class_Id = $CompSign{1}{$Symbol}{"Class"};
4813                    my $Class_Name = $TypeInfo{1}{$Class_Id}{"Name"};
4814                    if(defined $VirtualTable{1}{$Class_Name} and defined $VirtualTable{2}{$Class_Name}
4815                    and $VirtualTable{1}{$Class_Name}{$Symbol}!=$VirtualTable{2}{$Class_Name}{$Symbol})
4816                    { # check absolute position of a virtual method (including added and removed methods)
4817                        my %Class_Type = getType($Class_Id, 1);
4818                        my $ProblemType = "Virtual_Method_Position";
4819                        if($CompSign{1}{$Symbol}{"PureVirt"}) {
4820                            $ProblemType = "Pure_Virtual_Method_Position";
4821                        }
4822                        if(isUsedClass($Class_Id, 1, $Level))
4823                        {
4824                            my @Affected = ($Symbol, keys(%{$OverriddenMethods{1}{$Symbol}}));
4825                            foreach my $ASymbol (@Affected)
4826                            {
4827                                if(not symbolFilter($ASymbol, $CompSign{1}{$ASymbol}, "Affected", $Level, 1)) {
4828                                    next;
4829                                }
4830                                %{$CompatProblems{$Level}{$ASymbol}{$ProblemType}{$Symbol}}=(
4831                                    "Type_Name"=>$Class_Type{"Name"},
4832                                    "Old_Value"=>$CompSign{1}{$Symbol}{"RelPos"},
4833                                    "New_Value"=>$CompSign{2}{$PSymbol}{"RelPos"},
4834                                    "Target"=>$Symbol);
4835                            }
4836                            $VTableChanged_M{$Class_Type{"Name"}} = 1;
4837                        }
4838                    }
4839                }
4840            }
4841        }
4842        if($CompSign{1}{$Symbol}{"PureVirt"}
4843        or $CompSign{2}{$PSymbol}{"PureVirt"})
4844        { # do NOT check type changes in pure virtuals
4845            next;
4846        }
4847
4848        $CheckedSymbols{$Level}{$Symbol} = 1;
4849
4850        if($Symbol=~/\A(_Z|\?)/
4851        or keys(%{$CompSign{1}{$Symbol}{"Param"}})==keys(%{$CompSign{2}{$PSymbol}{"Param"}}))
4852        { # C/C++: changes in parameters
4853            foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{1}{$Symbol}{"Param"}}))
4854            { # checking parameters
4855                mergeParameters($Symbol, $PSymbol, $ParamPos, $ParamPos, $Level, 1);
4856            }
4857        }
4858        else
4859        { # C: added/removed parameters
4860            foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{2}{$PSymbol}{"Param"}}))
4861            { # checking added parameters
4862                my $PType2_Id = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos}{"type"};
4863                my $PType2_Name = $TypeInfo{2}{$PType2_Id}{"Name"};
4864                last if($PType2_Name eq "...");
4865                my $PName = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos}{"name"};
4866                my $PName_Old = (defined $CompSign{1}{$Symbol}{"Param"}{$ParamPos})?$CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"name"}:"";
4867                my $ParamPos_Prev = "-1";
4868                if($PName=~/\Ap\d+\Z/i)
4869                { # added unnamed parameter ( pN )
4870                    my @Positions1 = findParamPairByTypeAndPos($PType2_Name, $ParamPos, "backward", $Symbol, 1);
4871                    my @Positions2 = findParamPairByTypeAndPos($PType2_Name, $ParamPos, "backward", $Symbol, 2);
4872                    if($#Positions1==-1 or $#Positions2>$#Positions1) {
4873                        $ParamPos_Prev = "lost";
4874                    }
4875                }
4876                else {
4877                    $ParamPos_Prev = findParamPairByName($PName, $Symbol, 1);
4878                }
4879                if($ParamPos_Prev eq "lost")
4880                {
4881                    if($ParamPos>keys(%{$CompSign{1}{$Symbol}{"Param"}})-1)
4882                    {
4883                        my $ProblemType = "Added_Parameter";
4884                        if($PName=~/\Ap\d+\Z/) {
4885                            $ProblemType = "Added_Unnamed_Parameter";
4886                        }
4887                        %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=(
4888                            "Target"=>$PName,
4889                            "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
4890                            "Param_Type"=>$PType2_Name,
4891                            "New_Signature"=>$Symbol  );
4892                    }
4893                    else
4894                    {
4895                        my %ParamType_Pure = getPureType($PType2_Id, 2);
4896                        my $PairType_Id = $CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"type"};
4897                        my %PairType_Pure = getPureType($PairType_Id, 1);
4898                        if(($ParamType_Pure{"Name"} eq $PairType_Pure{"Name"} or $PType2_Name eq $TypeInfo{1}{$PairType_Id}{"Name"})
4899                        and findParamPairByName($PName_Old, $Symbol, 2) eq "lost")
4900                        {
4901                            if($PName_Old!~/\Ap\d+\Z/ and $PName!~/\Ap\d+\Z/)
4902                            {
4903                                %{$CompatProblems{$Level}{$Symbol}{"Renamed_Parameter"}{showPos($ParamPos)." Parameter"}}=(
4904                                    "Target"=>$PName_Old,
4905                                    "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
4906                                    "Param_Type"=>$PType2_Name,
4907                                    "Old_Value"=>$PName_Old,
4908                                    "New_Value"=>$PName,
4909                                    "New_Signature"=>$Symbol  );
4910                            }
4911                        }
4912                        else
4913                        {
4914                            my $ProblemType = "Added_Middle_Parameter";
4915                            if($PName=~/\Ap\d+\Z/) {
4916                                $ProblemType = "Added_Middle_Unnamed_Parameter";
4917                            }
4918                            %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=(
4919                                "Target"=>$PName,
4920                                "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
4921                                "Param_Type"=>$PType2_Name,
4922                                "New_Signature"=>$Symbol  );
4923                        }
4924                    }
4925                }
4926            }
4927            foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{1}{$Symbol}{"Param"}}))
4928            { # check relevant parameters
4929                my $PType1_Id = $CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"type"};
4930                my $ParamName1 = $CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"name"};
4931                # FIXME: find relevant parameter by name
4932                if(defined $CompSign{2}{$PSymbol}{"Param"}{$ParamPos})
4933                {
4934                    my $PType2_Id = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos}{"type"};
4935                    my $ParamName2 = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos}{"name"};
4936                    if($TypeInfo{1}{$PType1_Id}{"Name"} eq $TypeInfo{2}{$PType2_Id}{"Name"}
4937                    or ($ParamName1!~/\Ap\d+\Z/i and $ParamName1 eq $ParamName2)) {
4938                        mergeParameters($Symbol, $PSymbol, $ParamPos, $ParamPos, $Level, 0);
4939                    }
4940                }
4941            }
4942            foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{1}{$Symbol}{"Param"}}))
4943            { # checking removed parameters
4944                my $PType1_Id = $CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"type"};
4945                my $PType1_Name = $TypeInfo{1}{$PType1_Id}{"Name"};
4946                last if($PType1_Name eq "...");
4947                my $PName = $CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"name"};
4948                my $PName_New = (defined $CompSign{2}{$PSymbol}{"Param"}{$ParamPos})?$CompSign{2}{$PSymbol}{"Param"}{$ParamPos}{"name"}:"";
4949                my $ParamPos_New = "-1";
4950                if($PName=~/\Ap\d+\Z/i)
4951                { # removed unnamed parameter ( pN )
4952                    my @Positions1 = findParamPairByTypeAndPos($PType1_Name, $ParamPos, "forward", $Symbol, 1);
4953                    my @Positions2 = findParamPairByTypeAndPos($PType1_Name, $ParamPos, "forward", $Symbol, 2);
4954                    if($#Positions2==-1 or $#Positions2<$#Positions1) {
4955                        $ParamPos_New = "lost";
4956                    }
4957                }
4958                else {
4959                    $ParamPos_New = findParamPairByName($PName, $Symbol, 2);
4960                }
4961                if($ParamPos_New eq "lost")
4962                {
4963                    if($ParamPos>keys(%{$CompSign{2}{$PSymbol}{"Param"}})-1)
4964                    {
4965                        my $ProblemType = "Removed_Parameter";
4966                        if($PName=~/\Ap\d+\Z/) {
4967                            $ProblemType = "Removed_Unnamed_Parameter";
4968                        }
4969                        %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=(
4970                            "Target"=>$PName,
4971                            "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
4972                            "Param_Type"=>$PType1_Name,
4973                            "New_Signature"=>$Symbol);
4974                    }
4975                    elsif($ParamPos<keys(%{$CompSign{1}{$Symbol}{"Param"}})-1)
4976                    {
4977                        my %ParamType_Pure = getPureType($PType1_Id, 1);
4978                        my $PairType_Id = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos}{"type"};
4979                        my %PairType_Pure = getPureType($PairType_Id, 2);
4980                        if(($ParamType_Pure{"Name"} eq $PairType_Pure{"Name"} or $PType1_Name eq $TypeInfo{2}{$PairType_Id}{"Name"})
4981                        and findParamPairByName($PName_New, $Symbol, 1) eq "lost")
4982                        {
4983                            if($PName_New!~/\Ap\d+\Z/ and $PName!~/\Ap\d+\Z/)
4984                            {
4985                                %{$CompatProblems{$Level}{$Symbol}{"Renamed_Parameter"}{showPos($ParamPos)." Parameter"}}=(
4986                                    "Target"=>$PName,
4987                                    "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
4988                                    "Param_Type"=>$PType1_Name,
4989                                    "Old_Value"=>$PName,
4990                                    "New_Value"=>$PName_New,
4991                                    "New_Signature"=>$Symbol);
4992                            }
4993                        }
4994                        else
4995                        {
4996                            my $ProblemType = "Removed_Middle_Parameter";
4997                            if($PName=~/\Ap\d+\Z/) {
4998                                $ProblemType = "Removed_Middle_Unnamed_Parameter";
4999                            }
5000                            %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=(
5001                                "Target"=>$PName,
5002                                "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
5003                                "Param_Type"=>$PType1_Name,
5004                                "New_Signature"=>$Symbol);
5005                        }
5006                    }
5007                }
5008            }
5009        }
5010        # checking return type
5011        my $ReturnType1_Id = $CompSign{1}{$Symbol}{"Return"};
5012        my $ReturnType2_Id = $CompSign{2}{$PSymbol}{"Return"};
5013        my %RC_SubProblems = detectTypeChange($ReturnType1_Id, $ReturnType2_Id, "Return", $Level);
5014
5015        foreach my $SubProblemType (keys(%RC_SubProblems))
5016        {
5017            my $New_Value = $RC_SubProblems{$SubProblemType}{"New_Value"};
5018            my $Old_Value = $RC_SubProblems{$SubProblemType}{"Old_Value"};
5019            my %ProblemTypes = ();
5020
5021            if($CompSign{1}{$Symbol}{"Data"})
5022            {
5023                if($SubProblemType eq "Return_Type_And_Size") {
5024                    $ProblemTypes{"Global_Data_Type_And_Size"} = 1;
5025                }
5026                elsif($SubProblemType eq "Return_Type_Format") {
5027                    $ProblemTypes{"Global_Data_Type_Format"} = 1;
5028                }
5029                else {
5030                    $ProblemTypes{"Global_Data_Type"} = 1;
5031                }
5032
5033                # quals
5034                if($SubProblemType eq "Return_Type"
5035                or $SubProblemType eq "Return_Type_And_Size"
5036                or $SubProblemType eq "Return_Type_Format")
5037                {
5038                    if(my $RR = removedQual($Old_Value, $New_Value, "const"))
5039                    { # const to non-const
5040                        if($RR==2) {
5041                            $ProblemTypes{"Global_Data_Removed_Const"} = 1;
5042                        }
5043                        else {
5044                            $ProblemTypes{"Global_Data_Became_Non_Const"} = 1;
5045                        }
5046                        $ProblemTypes{"Global_Data_Type"} = 1;
5047                    }
5048                    elsif(my $RA = addedQual($Old_Value, $New_Value, "const"))
5049                    { # non-const to const
5050                        if($RA==2) {
5051                            $ProblemTypes{"Global_Data_Added_Const"} = 1;
5052                        }
5053                        else {
5054                            $ProblemTypes{"Global_Data_Became_Const"} = 1;
5055                        }
5056                        $ProblemTypes{"Global_Data_Type"} = 1;
5057                    }
5058                }
5059            }
5060            else
5061            {
5062                # quals
5063                if($SubProblemType eq "Return_Type"
5064                or $SubProblemType eq "Return_Type_And_Size"
5065                or $SubProblemType eq "Return_Type_Format")
5066                {
5067                    if(addedQual($Old_Value, $New_Value, "volatile"))
5068                    {
5069                        $ProblemTypes{"Return_Value_Became_Volatile"} = 1;
5070                        if($Level ne "Source"
5071                        or not cmpBTypes($Old_Value, $New_Value, 1, 2)) {
5072                            $ProblemTypes{"Return_Type"} = 1;
5073                        }
5074                    }
5075
5076                    if(my $RA = addedQual($Old_Value, $New_Value, "const"))
5077                    {
5078                        if($RA==2) {
5079                            $ProblemTypes{"Return_Type_Added_Const"} = 1;
5080                        }
5081                        else {
5082                            $ProblemTypes{"Return_Type_Became_Const"} = 1;
5083                        }
5084                        if($Level ne "Source"
5085                        or not cmpBTypes($Old_Value, $New_Value, 1, 2)) {
5086                            $ProblemTypes{"Return_Type"} = 1;
5087                        }
5088                    }
5089                }
5090            }
5091            if($Level eq "Binary"
5092            and not $CompSign{1}{$Symbol}{"Data"})
5093            {
5094                my (%Conv1, %Conv2) = ();
5095
5096                if($UseConv_Real{1}{"R"} and $UseConv_Real{2}{"R"})
5097                {
5098                    %Conv1 = callingConvention_R_Real($CompSign{1}{$Symbol});
5099                    %Conv2 = callingConvention_R_Real($CompSign{2}{$PSymbol});
5100                }
5101                else
5102                {
5103                    %Conv1 = callingConvention_R_Model($CompSign{1}{$Symbol}, 1);
5104                    %Conv2 = callingConvention_R_Model($CompSign{2}{$PSymbol}, 2);
5105                }
5106
5107                if($SubProblemType eq "Return_Type_Became_Void")
5108                {
5109                    if(keys(%{$CompSign{1}{$Symbol}{"Param"}}))
5110                    { # parameters stack has been affected
5111                        if($Conv1{"Method"} eq "stack") {
5112                            $ProblemTypes{"Return_Type_Became_Void_And_Stack_Layout"} = 1;
5113                        }
5114                        elsif($Conv1{"Hidden"}) {
5115                            $ProblemTypes{"Return_Type_Became_Void_And_Register"} = 1;
5116                        }
5117                    }
5118                }
5119                elsif($SubProblemType eq "Return_Type_From_Void")
5120                {
5121                    if(keys(%{$CompSign{1}{$Symbol}{"Param"}}))
5122                    { # parameters stack has been affected
5123                        if($Conv2{"Method"} eq "stack") {
5124                            $ProblemTypes{"Return_Type_From_Void_And_Stack_Layout"} = 1;
5125                        }
5126                        elsif($Conv2{"Hidden"}) {
5127                            $ProblemTypes{"Return_Type_From_Void_And_Register"} = 1;
5128                        }
5129                    }
5130                }
5131                elsif($SubProblemType eq "Return_Type"
5132                or $SubProblemType eq "Return_Type_And_Size"
5133                or $SubProblemType eq "Return_Type_Format")
5134                {
5135                    if($Conv1{"Method"} ne $Conv2{"Method"})
5136                    {
5137                        if($Conv1{"Method"} eq "stack")
5138                        { # returns in a register instead of a hidden first parameter
5139                            $ProblemTypes{"Return_Type_From_Stack_To_Register"} = 1;
5140                        }
5141                        else {
5142                            $ProblemTypes{"Return_Type_From_Register_To_Stack"} = 1;
5143                        }
5144                    }
5145                    else
5146                    {
5147                        if($Conv1{"Method"} eq "reg")
5148                        {
5149                            if($Conv1{"Registers"} ne $Conv2{"Registers"})
5150                            {
5151                                if($Conv1{"Hidden"}) {
5152                                    $ProblemTypes{"Return_Type_And_Register_Was_Hidden_Parameter"} = 1;
5153                                }
5154                                elsif($Conv2{"Hidden"}) {
5155                                    $ProblemTypes{"Return_Type_And_Register_Became_Hidden_Parameter"} = 1;
5156                                }
5157                                else {
5158                                    $ProblemTypes{"Return_Type_And_Register"} = 1;
5159                                }
5160                            }
5161                        }
5162                    }
5163                }
5164            }
5165
5166            if(not keys(%ProblemTypes))
5167            { # default
5168                $ProblemTypes{$SubProblemType} = 1;
5169            }
5170
5171            foreach my $ProblemType (keys(%ProblemTypes))
5172            { # additional
5173                $CompatProblems{$Level}{$Symbol}{$ProblemType}{"retval"} = $RC_SubProblems{$SubProblemType};
5174            }
5175        }
5176        if($ReturnType1_Id and $ReturnType2_Id)
5177        {
5178            @RecurTypes = ();
5179            my $Sub_SubProblems = mergeTypes($ReturnType1_Id, $ReturnType2_Id, $Level);
5180
5181            my $AddProblems = {};
5182
5183            if($CompSign{1}{$Symbol}{"Data"})
5184            {
5185                if($Level eq "Binary")
5186                {
5187                    if(getPLevel($ReturnType1_Id, 1)==0)
5188                    {
5189                        if(defined $Sub_SubProblems->{"DataType_Size"})
5190                        { # add "Global_Data_Size" problem
5191
5192                            foreach my $Loc (keys(%{$Sub_SubProblems->{"DataType_Size"}}))
5193                            {
5194                                if(index($Loc,"->")==-1)
5195                                {
5196                                    if($Loc eq $Sub_SubProblems->{"DataType_Size"}{$Loc}{"Type_Name"})
5197                                    {
5198                                        $AddProblems->{"Global_Data_Size"}{$Loc} = $Sub_SubProblems->{"DataType_Size"}{$Loc}; # add a new problem
5199                                        last;
5200                                    }
5201                                }
5202                            }
5203                        }
5204                    }
5205                    if(not defined $AddProblems->{"Global_Data_Size"})
5206                    {
5207                        if(defined $GlobalDataObject{1}{$Symbol}
5208                        and defined $GlobalDataObject{2}{$Symbol})
5209                        {
5210                            my $Old_Size = $GlobalDataObject{1}{$Symbol};
5211                            my $New_Size = $GlobalDataObject{2}{$Symbol};
5212                            if($Old_Size!=$New_Size)
5213                            {
5214                                $AddProblems->{"Global_Data_Size"}{"retval"} = {
5215                                    "Old_Size"=>$Old_Size*$BYTE,
5216                                    "New_Size"=>$New_Size*$BYTE };
5217                            }
5218                        }
5219                    }
5220                }
5221            }
5222
5223            foreach my $SubProblemType (keys(%{$AddProblems}))
5224            {
5225                foreach my $SubLocation (keys(%{$AddProblems->{$SubProblemType}}))
5226                {
5227                    my $NewLocation = "retval";
5228                    if($SubLocation and $SubLocation ne "retval") {
5229                        $NewLocation = "retval->".$SubLocation;
5230                    }
5231                    $CompatProblems{$Level}{$Symbol}{$SubProblemType}{$NewLocation} = $AddProblems->{$SubProblemType}{$SubLocation};
5232                }
5233            }
5234
5235            foreach my $SubProblemType (keys(%{$Sub_SubProblems}))
5236            {
5237                foreach my $SubLocation (keys(%{$Sub_SubProblems->{$SubProblemType}}))
5238                {
5239                    my $NewLocation = "retval";
5240                    if($SubLocation and $SubLocation ne "retval") {
5241                        $NewLocation = "retval->".$SubLocation;
5242                    }
5243                    $CompatProblems{$Level}{$Symbol}{$SubProblemType}{$NewLocation} = $Sub_SubProblems->{$SubProblemType}{$SubLocation};
5244                }
5245            }
5246        }
5247
5248        # checking object type
5249        my $ObjTId1 = $CompSign{1}{$Symbol}{"Class"};
5250        my $ObjTId2 = $CompSign{2}{$PSymbol}{"Class"};
5251        if($ObjTId1 and $ObjTId2
5252        and not $CompSign{1}{$Symbol}{"Static"}
5253        and not $CompSign{1}{$Symbol}{"Data"})
5254        {
5255            my ($ThisPtr1, $ThisPtr2) = (undef, undef);
5256            if($CompSign{1}{$Symbol}{"Const"})
5257            {
5258                $ThisPtr1 = getTypeIdByName($TypeInfo{1}{$ObjTId1}{"Name"}." const*const", 1);
5259                $ThisPtr2 = getTypeIdByName($TypeInfo{2}{$ObjTId2}{"Name"}." const*const", 2);
5260            }
5261            else
5262            {
5263                $ThisPtr1 = getTypeIdByName($TypeInfo{1}{$ObjTId1}{"Name"}."*const", 1);
5264                $ThisPtr2 = getTypeIdByName($TypeInfo{2}{$ObjTId2}{"Name"}."*const", 2);
5265            }
5266
5267            if($ThisPtr1 and $ThisPtr2)
5268            {
5269                @RecurTypes = ();
5270                my $Sub_SubProblems = mergeTypes($ThisPtr1, $ThisPtr2, $Level);
5271                foreach my $SubProblemType (keys(%{$Sub_SubProblems}))
5272                {
5273                    foreach my $SubLocation (keys(%{$Sub_SubProblems->{$SubProblemType}}))
5274                    {
5275                        my $NewLocation = ($SubLocation)?"this->".$SubLocation:"this";
5276                        $CompatProblems{$Level}{$Symbol}{$SubProblemType}{$NewLocation} = $Sub_SubProblems->{$SubProblemType}{$SubLocation};
5277                    }
5278                }
5279            }
5280        }
5281    }
5282
5283    if($Level eq "Binary") {
5284        mergeVTables($Level);
5285    }
5286
5287    # mark all affected symbols as "checked"
5288    foreach my $Symbol (keys(%{$CompatProblems{$Level}})) {
5289        $CheckedSymbols{$Level}{$Symbol} = 1;
5290    }
5291}
5292
5293sub rmQuals($$)
5294{
5295    my ($Value, $Qual) = @_;
5296    if(not $Qual) {
5297        return $Value;
5298    }
5299    if($Qual eq "all")
5300    { # all quals
5301        $Qual = "const|volatile|restrict";
5302    }
5303    while($Value=~s/\b$Qual\b//) {
5304        $Value = formatName($Value, "T");
5305    }
5306    return $Value;
5307}
5308
5309sub cmpBTypes($$$$)
5310{
5311    my ($T1, $T2, $V1, $V2) = @_;
5312    $T1 = uncoverTypedefs($T1, $V1);
5313    $T2 = uncoverTypedefs($T2, $V2);
5314    return (rmQuals($T1, "all") eq rmQuals($T2, "all"));
5315}
5316
5317sub addedQual($$$)
5318{
5319    my ($Old_Value, $New_Value, $Qual) = @_;
5320    return removedQual_I($New_Value, $Old_Value, 2, 1, $Qual);
5321}
5322
5323sub removedQual($$$)
5324{
5325    my ($Old_Value, $New_Value, $Qual) = @_;
5326    return removedQual_I($Old_Value, $New_Value, 1, 2, $Qual);
5327}
5328
5329sub removedQual_I($$$$$)
5330{
5331    my ($Old_Value, $New_Value, $V1, $V2, $Qual) = @_;
5332    $Old_Value = uncoverTypedefs($Old_Value, $V1);
5333    $New_Value = uncoverTypedefs($New_Value, $V2);
5334
5335    if($Old_Value eq $New_Value)
5336    { # equal types
5337        return 0;
5338    }
5339    if($Old_Value!~/\b$Qual\b/)
5340    { # without a qual
5341        return 0;
5342    }
5343    elsif($New_Value!~/\b$Qual\b/)
5344    { # became non-qual
5345        return 1;
5346    }
5347    else
5348    {
5349        my @BQ1 = getQualModel($Old_Value, $Qual);
5350        my @BQ2 = getQualModel($New_Value, $Qual);
5351        foreach (0 .. $#BQ1)
5352        { # removed qual
5353            if($BQ1[$_]==1
5354            and $BQ2[$_]!=1)
5355            {
5356                return 2;
5357            }
5358        }
5359    }
5360    return 0;
5361}
5362
5363sub getQualModel($$)
5364{
5365    my ($Value, $Qual) = @_;
5366    if(not $Qual) {
5367        return $Value;
5368    }
5369
5370    # cleaning
5371    while($Value=~/(\w+)/)
5372    {
5373        my $W = $1;
5374
5375        if($W eq $Qual) {
5376            $Value=~s/\b$W\b/\@/g;
5377        }
5378        else {
5379            $Value=~s/\b$W\b//g;
5380        }
5381    }
5382
5383    $Value=~s/\@/$Qual/g;
5384    $Value=~s/[^\*\&\w]+//g;
5385
5386    # modeling
5387    # int*const*const == 011
5388    # int**const == 001
5389    my @Model = ();
5390    my @Elems = split(/[\*\&]/, $Value);
5391    if(not @Elems) {
5392        return (0);
5393    }
5394    foreach (@Elems)
5395    {
5396        if($_ eq $Qual) {
5397            push(@Model, 1);
5398        }
5399        else {
5400            push(@Model, 0);
5401        }
5402    }
5403
5404    return @Model;
5405}
5406
5407my %StringTypes = map {$_=>1} (
5408    "char*",
5409    "char const*"
5410);
5411
5412my %CharTypes = map {$_=>1} (
5413    "char",
5414    "char const"
5415);
5416
5417sub showVal($$$)
5418{
5419    my ($Value, $TypeId, $LVer) = @_;
5420    my %PureType = getPureType($TypeId, $LVer);
5421    my $TName = uncoverTypedefs($PureType{"Name"}, $LVer);
5422
5423    if(defined $StringTypes{$TName} or $TName=~/string/i)
5424    { # strings
5425        return "\"$Value\"";
5426    }
5427    elsif(defined $CharTypes{$TName})
5428    { # characters
5429        return "\'$Value\'";
5430    }
5431    if($Value eq "")
5432    { # other
5433        return "\'\'";
5434    }
5435
5436    return $Value;
5437}
5438
5439sub getRegs($$$)
5440{
5441    my ($LVer, $Symbol, $Pos) = @_;
5442
5443    if(defined $CompSign{$LVer}{$Symbol}{"Reg"})
5444    {
5445        my %Regs = ();
5446        foreach my $Elem (sort keys(%{$CompSign{$LVer}{$Symbol}{"Reg"}}))
5447        {
5448            if(index($Elem, $Pos)==0
5449            and $Elem=~/\A$Pos([\.\+]|\Z)/) {
5450                $Regs{$CompSign{$LVer}{$Symbol}{"Reg"}{$Elem}} = 1;
5451            }
5452        }
5453
5454        return join(", ", sort keys(%Regs));
5455    }
5456    elsif(defined $CompSign{$LVer}{$Symbol}{"Param"}
5457    and defined $CompSign{$LVer}{$Symbol}{"Param"}{0}
5458    and not defined $CompSign{$LVer}{$Symbol}{"Param"}{0}{"offset"})
5459    {
5460        return "unknown";
5461    }
5462
5463    return undef;
5464}
5465
5466sub mergeParameters($$$$$$)
5467{
5468    my ($Symbol, $PSymbol, $ParamPos1, $ParamPos2, $Level, $CheckRenamed) = @_;
5469
5470    my $PTid1 = $CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"type"};
5471    my $PTid2 = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"type"};
5472
5473    if(not $PTid1
5474    or not $PTid2) {
5475        return;
5476    }
5477
5478    my $PName1 = $CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"name"};
5479    my $PName2 = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"name"};
5480
5481    if(index($Symbol, "_Z")==0
5482    or index($Symbol, "?")==0)
5483    { # do not merge "this"
5484        if($PName1 eq "this" or $PName2 eq "this") {
5485            return;
5486        }
5487    }
5488
5489    my %Type1 = getType($PTid1, 1);
5490    my %Type2 = getType($PTid2, 2);
5491
5492    my %PureType1 = getPureType($PTid1, 1);
5493
5494    my %BaseType1 = getBaseType($PTid1, 1);
5495    my %BaseType2 = getBaseType($PTid2, 2);
5496
5497    my $ParamLoc = ($PName1)?$PName1:showPos($ParamPos1)." Parameter";
5498
5499    if($Level eq "Binary")
5500    {
5501        if($CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"reg"}
5502        and not $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"reg"})
5503        {
5504            %{$CompatProblems{$Level}{$Symbol}{"Parameter_Became_Non_Register"}{$ParamLoc}}=(
5505                "Target"=>$PName1,
5506                "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1)  );
5507        }
5508        elsif(not $CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"reg"}
5509        and $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"reg"})
5510        {
5511            %{$CompatProblems{$Level}{$Symbol}{"Parameter_Became_Register"}{$ParamLoc}}=(
5512                "Target"=>$PName1,
5513                "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1)  );
5514        }
5515
5516        if(defined $UsedDump{1}{"DWARF"}
5517        and defined $UsedDump{2}{"DWARF"})
5518        {
5519            my $Old_Regs = getRegs(1, $Symbol, $ParamPos1);
5520            my $New_Regs = getRegs(2, $PSymbol, $ParamPos2);
5521
5522            my $Old_Offset = $CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"offset"};
5523            my $New_Offset = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"offset"};
5524
5525            if($Old_Regs ne "unknown"
5526            and $New_Regs ne "unknown")
5527            {
5528                if($Old_Regs and $New_Regs)
5529                {
5530                    if($Old_Regs ne $New_Regs)
5531                    {
5532                        %{$CompatProblems{$Level}{$Symbol}{"Parameter_Changed_Register"}{$ParamLoc}}=(
5533                            "Target"=>$PName1,
5534                            "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
5535                            "Old_Value"=>$Old_Regs,
5536                            "New_Value"=>$New_Regs  );
5537                    }
5538                }
5539                elsif($Old_Regs and not $New_Regs and $New_Offset ne "")
5540                {
5541                    %{$CompatProblems{$Level}{$Symbol}{"Parameter_From_Register"}{$ParamLoc}}=(
5542                        "Target"=>$PName1,
5543                        "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
5544                        "Old_Value"=>$Old_Regs  );
5545                }
5546                elsif(not $Old_Regs and $Old_Offset ne "" and $New_Regs)
5547                {
5548                    %{$CompatProblems{$Level}{$Symbol}{"Parameter_To_Register"}{$ParamLoc}}=(
5549                        "Target"=>$PName1,
5550                        "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
5551                        "New_Value"=>$New_Regs  );
5552                }
5553            }
5554
5555            if($Old_Offset ne ""
5556            and $New_Offset ne "")
5557            {
5558                if($Old_Offset ne $New_Offset)
5559                {
5560                    my $Start1 = $CompSign{1}{$Symbol}{"Param"}{0}{"offset"};
5561                    my $Start2 = $CompSign{2}{$PSymbol}{"Param"}{0}{"offset"};
5562
5563                    $Old_Offset = $Old_Offset - $Start1;
5564                    $New_Offset = $New_Offset - $Start2;
5565
5566                    if($Old_Offset ne $New_Offset)
5567                    {
5568                        %{$CompatProblems{$Level}{$Symbol}{"Parameter_Changed_Offset"}{$ParamLoc}}=(
5569                            "Target"=>$PName1,
5570                            "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
5571                            "Old_Value"=>$Old_Offset,
5572                            "New_Value"=>$New_Offset  );
5573                    }
5574                }
5575            }
5576        }
5577    }
5578
5579    my $Value_Old = $CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"default"};
5580    my $Value_New = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"default"};
5581
5582    if(defined $Value_Old)
5583    {
5584        $Value_Old = showVal($Value_Old, $PTid1, 1);
5585        if(defined $Value_New)
5586        {
5587            $Value_New = showVal($Value_New, $PTid2, 2);
5588            if($Value_Old ne $Value_New)
5589            { # FIXME: how to distinguish "0" and 0 (NULL)
5590                %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Changed"}{$ParamLoc}}=(
5591                    "Target"=>$PName1,
5592                    "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
5593                    "Old_Value"=>$Value_Old,
5594                    "New_Value"=>$Value_New  );
5595            }
5596        }
5597        else
5598        {
5599            %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Removed"}{$ParamLoc}}=(
5600                "Target"=>$PName1,
5601                "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
5602                "Old_Value"=>$Value_Old  );
5603        }
5604    }
5605    elsif(defined $Value_New)
5606    {
5607        $Value_New = showVal($Value_New, $PTid2, 2);
5608        %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Added"}{$ParamLoc}}=(
5609            "Target"=>$PName1,
5610            "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
5611            "New_Value"=>$Value_New  );
5612    }
5613
5614    if($CheckRenamed)
5615    {
5616        if($PName1 and $PName2 and $PName1 ne $PName2
5617        and $PTid1!=-1 and $PTid2!=-1
5618        and $PName1!~/\Ap\d+\Z/ and $PName2!~/\Ap\d+\Z/)
5619        { # except unnamed "..." value list (Id=-1)
5620            %{$CompatProblems{$Level}{$Symbol}{"Renamed_Parameter"}{showPos($ParamPos1)." Parameter"}}=(
5621                "Target"=>$PName1,
5622                "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
5623                "Param_Type"=>$TypeInfo{1}{$PTid1}{"Name"},
5624                "Old_Value"=>$PName1,
5625                "New_Value"=>$PName2,
5626                "New_Signature"=>$Symbol);
5627        }
5628    }
5629
5630    # checking type change (replace)
5631    my %SubProblems = detectTypeChange($PTid1, $PTid2, "Parameter", $Level);
5632
5633    foreach my $SubProblemType (keys(%SubProblems))
5634    { # add new problems, remove false alarms
5635        my $New_Value = $SubProblems{$SubProblemType}{"New_Value"};
5636        my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"};
5637
5638        # quals
5639        if($SubProblemType eq "Parameter_Type"
5640        or $SubProblemType eq "Parameter_Type_And_Size"
5641        or $SubProblemType eq "Parameter_Type_Format")
5642        {
5643            if(addedQual($Old_Value, $New_Value, "restrict")) {
5644                %{$SubProblems{"Parameter_Became_Restrict"}} = %{$SubProblems{$SubProblemType}};
5645            }
5646            elsif(removedQual($Old_Value, $New_Value, "restrict")) {
5647                %{$SubProblems{"Parameter_Became_Non_Restrict"}} = %{$SubProblems{$SubProblemType}};
5648            }
5649
5650            if(removedQual($Old_Value, $New_Value, "volatile")) {
5651                %{$SubProblems{"Parameter_Became_Non_Volatile"}} = %{$SubProblems{$SubProblemType}};
5652            }
5653
5654            if($Type2{"Type"} eq "Const" and $BaseType2{"Name"} eq $Type1{"Name"}
5655            and $Type1{"Type"}=~/Intrinsic|Class|Struct|Union|Enum/)
5656            { # int to "int const"
5657                delete($SubProblems{$SubProblemType});
5658            }
5659            elsif($Type1{"Type"} eq "Const" and $BaseType1{"Name"} eq $Type2{"Name"}
5660            and $Type2{"Type"}=~/Intrinsic|Class|Struct|Union|Enum/)
5661            { # "int const" to int
5662                delete($SubProblems{$SubProblemType});
5663            }
5664            elsif(my $RR = removedQual($Old_Value, $New_Value, "const"))
5665            { # "const" to non-"const"
5666                if($RR==2) {
5667                    %{$SubProblems{"Parameter_Removed_Const"}} = %{$SubProblems{$SubProblemType}};
5668                }
5669                else {
5670                    %{$SubProblems{"Parameter_Became_Non_Const"}} = %{$SubProblems{$SubProblemType}};
5671                }
5672            }
5673        }
5674    }
5675
5676    if($Level eq "Source")
5677    {
5678        foreach my $SubProblemType (keys(%SubProblems))
5679        {
5680            my $New_Value = $SubProblems{$SubProblemType}{"New_Value"};
5681            my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"};
5682
5683            if($SubProblemType eq "Parameter_Type")
5684            {
5685                if(cmpBTypes($Old_Value, $New_Value, 1, 2)) {
5686                    delete($SubProblems{$SubProblemType});
5687                }
5688            }
5689        }
5690    }
5691
5692    foreach my $SubProblemType (keys(%SubProblems))
5693    { # modify/register problems
5694        my $New_Value = $SubProblems{$SubProblemType}{"New_Value"};
5695        my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"};
5696        my $New_Size = $SubProblems{$SubProblemType}{"New_Size"};
5697        my $Old_Size = $SubProblems{$SubProblemType}{"Old_Size"};
5698
5699        my $NewProblemType = $SubProblemType;
5700        if($Old_Value eq "..." and $New_Value ne "...")
5701        { # change from "..." to "int"
5702            if($ParamPos1==0)
5703            { # ISO C requires a named argument before "..."
5704                next;
5705            }
5706            $NewProblemType = "Parameter_Became_Non_VaList";
5707        }
5708        elsif($New_Value eq "..." and $Old_Value ne "...")
5709        { # change from "int" to "..."
5710            if($ParamPos2==0)
5711            { # ISO C requires a named argument before "..."
5712                next;
5713            }
5714            $NewProblemType = "Parameter_Became_VaList";
5715        }
5716        elsif($Level eq "Binary" and ($SubProblemType eq "Parameter_Type_And_Size"
5717        or $SubProblemType eq "Parameter_Type" or $SubProblemType eq "Parameter_Type_Format"))
5718        {
5719            my (%Conv1, %Conv2) = ();
5720
5721            if($UseConv_Real{1}{"P"} and $UseConv_Real{2}{"P"})
5722            { # real
5723                %Conv1 = callingConvention_P_Real($CompSign{1}{$Symbol}, $ParamPos1);
5724                %Conv2 = callingConvention_P_Real($CompSign{2}{$PSymbol}, $ParamPos2);
5725            }
5726            else
5727            { # model
5728                %Conv1 = callingConvention_P_Model($CompSign{1}{$Symbol}, $ParamPos1, 1);
5729                %Conv2 = callingConvention_P_Model($CompSign{2}{$PSymbol}, $ParamPos2, 2);
5730            }
5731
5732            my $ArrayType = ($Type1{"Type"} eq "Array" and $Type2{"Type"} eq "Array"); # Fortran
5733
5734            if($Conv1{"Method"} eq $Conv2{"Method"})
5735            {
5736                if($Conv1{"Method"} eq "stack")
5737                {
5738                    if($Old_Size ne $New_Size and not $ArrayType) { # FIXME: isMemPadded, getOffset
5739                        $NewProblemType = "Parameter_Type_And_Stack";
5740                    }
5741                }
5742                elsif($Conv1{"Method"} eq "reg")
5743                {
5744                    if($Conv1{"Registers"} ne $Conv2{"Registers"}) {
5745                        $NewProblemType = "Parameter_Type_And_Register";
5746                    }
5747                }
5748            }
5749            elsif($Conv1{"Method"} ne "unknown"
5750            and $Conv2{"Method"} ne "unknown")
5751            {
5752                if($Conv1{"Method"} eq "stack") {
5753                    $NewProblemType = "Parameter_Type_From_Stack_To_Register";
5754                }
5755                elsif($Conv1{"Method"} eq "register") {
5756                    $NewProblemType = "Parameter_Type_From_Register_To_Stack";
5757                }
5758            }
5759            $SubProblems{$SubProblemType}{"Old_Reg"} = $Conv1{"Registers"};
5760            $SubProblems{$SubProblemType}{"New_Reg"} = $Conv2{"Registers"};
5761        }
5762        %{$CompatProblems{$Level}{$Symbol}{$NewProblemType}{$ParamLoc}}=(
5763            "Target"=>$PName1,
5764            "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
5765            "New_Signature"=>$Symbol);
5766        @{$CompatProblems{$Level}{$Symbol}{$NewProblemType}{$ParamLoc}}{keys(%{$SubProblems{$SubProblemType}})} = values %{$SubProblems{$SubProblemType}};
5767    }
5768
5769    @RecurTypes = ();
5770
5771    # checking type definition changes
5772    my $Sub_SubProblems = mergeTypes($PTid1, $PTid2, $Level);
5773    foreach my $SubProblemType (keys(%{$Sub_SubProblems}))
5774    {
5775        foreach my $SubLoc (keys(%{$Sub_SubProblems->{$SubProblemType}}))
5776        {
5777            my $NewProblemType = $SubProblemType;
5778            if($SubProblemType eq "DataType_Size")
5779            {
5780                if($PureType1{"Type"} ne "Pointer"
5781                and $PureType1{"Type"} ne "Ref"
5782                and index($SubLoc, "->")==-1)
5783                { # stack has been affected
5784                    $NewProblemType = "DataType_Size_And_Stack";
5785                }
5786            }
5787            my $NewLoc = $ParamLoc;
5788            if($SubLoc) {
5789                $NewLoc .= "->".$SubLoc;
5790            }
5791            $CompatProblems{$Level}{$Symbol}{$NewProblemType}{$NewLoc} = $Sub_SubProblems->{$SubProblemType}{$SubLoc};
5792        }
5793    }
5794}
5795
5796sub findParamPairByName($$$)
5797{
5798    my ($Name, $Symbol, $LVer) = @_;
5799    foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{$LVer}{$Symbol}{"Param"}}))
5800    {
5801        next if(not defined $CompSign{$LVer}{$Symbol}{"Param"}{$ParamPos});
5802        if($CompSign{$LVer}{$Symbol}{"Param"}{$ParamPos}{"name"} eq $Name)
5803        {
5804            return $ParamPos;
5805        }
5806    }
5807    return "lost";
5808}
5809
5810sub findParamPairByTypeAndPos($$$$$)
5811{
5812    my ($TypeName, $MediumPos, $Order, $Symbol, $LVer) = @_;
5813    my @Positions = ();
5814    foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{$LVer}{$Symbol}{"Param"}}))
5815    {
5816        next if($Order eq "backward" and $ParamPos>$MediumPos);
5817        next if($Order eq "forward" and $ParamPos<$MediumPos);
5818        next if(not defined $CompSign{$LVer}{$Symbol}{"Param"}{$ParamPos});
5819        my $PTypeId = $CompSign{$LVer}{$Symbol}{"Param"}{$ParamPos}{"type"};
5820        if($TypeInfo{$LVer}{$PTypeId}{"Name"} eq $TypeName) {
5821            push(@Positions, $ParamPos);
5822        }
5823    }
5824    return @Positions;
5825}
5826
5827sub diffTypes($$$)
5828{
5829    if(defined $Cache{"diffTypes"}{$_[2]}{$_[0]}{$_[1]}) {
5830        return $Cache{"diffTypes"}{$_[2]}{$_[0]}{$_[1]};
5831    }
5832    if(isRecurType($_[0], $_[1], \@RecurTypes_Diff))
5833    { # skip recursive declarations
5834        return 0;
5835    }
5836
5837    pushType($_[0], $_[1], \@RecurTypes_Diff);
5838    my $Diff = diffTypes_I(@_);
5839    pop(@RecurTypes_Diff);
5840
5841    return ($Cache{"diffTypes"}{$_[2]}{$_[0]}{$_[1]} = $Diff);
5842}
5843
5844sub diffTypes_I($$$)
5845{
5846    my ($Type1_Id, $Type2_Id, $Level) = @_;
5847
5848    my %Type1_Pure = getPureType($Type1_Id, 1);
5849    my %Type2_Pure = getPureType($Type2_Id, 2);
5850
5851    if($Type1_Pure{"Name"} eq $Type2_Pure{"Name"})
5852    { # equal types
5853        return 0;
5854    }
5855    if($Type1_Pure{"Name"} eq "void")
5856    { # from void* to something
5857        return 0;
5858    }
5859    if($Type2_Pure{"Name"} eq "void")
5860    { # from something to void*
5861        return 0;
5862    }
5863    if($Type1_Pure{"Name"}=~/\*/
5864    or $Type2_Pure{"Name"}=~/\*/)
5865    { # compared in detectTypeChange()
5866        return 0;
5867    }
5868
5869    my %FloatType = map {$_=>1} (
5870        "float",
5871        "double",
5872        "long double"
5873    );
5874
5875    my $T1 = $Type1_Pure{"Type"};
5876    my $T2 = $Type2_Pure{"Type"};
5877
5878    if($T1 eq "Struct"
5879    and $T2 eq "Class")
5880    { # compare as data structures
5881        $T2 = "Struct";
5882    }
5883
5884    if($T1 eq "Class"
5885    and $T2 eq "Struct")
5886    { # compare as data structures
5887        $T1 = "Struct";
5888    }
5889
5890    if($T1 ne $T2)
5891    { # different types
5892        if($T1 eq "Intrinsic"
5893        and $T2 eq "Enum")
5894        { # "int" to "enum"
5895            return 0;
5896        }
5897        elsif($T2 eq "Intrinsic"
5898        and $T1 eq "Enum")
5899        { # "enum" to "int"
5900            return 0;
5901        }
5902        else
5903        { # union to struct
5904          #  ...
5905            return 1;
5906        }
5907    }
5908    else
5909    {
5910        if($T1 eq "Intrinsic")
5911        {
5912            if($FloatType{$Type1_Pure{"Name"}}
5913            or $FloatType{$Type2_Pure{"Name"}})
5914            { # "float" to "double"
5915              # "float" to "int"
5916                if($Level eq "Source")
5917                { # Safe
5918                    return 0;
5919                }
5920                else {
5921                    return 1;
5922                }
5923            }
5924        }
5925        elsif($T1=~/Class|Struct|Union|Enum/)
5926        {
5927            my @Membs1 = keys(%{$Type1_Pure{"Memb"}});
5928            my @Membs2 = keys(%{$Type2_Pure{"Memb"}});
5929            if(not @Membs1
5930            or not @Membs2)
5931            { # private
5932                return 0;
5933            }
5934            if($#Membs1!=$#Membs2)
5935            { # different number of elements
5936                return 1;
5937            }
5938            if($T1 eq "Enum")
5939            {
5940                foreach my $Pos (@Membs1)
5941                { # compare elements by name and value
5942                    if($Type1_Pure{"Memb"}{$Pos}{"name"} ne $Type2_Pure{"Memb"}{$Pos}{"name"}
5943                    or $Type1_Pure{"Memb"}{$Pos}{"value"} ne $Type2_Pure{"Memb"}{$Pos}{"value"})
5944                    { # different names
5945                        return 1;
5946                    }
5947                }
5948            }
5949            else
5950            {
5951                foreach my $Pos (@Membs1)
5952                {
5953                    if($Level eq "Source")
5954                    {
5955                        if($Type1_Pure{"Memb"}{$Pos}{"name"} ne $Type2_Pure{"Memb"}{$Pos}{"name"})
5956                        { # different names
5957                            return 1;
5958                        }
5959                    }
5960
5961                    my %MT1 = %{$TypeInfo{1}{$Type1_Pure{"Memb"}{$Pos}{"type"}}};
5962                    my %MT2 = %{$TypeInfo{2}{$Type2_Pure{"Memb"}{$Pos}{"type"}}};
5963
5964                    if($MT1{"Name"} ne $MT2{"Name"}
5965                    or isAnon($MT1{"Name"}) or isAnon($MT2{"Name"}))
5966                    {
5967                        my $PL1 = getPLevel($MT1{"Tid"}, 1);
5968                        my $PL2 = getPLevel($MT2{"Tid"}, 2);
5969
5970                        if($PL1 ne $PL2)
5971                        { # different pointer level
5972                            return 1;
5973                        }
5974
5975                        # compare base types
5976                        my %BT1 = getBaseType($MT1{"Tid"}, 1);
5977                        my %BT2 = getBaseType($MT2{"Tid"}, 2);
5978
5979                        if(diffTypes($BT1{"Tid"}, $BT2{"Tid"}, $Level))
5980                        { # different types
5981                            return 1;
5982                        }
5983                    }
5984                }
5985            }
5986        }
5987        else
5988        {
5989            # TODO: arrays, etc.
5990        }
5991    }
5992    return 0;
5993}
5994
5995sub detectTypeChange($$$$)
5996{
5997    my ($Type1_Id, $Type2_Id, $Prefix, $Level) = @_;
5998    if(not $Type1_Id or not $Type2_Id) {
5999        return ();
6000    }
6001    my %LocalProblems = ();
6002
6003    my %Type1 = getType($Type1_Id, 1);
6004    my %Type2 = getType($Type2_Id, 2);
6005
6006    if(not $Type1{"Name"} or not $Type2{"Name"}) {
6007        return ();
6008    }
6009
6010    my %Type1_Pure = getPureType($Type1_Id, 1);
6011    my %Type2_Pure = getPureType($Type2_Id, 2);
6012
6013    if(defined $In::Opt{"SkipTypedefUncover"})
6014    {
6015        if($Type1_Pure{"Name"} eq $Type2_Pure{"Name"}) {
6016            return ();
6017        }
6018
6019        if(cmpBTypes($Type1_Pure{"Name"}, $Type2_Pure{"Name"}, 1, 2)) {
6020            return ();
6021        }
6022    }
6023
6024    my %Type1_Base = ($Type1_Pure{"Type"} eq "Array")?getOneStepBaseType($Type1_Pure{"Tid"}, 1):getBaseType($Type1_Id, 1);
6025    my %Type2_Base = ($Type2_Pure{"Type"} eq "Array")?getOneStepBaseType($Type2_Pure{"Tid"}, 2):getBaseType($Type2_Id, 2);
6026
6027    if(not $Type1_Base{"Name"} or not $Type2_Base{"Name"}) {
6028        return ();
6029    }
6030
6031    if(defined $UsedDump{1}{"DWARF"})
6032    {
6033        if($Type1_Pure{"Name"} eq "__unknown__"
6034        or $Type2_Pure{"Name"} eq "__unknown__"
6035        or $Type1_Base{"Name"} eq "__unknown__"
6036        or $Type2_Base{"Name"} eq "__unknown__")
6037        { # Error ABI dump
6038            return ();
6039        }
6040    }
6041
6042    my $Type1_PLevel = getPLevel($Type1_Id, 1);
6043    my $Type2_PLevel = getPLevel($Type2_Id, 2);
6044
6045    if($Type1_PLevel eq "" or $Type2_PLevel eq "") {
6046        return ();
6047    }
6048
6049    if($Type1_Base{"Name"} ne $Type2_Base{"Name"}
6050    and ($Type1{"Name"} eq $Type2{"Name"} or ($Type1_PLevel>=1 and $Type1_PLevel==$Type2_PLevel
6051    and $Type1_Base{"Name"} ne "void" and $Type2_Base{"Name"} ne "void")))
6052    { # base type change
6053        if($Type1{"Name"} eq $Type2{"Name"})
6054        {
6055            if($Type1{"Type"} eq "Typedef" and $Type2{"Type"} eq "Typedef")
6056            { # will be reported in mergeTypes() as typedef problem
6057                return ();
6058            }
6059            my %Typedef_1 = goToFirst($Type1{"Tid"}, 1, "Typedef");
6060            my %Typedef_2 = goToFirst($Type2{"Tid"}, 2, "Typedef");
6061            if(%Typedef_1 and %Typedef_2)
6062            {
6063                if($Typedef_1{"Name"} eq $Typedef_2{"Name"}
6064                and $Typedef_1{"Type"} eq "Typedef" and $Typedef_2{"Type"} eq "Typedef")
6065                { # const Typedef
6066                    return ();
6067                }
6068            }
6069        }
6070        if($Type1_Base{"Name"}!~/anon\-/ and $Type2_Base{"Name"}!~/anon\-/)
6071        {
6072            if($Level eq "Binary"
6073            and $Type1_Base{"Size"} and $Type2_Base{"Size"}
6074            and $Type1_Base{"Size"} ne $Type2_Base{"Size"})
6075            {
6076                %{$LocalProblems{$Prefix."_BaseType_And_Size"}}=(
6077                    "Old_Value"=>$Type1_Base{"Name"},
6078                    "New_Value"=>$Type2_Base{"Name"},
6079                    "Old_Size"=>$Type1_Base{"Size"}*$BYTE,
6080                    "New_Size"=>$Type2_Base{"Size"}*$BYTE);
6081            }
6082            else
6083            {
6084                if(diffTypes($Type1_Base{"Tid"}, $Type2_Base{"Tid"}, $Level))
6085                { # format change
6086                    %{$LocalProblems{$Prefix."_BaseType_Format"}}=(
6087                        "Old_Value"=>$Type1_Base{"Name"},
6088                        "New_Value"=>$Type2_Base{"Name"},
6089                        "Old_Size"=>$Type1_Base{"Size"}*$BYTE,
6090                        "New_Size"=>$Type2_Base{"Size"}*$BYTE);
6091                }
6092                elsif(tNameLock($Type1_Base{"Tid"}, $Type2_Base{"Tid"}))
6093                {
6094                    %{$LocalProblems{$Prefix."_BaseType"}}=(
6095                        "Old_Value"=>$Type1_Base{"Name"},
6096                        "New_Value"=>$Type2_Base{"Name"},
6097                        "Old_Size"=>$Type1_Base{"Size"}*$BYTE,
6098                        "New_Size"=>$Type2_Base{"Size"}*$BYTE);
6099                }
6100            }
6101        }
6102    }
6103    elsif($Type1{"Name"} ne $Type2{"Name"})
6104    { # type change
6105        if($Type1{"Name"}!~/anon\-/ and $Type2{"Name"}!~/anon\-/)
6106        {
6107            my $ArrayType = ($Type1{"Type"} eq "Array" and $Type2{"Type"} eq "Array"); # Fortran
6108
6109            if($Prefix eq "Return"
6110            and $Type1_Pure{"Name"} eq "void")
6111            {
6112                %{$LocalProblems{"Return_Type_From_Void"}}=(
6113                    "New_Value"=>$Type2{"Name"},
6114                    "New_Size"=>$Type2{"Size"}*$BYTE);
6115            }
6116            elsif($Prefix eq "Return"
6117            and $Type2_Pure{"Name"} eq "void")
6118            {
6119                %{$LocalProblems{"Return_Type_Became_Void"}}=(
6120                    "Old_Value"=>$Type1{"Name"},
6121                    "Old_Size"=>$Type1{"Size"}*$BYTE);
6122            }
6123            else
6124            {
6125                if($Level eq "Binary"
6126                and $Type1{"Size"} and $Type2{"Size"}
6127                and $Type1{"Size"} ne $Type2{"Size"}
6128                and not $ArrayType)
6129                {
6130                    %{$LocalProblems{$Prefix."_Type_And_Size"}}=(
6131                        "Old_Value"=>$Type1{"Name"},
6132                        "New_Value"=>$Type2{"Name"},
6133                        "Old_Size"=>$Type1{"Size"}*$BYTE,
6134                        "New_Size"=>$Type2{"Size"}*$BYTE);
6135                }
6136                else
6137                {
6138                    if(diffTypes($Type1_Id, $Type2_Id, $Level))
6139                    { # format change
6140                        %{$LocalProblems{$Prefix."_Type_Format"}}=(
6141                            "Old_Value"=>$Type1{"Name"},
6142                            "New_Value"=>$Type2{"Name"},
6143                            "Old_Size"=>$Type1{"Size"}*$BYTE,
6144                            "New_Size"=>$Type2{"Size"}*$BYTE);
6145                    }
6146                    elsif(tNameLock($Type1_Id, $Type2_Id))
6147                    { # FIXME: correct this condition
6148                        %{$LocalProblems{$Prefix."_Type"}}=(
6149                            "Old_Value"=>$Type1{"Name"},
6150                            "New_Value"=>$Type2{"Name"},
6151                            "Old_Size"=>$Type1{"Size"}*$BYTE,
6152                            "New_Size"=>$Type2{"Size"}*$BYTE);
6153                    }
6154                }
6155            }
6156        }
6157    }
6158    if($Type1_PLevel!=$Type2_PLevel)
6159    {
6160        if($Type1{"Name"} ne "void" and $Type1{"Name"} ne "..."
6161        and $Type2{"Name"} ne "void" and $Type2{"Name"} ne "...")
6162        {
6163            if($Level eq "Source")
6164            {
6165                %{$LocalProblems{$Prefix."_PointerLevel"}}=(
6166                    "Old_Value"=>$Type1_PLevel,
6167                    "New_Value"=>$Type2_PLevel);
6168            }
6169            else
6170            {
6171                if($Type2_PLevel>$Type1_PLevel)
6172                {
6173                    %{$LocalProblems{$Prefix."_PointerLevel_Increased"}}=(
6174                        "Old_Value"=>$Type1_PLevel,
6175                        "New_Value"=>$Type2_PLevel);
6176                }
6177                else
6178                {
6179                    %{$LocalProblems{$Prefix."_PointerLevel_Decreased"}}=(
6180                        "Old_Value"=>$Type1_PLevel,
6181                        "New_Value"=>$Type2_PLevel);
6182                }
6183            }
6184        }
6185    }
6186    if($Type1_Pure{"Type"} eq "Array"
6187    and $Type1_Pure{"BaseType"})
6188    { # base_type[N] -> base_type[N]
6189      # base_type: older_structure -> typedef to newer_structure
6190        my %SubProblems = detectTypeChange($Type1_Base{"Tid"}, $Type2_Base{"Tid"}, $Prefix, $Level);
6191        foreach my $SubProblemType (keys(%SubProblems))
6192        {
6193            $SubProblemType=~s/_Type/_BaseType/g;
6194            next if(defined $LocalProblems{$SubProblemType});
6195            foreach my $Attr (keys(%{$SubProblems{$SubProblemType}})) {
6196                $LocalProblems{$SubProblemType}{$Attr} = $SubProblems{$SubProblemType}{$Attr};
6197            }
6198        }
6199    }
6200    return %LocalProblems;
6201}
6202
6203sub tNameLock($$)
6204{
6205    my ($Tid1, $Tid2) = @_;
6206
6207    my $TN1 = $TypeInfo{1}{$Tid1}{"Name"};
6208    my $TN2 = $TypeInfo{2}{$Tid2}{"Name"};
6209
6210    my $TT1 = $TypeInfo{1}{$Tid1}{"Type"};
6211    my $TT2 = $TypeInfo{2}{$Tid2}{"Type"};
6212
6213    if($In::ABI{1}{"GccVersion"} ne $In::ABI{2}{"GccVersion"})
6214    { # different formats
6215        my %Base1 = getType($Tid1, 1);
6216        while(defined $Base1{"Type"} and $Base1{"Type"} eq "Typedef") {
6217            %Base1 = getOneStepBaseType($Base1{"Tid"}, 1);
6218        }
6219        my %Base2 = getType($Tid2, 2);
6220        while(defined $Base2{"Type"} and $Base2{"Type"} eq "Typedef") {
6221            %Base2 = getOneStepBaseType($Base2{"Tid"}, 2);
6222        }
6223        my $BName1 = uncoverTypedefs($Base1{"Name"}, 1);
6224        my $BName2 = uncoverTypedefs($Base2{"Name"}, 2);
6225
6226        if($BName1 eq $BName2)
6227        { # equal base types
6228            return 0;
6229        }
6230    }
6231    else
6232    {
6233        # typedef struct {...} type_t
6234        # typedef struct type_t {...} type_t
6235        if(index($TN1, " ".$TN2)!=-1)
6236        {
6237            if($TN1=~/\A(struct|union|enum) \Q$TN2\E\Z/) {
6238                return 0;
6239            }
6240        }
6241        if(index($TN2, " ".$TN1)!=-1)
6242        {
6243            if($TN2=~/\A(struct|union|enum) \Q$TN1\E\Z/) {
6244                return 0;
6245            }
6246        }
6247
6248        if($TT1 eq "FuncPtr"
6249        and $TT2 eq "FuncPtr")
6250        {
6251            my $TN1_C = $TN1;
6252            my $TN2_C = $TN2;
6253
6254            $TN1_C=~s/\b(struct|union) //g;
6255            $TN2_C=~s/\b(struct|union) //g;
6256
6257            if($TN1_C eq $TN2_C) {
6258                return 0;
6259            }
6260        }
6261    }
6262
6263    my ($N1, $N2) = ($TN1, $TN2);
6264    $N1=~s/\b(struct|union) //g;
6265    $N2=~s/\b(struct|union) //g;
6266
6267    if($N1 eq $N2)
6268    { # QList<struct QUrl> and QList<QUrl>
6269        return 0;
6270    }
6271
6272    return 1;
6273}
6274
6275sub showArch($)
6276{
6277    my $Arch = $_[0];
6278    if($Arch eq "arm"
6279    or $Arch eq "mips") {
6280        return uc($Arch);
6281    }
6282    return $Arch;
6283}
6284
6285sub getReportTitle($)
6286{
6287    my $Level = $_[0];
6288
6289    my $ArchInfo = " on <span style='color:Blue;'>".showArch($In::ABI{1}{"Arch"})."</span>";
6290    if($In::ABI{1}{"Arch"} ne $In::ABI{2}{"Arch"}
6291    or $Level eq "Source")
6292    { # don't show architecture in the header
6293        $ArchInfo = "";
6294    }
6295    my $Title = "";
6296    if($Level eq "Source") {
6297        $Title .= "Source compatibility";
6298    }
6299    elsif($Level eq "Binary") {
6300        $Title .= "Binary compatibility";
6301    }
6302    else {
6303        $Title .= "API compatibility";
6304    }
6305
6306    my $V1 = $In::Desc{1}{"Version"};
6307    my $V2 = $In::Desc{2}{"Version"};
6308
6309    if($UsedDump{1}{"DWARF"} and $UsedDump{2}{"DWARF"})
6310    {
6311        my $M1 = $In::ABI{1}{"LibraryName"};
6312        my $M2 = $In::ABI{2}{"LibraryName"};
6313
6314        my $M1S = $M1;
6315        my $M2S = $M2;
6316
6317        $M1S=~s/(\.so|\.ko)\..+/$1/ig;
6318        $M2S=~s/(\.so|\.ko)\..+/$1/ig;
6319
6320        if($M1S eq $M2S
6321        and $V1 ne "X" and $V2 ne "Y")
6322        {
6323            $Title .= " report for the <span style='color:Blue;'>$M1S</span> ".$In::Opt{"TargetComponent"};
6324            $Title .= " between <span style='color:Red;'>".$V1."</span> and <span style='color:Red;'>".$V2."</span> versions";
6325        }
6326        else
6327        {
6328            $Title .= " report between <span style='color:Blue;'>$M1</span> (<span style='color:Red;'>".$V1."</span>)";
6329            $Title .= " and <span style='color:Blue;'>$M2</span> (<span style='color:Red;'>".$V2."</span>) objects";
6330        }
6331    }
6332    else
6333    {
6334        $Title .= " report for the <span style='color:Blue;'>".$In::Opt{"TargetTitle"}."</span> ".$In::Opt{"TargetComponent"};
6335        $Title .= " between <span style='color:Red;'>".$V1."</span> and <span style='color:Red;'>".$V2."</span> versions";
6336    }
6337
6338    $Title .= $ArchInfo;
6339
6340    if($In::Opt{"AppPath"}) {
6341        $Title .= " (relating to the portability of application <span style='color:Blue;'>".getFilename($In::Opt{"AppPath"})."</span>)";
6342    }
6343    $Title = "<h1>".$Title."</h1>\n";
6344    return $Title;
6345}
6346
6347sub getCheckedHeaders($)
6348{
6349    my $LVer = $_[0];
6350
6351    my @Headers = ();
6352
6353    foreach my $Path (keys(%{$In::ABI{$LVer}{"Headers"}}))
6354    {
6355        my $Name = getFilename($Path);
6356
6357        if(not isTargetHeader($Name, $LVer)) {
6358            next;
6359        }
6360
6361        if(skipHeader($Name, $LVer)) {
6362            next;
6363        }
6364
6365        push(@Headers, $Path);
6366    }
6367
6368    return @Headers;
6369}
6370
6371sub getSourceInfo()
6372{
6373    my ($CheckedHeaders, $CheckedSources, $CheckedLibs) = ("", "");
6374
6375    if(my @Headers = getCheckedHeaders(1))
6376    {
6377        $CheckedHeaders = "<a name='Headers'></a>";
6378        if($In::Opt{"OldStyle"}) {
6379            $CheckedHeaders .= "<h2>Header Files (".($#Headers+1).")</h2>";
6380        }
6381        else {
6382            $CheckedHeaders .= "<h2>Header Files <span class='gray'>&nbsp;".($#Headers+1)."&nbsp;</span></h2>";
6383        }
6384        $CheckedHeaders .= "<hr/>\n";
6385        $CheckedHeaders .= "<div class='h_list'>\n";
6386        foreach my $Path (sort {lc($a) cmp lc($b)} @Headers) {
6387            $CheckedHeaders .= getFilename($Path)."<br/>\n";
6388        }
6389        $CheckedHeaders .= "</div>\n";
6390        $CheckedHeaders .= "<br/>$TOP_REF<br/>\n";
6391    }
6392
6393    if(my @Sources = keys(%{$In::ABI{1}{"Sources"}}))
6394    {
6395        $CheckedSources = "<a name='Sources'></a>";
6396        if($In::Opt{"OldStyle"}) {
6397            $CheckedSources .= "<h2>Source Files (".($#Sources+1).")</h2>";
6398        }
6399        else {
6400            $CheckedSources .= "<h2>Source Files <span class='gray'>&nbsp;".($#Sources+1)."&nbsp;</span></h2>";
6401        }
6402        $CheckedSources .= "<hr/>\n";
6403        $CheckedSources .= "<div class='h_list'>\n";
6404        foreach my $Path (sort {lc($a) cmp lc($b)} @Sources) {
6405            $CheckedSources .= getFilename($Path)."<br/>\n";
6406        }
6407        $CheckedSources .= "</div>\n";
6408        $CheckedSources .= "<br/>$TOP_REF<br/>\n";
6409    }
6410
6411    if(not $In::Opt{"CheckHeadersOnly"})
6412    {
6413        $CheckedLibs = "<a name='Libs'></a>";
6414        if($In::Opt{"OldStyle"}) {
6415            $CheckedLibs .= "<h2>".getObjTitle()." (".keys(%{$In::ABI{1}{"Symbols"}}).")</h2>";
6416        }
6417        else {
6418            $CheckedLibs .= "<h2>".getObjTitle()." <span class='gray'>&nbsp;".keys(%{$In::ABI{1}{"Symbols"}})."&nbsp;</span></h2>";
6419        }
6420        $CheckedLibs .= "<hr/>\n";
6421        $CheckedLibs .= "<div class='lib_list'>\n";
6422        foreach my $Library (sort {lc($a) cmp lc($b)}  keys(%{$In::ABI{1}{"Symbols"}})) {
6423            $CheckedLibs .= $Library."<br/>\n";
6424        }
6425        $CheckedLibs .= "</div>\n";
6426        $CheckedLibs .= "<br/>$TOP_REF<br/>\n";
6427    }
6428
6429    return $CheckedHeaders.$CheckedSources.$CheckedLibs;
6430}
6431
6432sub getObjTitle()
6433{
6434    if(defined $UsedDump{1}{"DWARF"}) {
6435        return "Objects";
6436    }
6437
6438    return "Libraries";
6439}
6440
6441sub getTypeProblemsCount($$)
6442{
6443    my ($TargetSeverity, $Level) = @_;
6444    my $Count = 0;
6445
6446    foreach my $Type_Name (sort keys(%{$TypeChanges{$Level}}))
6447    {
6448        my %Kinds_Target = ();
6449        foreach my $Kind (keys(%{$TypeChanges{$Level}{$Type_Name}}))
6450        {
6451            if($CompatRules{$Level}{$Kind}{"Severity"} ne $TargetSeverity) {
6452                next;
6453            }
6454
6455            foreach my $Loc (keys(%{$TypeChanges{$Level}{$Type_Name}{$Kind}}))
6456            {
6457                my $Target = $TypeChanges{$Level}{$Type_Name}{$Kind}{$Loc}{"Target"};
6458
6459                if($Kinds_Target{$Kind}{$Target}) {
6460                    next;
6461                }
6462
6463                $Kinds_Target{$Kind}{$Target} = 1;
6464                $Count += 1;
6465            }
6466        }
6467    }
6468    return $Count;
6469}
6470
6471sub getSummary($)
6472{
6473    my $Level = $_[0];
6474    my ($Added, $Removed, $I_Problems_High, $I_Problems_Medium, $I_Problems_Low, $T_Problems_High,
6475    $C_Problems_Low, $T_Problems_Medium, $T_Problems_Low, $I_Other, $T_Other, $C_Other) = (0,0,0,0,0,0,0,0,0,0,0,0);
6476    %{$RESULT{$Level}} = (
6477        "Problems"=>0,
6478        "Warnings"=>0,
6479        "Affected"=>0);
6480    # check rules
6481    foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
6482    {
6483        foreach my $Kind (keys(%{$CompatProblems{$Level}{$Symbol}}))
6484        {
6485            if(not defined $CompatRules{$Level}{$Kind})
6486            { # unknown rule
6487                if(not $UnknownRules{$Level}{$Kind})
6488                { # only one warning
6489                    printMsg("WARNING", "unknown rule \"$Kind\" (\"$Level\")");
6490                    $UnknownRules{$Level}{$Kind}=1;
6491                }
6492                delete($CompatProblems{$Level}{$Symbol}{$Kind});
6493            }
6494        }
6495    }
6496    foreach my $Constant (sort keys(%{$CompatProblems_Constants{$Level}}))
6497    {
6498        foreach my $Kind (keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
6499        {
6500            if(not defined $CompatRules{$Level}{$Kind})
6501            { # unknown rule
6502                if(not $UnknownRules{$Level}{$Kind})
6503                { # only one warning
6504                    printMsg("WARNING", "unknown rule \"$Kind\" (\"$Level\")");
6505                    $UnknownRules{$Level}{$Kind}=1;
6506                }
6507                delete($CompatProblems_Constants{$Level}{$Constant}{$Kind});
6508            }
6509        }
6510    }
6511    foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
6512    {
6513        foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
6514        {
6515            if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols")
6516            {
6517                my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
6518                foreach my $Loc (sort keys(%{$CompatProblems{$Level}{$Symbol}{$Kind}}))
6519                {
6520                    if($Kind eq "Added_Symbol") {
6521                        $Added += 1;
6522                    }
6523                    elsif($Kind eq "Removed_Symbol")
6524                    {
6525                        $Removed += 1;
6526                        $TotalAffected{$Level}{$Symbol} = $Severity;
6527                    }
6528                    else
6529                    {
6530                        if($Severity eq "Safe") {
6531                            $I_Other += 1;
6532                        }
6533                        elsif($Severity eq "High") {
6534                            $I_Problems_High += 1;
6535                        }
6536                        elsif($Severity eq "Medium") {
6537                            $I_Problems_Medium += 1;
6538                        }
6539                        elsif($Severity eq "Low") {
6540                            $I_Problems_Low += 1;
6541                        }
6542                        if(($Severity ne "Low" or $In::Opt{"StrictCompat"})
6543                        and $Severity ne "Safe") {
6544                            $TotalAffected{$Level}{$Symbol} = $Severity;
6545                        }
6546                    }
6547                }
6548            }
6549        }
6550    }
6551
6552    my %MethodTypeIndex = ();
6553
6554    foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
6555    {
6556        foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
6557        {
6558            if($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
6559            {
6560                my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
6561                if(($Severity ne "Low" or $In::Opt{"StrictCompat"})
6562                and $Severity ne "Safe")
6563                {
6564                    if(my $Sev = $TotalAffected{$Level}{$Symbol})
6565                    {
6566                        if($Severity_Val{$Severity}>$Severity_Val{$Sev}) {
6567                            $TotalAffected{$Level}{$Symbol} = $Severity;
6568                        }
6569                    }
6570                    else {
6571                        $TotalAffected{$Level}{$Symbol} = $Severity;
6572                    }
6573                }
6574
6575                my $LSK = $CompatProblems{$Level}{$Symbol}{$Kind};
6576                my (@Locs1, @Locs2) = ();
6577                foreach my $Loc (sort keys(%{$LSK}))
6578                {
6579                    if(index($Loc, "retval")==0 or index($Loc, "this")==0) {
6580                        push(@Locs2, $Loc);
6581                    }
6582                    else {
6583                        push(@Locs1, $Loc);
6584                    }
6585                }
6586
6587                foreach my $Loc (@Locs1, @Locs2)
6588                {
6589                    my $Type = $LSK->{$Loc}{"Type_Name"};
6590                    my $Target = $LSK->{$Loc}{"Target"};
6591
6592                    if(defined $MethodTypeIndex{$Symbol}{$Type}{$Kind}{$Target})
6593                    { # one location for one type and target
6594                        next;
6595                    }
6596                    $MethodTypeIndex{$Symbol}{$Type}{$Kind}{$Target} = 1;
6597
6598                    $TypeChanges{$Level}{$Type}{$Kind}{$Loc} = $LSK->{$Loc};
6599                    $TypeProblemsIndex{$Level}{$Type}{$Kind}{$Loc}{$Symbol} = 1;
6600                }
6601            }
6602        }
6603    }
6604
6605    # clean memory
6606    %MethodTypeIndex = ();
6607
6608    $T_Problems_High = getTypeProblemsCount("High", $Level);
6609    $T_Problems_Medium = getTypeProblemsCount("Medium", $Level);
6610    $T_Problems_Low = getTypeProblemsCount("Low", $Level);
6611    $T_Other = getTypeProblemsCount("Safe", $Level);
6612
6613    # changed and removed public symbols
6614    my $SCount = keys(%{$CheckedSymbols{$Level}});
6615    if($In::Opt{"ExtendedCheck"})
6616    { # don't count external_func_0 for constants
6617        $SCount-=1;
6618    }
6619    if($SCount)
6620    {
6621        my %Weight = (
6622            "High" => 100,
6623            "Medium" => 50,
6624            "Low" => 25
6625        );
6626        foreach (keys(%{$TotalAffected{$Level}})) {
6627            $RESULT{$Level}{"Affected"}+=$Weight{$TotalAffected{$Level}{$_}};
6628        }
6629        $RESULT{$Level}{"Affected"} = $RESULT{$Level}{"Affected"}/$SCount;
6630    }
6631    else {
6632        $RESULT{$Level}{"Affected"} = 0;
6633    }
6634
6635    $RESULT{$Level}{"Affected"} = showNum($RESULT{$Level}{"Affected"});
6636    if($RESULT{$Level}{"Affected"}>=100) {
6637        $RESULT{$Level}{"Affected"} = 100;
6638    }
6639
6640    $RESULT{$Level}{"Problems"} += $Removed;
6641    $RESULT{$Level}{"Problems"} += $T_Problems_High + $I_Problems_High;
6642    $RESULT{$Level}{"Problems"} += $T_Problems_Medium + $I_Problems_Medium;
6643    if($In::Opt{"StrictCompat"}) {
6644        $RESULT{$Level}{"Problems"} += $T_Problems_Low + $I_Problems_Low;
6645    }
6646    else {
6647        $RESULT{$Level}{"Warnings"} += $T_Problems_Low + $I_Problems_Low;
6648    }
6649    if($In::Opt{"WarnNewSym"}) {
6650        $RESULT{$Level}{"Problems"} += $Added;
6651    }
6652
6653    foreach my $Constant (keys(%{$CompatProblems_Constants{$Level}}))
6654    {
6655        foreach my $Kind (keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
6656        {
6657            my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
6658            if($Severity eq "Safe")
6659            {
6660                $C_Other+=1;
6661            }
6662            elsif($Severity eq "Low")
6663            {
6664                $C_Problems_Low+=1;
6665            }
6666        }
6667    }
6668
6669    if($C_Problems_Low)
6670    {
6671        if($In::Opt{"StrictCompat"}) {
6672            $RESULT{$Level}{"Problems"} += $C_Problems_Low;
6673        }
6674        else {
6675            $RESULT{$Level}{"Warnings"} += $C_Problems_Low;
6676        }
6677    }
6678    if($RESULT{$Level}{"Problems"}
6679    and $RESULT{$Level}{"Affected"}) {
6680        $RESULT{$Level}{"Verdict"} = "incompatible";
6681    }
6682    else {
6683        $RESULT{$Level}{"Verdict"} = "compatible";
6684    }
6685
6686    my $TotalTypes = keys(%{$CheckedTypes{$Level}});
6687    if(not $TotalTypes)
6688    { # list all the types
6689        $TotalTypes = keys(%{$TName_Tid{1}});
6690    }
6691
6692    my $TotalSymbols = keys(%{$CheckedSymbols{$Level}});
6693
6694    if($In::Opt{"ExtendedCheck"}) {
6695        $TotalSymbols -= keys(%ExtendedSymbols);
6696    }
6697
6698    my $AnyChanged = ($Added or $Removed or $I_Problems_High or $I_Problems_Medium or $I_Problems_Low or $T_Problems_High or
6699    $C_Problems_Low or $T_Problems_Medium or $T_Problems_Low or $I_Other or $T_Other or $C_Other);
6700
6701    my ($Arch1, $Arch2) = ($In::ABI{1}{"Arch"}, $In::ABI{2}{"Arch"});
6702    my ($GccV1, $GccV2) = ($In::ABI{1}{"GccVersion"}, $In::ABI{2}{"GccVersion"});
6703    my ($ClangV1, $ClangV2) = ($In::ABI{1}{"ClangVersion"}, $In::ABI{2}{"ClangVersion"});
6704
6705    my ($TestInfo, $TestResults, $Problem_Summary) = ();
6706
6707    if($In::Opt{"ReportFormat"} eq "xml")
6708    { # XML
6709        # test info
6710        $TestInfo .= "  <library>".$In::Opt{"TargetLib"}."</library>\n";
6711        $TestInfo .= "  <version1>\n";
6712        $TestInfo .= "    <number>".$In::Desc{1}{"Version"}."</number>\n";
6713        $TestInfo .= "    <arch>$Arch1</arch>\n";
6714        if($GccV1) {
6715            $TestInfo .= "    <gcc>$GccV1</gcc>\n";
6716        }
6717        elsif($ClangV1) {
6718            $TestInfo .= "    <clang>$ClangV1</clang>\n";
6719        }
6720        $TestInfo .= "  </version1>\n";
6721
6722        $TestInfo .= "  <version2>\n";
6723        $TestInfo .= "    <number>".$In::Desc{2}{"Version"}."</number>\n";
6724        $TestInfo .= "    <arch>$Arch2</arch>\n";
6725        if($GccV2) {
6726            $TestInfo .= "    <gcc>$GccV2</gcc>\n";
6727        }
6728        elsif($ClangV2) {
6729            $TestInfo .= "    <clang>$ClangV2</clang>\n";
6730        }
6731        $TestInfo .= "  </version2>\n";
6732        $TestInfo = "<test_info>\n".$TestInfo."</test_info>\n\n";
6733
6734        # test results
6735        if(my @Headers = keys(%{$In::ABI{1}{"Headers"}}))
6736        {
6737            $TestResults .= "  <headers>\n";
6738            foreach my $Name (sort {lc($a) cmp lc($b)} @Headers) {
6739                $TestResults .= "    <name>".getFilename($Name)."</name>\n";
6740            }
6741            $TestResults .= "  </headers>\n";
6742        }
6743
6744        if(my @Sources = keys(%{$In::ABI{1}{"Sources"}}))
6745        {
6746            $TestResults .= "  <sources>\n";
6747            foreach my $Name (sort {lc($a) cmp lc($b)} @Sources) {
6748                $TestResults .= "    <name>".getFilename($Name)."</name>\n";
6749            }
6750            $TestResults .= "  </sources>\n";
6751        }
6752
6753        $TestResults .= "  <libs>\n";
6754        foreach my $Library (sort {lc($a) cmp lc($b)}  keys(%{$In::ABI{1}{"Symbols"}}))
6755        {
6756            $TestResults .= "    <name>$Library</name>\n";
6757        }
6758        $TestResults .= "  </libs>\n";
6759
6760        $TestResults .= "  <symbols>".$TotalSymbols."</symbols>\n";
6761        $TestResults .= "  <types>".$TotalTypes."</types>\n";
6762
6763        $TestResults .= "  <verdict>".$RESULT{$Level}{"Verdict"}."</verdict>\n";
6764        $TestResults .= "  <affected>".$RESULT{$Level}{"Affected"}."</affected>\n";
6765        $TestResults = "<test_results>\n".$TestResults."</test_results>\n\n";
6766
6767        # problem summary
6768        $Problem_Summary .= "  <added_symbols>".$Added."</added_symbols>\n";
6769        $Problem_Summary .= "  <removed_symbols>".$Removed."</removed_symbols>\n";
6770
6771        $Problem_Summary .= "  <problems_with_types>\n";
6772        $Problem_Summary .= "    <high>$T_Problems_High</high>\n";
6773        $Problem_Summary .= "    <medium>$T_Problems_Medium</medium>\n";
6774        $Problem_Summary .= "    <low>$T_Problems_Low</low>\n";
6775        $Problem_Summary .= "    <safe>$T_Other</safe>\n";
6776        $Problem_Summary .= "  </problems_with_types>\n";
6777
6778        $Problem_Summary .= "  <problems_with_symbols>\n";
6779        $Problem_Summary .= "    <high>$I_Problems_High</high>\n";
6780        $Problem_Summary .= "    <medium>$I_Problems_Medium</medium>\n";
6781        $Problem_Summary .= "    <low>$I_Problems_Low</low>\n";
6782        $Problem_Summary .= "    <safe>$I_Other</safe>\n";
6783        $Problem_Summary .= "  </problems_with_symbols>\n";
6784
6785        $Problem_Summary .= "  <problems_with_constants>\n";
6786        $Problem_Summary .= "    <low>$C_Problems_Low</low>\n";
6787        $Problem_Summary .= "  </problems_with_constants>\n";
6788
6789        $Problem_Summary = "<problem_summary>\n".$Problem_Summary."</problem_summary>\n\n";
6790
6791        return ($TestInfo.$TestResults.$Problem_Summary, "", $AnyChanged);
6792    }
6793    else
6794    { # HTML
6795        # test info
6796        $TestInfo = "<h2>Test Info</h2><hr/>\n";
6797        $TestInfo .= "<table class='summary'>\n";
6798
6799        if($In::Opt{"TargetComponent"} eq "library") {
6800            $TestInfo .= "<tr><th>Library Name</th><td>".$In::Opt{"TargetTitle"}."</td></tr>\n";
6801        }
6802        else {
6803            $TestInfo .= "<tr><th>Module Name</th><td>".$In::Opt{"TargetTitle"}."</td></tr>\n";
6804        }
6805
6806        my (@VInf1, @VInf2, $AddTestInfo) = ();
6807
6808        # CPU arch
6809        if($Arch1 eq $Arch2)
6810        { # go to the separate section
6811            $AddTestInfo .= "<tr><th>Arch</th><td>".showArch($Arch1)."</td></tr>\n";
6812        }
6813        else
6814        { # go to the version number
6815            push(@VInf1, showArch($Arch1));
6816            push(@VInf2, showArch($Arch2));
6817        }
6818
6819        if($Level eq "Binary"
6820        and $In::Opt{"Target"} ne "windows")
6821        {
6822            if($GccV1 and $GccV2)
6823            { # GCC version
6824                if($GccV1 eq $GccV2)
6825                { # go to the separate section
6826                    $AddTestInfo .= "<tr><th>GCC Version</th><td>$GccV1</td></tr>\n";
6827                }
6828                else
6829                { # go to the version number
6830                    push(@VInf1, "gcc ".$GccV1);
6831                    push(@VInf2, "gcc ".$GccV2);
6832                }
6833            }
6834            elsif($ClangV1 and $ClangV2)
6835            { # Clang version
6836                if($ClangV1 eq $ClangV2)
6837                { # go to the separate section
6838                    $AddTestInfo .= "<tr><th>Clang Version</th><td>$ClangV1</td></tr>\n";
6839                }
6840                else
6841                { # go to the version number
6842                    push(@VInf1, "clang ".$ClangV1);
6843                    push(@VInf2, "clang ".$ClangV2);
6844                }
6845            }
6846            elsif($GccV1 and $ClangV2)
6847            {
6848                push(@VInf1, "gcc ".$GccV1);
6849                push(@VInf2, "clang ".$ClangV2);
6850            }
6851            elsif($ClangV1 and $GccV2)
6852            {
6853                push(@VInf1, "clang ".$ClangV1);
6854                push(@VInf2, "gcc ".$GccV2);
6855            }
6856        }
6857        # show long version names with GCC version and CPU architecture name (if different)
6858        $TestInfo .= "<tr><th>Version #1</th><td>".$In::Desc{1}{"Version"}.(@VInf1?" (".join(", ", reverse(@VInf1)).")":"")."</td></tr>\n";
6859        $TestInfo .= "<tr><th>Version #2</th><td>".$In::Desc{2}{"Version"}.(@VInf2?" (".join(", ", reverse(@VInf2)).")":"")."</td></tr>\n";
6860        $TestInfo .= $AddTestInfo;
6861
6862        if($In::Opt{"ExtendedCheck"}) {
6863            $TestInfo .= "<tr><th>Mode</th><td>Extended</td></tr>\n";
6864        }
6865        if($In::Opt{"JoinReport"})
6866        {
6867            if($Level eq "Binary") {
6868                $TestInfo .= "<tr><th>Subject</th><td width='150px'>Binary Compatibility</td></tr>\n"; # Run-time
6869            }
6870            elsif($Level eq "Source") {
6871                $TestInfo .= "<tr><th>Subject</th><td width='150px'>Source Compatibility</td></tr>\n"; # Build-time
6872            }
6873        }
6874        $TestInfo .= "</table>\n";
6875
6876        # test results
6877        $TestResults = "<h2>Test Results</h2><hr/>\n";
6878        $TestResults .= "<table class='summary'>";
6879
6880        if(my @Headers = getCheckedHeaders(1))
6881        {
6882            my $Headers_Link = "<a href='#Headers' style='color:Blue;'>".($#Headers + 1)."</a>";
6883            $TestResults .= "<tr><th>Total Header Files</th><td>".$Headers_Link."</td></tr>\n";
6884        }
6885
6886        if(my @Sources = keys(%{$In::ABI{1}{"Sources"}}))
6887        {
6888            my $Src_Link = "<a href='#Sources' style='color:Blue;'>".($#Sources + 1)."</a>";
6889            $TestResults .= "<tr><th>Total Source Files</th><td>".$Src_Link."</td></tr>\n";
6890        }
6891
6892        if(not $In::Opt{"ExtendedCheck"})
6893        {
6894            my $Libs_Link = "0";
6895            $Libs_Link = "<a href='#Libs' style='color:Blue;'>".keys(%{$In::ABI{1}{"Symbols"}})."</a>" if(keys(%{$In::ABI{1}{"Symbols"}})>0);
6896            $TestResults .= "<tr><th>Total ".getObjTitle()."</th><td>".($In::Opt{"CheckHeadersOnly"}?"0&#160;(not&#160;analyzed)":$Libs_Link)."</td></tr>\n";
6897        }
6898
6899        $TestResults .= "<tr><th>Total Symbols / Types</th><td>".$TotalSymbols." / ".$TotalTypes."</td></tr>\n";
6900
6901        my $META_DATA = "verdict:".$RESULT{$Level}{"Verdict"}.";";
6902        if($In::Opt{"JoinReport"}) {
6903            $META_DATA = "kind:".lc($Level).";".$META_DATA;
6904        }
6905
6906        my $BC_Rate = showNum(100 - $RESULT{$Level}{"Affected"});
6907
6908        $TestResults .= "<tr><th>Compatibility</th>\n";
6909        if($RESULT{$Level}{"Verdict"} eq "incompatible")
6910        {
6911            my $Cl = "incompatible";
6912            if($BC_Rate>=90) {
6913                $Cl = "warning";
6914            }
6915            elsif($BC_Rate>=80) {
6916                $Cl = "almost_compatible";
6917            }
6918
6919            $TestResults .= "<td class=\'$Cl\'>".$BC_Rate."%</td>\n";
6920        }
6921        else {
6922            $TestResults .= "<td class=\'compatible\'>100%</td>\n";
6923        }
6924        $TestResults .= "</tr>\n";
6925        $TestResults .= "</table>\n";
6926
6927        $META_DATA .= "affected:".$RESULT{$Level}{"Affected"}.";";# in percents
6928        # problem summary
6929        $Problem_Summary = "<h2>Problem Summary</h2><hr/>\n";
6930        $Problem_Summary .= "<table class='summary'>";
6931        $Problem_Summary .= "<tr><th></th><th style='text-align:center;'>Severity</th><th style='text-align:center;'>Count</th></tr>";
6932
6933        my $Added_Link = "0";
6934        if($Added>0)
6935        {
6936            if($In::Opt{"JoinReport"}) {
6937                $Added_Link = "<a href='#".$Level."_Added' style='color:Blue;'>$Added</a>";
6938            }
6939            else {
6940                $Added_Link = "<a href='#Added' style='color:Blue;'>$Added</a>";
6941            }
6942        }
6943        $META_DATA .= "added:$Added;";
6944        $Problem_Summary .= "<tr><th>Added Symbols</th><td>-</td><td".getStyle("I", "Added", $Added).">$Added_Link</td></tr>\n";
6945
6946        my $Removed_Link = "0";
6947        if($Removed>0)
6948        {
6949            if($In::Opt{"JoinReport"}) {
6950                $Removed_Link = "<a href='#".$Level."_Removed' style='color:Blue;'>$Removed</a>"
6951            }
6952            else {
6953                $Removed_Link = "<a href='#Removed' style='color:Blue;'>$Removed</a>"
6954            }
6955        }
6956        $META_DATA .= "removed:$Removed;";
6957        $Problem_Summary .= "<tr><th>Removed Symbols</th>";
6958        $Problem_Summary .= "<td>High</td><td".getStyle("I", "Removed", $Removed).">$Removed_Link</td></tr>\n";
6959
6960        my $TH_Link = "0";
6961        $TH_Link = "<a href='#".getAnchor("Type", $Level, "High")."' style='color:Blue;'>$T_Problems_High</a>" if($T_Problems_High>0);
6962        $META_DATA .= "type_problems_high:$T_Problems_High;";
6963        $Problem_Summary .= "<tr><th rowspan='3'>Problems with<br/>Data Types</th>";
6964        $Problem_Summary .= "<td>High</td><td".getStyle("T", "High", $T_Problems_High).">$TH_Link</td></tr>\n";
6965
6966        my $TM_Link = "0";
6967        $TM_Link = "<a href='#".getAnchor("Type", $Level, "Medium")."' style='color:Blue;'>$T_Problems_Medium</a>" if($T_Problems_Medium>0);
6968        $META_DATA .= "type_problems_medium:$T_Problems_Medium;";
6969        $Problem_Summary .= "<tr><td>Medium</td><td".getStyle("T", "Medium", $T_Problems_Medium).">$TM_Link</td></tr>\n";
6970
6971        my $TL_Link = "0";
6972        $TL_Link = "<a href='#".getAnchor("Type", $Level, "Low")."' style='color:Blue;'>$T_Problems_Low</a>" if($T_Problems_Low>0);
6973        $META_DATA .= "type_problems_low:$T_Problems_Low;";
6974        $Problem_Summary .= "<tr><td>Low</td><td".getStyle("T", "Low", $T_Problems_Low).">$TL_Link</td></tr>\n";
6975
6976        my $IH_Link = "0";
6977        $IH_Link = "<a href='#".getAnchor("Symbol", $Level, "High")."' style='color:Blue;'>$I_Problems_High</a>" if($I_Problems_High>0);
6978        $META_DATA .= "interface_problems_high:$I_Problems_High;";
6979        $Problem_Summary .= "<tr><th rowspan='3'>Problems with<br/>Symbols</th>";
6980        $Problem_Summary .= "<td>High</td><td".getStyle("I", "High", $I_Problems_High).">$IH_Link</td></tr>\n";
6981
6982        my $IM_Link = "0";
6983        $IM_Link = "<a href='#".getAnchor("Symbol", $Level, "Medium")."' style='color:Blue;'>$I_Problems_Medium</a>" if($I_Problems_Medium>0);
6984        $META_DATA .= "interface_problems_medium:$I_Problems_Medium;";
6985        $Problem_Summary .= "<tr><td>Medium</td><td".getStyle("I", "Medium", $I_Problems_Medium).">$IM_Link</td></tr>\n";
6986
6987        my $IL_Link = "0";
6988        $IL_Link = "<a href='#".getAnchor("Symbol", $Level, "Low")."' style='color:Blue;'>$I_Problems_Low</a>" if($I_Problems_Low>0);
6989        $META_DATA .= "interface_problems_low:$I_Problems_Low;";
6990        $Problem_Summary .= "<tr><td>Low</td><td".getStyle("I", "Low", $I_Problems_Low).">$IL_Link</td></tr>\n";
6991
6992        my $ChangedConstants_Link = "0";
6993        if(keys(%{$CheckedSymbols{$Level}}) and $C_Problems_Low) {
6994            $ChangedConstants_Link = "<a href='#".getAnchor("Constant", $Level, "Low")."' style='color:Blue;'>$C_Problems_Low</a>";
6995        }
6996        $META_DATA .= "changed_constants:$C_Problems_Low;";
6997        $Problem_Summary .= "<tr><th>Problems with<br/>Constants</th><td>Low</td><td".getStyle("C", "Low", $C_Problems_Low).">$ChangedConstants_Link</td></tr>\n";
6998
6999        # Safe Changes
7000        if($T_Other)
7001        {
7002            my $TS_Link = "<a href='#".getAnchor("Type", $Level, "Safe")."' style='color:Blue;'>$T_Other</a>";
7003            $Problem_Summary .= "<tr><th>Other Changes<br/>in Data Types</th><td>-</td><td".getStyle("T", "Safe", $T_Other).">$TS_Link</td></tr>\n";
7004            $META_DATA .= "type_changes_other:$T_Other;";
7005        }
7006
7007        if($I_Other)
7008        {
7009            my $IS_Link = "<a href='#".getAnchor("Symbol", $Level, "Safe")."' style='color:Blue;'>$I_Other</a>";
7010            $Problem_Summary .= "<tr><th>Other Changes<br/>in Symbols</th><td>-</td><td".getStyle("I", "Safe", $I_Other).">$IS_Link</td></tr>\n";
7011            $META_DATA .= "interface_changes_other:$I_Other;";
7012        }
7013
7014        if($C_Other)
7015        {
7016            my $CS_Link = "<a href='#".getAnchor("Constant", $Level, "Safe")."' style='color:Blue;'>$C_Other</a>";
7017            $Problem_Summary .= "<tr><th>Other Changes<br/>in Constants</th><td>-</td><td".getStyle("C", "Safe", $C_Other).">$CS_Link</td></tr>\n";
7018            $META_DATA .= "constant_changes_other:$C_Other;";
7019        }
7020
7021        $META_DATA .= "tool_version:$TOOL_VERSION";
7022        $Problem_Summary .= "</table>\n";
7023
7024        return ($TestInfo.$TestResults.$Problem_Summary, $META_DATA, $AnyChanged);
7025    }
7026}
7027
7028sub getStyle($$$)
7029{
7030    my ($Subj, $Act, $Num) = @_;
7031    my %Style = (
7032        "Added"=>"new",
7033        "Removed"=>"failed",
7034        "Safe"=>"passed",
7035        "Low"=>"warning",
7036        "Medium"=>"failed",
7037        "High"=>"failed"
7038    );
7039
7040    if($Num>0) {
7041        return " class='".$Style{$Act}."'";
7042    }
7043
7044    return "";
7045}
7046
7047sub getReportChangedConstants($$)
7048{
7049    my ($TargetSeverity, $Level) = @_;
7050    my $CHANGED_CONSTANTS = "";
7051
7052    my %ReportMap = ();
7053    foreach my $Constant (keys(%{$CompatProblems_Constants{$Level}}))
7054    {
7055        my $Header = $Constants{1}{$Constant}{"Header"};
7056        if(not $Header) {
7057            $Header = $Constants{1}{$Constant}{"Source"};
7058        }
7059        if(not $Header)
7060        { # added
7061            $Header = $Constants{2}{$Constant}{"Header"};
7062            if(not $Header) {
7063                $Header = $Constants{2}{$Constant}{"Source"}
7064            }
7065        }
7066
7067        foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
7068        {
7069            if(not defined $CompatRules{$Level}{$Kind}) {
7070                next;
7071            }
7072            if($TargetSeverity ne $CompatRules{$Level}{$Kind}{"Severity"}) {
7073                next;
7074            }
7075            $ReportMap{$Header}{$Constant}{$Kind} = 1;
7076        }
7077    }
7078
7079    if($In::Opt{"ReportFormat"} eq "xml")
7080    { # XML
7081        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
7082        {
7083            $CHANGED_CONSTANTS .= "  <header name=\"$HeaderName\">\n";
7084            foreach my $Constant (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
7085            {
7086                $CHANGED_CONSTANTS .= "    <constant name=\"$Constant\">\n";
7087                foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$Constant}}))
7088                {
7089                    my $Change = $CompatRules{$Level}{$Kind}{"Change"};
7090                    my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
7091                    my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"};
7092
7093                    $CHANGED_CONSTANTS .= "      <problem id=\"$Kind\">\n";
7094                    $CHANGED_CONSTANTS .= "        <change".getXmlParams($Change, $CompatProblems_Constants{$Level}{$Constant}{$Kind}).">$Change</change>\n";
7095                    $CHANGED_CONSTANTS .= "        <effect".getXmlParams($Effect, $CompatProblems_Constants{$Level}{$Constant}{$Kind}).">$Effect</effect>\n";
7096                    if($Overcome) {
7097                        $CHANGED_CONSTANTS .= "        <overcome".getXmlParams($Overcome, $CompatProblems_Constants{$Level}{$Constant}{$Kind}).">$Overcome</overcome>\n";
7098                    }
7099                    $CHANGED_CONSTANTS .= "      </problem>\n";
7100                }
7101                $CHANGED_CONSTANTS .= "    </constant>\n";
7102            }
7103            $CHANGED_CONSTANTS .= "    </header>\n";
7104        }
7105        $CHANGED_CONSTANTS = "<problems_with_constants severity=\"Low\">\n".$CHANGED_CONSTANTS."</problems_with_constants>\n\n";
7106    }
7107    else
7108    { # HTML
7109        my $ProblemsNum = 0;
7110        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
7111        {
7112            $CHANGED_CONSTANTS .= "<span class='h_name'>$HeaderName</span><br/>\n";
7113            foreach my $Constant (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
7114            {
7115                my $Report = "";
7116
7117                foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$Constant}}))
7118                {
7119                    my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $CompatProblems_Constants{$Level}{$Constant}{$Kind});
7120                    my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
7121                    $Report .= "<tr>\n<th>1</th>\n<td>".$Change."</td>\n<td>$Effect</td>\n</tr>\n";
7122                    $ProblemsNum += 1;
7123                }
7124                if($Report)
7125                {
7126                    $Report = $ContentDivStart."<table class='ptable'>\n<tr>\n<th class='pn'></th>\n<th class='chg'>Change</th>\n<th>Effect</th>\n</tr>\n".$Report."</table>\n<br/>\n$ContentDivEnd\n";
7127                    $Report = $ContentSpanStart."<span class='ext'>[+]</span> ".$Constant.$ContentSpanEnd."<br/>\n".$Report;
7128                    $Report = insertIDs($Report);
7129                }
7130                $CHANGED_CONSTANTS .= $Report;
7131            }
7132            $CHANGED_CONSTANTS .= "<br/>\n";
7133        }
7134        if($CHANGED_CONSTANTS)
7135        {
7136            my $Title = "Problems with Constants, $TargetSeverity Severity";
7137            if($TargetSeverity eq "Safe")
7138            { # Safe Changes
7139                $Title = "Other Changes in Constants";
7140            }
7141            if($In::Opt{"OldStyle"}) {
7142                $CHANGED_CONSTANTS = "<h2>$Title ($ProblemsNum)</h2><hr/>\n".$CHANGED_CONSTANTS;
7143            }
7144            else {
7145                $CHANGED_CONSTANTS = "<h2>$Title <span".getStyle("C", $TargetSeverity, $ProblemsNum).">&nbsp;$ProblemsNum&nbsp;</span></h2><hr/>\n".$CHANGED_CONSTANTS;
7146            }
7147            $CHANGED_CONSTANTS = "<a name='".getAnchor("Constant", $Level, $TargetSeverity)."'></a>\n".$CHANGED_CONSTANTS.$TOP_REF."<br/>\n";
7148        }
7149    }
7150    return $CHANGED_CONSTANTS;
7151}
7152
7153sub getTitle($$$)
7154{
7155    my ($Header, $Library, $NameSpace) = @_;
7156    my $Title = "";
7157
7158    if($Header and $Library)
7159    {
7160        $Title .= "<span class='h_name'>$Header</span>";
7161        $Title .= ", <span class='lib_name'>$Library</span><br/>\n";
7162    }
7163    elsif($Library) {
7164        $Title .= "<span class='lib_name'>$Library</span><br/>\n";
7165    }
7166    elsif($Header) {
7167        $Title .= "<span class='h_name'>$Header</span><br/>\n";
7168    }
7169
7170    if($NameSpace) {
7171        $Title .= "<span class='ns'>namespace <b>$NameSpace</b></span><br/>\n";
7172    }
7173
7174    return $Title;
7175}
7176
7177sub getReportAdded($)
7178{
7179    my $Level = $_[0];
7180    my $ADDED_INTERFACES = "";
7181    my %ReportMap = ();
7182    foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
7183    {
7184        foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
7185        {
7186            if($Kind eq "Added_Symbol")
7187            {
7188                my $HeaderName = $CompSign{2}{$Symbol}{"Header"};
7189                if(not $HeaderName) {
7190                    $HeaderName = $CompSign{2}{$Symbol}{"Source"};
7191                }
7192
7193                my $DyLib = $In::ABI{2}{"SymLib"}{$Symbol};
7194                if($Level eq "Source" and $In::Opt{"ReportFormat"} eq "html")
7195                { # do not show library name in the HTML report
7196                    $DyLib = "";
7197                }
7198                $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1;
7199            }
7200        }
7201    }
7202    if($In::Opt{"ReportFormat"} eq "xml")
7203    { # XML
7204        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
7205        {
7206            $ADDED_INTERFACES .= "  <header name=\"$HeaderName\">\n";
7207            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
7208            {
7209                $ADDED_INTERFACES .= "    <library name=\"$DyLib\">\n";
7210                foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
7211                    $ADDED_INTERFACES .= "      <name>$Symbol</name>\n";
7212                }
7213                $ADDED_INTERFACES .= "    </library>\n";
7214            }
7215            $ADDED_INTERFACES .= "  </header>\n";
7216        }
7217        $ADDED_INTERFACES = "<added_symbols>\n".$ADDED_INTERFACES."</added_symbols>\n\n";
7218    }
7219    else
7220    { # HTML
7221        my $Added_Number = 0;
7222        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
7223        {
7224            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
7225            {
7226                my %NameSpaceSymbols = ();
7227                foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
7228                    $NameSpaceSymbols{selectSymbolNs($Symbol, 2)}{$Symbol} = 1;
7229                }
7230                foreach my $NameSpace (sort keys(%NameSpaceSymbols))
7231                {
7232                    $ADDED_INTERFACES .= getTitle($HeaderName, $DyLib, $NameSpace);
7233                    my @SortedInterfaces = sort {lc($CompSign{2}{$a}{"Unmangled"}) cmp lc($CompSign{2}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$NameSpaceSymbols{$NameSpace}});
7234                    foreach my $Symbol (@SortedInterfaces)
7235                    {
7236                        $Added_Number += 1;
7237                        my $Signature = highLight_ItalicColor($Symbol, 2);
7238                        if($NameSpace) {
7239                            $Signature = cutNs($Signature, $NameSpace);
7240                        }
7241
7242                        if($Symbol=~/\A(_Z|\?)/) {
7243                            $ADDED_INTERFACES .= insertIDs($ContentSpanStart.$Signature.$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mngl'>$Symbol</span>\n<br/>\n<br/>\n".$ContentDivEnd."\n");
7244                        }
7245                        else {
7246                            $ADDED_INTERFACES .= "<span class=\"iname\">".$Signature."</span><br/>\n";
7247                        }
7248                    }
7249                    $ADDED_INTERFACES .= "<br/>\n";
7250                }
7251            }
7252        }
7253        if($ADDED_INTERFACES)
7254        {
7255            my $Anchor = "<a name='Added'></a>";
7256            if($In::Opt{"JoinReport"}) {
7257                $Anchor = "<a name='".$Level."_Added'></a>";
7258            }
7259            if($In::Opt{"OldStyle"}) {
7260                $ADDED_INTERFACES = "<h2>Added Symbols ($Added_Number)</h2><hr/>\n".$ADDED_INTERFACES;
7261            }
7262            else {
7263                $ADDED_INTERFACES = "<h2>Added Symbols <span".getStyle("I", "Added", $Added_Number).">&nbsp;$Added_Number&nbsp;</span></h2><hr/>\n".$ADDED_INTERFACES;
7264            }
7265            $ADDED_INTERFACES = $Anchor.$ADDED_INTERFACES.$TOP_REF."<br/>\n";
7266        }
7267    }
7268    return $ADDED_INTERFACES;
7269}
7270
7271sub getReportRemoved($)
7272{
7273    my $Level = $_[0];
7274    my $REMOVED_INTERFACES = "";
7275    my %ReportMap = ();
7276    foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
7277    {
7278        foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
7279        {
7280            if($Kind eq "Removed_Symbol")
7281            {
7282                my $HeaderName = $CompSign{1}{$Symbol}{"Header"};
7283                if(not $HeaderName) {
7284                    $HeaderName = $CompSign{1}{$Symbol}{"Source"};
7285                }
7286
7287                my $DyLib = $In::ABI{1}{"SymLib"}{$Symbol};
7288                if($Level eq "Source" and $In::Opt{"ReportFormat"} eq "html")
7289                { # do not show library name in HTML report
7290                    $DyLib = "";
7291                }
7292                $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1;
7293            }
7294        }
7295    }
7296    if($In::Opt{"ReportFormat"} eq "xml")
7297    { # XML
7298        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
7299        {
7300            $REMOVED_INTERFACES .= "  <header name=\"$HeaderName\">\n";
7301            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
7302            {
7303                $REMOVED_INTERFACES .= "    <library name=\"$DyLib\">\n";
7304                foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
7305                    $REMOVED_INTERFACES .= "      <name>$Symbol</name>\n";
7306                }
7307                $REMOVED_INTERFACES .= "    </library>\n";
7308            }
7309            $REMOVED_INTERFACES .= "  </header>\n";
7310        }
7311        $REMOVED_INTERFACES = "<removed_symbols>\n".$REMOVED_INTERFACES."</removed_symbols>\n\n";
7312    }
7313    else
7314    { # HTML
7315        my $Removed_Number = 0;
7316        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
7317        {
7318            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
7319            {
7320                my %NameSpaceSymbols = ();
7321                foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
7322                    $NameSpaceSymbols{selectSymbolNs($Interface, 1)}{$Interface} = 1;
7323                }
7324                foreach my $NameSpace (sort keys(%NameSpaceSymbols))
7325                {
7326                    $REMOVED_INTERFACES .= getTitle($HeaderName, $DyLib, $NameSpace);
7327                    my @SortedInterfaces = sort {lc($CompSign{1}{$a}{"Unmangled"}) cmp lc($CompSign{1}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$NameSpaceSymbols{$NameSpace}});
7328                    foreach my $Symbol (@SortedInterfaces)
7329                    {
7330                        $Removed_Number += 1;
7331                        my $Signature = highLight_ItalicColor($Symbol, 1);
7332                        if($NameSpace) {
7333                            $Signature = cutNs($Signature, $NameSpace);
7334                        }
7335                        if($Symbol=~/\A(_Z|\?)/) {
7336                            $REMOVED_INTERFACES .= insertIDs($ContentSpanStart.$Signature.$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mngl'>$Symbol</span>\n<br/>\n<br/>\n".$ContentDivEnd."\n");
7337                        }
7338                        else {
7339                            $REMOVED_INTERFACES .= "<span class=\"iname\">".$Signature."</span><br/>\n";
7340                        }
7341                    }
7342                }
7343                $REMOVED_INTERFACES .= "<br/>\n";
7344            }
7345        }
7346        if($REMOVED_INTERFACES)
7347        {
7348            my $Anchor = "<a name='Removed'></a><a name='Withdrawn'></a>";
7349            if($In::Opt{"JoinReport"}) {
7350                $Anchor = "<a name='".$Level."_Removed'></a><a name='".$Level."_Withdrawn'></a>";
7351            }
7352            if($In::Opt{"OldStyle"}) {
7353                $REMOVED_INTERFACES = "<h2>Removed Symbols ($Removed_Number)</h2><hr/>\n".$REMOVED_INTERFACES;
7354            }
7355            else {
7356                $REMOVED_INTERFACES = "<h2>Removed Symbols <span".getStyle("I", "Removed", $Removed_Number).">&nbsp;$Removed_Number&nbsp;</span></h2><hr/>\n".$REMOVED_INTERFACES;
7357            }
7358
7359            $REMOVED_INTERFACES = $Anchor.$REMOVED_INTERFACES.$TOP_REF."<br/>\n";
7360        }
7361    }
7362    return $REMOVED_INTERFACES;
7363}
7364
7365sub getXmlParams($$)
7366{
7367    my ($Content, $Problem) = @_;
7368
7369    my %XMLparams = ();
7370    foreach my $Attr (sort {$b cmp $a} keys(%{$Problem}))
7371    {
7372        my $Macro = "\@".lc($Attr);
7373        if($Content=~/\Q$Macro\E/)
7374        {
7375            my $Value = $Problem->{$Attr};
7376
7377            if($Attr eq "Param_Pos") {
7378                $Value = showPos($Value);
7379            }
7380
7381            $XMLparams{lc($Attr)} = $Value;
7382        }
7383    }
7384    my @PString = ();
7385    foreach my $P (sort {$b cmp $a} keys(%XMLparams)) {
7386        push(@PString, $P."=\"".xmlSpecChars($XMLparams{$P})."\"");
7387    }
7388    if(@PString) {
7389        return " ".join(" ", @PString);
7390    }
7391    return "";
7392}
7393
7394sub addMarkup($)
7395{
7396    my $Content = $_[0];
7397
7398    # auto-markup
7399    $Content=~s/\n[ ]*//; # spaces
7400    $Content=~s!(\@\w+\s*\(\@\w+\))!<nowrap>$1</nowrap>!g; # @old_type (@old_size)
7401    $Content=~s!(... \(\w+\))!<nowrap><b>$1</b></nowrap>!g; # ... (va_list)
7402    $Content=~s!<nowrap>(.+?)</nowrap>!<span class='nowrap'>$1</span>!g;
7403    $Content=~s!([2-9]\))!<br/>$1!g; # 1), 2), ...
7404
7405    if($Content=~/\ANOTE:/)
7406    { # notes
7407        $Content=~s!(NOTE):!<b>$1</b>:!g;
7408    }
7409    else {
7410        $Content=~s!(NOTE):!<br/><br/><b>$1</b>:!g;
7411    }
7412    $Content=~s! (out)-! <b>$1</b>-!g; # out-parameters
7413
7414    my @Keywords = (
7415        "void",
7416        "const",
7417        "static",
7418        "restrict",
7419        "volatile",
7420        "register",
7421        "virtual"
7422    );
7423    my $MKeys = join("|", @Keywords);
7424    foreach (@Keywords) {
7425        $MKeys .= "|non-".$_;
7426    }
7427    $Content=~s!(added\s*|to\s*|from\s*|became\s*)($MKeys)([^\w-]|\Z)!$1<b>$2</b>$3!ig; # intrinsic types, modifiers
7428
7429    # Markdown
7430    $Content=~s!\*\*([\w\-]+?)\*\*!<b>$1</b>!ig;
7431    $Content=~s!\*([\w\-]+?)\*!<i>$1</i>!ig;
7432
7433    return $Content;
7434}
7435
7436sub applyMacroses($$$$)
7437{
7438    my ($Level, $Kind, $Content, $Problem) = @_;
7439
7440    $Problem->{"Word_Size"} = $In::ABI{2}{"WordSize"};
7441    $Content = addMarkup($Content);
7442
7443    # macros
7444    foreach my $Attr (sort {$b cmp $a} keys(%{$Problem}))
7445    {
7446        my $Macro = "\@".lc($Attr);
7447        my $Value = $Problem->{$Attr};
7448        if(not defined $Value
7449        or $Value eq "") {
7450            next;
7451        }
7452
7453        if(index($Content, $Macro)==-1) {
7454            next;
7455        }
7456
7457        if($Attr eq "Param_Pos") {
7458            $Value = showPos($Value);
7459        }
7460
7461        if($Value=~/\s/) {
7462            $Value = "<span class='value'>".specChars($Value)."</span>";
7463        }
7464        elsif($Value=~/\A\d+\Z/
7465        and ($Attr eq "Old_Size" or $Attr eq "New_Size"))
7466        { # bits to bytes
7467            if($Value % $BYTE)
7468            { # bits
7469                if($Value==1) {
7470                    $Value = "<b>".$Value."</b> bit";
7471                }
7472                else {
7473                    $Value = "<b>".$Value."</b> bits";
7474                }
7475            }
7476            else
7477            { # bytes
7478                $Value /= $BYTE;
7479                if($Value==1) {
7480                    $Value = "<b>".$Value."</b> byte";
7481                }
7482                else {
7483                    $Value = "<b>".$Value."</b> bytes";
7484                }
7485            }
7486        }
7487        else
7488        {
7489            my $Fmt = "Class|Name|Qual|HTML|Desc";
7490            if($Kind!~/Overridden/) {
7491                $Fmt = "Name|Qual|HTML|Desc";
7492            }
7493
7494            my $V1 = (defined $CompSign{1}{$Value} and defined $CompSign{1}{$Value}{"ShortName"});
7495            my $V2 = (defined $CompSign{2}{$Value} and defined $CompSign{2}{$Value}{"ShortName"});
7496
7497            if($Kind!~/Symbol_Became|Symbol_Changed|Method_Became/
7498            and ($V1 or $V2))
7499            { # symbols
7500                if($V1) {
7501                    $Value = blackName(getSignature($Value, 1, $Fmt));
7502                }
7503                else {
7504                    $Value = blackName(getSignature($Value, 2, $Fmt));
7505                }
7506            }
7507            else
7508            {
7509                $Value = "<b>".specChars($Value)."</b>";
7510            }
7511        }
7512        $Content=~s/\Q$Macro\E/$Value/g;
7513    }
7514
7515    if($Content=~/(\A|[^\@\w])\@\w/)
7516    {
7517        if(not $IncompleteRules{$Level}{$Kind})
7518        { # only one warning
7519            printMsg("WARNING", "incomplete rule \"$Kind\" (\"$Level\")");
7520            $IncompleteRules{$Level}{$Kind} = 1;
7521        }
7522    }
7523    return $Content;
7524}
7525
7526sub getReportSymbolProblems($$)
7527{
7528    my ($TargetSeverity, $Level) = @_;
7529    my $INTERFACE_PROBLEMS = "";
7530    my (%ReportMap, %SymbolChanges) = ();
7531
7532    foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
7533    {
7534        my ($SN, $SS, $SV) = symbolParts($Symbol);
7535        if($SV and defined $CompatProblems{$Level}{$SN}) {
7536            next;
7537        }
7538
7539        if(not defined $CompSign{1}{$Symbol})
7540        { # added symbols
7541            next;
7542        }
7543
7544        my $HeaderName = $CompSign{1}{$Symbol}{"Header"};
7545        if(not $HeaderName) {
7546            $HeaderName = $CompSign{1}{$Symbol}{"Source"};
7547        }
7548
7549        my $DyLib = $In::ABI{1}{"SymLib"}{$Symbol};
7550        if(not $DyLib and my $VSym = $In::ABI{1}{"SymbolVersion"}{$Symbol})
7551        { # Symbol with Version
7552            $DyLib = $In::ABI{1}{"SymLib"}{$VSym};
7553        }
7554        if(not $DyLib)
7555        { # const global data
7556            $DyLib = "";
7557        }
7558        if($Level eq "Source" and $In::Opt{"ReportFormat"} eq "html")
7559        { # do not show library name in HTML report
7560            $DyLib = "";
7561        }
7562
7563        foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
7564        {
7565            if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols"
7566            and $Kind ne "Added_Symbol" and $Kind ne "Removed_Symbol")
7567            {
7568                my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
7569                if($Severity eq $TargetSeverity)
7570                {
7571                    $SymbolChanges{$Symbol}{$Kind} = $CompatProblems{$Level}{$Symbol}{$Kind};
7572                    $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1;
7573                }
7574            }
7575        }
7576    }
7577
7578    if($In::Opt{"ReportFormat"} eq "xml")
7579    { # XML
7580        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
7581        {
7582            $INTERFACE_PROBLEMS .= "  <header name=\"$HeaderName\">\n";
7583            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
7584            {
7585                $INTERFACE_PROBLEMS .= "    <library name=\"$DyLib\">\n";
7586
7587                my @SortedInterfaces = sort {lc($CompSign{1}{$a}{"Unmangled"}) cmp lc($CompSign{1}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$DyLib}});
7588                foreach my $Symbol (@SortedInterfaces)
7589                {
7590                    $INTERFACE_PROBLEMS .= "      <symbol name=\"$Symbol\">\n";
7591                    foreach my $Kind (sort keys(%{$SymbolChanges{$Symbol}}))
7592                    {
7593                        foreach my $Loc (sort keys(%{$SymbolChanges{$Symbol}{$Kind}}))
7594                        {
7595                            my $ProblemAttr = $SymbolChanges{$Symbol}{$Kind}{$Loc};
7596
7597                            $INTERFACE_PROBLEMS .= "        <problem id=\"$Kind\">\n";
7598                            my $Change = $CompatRules{$Level}{$Kind}{"Change"};
7599                            $INTERFACE_PROBLEMS .= "          <change".getXmlParams($Change, $ProblemAttr).">$Change</change>\n";
7600                            my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
7601                            $INTERFACE_PROBLEMS .= "          <effect".getXmlParams($Effect, $ProblemAttr).">$Effect</effect>\n";
7602                            if(my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"}) {
7603                                $INTERFACE_PROBLEMS .= "          <overcome".getXmlParams($Overcome, $ProblemAttr).">$Overcome</overcome>\n";
7604                            }
7605                            $INTERFACE_PROBLEMS .= "        </problem>\n";
7606                        }
7607                    }
7608                    $INTERFACE_PROBLEMS .= "      </symbol>\n";
7609                }
7610                $INTERFACE_PROBLEMS .= "    </library>\n";
7611            }
7612            $INTERFACE_PROBLEMS .= "  </header>\n";
7613        }
7614        $INTERFACE_PROBLEMS = "<problems_with_symbols severity=\"$TargetSeverity\">\n".$INTERFACE_PROBLEMS."</problems_with_symbols>\n\n";
7615    }
7616    else
7617    { # HTML
7618        my $ProblemsNum = 0;
7619        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
7620        {
7621            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
7622            {
7623                my (%NameSpaceSymbols, %NewSignature) = ();
7624                foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
7625                    $NameSpaceSymbols{selectSymbolNs($Symbol, 1)}{$Symbol} = 1;
7626                }
7627                foreach my $NameSpace (sort keys(%NameSpaceSymbols))
7628                {
7629                    $INTERFACE_PROBLEMS .= getTitle($HeaderName, $DyLib, $NameSpace);
7630
7631                    my @SortedInterfaces = sort {lc($CompSign{1}{$a}{"Unmangled"}) cmp lc($CompSign{1}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$NameSpaceSymbols{$NameSpace}});
7632                    foreach my $Symbol (@SortedInterfaces)
7633                    {
7634                        my $SYMBOL_REPORT = "";
7635                        my $ProblemNum = 1;
7636                        foreach my $Kind (sort keys(%{$SymbolChanges{$Symbol}}))
7637                        {
7638                            foreach my $Loc (sort keys(%{$SymbolChanges{$Symbol}{$Kind}}))
7639                            {
7640                                my $ProblemAttr = $SymbolChanges{$Symbol}{$Kind}{$Loc};
7641
7642                                if(my $NSign = $ProblemAttr->{"New_Signature"}) {
7643                                    $NewSignature{$Symbol} = $NSign;
7644                                }
7645
7646                                if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $ProblemAttr))
7647                                {
7648                                    my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, $ProblemAttr);
7649                                    $SYMBOL_REPORT .= "<tr>\n<th>$ProblemNum</th>\n<td>".$Change."</td>\n<td>".$Effect."</td>\n</tr>\n";
7650                                    $ProblemNum += 1;
7651                                    $ProblemsNum += 1;
7652                                }
7653                            }
7654                        }
7655                        $ProblemNum -= 1;
7656                        if($SYMBOL_REPORT)
7657                        {
7658                            my $ShowSymbol = highLight_ItalicColor($Symbol, 1);
7659
7660                            if($NameSpace)
7661                            {
7662                                $SYMBOL_REPORT = cutNs($SYMBOL_REPORT, $NameSpace);
7663                                $ShowSymbol = cutNs($ShowSymbol, $NameSpace);
7664                            }
7665
7666                            $INTERFACE_PROBLEMS .= $ContentSpanStart."<span class='ext'>[+]</span> ".$ShowSymbol;
7667                            if($In::Opt{"OldStyle"}) {
7668                                $INTERFACE_PROBLEMS .= " ($ProblemNum)";
7669                            }
7670                            else {
7671                                $INTERFACE_PROBLEMS .= " <span".getStyle("I", $TargetSeverity, $ProblemNum).">&nbsp;$ProblemNum&nbsp;</span>";
7672                            }
7673                            $INTERFACE_PROBLEMS .= $ContentSpanEnd."<br/>\n";
7674                            $INTERFACE_PROBLEMS .= $ContentDivStart."\n";
7675
7676                            if(my $NSign = $NewSignature{$Symbol})
7677                            { # argument list changed to
7678                                $NSign = highLight_ItalicColor($NSign, 2);
7679                                if($NameSpace) {
7680                                    $NSign = cutNs($NSign, $NameSpace);
7681                                }
7682                                $INTERFACE_PROBLEMS .= "\n<span class='new_sign_lbl'>&#8675;</span>\n<br/>\n<span class='new_sign'>".$NSign."</span><br/>\n";
7683                            }
7684
7685                            if($Symbol=~/\A(_Z|\?)/) {
7686                                $INTERFACE_PROBLEMS .= "<span class='mngl pleft'>$Symbol</span><br/>\n";
7687                            }
7688
7689                            $INTERFACE_PROBLEMS .= "<table class='ptable'>\n<tr>\n<th class='pn'></th>\n<th class='chg'>Change</th>\n<th>Effect</th>\n</tr>\n$SYMBOL_REPORT</table>\n<br/>\n";
7690                            $INTERFACE_PROBLEMS .= $ContentDivEnd;
7691                        }
7692                    }
7693                    $INTERFACE_PROBLEMS .= "<br/>\n";
7694                }
7695            }
7696        }
7697
7698        if($INTERFACE_PROBLEMS)
7699        {
7700            $INTERFACE_PROBLEMS = insertIDs($INTERFACE_PROBLEMS);
7701            my $Title = "Problems with Symbols, $TargetSeverity Severity";
7702            if($TargetSeverity eq "Safe")
7703            { # Safe Changes
7704                $Title = "Other Changes in Symbols";
7705            }
7706            if($In::Opt{"OldStyle"}) {
7707                $INTERFACE_PROBLEMS = "<h2>$Title ($ProblemsNum)</h2><hr/>\n".$INTERFACE_PROBLEMS;
7708            }
7709            else {
7710                $INTERFACE_PROBLEMS = "<h2>$Title <span".getStyle("I", $TargetSeverity, $ProblemsNum).">&nbsp;$ProblemsNum&nbsp;</span></h2><hr/>\n".$INTERFACE_PROBLEMS;
7711            }
7712            $INTERFACE_PROBLEMS = "<a name=\'".getAnchor("Symbol", $Level, $TargetSeverity)."\'></a><a name=\'".getAnchor("Interface", $Level, $TargetSeverity)."\'></a>\n".$INTERFACE_PROBLEMS.$TOP_REF."<br/>\n";
7713        }
7714    }
7715    return $INTERFACE_PROBLEMS;
7716}
7717
7718sub cutNs($$)
7719{
7720    my ($N, $Ns) = @_;
7721    $N=~s/\b\Q$Ns\E:://g;
7722    return $N;
7723}
7724
7725sub getReportTypeProblems($$)
7726{
7727    my ($TargetSeverity, $Level) = @_;
7728    my $TYPE_PROBLEMS = "";
7729
7730    my %ReportMap = ();
7731    my %TypeChanges_Sev = ();
7732
7733    foreach my $TypeName (keys(%{$TypeChanges{$Level}}))
7734    {
7735        my $Tid = $TName_Tid{1}{$TypeName};
7736        my $HeaderName = $TypeInfo{1}{$Tid}{"Header"};
7737        if(not $HeaderName) {
7738            $HeaderName = $TypeInfo{1}{$Tid}{"Source"};
7739        }
7740
7741        foreach my $Kind (keys(%{$TypeChanges{$Level}{$TypeName}}))
7742        {
7743            if($CompatRules{$Level}{$Kind}{"Severity"} ne $TargetSeverity) {
7744                next;
7745            }
7746
7747            foreach my $Loc (keys(%{$TypeChanges{$Level}{$TypeName}{$Kind}}))
7748            {
7749                $ReportMap{$HeaderName}{$TypeName} = 1;
7750                $TypeChanges_Sev{$TypeName}{$Kind}{$Loc} = $TypeChanges{$Level}{$TypeName}{$Kind}{$Loc};
7751            }
7752        }
7753    }
7754
7755    if($In::Opt{"ReportFormat"} eq "xml")
7756    { # XML
7757        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
7758        {
7759            $TYPE_PROBLEMS .= "  <header name=\"$HeaderName\">\n";
7760            foreach my $TypeName (keys(%{$ReportMap{$HeaderName}}))
7761            {
7762                my (%Kinds_Locations, %Kinds_Target) = ();
7763                $TYPE_PROBLEMS .= "    <type name=\"".xmlSpecChars($TypeName)."\">\n";
7764                foreach my $Kind (sort {$b=~/Size/ <=> $a=~/Size/} sort keys(%{$TypeChanges_Sev{$TypeName}}))
7765                {
7766                    foreach my $Loc (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges_Sev{$TypeName}{$Kind}}))
7767                    {
7768                        $Kinds_Locations{$Kind}{$Loc} = 1;
7769
7770                        my $Target = $TypeChanges_Sev{$TypeName}{$Kind}{$Loc}{"Target"};
7771                        if($Kinds_Target{$Kind}{$Target}) {
7772                            next;
7773                        }
7774                        $Kinds_Target{$Kind}{$Target} = 1;
7775
7776                        my $ProblemAttr = $TypeChanges_Sev{$TypeName}{$Kind}{$Loc};
7777                        $TYPE_PROBLEMS .= "      <problem id=\"$Kind\">\n";
7778                        my $Change = $CompatRules{$Level}{$Kind}{"Change"};
7779                        $TYPE_PROBLEMS .= "        <change".getXmlParams($Change, $ProblemAttr).">$Change</change>\n";
7780                        my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
7781                        $TYPE_PROBLEMS .= "        <effect".getXmlParams($Effect, $ProblemAttr).">$Effect</effect>\n";
7782                        if(my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"}) {
7783                            $TYPE_PROBLEMS .= "        <overcome".getXmlParams($Overcome, $ProblemAttr).">$Overcome</overcome>\n";
7784                        }
7785                        $TYPE_PROBLEMS .= "      </problem>\n";
7786                    }
7787                }
7788                $TYPE_PROBLEMS .= getAffectedSymbols($Level, $TypeName, \%Kinds_Locations);
7789                if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%Kinds_Locations)) {
7790                    $TYPE_PROBLEMS .= showVTables($TypeName);
7791                }
7792                $TYPE_PROBLEMS .= "    </type>\n";
7793            }
7794            $TYPE_PROBLEMS .= "  </header>\n";
7795        }
7796        $TYPE_PROBLEMS = "<problems_with_types severity=\"$TargetSeverity\">\n".$TYPE_PROBLEMS."</problems_with_types>\n\n";
7797    }
7798    else
7799    { # HTML
7800        my $ProblemsNum = 0;
7801        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
7802        {
7803            my (%NameSpace_Type) = ();
7804            foreach my $TypeName (keys(%{$ReportMap{$HeaderName}})) {
7805                $NameSpace_Type{selectTypeNs($TypeName, 1)}{$TypeName} = 1;
7806            }
7807            foreach my $NameSpace (sort keys(%NameSpace_Type))
7808            {
7809                $TYPE_PROBLEMS .= getTitle($HeaderName, "", $NameSpace);
7810                my @SortedTypes = sort {lc(showType($a, 0, 1)) cmp lc(showType($b, 0, 1))} keys(%{$NameSpace_Type{$NameSpace}});
7811                foreach my $TypeName (@SortedTypes)
7812                {
7813                    my $ProblemNum = 1;
7814                    my $TYPE_REPORT = "";
7815                    my (%Kinds_Locations, %Kinds_Target) = ();
7816
7817                    foreach my $Kind (sort {(index($b, "Size")!=-1) cmp (index($a, "Size")!=-1)} sort keys(%{$TypeChanges_Sev{$TypeName}}))
7818                    {
7819                        foreach my $Loc (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges_Sev{$TypeName}{$Kind}}))
7820                        {
7821                            $Kinds_Locations{$Kind}{$Loc} = 1;
7822
7823                            my $Target = $TypeChanges_Sev{$TypeName}{$Kind}{$Loc}{"Target"};
7824                            if($Kinds_Target{$Kind}{$Target}) {
7825                                next;
7826                            }
7827                            $Kinds_Target{$Kind}{$Target} = 1;
7828
7829                            my $ProblemAttr = $TypeChanges_Sev{$TypeName}{$Kind}{$Loc};
7830                            if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $ProblemAttr))
7831                            {
7832                                my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, $ProblemAttr);
7833                                $TYPE_REPORT .= "<tr>\n<th>$ProblemNum</th>\n<td>".$Change."</td>\n<td>$Effect</td>\n</tr>\n";
7834                                $ProblemNum += 1;
7835                                $ProblemsNum += 1;
7836                            }
7837                        }
7838                    }
7839                    $ProblemNum -= 1;
7840                    if($TYPE_REPORT)
7841                    {
7842                        my $Affected = getAffectedSymbols($Level, $TypeName, \%Kinds_Locations);
7843                        my $ShowVTables = "";
7844                        if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%Kinds_Locations)) {
7845                            $ShowVTables = showVTables($TypeName);
7846                        }
7847
7848                        my $ShowType = showType($TypeName, 1, 1);
7849
7850                        if($NameSpace)
7851                        {
7852                            $TYPE_REPORT = cutNs($TYPE_REPORT, $NameSpace);
7853                            $ShowType = cutNs($ShowType, $NameSpace);
7854                            $Affected = cutNs($Affected, $NameSpace);
7855                            $ShowVTables = cutNs($ShowVTables, $NameSpace);
7856                        }
7857
7858                        $TYPE_PROBLEMS .= $ContentSpanStart."<span class='ext'>[+]</span> ".$ShowType;
7859                        if($In::Opt{"OldStyle"}) {
7860                            $TYPE_PROBLEMS .= " ($ProblemNum)";
7861                        }
7862                        else {
7863                            $TYPE_PROBLEMS .= " <span".getStyle("T", $TargetSeverity, $ProblemNum).">&nbsp;$ProblemNum&nbsp;</span>";
7864                        }
7865                        $TYPE_PROBLEMS .= $ContentSpanEnd;
7866                        $TYPE_PROBLEMS .= "<br/>\n".$ContentDivStart."<table class='ptable'><tr>\n";
7867                        $TYPE_PROBLEMS .= "<th class='pn'></th>\n<th class='chg'>Change</th>\n";
7868                        $TYPE_PROBLEMS .= "<th>Effect</th></tr>".$TYPE_REPORT."</table>\n";
7869                        $TYPE_PROBLEMS .= $ShowVTables.$Affected."<br/><br/>".$ContentDivEnd."\n";
7870                    }
7871                }
7872                $TYPE_PROBLEMS .= "<br/>\n";
7873            }
7874        }
7875
7876        if($TYPE_PROBLEMS)
7877        {
7878            $TYPE_PROBLEMS = insertIDs($TYPE_PROBLEMS);
7879            my $Title = "Problems with Data Types, $TargetSeverity Severity";
7880            if($TargetSeverity eq "Safe")
7881            { # Safe Changes
7882                $Title = "Other Changes in Data Types";
7883            }
7884            if($In::Opt{"OldStyle"}) {
7885                $TYPE_PROBLEMS = "<h2>$Title ($ProblemsNum)</h2><hr/>\n".$TYPE_PROBLEMS;
7886            }
7887            else {
7888                $TYPE_PROBLEMS = "<h2>$Title <span".getStyle("T", $TargetSeverity, $ProblemsNum).">&nbsp;$ProblemsNum&nbsp;</span></h2><hr/>\n".$TYPE_PROBLEMS;
7889            }
7890            $TYPE_PROBLEMS = "<a name=\'".getAnchor("Type", $Level, $TargetSeverity)."\'></a>\n".$TYPE_PROBLEMS.$TOP_REF."<br/>\n";
7891        }
7892    }
7893    return $TYPE_PROBLEMS;
7894}
7895
7896sub showType($$$)
7897{
7898    my ($Name, $Html, $LVer) = @_;
7899    my $TType = $TypeInfo{$LVer}{$TName_Tid{$LVer}{$Name}}{"Type"};
7900    $TType = lc($TType);
7901    if($TType=~/struct|union|enum/) {
7902        $Name=~s/\A\Q$TType\E //g;
7903    }
7904    if($Html) {
7905        $Name = "<span class='ttype'>".$TType."</span> ".specChars($Name);
7906    }
7907    else {
7908        $Name = $TType." ".$Name;
7909    }
7910    return $Name;
7911}
7912
7913sub getAnchor($$$)
7914{
7915    my ($Kind, $Level, $Severity) = @_;
7916    if($In::Opt{"JoinReport"})
7917    {
7918        if($Severity eq "Safe") {
7919            return "Other_".$Level."_Changes_In_".$Kind."s";
7920        }
7921        else {
7922            return $Kind."_".$Level."_Problems_".$Severity;
7923        }
7924    }
7925    else
7926    {
7927        if($Severity eq "Safe") {
7928            return "Other_Changes_In_".$Kind."s";
7929        }
7930        else {
7931            return $Kind."_Problems_".$Severity;
7932        }
7933    }
7934}
7935
7936sub showVTables($)
7937{
7938    my $TypeName = $_[0];
7939    my $TypeId1 = $TName_Tid{1}{$TypeName};
7940    my %Type1 = getType($TypeId1, 1);
7941    if(defined $Type1{"VTable"}
7942    and keys(%{$Type1{"VTable"}}))
7943    {
7944        my $TypeId2 = $TName_Tid{2}{$TypeName};
7945        my %Type2 = getType($TypeId2, 2);
7946        if(defined $Type2{"VTable"}
7947        and keys(%{$Type2{"VTable"}}))
7948        {
7949            my %Indexes = map {$_=>1} (keys(%{$Type1{"VTable"}}), keys(%{$Type2{"VTable"}}));
7950            my %Entries = ();
7951            foreach my $Index (sort {$a<=>$b} (keys(%Indexes)))
7952            {
7953                $Entries{$Index}{"E1"} = simpleVEntry($Type1{"VTable"}{$Index});
7954                $Entries{$Index}{"E2"} = simpleVEntry($Type2{"VTable"}{$Index});
7955            }
7956            my $VTABLES = "";
7957            if($In::Opt{"ReportFormat"} eq "xml")
7958            { # XML
7959                $VTABLES .= "      <vtable>\n";
7960                foreach my $Index (sort {$a<=>$b} (keys(%Entries)))
7961                {
7962                    $VTABLES .= "        <entry offset=\"".$Index."\">\n";
7963                    $VTABLES .= "          <old>".xmlSpecChars($Entries{$Index}{"E1"})."</old>\n";
7964                    $VTABLES .= "          <new>".xmlSpecChars($Entries{$Index}{"E2"})."</new>\n";
7965                    $VTABLES .= "        </entry>\n";
7966                }
7967                $VTABLES .= "      </vtable>\n\n";
7968            }
7969            else
7970            { # HTML
7971                $VTABLES .= "<table class='vtable'>";
7972                $VTABLES .= "<tr><th>Offset</th>";
7973                $VTABLES .= "<th>Virtual Table (Old) - ".(keys(%{$Type1{"VTable"}}))." entries</th>";
7974                $VTABLES .= "<th>Virtual Table (New) - ".(keys(%{$Type2{"VTable"}}))." entries</th></tr>";
7975                foreach my $Index (sort {$a<=>$b} (keys(%Entries)))
7976                {
7977                    my ($Color1, $Color2) = ("", "");
7978
7979                    my $E1 = $Entries{$Index}{"E1"};
7980                    my $E2 = $Entries{$Index}{"E2"};
7981
7982                    if($E1 ne $E2
7983                    and $E1!~/ 0x/
7984                    and $E2!~/ 0x/)
7985                    {
7986                        if($Entries{$Index}{"E1"})
7987                        {
7988                            $Color1 = " class='failed'";
7989                            $Color2 = " class='failed'";
7990                        }
7991                        else {
7992                            $Color2 = " class='warning'";
7993                        }
7994                    }
7995                    $VTABLES .= "<tr><th>".$Index."</th>\n";
7996                    $VTABLES .= "<td$Color1>".specChars($Entries{$Index}{"E1"})."</td>\n";
7997                    $VTABLES .= "<td$Color2>".specChars($Entries{$Index}{"E2"})."</td></tr>\n";
7998                }
7999                $VTABLES .= "</table><br/>\n";
8000                $VTABLES = $ContentDivStart.$VTABLES.$ContentDivEnd;
8001                $VTABLES = $ContentSpanStart_Info."[+] show v-table (old and new)".$ContentSpanEnd."<br/>\n".$VTABLES;
8002            }
8003            return $VTABLES;
8004        }
8005    }
8006    return "";
8007}
8008
8009sub simpleVEntry($)
8010{
8011    my $VEntry = $_[0];
8012    if(not defined $VEntry
8013    or $VEntry eq "") {
8014        return "";
8015    }
8016
8017    $VEntry=~s/ \[.+?\]\Z//; # support for ABI Dumper
8018    $VEntry=~s/\A(.+)::(_ZThn.+)\Z/$2/; # thunks
8019    $VEntry=~s/_ZTI\w+/typeinfo/g; # typeinfo
8020    if($VEntry=~/\A_ZThn.+\Z/) {
8021        $VEntry = "non-virtual thunk";
8022    }
8023    $VEntry=~s/\A\(int \(\*\)\(...\)\)\s*([a-z_])/$1/i;
8024    # support for old GCC versions
8025    $VEntry=~s/\A0u\Z/(int (*)(...))0/;
8026    $VEntry=~s/\A4294967268u\Z/(int (*)(...))-0x000000004/;
8027    $VEntry=~s/\A&_Z\Z/& _Z/;
8028    $VEntry=~s/([^:]+)::\~([^:]+)\Z/~$1/; # destructors
8029    return $VEntry;
8030}
8031
8032sub adjustParamPos($$$)
8033{
8034    my ($Pos, $Symbol, $LVer) = @_;
8035    if(defined $CompSign{$LVer}{$Symbol})
8036    {
8037        if(not $CompSign{$LVer}{$Symbol}{"Static"}
8038        and $CompSign{$LVer}{$Symbol}{"Class"})
8039        {
8040            return $Pos-1;
8041        }
8042
8043        return $Pos;
8044    }
8045
8046    return undef;
8047}
8048
8049sub getParamPos($$$)
8050{
8051    my ($Name, $Symbol, $LVer) = @_;
8052
8053    if(defined $CompSign{$LVer}{$Symbol}
8054    and defined $CompSign{$LVer}{$Symbol}{"Param"})
8055    {
8056        my $Info = $CompSign{$LVer}{$Symbol};
8057        foreach (keys(%{$Info->{"Param"}}))
8058        {
8059            if($Info->{"Param"}{$_}{"name"} eq $Name)
8060            {
8061                return $_;
8062            }
8063        }
8064    }
8065
8066    return undef;
8067}
8068
8069sub getParamName($)
8070{
8071    my $Loc = $_[0];
8072    $Loc=~s/\->.*//g;
8073    return $Loc;
8074}
8075
8076sub getAffectedSymbols($$$)
8077{
8078    my ($Level, $Target_TypeName, $Kinds_Locations) = @_;
8079
8080    my $LIMIT = 10;
8081    if(defined $In::Opt{"AffectLimit"}) {
8082        $LIMIT = $In::Opt{"AffectLimit"};
8083    }
8084
8085    my %SymSel = ();
8086
8087    foreach my $Kind (sort keys(%{$Kinds_Locations}))
8088    {
8089        my @Locs = sort {(index($a, "retval")!=-1) cmp (index($b, "retval")!=-1)} sort {length($a)<=>length($b)} sort keys(%{$Kinds_Locations->{$Kind}});
8090
8091        foreach my $Loc (@Locs)
8092        {
8093            foreach my $Symbol (keys(%{$TypeProblemsIndex{$Level}{$Target_TypeName}{$Kind}{$Loc}}))
8094            {
8095                if(index($Symbol, "_Z")==0
8096                and $Symbol=~/(C4|C2|D4|D2|D0)[EI]/)
8097                { # duplicated problems for C2/C4 constructors, D2/D4 and D0 destructors
8098                    next;
8099                }
8100
8101                if(index($Symbol, "\@")!=-1
8102                or index($Symbol, "\$")!=-1)
8103                {
8104                    my ($SN, $SS, $SV) = symbolParts($Symbol);
8105
8106                    if($Level eq "Source")
8107                    { # remove symbol version
8108                        $Symbol = $SN;
8109                    }
8110
8111                    if($SV and defined $CompatProblems{$Level}{$SN})
8112                    { # duplicated problems for versioned symbols
8113                        next;
8114                    }
8115                }
8116
8117                if(not defined $SymSel{$Symbol})
8118                {
8119                    $SymSel{$Symbol}{"Kind"} = $Kind;
8120                    $SymSel{$Symbol}{"Loc"} = $Loc;
8121                }
8122            }
8123        }
8124    }
8125
8126    my $Affected = "";
8127    my $SNum = 0;
8128
8129    if($In::Opt{"ReportFormat"} eq "xml")
8130    { # XML
8131        $Affected .= "      <affected>\n";
8132
8133        foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymSel))
8134        {
8135            my $Kind = $SymSel{$Symbol}{"Kind"};
8136            my $Loc = $SymSel{$Symbol}{"Loc"};
8137
8138            my $PName = getParamName($Loc);
8139            my $Des = getAffectDesc($Level, $Symbol, $Kind, $Loc);
8140
8141            my $Target = "";
8142            if($PName)
8143            {
8144                $Target .= " param=\"$PName\"";
8145                $Des=~s/parameter $PName /parameter \@param /;
8146            }
8147            elsif($Loc=~/\Aretval(\-|\Z)/i) {
8148                $Target .= " affected=\"retval\"";
8149            }
8150            elsif($Loc=~/\Athis(\-|\Z)/i) {
8151                $Target .= " affected=\"this\"";
8152            }
8153
8154            if($Des=~s/\AField ([^\s]+) /Field \@field /) {
8155                $Target .= " field=\"$1\"";
8156            }
8157
8158            $Affected .= "        <symbol name=\"$Symbol\"$Target>\n";
8159            $Affected .= "          <comment>".xmlSpecChars($Des)."</comment>\n";
8160            $Affected .= "        </symbol>\n";
8161
8162            if(++$SNum>=$LIMIT) {
8163                last;
8164            }
8165        }
8166        $Affected .= "      </affected>\n";
8167    }
8168    else
8169    { # HTML
8170        foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymSel))
8171        {
8172            my $Kind = $SymSel{$Symbol}{"Kind"};
8173            my $Loc = $SymSel{$Symbol}{"Loc"};
8174
8175            my $Des = getAffectDesc($Level, $Symbol, $Kind, $Loc);
8176            my $PName = getParamName($Loc);
8177            my $Pos = adjustParamPos(getParamPos($PName, $Symbol, 1), $Symbol, 1);
8178
8179            $Affected .= "<span class='iname_a'>".getSignature($Symbol, 1, "Class|Name|Param|HTML|Italic|Target=".$Pos)."</span><br/>\n";
8180            $Affected .= "<div class='affect'>".specChars($Des)."</div>\n";
8181
8182            if(++$SNum>=$LIMIT) {
8183                last;
8184            }
8185        }
8186
8187        my $Total = keys(%SymSel);
8188
8189        if($Total>$LIMIT) {
8190            $Affected .= " <b>...</b>\n<br/>\n"; # and others ...
8191        }
8192
8193        $Affected = "<div class='affected'>".$Affected."</div>\n";
8194        if($Affected)
8195        {
8196
8197            my $Per = showNum($Total*100/keys(%{$CheckedSymbols{$Level}}));
8198            $Affected = $ContentDivStart.$Affected.$ContentDivEnd;
8199            $Affected = $ContentSpanStart_Affected."[+] affected symbols: $Total ($Per\%)".$ContentSpanEnd.$Affected;
8200        }
8201    }
8202
8203    return $Affected;
8204}
8205
8206sub cmpLocations($$)
8207{
8208    my ($L1, $L2) = @_;
8209    if((index($L2, "retval")==0 or index($L2, "this")==0)
8210    and (index($L1, "retval")!=0 and index($L1, "this")!=0))
8211    {
8212        if(index($L1, "->")==-1) {
8213            return 1;
8214        }
8215        elsif(index($L2, "->")!=-1) {
8216            return 1;
8217        }
8218    }
8219    return 0;
8220}
8221
8222sub getAffectDesc($$$$)
8223{
8224    my ($Level, $Symbol, $Kind, $Loc) = @_;
8225
8226    if($ExtendedSymbols{$Symbol}) {
8227        return "This is a symbol from an external library that may use subject library and change the ABI after recompiling.";
8228    }
8229
8230    my $PAttr = $CompatProblems{$Level}{$Symbol}{$Kind}{$Loc};
8231
8232    $Loc=~s/\A(.*)\-\>(.+?)\Z/$1/; # without the latest affected field
8233
8234    my @Sentence = ();
8235
8236    if($Kind eq "Overridden_Virtual_Method"
8237    or $Kind eq "Overridden_Virtual_Method_B") {
8238        push(@Sentence, "The method '".$PAttr->{"New_Value"}."' will be called instead of this method.");
8239    }
8240    elsif($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
8241    {
8242        my %SymInfo = %{$CompSign{1}{$Symbol}};
8243
8244        if($Loc eq "this" or $Kind=~/(\A|_)Virtual(_|\Z)/)
8245        {
8246            my $MType = "method";
8247            if($SymInfo{"Constructor"}) {
8248                $MType = "constructor";
8249            }
8250            elsif($SymInfo{"Destructor"}) {
8251                $MType = "destructor";
8252            }
8253
8254            my $ClassName = $TypeInfo{1}{$SymInfo{"Class"}}{"Name"};
8255
8256            if($ClassName eq $PAttr->{"Type_Name"}) {
8257                push(@Sentence, "This $MType is from \'".$PAttr->{"Type_Name"}."\' class.");
8258            }
8259            else {
8260                push(@Sentence, "This $MType is from derived class \'".$ClassName."\'.");
8261            }
8262        }
8263        else
8264        {
8265            my $TypeID = undef;
8266
8267            if($Loc=~/retval/)
8268            { # return value
8269                if(index($Loc, "->")!=-1) {
8270                    push(@Sentence, "Field \'".$Loc."\' in the return value");
8271                }
8272                else {
8273                    push(@Sentence, "Return value");
8274                }
8275
8276                $TypeID = $SymInfo{"Return"};
8277            }
8278            elsif($Loc=~/this/)
8279            { # "this" pointer
8280                if(index($Loc, "->")!=-1) {
8281                    push(@Sentence, "Field \'".$Loc."\' in the object of this method");
8282                }
8283                else {
8284                    push(@Sentence, "\'this\' pointer");
8285                }
8286
8287                $TypeID = $SymInfo{"Class"};
8288            }
8289            else
8290            { # parameters
8291                my $PName = getParamName($Loc);
8292                my $PPos = getParamPos($PName, $Symbol, 1);
8293
8294                if(index($Loc, "->")!=-1) {
8295                    push(@Sentence, "Field \'".$Loc."\' in ".showPos(adjustParamPos($PPos, $Symbol, 1))." parameter");
8296                }
8297                else {
8298                    push(@Sentence, showPos(adjustParamPos($PPos, $Symbol, 1))." parameter");
8299                }
8300                if($PName) {
8301                    push(@Sentence, "\'".$PName."\'");
8302                }
8303
8304                $TypeID = $SymInfo{"Param"}{$PPos}{"type"};
8305            }
8306
8307            if($Loc!~/this/)
8308            {
8309                if(my %PureType = getPureType($TypeID, 1))
8310                {
8311                    if($PureType{"Type"} eq "Pointer") {
8312                        push(@Sentence, "(pointer)");
8313                    }
8314                    elsif($PureType{"Type"} eq "Ref") {
8315                        push(@Sentence, "(reference)");
8316                    }
8317                }
8318            }
8319
8320            if($Loc eq "this") {
8321                push(@Sentence, "has base type \'".$PAttr->{"Type_Name"}."\'.");
8322            }
8323            else
8324            {
8325                my $Loc_T = $Loc;
8326                $Loc_T=~s/\A\w+(\->|\Z)//; # location in type
8327
8328                my $TypeID_Problem = $TypeID;
8329                if($Loc_T) {
8330                    $TypeID_Problem = getFieldType($Loc_T, $TypeID, 1);
8331                }
8332
8333                if($TypeInfo{1}{$TypeID_Problem}{"Name"} eq $PAttr->{"Type_Name"}) {
8334                    push(@Sentence, "is of type \'".$PAttr->{"Type_Name"}."\'.");
8335                }
8336                else {
8337                    push(@Sentence, "has base type \'".$PAttr->{"Type_Name"}."\'.");
8338                }
8339            }
8340        }
8341    }
8342
8343    my $Sent = join(" ", @Sentence);
8344
8345    $Sent=~s/->/./g;
8346
8347    if($In::Opt{"ReportFormat"} eq "xml") {
8348        $Sent=~s/'//g;
8349    }
8350
8351    return $Sent;
8352}
8353
8354sub getFieldType($$$)
8355{
8356    my ($Loc, $TypeId, $LVer) = @_;
8357
8358    my @Fields = split(/\->/, $Loc);
8359
8360    foreach my $Name (@Fields)
8361    {
8362        my %Info = getBaseType($TypeId, $LVer);
8363
8364        foreach my $Pos (keys(%{$Info{"Memb"}}))
8365        {
8366            if($Info{"Memb"}{$Pos}{"name"} eq $Name)
8367            {
8368                $TypeId = $Info{"Memb"}{$Pos}{"type"};
8369                last;
8370            }
8371        }
8372    }
8373
8374    return $TypeId;
8375}
8376
8377sub writeReport($$)
8378{
8379    my ($Level, $Report) = @_;
8380    if($In::Opt{"ReportFormat"} eq "xml") {
8381        $Report = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".$Report;
8382    }
8383    if($In::Opt{"StdOut"})
8384    { # --stdout option
8385        print STDOUT $Report;
8386    }
8387    else
8388    {
8389        my $RPath = getReportPath($Level);
8390        mkpath(getDirname($RPath));
8391
8392        open(REPORT, ">", $RPath) || die ("can't open file \'$RPath\': $!\n");
8393        print REPORT $Report;
8394        close(REPORT);
8395    }
8396}
8397
8398sub getReport($)
8399{
8400    my $Level = $_[0];
8401    if($In::Opt{"ReportFormat"} eq "xml")
8402    { # XML
8403        if($Level eq "Join")
8404        {
8405            my $Report = "<reports>\n";
8406            $Report .= getReport("Binary");
8407            $Report .= getReport("Source");
8408            $Report .= "</reports>\n";
8409            return $Report;
8410        }
8411        else
8412        {
8413            my $Report = "<report kind=\"".lc($Level)."\" version=\"$XML_REPORT_VERSION\">\n\n";
8414            my ($Summary, $MetaData, $AnyChanged) = getSummary($Level);
8415            $Report .= $Summary."\n";
8416            $Report .= getReportProblems_All($Level);
8417            $Report .= "</report>\n";
8418            return $Report;
8419        }
8420    }
8421    else
8422    { # HTML
8423        my $CssStyles = readModule("Styles", "Report.css");
8424        my $JScripts = readModule("Scripts", "Sections.js");
8425        if($Level eq "Join")
8426        {
8427            $CssStyles .= "\n".readModule("Styles", "Tabs.css");
8428            $JScripts .= "\n".readModule("Scripts", "Tabs.js");
8429            my $Title = $In::Opt{"TargetTitle"}.": ".$In::Desc{1}{"Version"}." to ".$In::Desc{2}{"Version"}." compatibility report";
8430            my $Keywords = $In::Opt{"TargetTitle"}.", compatibility, API, ABI, report";
8431            my $Des = "API/ABI compatibility report for the ".$In::Opt{"TargetTitle"}." ".$In::Opt{"TargetComponent"}." between ".$In::Desc{1}{"Version"}." and ".$In::Desc{2}{"Version"}." versions";
8432            my ($BSummary, $BMetaData, $BAnyChanged) = getSummary("Binary");
8433            my ($SSummary, $SMetaData, $SAnyChanged) = getSummary("Source");
8434            my $Report = "<!-\- $BMetaData -\->\n<!-\- $SMetaData -\->\n".composeHTML_Head($Title, $Keywords, $Des, $CssStyles, $JScripts, ($BAnyChanged or $SAnyChanged))."<body><a name='Source'></a><a name='Binary'></a><a name='Top'></a>";
8435            $Report .= getReportTitle("Join")."
8436            <br/>
8437            <div class='tabset'>
8438            <a id='BinaryID' href='#BinaryTab' class='tab active'>Binary<br/>Compatibility</a>
8439            <a id='SourceID' href='#SourceTab' style='margin-left:3px' class='tab disabled'>Source<br/>Compatibility</a>
8440            </div>";
8441            $Report .= "<div id='BinaryTab' class='tab'>\n$BSummary\n".getReportProblems_All("Binary").getSourceInfo()."<br/><br/><br/></div>";
8442            $Report .= "<div id='SourceTab' class='tab'>\n$SSummary\n".getReportProblems_All("Source").getSourceInfo()."<br/><br/><br/></div>";
8443            $Report .= getReportFooter();
8444            $Report .= "\n</body></html>\n";
8445            return $Report;
8446        }
8447        else
8448        {
8449            my ($Summary, $MetaData, $AnyChanged) = getSummary($Level);
8450            my $Title = $In::Opt{"TargetTitle"}.": ".$In::Desc{1}{"Version"}." to ".$In::Desc{2}{"Version"}." ".lc($Level)." compatibility report";
8451            my $Keywords = $In::Opt{"TargetTitle"}.", ".lc($Level)." compatibility, API, report";
8452            my $Des = "$Level compatibility report for the ".$In::Opt{"TargetTitle"}." ".$In::Opt{"TargetComponent"}." between ".$In::Desc{1}{"Version"}." and ".$In::Desc{2}{"Version"}." versions";
8453            if($Level eq "Binary")
8454            {
8455                if($In::ABI{1}{"Arch"} eq $In::ABI{2}{"Arch"}) {
8456                    $Des .= " on ".showArch($In::ABI{1}{"Arch"});
8457                }
8458            }
8459            my $Report = "<!-\- $MetaData -\->\n".composeHTML_Head($Title, $Keywords, $Des, $CssStyles, $JScripts, $AnyChanged)."\n<body>\n<div><a name='Top'></a>\n";
8460            $Report .= getReportTitle($Level)."\n".$Summary."\n";
8461            $Report .= getReportProblems_All($Level);
8462            $Report .= getSourceInfo();
8463            $Report .= "</div>\n<br/><br/><br/>\n";
8464            $Report .= getReportFooter();
8465            $Report .= "\n</body></html>\n";
8466            return $Report;
8467        }
8468    }
8469}
8470
8471sub getReportProblems_All($)
8472{
8473    my $Level = $_[0];
8474
8475    my $Report = getReportAdded($Level).getReportRemoved($Level);
8476    $Report .= getReportProblems("High", $Level);
8477    $Report .= getReportProblems("Medium", $Level);
8478    $Report .= getReportProblems("Low", $Level);
8479    $Report .= getReportProblems("Safe", $Level);
8480
8481    # clean memory
8482    delete($TypeProblemsIndex{$Level});
8483    delete($TypeChanges{$Level});
8484    delete($CompatProblems{$Level});
8485
8486    return $Report;
8487}
8488
8489sub createReport()
8490{
8491    if($In::Opt{"JoinReport"}) {
8492        writeReport("Join", getReport("Join"));
8493    }
8494    elsif($In::Opt{"DoubleReport"})
8495    { # default
8496        writeReport("Binary", getReport("Binary"));
8497        writeReport("Source", getReport("Source"));
8498    }
8499    elsif($In::Opt{"BinOnly"})
8500    { # --binary
8501        writeReport("Binary", getReport("Binary"));
8502    }
8503    elsif($In::Opt{"SrcOnly"})
8504    { # --source
8505        writeReport("Source", getReport("Source"));
8506    }
8507}
8508
8509sub getReportFooter()
8510{
8511    my $Footer = "";
8512
8513    $Footer .= "<hr/>\n";
8514    $Footer .= "<div class='footer' align='right'>";
8515    $Footer .= "<i>Generated by <a href='".$HomePage{"Dev"}."'>ABI Compliance Checker</a> $TOOL_VERSION &#160;</i>\n";
8516    $Footer .= "</div>\n";
8517    $Footer .= "<br/>\n";
8518
8519    return $Footer;
8520}
8521
8522sub getReportProblems($$)
8523{
8524    my ($Severity, $Level) = @_;
8525
8526    my $Report = getReportTypeProblems($Severity, $Level);
8527    if(my $SProblems = getReportSymbolProblems($Severity, $Level)) {
8528        $Report .= $SProblems;
8529    }
8530
8531    if($Severity eq "Low" or $Severity eq "Safe") {
8532        $Report .= getReportChangedConstants($Severity, $Level);
8533    }
8534
8535    if($In::Opt{"ReportFormat"} eq "html")
8536    {
8537        if($Report)
8538        { # add anchor
8539            if($In::Opt{"JoinReport"})
8540            {
8541                if($Severity eq "Safe") {
8542                    $Report = "<a name=\'Other_".$Level."_Changes\'></a>".$Report;
8543                }
8544                else {
8545                    $Report = "<a name=\'".$Severity."_Risk_".$Level."_Problems\'></a>".$Report;
8546                }
8547            }
8548            else
8549            {
8550                if($Severity eq "Safe") {
8551                    $Report = "<a name=\'Other_Changes\'></a>".$Report;
8552                }
8553                else {
8554                    $Report = "<a name=\'".$Severity."_Risk_Problems\'></a>".$Report;
8555                }
8556            }
8557        }
8558    }
8559    return $Report;
8560}
8561
8562sub composeHTML_Head($$$$$$)
8563{
8564    my ($Title, $Keywords, $Des, $Styles, $Scripts, $AnyChanged) = @_;
8565
8566    my $Head = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
8567    $Head .= "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n";
8568    $Head .= "<head>\n";
8569    $Head .= "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
8570    $Head .= "<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n";
8571    $Head .= "<meta name=\"keywords\" content=\"$Keywords\" />\n";
8572    $Head .= "<meta name=\"description\" content=\"$Des\" />\n";
8573
8574    if(not $AnyChanged) {
8575        $Head .= "<meta name=\"robots\" content=\"noindex\" />\n";
8576    }
8577
8578    $Head .= "<title>$Title</title>\n";
8579    $Head .= "<style type=\"text/css\">\n$Styles</style>\n";
8580    $Head .= "<script type=\"text/javascript\" language=\"JavaScript\">\n<!--\n$Scripts\n-->\n</script>\n";
8581    $Head .= "</head>\n";
8582
8583    return $Head;
8584}
8585
8586sub insertIDs($)
8587{
8588    my $Text = $_[0];
8589    while($Text=~/CONTENT_ID/)
8590    {
8591        if(int($Content_Counter)%2) {
8592            $ContentID -= 1;
8593        }
8594        $Text=~s/CONTENT_ID/c_$ContentID/;
8595        $ContentID += 1;
8596        $Content_Counter += 1;
8597    }
8598    return $Text;
8599}
8600
8601sub uncoverConstant($$)
8602{
8603    my ($LVer, $Constant) = @_;
8604
8605    if(isCyclical(\@RecurConstant, $Constant)) {
8606        return $Constant;
8607    }
8608
8609    if(defined $Cache{"uncoverConstant"}{$LVer}{$Constant}) {
8610        return $Cache{"uncoverConstant"}{$LVer}{$Constant};
8611    }
8612
8613    if(defined $Constants{$LVer}{$Constant})
8614    {
8615        my $Value = $Constants{$LVer}{$Constant}{"Value"};
8616        if(defined $Constants{$LVer}{$Value})
8617        {
8618            push(@RecurConstant, $Constant);
8619            my $Uncovered = uncoverConstant($LVer, $Value);
8620            if($Uncovered ne "") {
8621                $Value = $Uncovered;
8622            }
8623            pop(@RecurConstant);
8624        }
8625
8626        # FIXME: uncover $Value using all the enum constants
8627        # USE CASE: change of define NC_LONG from NC_INT (enum value) to NC_INT (define)
8628        return ($Cache{"uncoverConstant"}{$LVer}{$Constant} = $Value);
8629    }
8630    return ($Cache{"uncoverConstant"}{$LVer}{$Constant} = "");
8631}
8632
8633sub simpleConstant($$)
8634{
8635    my ($LVer, $Value) = @_;
8636    if($Value=~/\W/)
8637    {
8638        my $Value_Copy = $Value;
8639        while($Value_Copy=~s/([a-z_]\w+)/\@/i)
8640        {
8641            my $Word = $1;
8642            if($Value!~/$Word\s*\(/)
8643            {
8644                my $Val = uncoverConstant($LVer, $Word);
8645                if($Val ne "")
8646                {
8647                    $Value=~s/\b$Word\b/$Val/g;
8648                }
8649            }
8650        }
8651    }
8652    return $Value;
8653}
8654
8655sub computeValue($)
8656{
8657    my $Value = $_[0];
8658
8659    if($Value=~/\A\((-?[\d]+)\)\Z/) {
8660        return $1;
8661    }
8662
8663    if($Value=~/\A[\d\-\+()]+\Z/) {
8664        return eval($Value);
8665    }
8666
8667    return $Value;
8668}
8669
8670my $IgnoreConstant = join("|",
8671    "VERSION",
8672    "VERSIONCODE",
8673    "VERNUM",
8674    "VERS_INFO",
8675    "PATCHLEVEL",
8676    "INSTALLPREFIX",
8677    "VBUILD",
8678    "VPATCH",
8679    "VMINOR",
8680    "BUILD_STRING",
8681    "BUILD_TIME",
8682    "PACKAGE_STRING",
8683    "PRODUCTION",
8684    "CONFIGURE_COMMAND",
8685    "INSTALLDIR",
8686    "BINDIR",
8687    "CONFIG_FILE_PATH",
8688    "DATADIR",
8689    "EXTENSION_DIR",
8690    "INCLUDE_PATH",
8691    "LIBDIR",
8692    "LOCALSTATEDIR",
8693    "SBINDIR",
8694    "SYSCONFDIR",
8695    "RELEASE",
8696    "SOURCE_ID",
8697    "SUBMINOR",
8698    "MINOR",
8699    "MINNOR",
8700    "MINORVERSION",
8701    "MAJOR",
8702    "MAJORVERSION",
8703    "MICRO",
8704    "MICROVERSION",
8705    "BINARY_AGE",
8706    "INTERFACE_AGE",
8707    "CORE_ABI",
8708    "PATCH",
8709    "COPYRIGHT",
8710    "TIMESTAMP",
8711    "REVISION",
8712    "PACKAGE_TAG",
8713    "PACKAGEDATE",
8714    "NUMVERSION",
8715    "Release",
8716    "Version"
8717);
8718
8719sub constantFilter($$$)
8720{
8721    my ($Name, $Value, $Level) = @_;
8722
8723    if($Level eq "Binary")
8724    {
8725        if($Name=~/_t\Z/)
8726        { # __malloc_ptr_t
8727            return 1;
8728        }
8729        if($Name=~/(\A|_)($IgnoreConstant)(_|\Z)/)
8730        { # version
8731            return 1;
8732        }
8733        if($Name=~/(\A|[a-z])(Release|Version)([A-Z]|\Z)/)
8734        { # version
8735            return 1;
8736        }
8737        my $LShort = $In::Opt{"TargetLibShort"};
8738        if($Name=~/(\A|_)(lib|open|)$LShort(_|)(VERSION|VER|DATE|API|PREFIX)(_|\Z)/i)
8739        { # version
8740            return 1;
8741        }
8742        if($Value=~/\A('|"|)[\/\\]\w+([\/\\]|:|('|"|)\Z)/ or $Value=~/[\/\\]\w+[\/\\]\w+/)
8743        { # /lib64:/usr/lib64:/lib:/usr/lib:/usr/X11R6/lib/Xaw3d ...
8744            return 1;
8745        }
8746
8747        if($Value=~/\A["'].*['"]/i)
8748        { # string
8749            return 0;
8750        }
8751
8752        if($Value=~/\A[({]*\s*[a-z_]+\w*(\s+|[\|,])/i)
8753        { # static int gcry_pth_init
8754          # extern ABC
8755          # (RE_BACKSLASH_ESCAPE_IN_LISTS | RE...
8756          # { H5FD_MEM_SUPER, H5FD_MEM_SUPER, ...
8757            return 1;
8758        }
8759        if($Value=~/\w+\s*\(/i)
8760        { # foo(p)
8761            return 1;
8762        }
8763        if($Value=~/\A[a-z_]+\w*\Z/i)
8764        { # asn1_node_st
8765          # __SMTH_P
8766            return 1;
8767        }
8768    }
8769
8770    return 0;
8771}
8772
8773sub mergeConstants($)
8774{
8775    my $Level = $_[0];
8776    foreach my $Constant (sort keys(%{$Constants{1}}))
8777    {
8778        if($In::Desc{1}{"SkipConstants"}{$Constant})
8779        { # skipped by the user
8780            next;
8781        }
8782
8783        if(my $Header = $Constants{1}{$Constant}{"Header"})
8784        {
8785            if(not isTargetHeader($Header, 1)
8786            and not isTargetHeader($Header, 2)) {
8787                next;
8788            }
8789        }
8790        elsif(my $Source = $Constants{1}{$Constant}{"Source"})
8791        {
8792            if(not isTargetSource($Source, 1)
8793            and not isTargetSource($Source, 2)) {
8794                next;
8795            }
8796        }
8797        else {
8798            next;
8799        }
8800
8801        my $Old_Value = uncoverConstant(1, $Constant);
8802
8803        if(constantFilter($Constant, $Old_Value, $Level))
8804        { # separate binary and source problems
8805            next;
8806        }
8807
8808        if(not defined $Constants{2}{$Constant}{"Value"})
8809        { # removed
8810            if(not defined $In::Opt{"SkipRemovedConstants"})
8811            {
8812                %{$CompatProblems_Constants{$Level}{$Constant}{"Removed_Constant"}} = (
8813                    "Target"=>$Constant,
8814                    "Old_Value"=>$Old_Value  );
8815            }
8816            next;
8817        }
8818
8819        if($Constants{2}{$Constant}{"Value"} eq "")
8820        { # empty value
8821          # TODO: implement a rule
8822            next;
8823        }
8824
8825        my $New_Value = uncoverConstant(2, $Constant);
8826
8827        my $Old_Value_Pure = $Old_Value;
8828        my $New_Value_Pure = $New_Value;
8829
8830        $Old_Value_Pure=~s/(\W)\s+/$1/g;
8831        $Old_Value_Pure=~s/\s+(\W)/$1/g;
8832        $New_Value_Pure=~s/(\W)\s+/$1/g;
8833        $New_Value_Pure=~s/\s+(\W)/$1/g;
8834
8835        next if($New_Value_Pure eq "" or $Old_Value_Pure eq "");
8836
8837        if($New_Value_Pure ne $Old_Value_Pure)
8838        { # different values
8839            if(simpleConstant(1, $Old_Value) eq simpleConstant(2, $New_Value))
8840            { # complex values
8841                next;
8842            }
8843            if(computeValue($Old_Value) eq computeValue($New_Value))
8844            { # expressions
8845                next;
8846            }
8847            if(convertInteger($Old_Value) eq convertInteger($New_Value))
8848            { # 0x0001 and 0x1, 0x1 and 1 equal constants
8849                next;
8850            }
8851            if($Old_Value eq "0" and $New_Value eq "NULL")
8852            { # 0 => NULL
8853                next;
8854            }
8855            if($Old_Value eq "NULL" and $New_Value eq "0")
8856            { # NULL => 0
8857                next;
8858            }
8859            %{$CompatProblems_Constants{$Level}{$Constant}{"Changed_Constant"}} = (
8860                "Target"=>$Constant,
8861                "Old_Value"=>$Old_Value,
8862                "New_Value"=>$New_Value  );
8863        }
8864    }
8865
8866    if(defined $In::Opt{"SkipAddedConstants"}) {
8867        return;
8868    }
8869
8870    foreach my $Constant (keys(%{$Constants{2}}))
8871    {
8872        if(not defined $Constants{1}{$Constant}{"Value"})
8873        {
8874            if($In::Desc{2}{"SkipConstants"}{$Constant})
8875            { # skipped by the user
8876                next;
8877            }
8878
8879            if(my $Header = $Constants{2}{$Constant}{"Header"})
8880            {
8881                if(not isTargetHeader($Header, 1)
8882                and not isTargetHeader($Header, 2))
8883                { # user-defined header
8884                    next;
8885                }
8886            }
8887            elsif(my $Source = $Constants{2}{$Constant}{"Source"})
8888            {
8889                if(not isTargetSource($Source, 1)
8890                and not isTargetSource($Source, 2))
8891                { # user-defined header
8892                    next;
8893                }
8894            }
8895            else {
8896                next;
8897            }
8898
8899            my $New_Value = uncoverConstant(2, $Constant);
8900            if(not defined $New_Value or $New_Value eq "") {
8901                next;
8902            }
8903
8904            if(constantFilter($Constant, $New_Value, $Level))
8905            { # separate binary and source problems
8906                next;
8907            }
8908
8909            %{$CompatProblems_Constants{$Level}{$Constant}{"Added_Constant"}} = (
8910                "Target"=>$Constant,
8911                "New_Value"=>$New_Value  );
8912        }
8913    }
8914}
8915
8916sub convertInteger($)
8917{
8918    my $Value = $_[0];
8919    if($Value=~/\A0x[a-f0-9]+\Z/)
8920    { # hexadecimal
8921        return hex($Value);
8922    }
8923    elsif($Value=~/\A0[0-7]+\Z/)
8924    { # octal
8925        return oct($Value);
8926    }
8927    elsif($Value=~/\A0b[0-1]+\Z/)
8928    { # binary
8929        return oct($Value);
8930    }
8931    else {
8932        return $Value;
8933    }
8934}
8935
8936sub getSymbolSize($$)
8937{ # size from the shared library
8938    my ($Symbol, $LVer) = @_;
8939
8940    if(defined $In::ABI{$LVer}{"SymLib"}{$Symbol}
8941    and my $LibName = $In::ABI{$LVer}{"SymLib"}{$Symbol})
8942    {
8943        if(defined $In::ABI{$LVer}{"Symbols"}{$LibName}{$Symbol}
8944        and my $Size = $In::ABI{$LVer}{"Symbols"}{$LibName}{$Symbol})
8945        {
8946            if($Size<0) {
8947                return -$Size;
8948            }
8949        }
8950    }
8951    return 0;
8952}
8953
8954sub createSymbolsList($$$$$)
8955{
8956    my ($DPath, $SaveTo, $LName, $LVersion, $ArchName) = @_;
8957
8958    $In::ABI{1} = readABIDump(1, $DPath);
8959    initAliases(1);
8960
8961    prepareSymbols(1);
8962
8963    my %SymbolHeaderLib = ();
8964    my $Total = 0;
8965
8966    # Get List
8967    foreach my $Symbol (sort keys(%{$CompSign{1}}))
8968    {
8969        if(not linkSymbol($Symbol, 1, "-Deps"))
8970        { # skip src only and all external functions
8971            next;
8972        }
8973        if(not symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Public", "Binary", 1))
8974        { # skip other symbols
8975            next;
8976        }
8977        my $HeaderName = $CompSign{1}{$Symbol}{"Header"};
8978        if(not $HeaderName)
8979        { # skip src only and all external functions
8980            next;
8981        }
8982        my $DyLib = $In::ABI{1}{"SymLib"}{$Symbol};
8983        if(not $DyLib)
8984        { # skip src only and all external functions
8985            next;
8986        }
8987        $SymbolHeaderLib{$HeaderName}{$DyLib}{$Symbol} = 1;
8988        $Total += 1;
8989    }
8990    # Draw List
8991    my $SYMBOLS_LIST = "<h1>Public symbols in <span style='color:Blue;'>$LName</span> (<span style='color:Red;'>$LVersion</span>)";
8992    $SYMBOLS_LIST .= " on <span style='color:Blue;'>".showArch($ArchName)."</span><br/>Total: $Total</h1><br/>";
8993    foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%SymbolHeaderLib))
8994    {
8995        foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$SymbolHeaderLib{$HeaderName}}))
8996        {
8997            my %NS_Symbol = ();
8998            foreach my $Symbol (keys(%{$SymbolHeaderLib{$HeaderName}{$DyLib}})) {
8999                $NS_Symbol{selectSymbolNs($Symbol, 1)}{$Symbol} = 1;
9000            }
9001            foreach my $NameSpace (sort keys(%NS_Symbol))
9002            {
9003                $SYMBOLS_LIST .= getTitle($HeaderName, $DyLib, $NameSpace);
9004                my @SortedInterfaces = sort {lc($CompSign{1}{$a}{"Unmangled"}) cmp lc($CompSign{1}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$NS_Symbol{$NameSpace}});
9005                foreach my $Symbol (@SortedInterfaces)
9006                {
9007                    my $SubReport = "";
9008                    my $Signature = highLight_ItalicColor($Symbol, 1);
9009                    if($NameSpace) {
9010                        $Signature = cutNs($Signature, $NameSpace);
9011                    }
9012                    if($Symbol=~/\A(_Z|\?)/) {
9013                        $SubReport = insertIDs($ContentSpanStart.$Signature.$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mngl'>$Symbol</span><br/><br/>".$ContentDivEnd."\n");
9014                    }
9015                    else {
9016                        $SubReport = "<span class='iname'>".$Signature."</span><br/>\n";
9017                    }
9018                    $SYMBOLS_LIST .= $SubReport;
9019                }
9020            }
9021            $SYMBOLS_LIST .= "<br/>\n";
9022        }
9023    }
9024    # clear info
9025    (%CompSign, %ClassMethods, %AllocableClass, %ClassNames, %In::ABI) = ();
9026
9027    ($Content_Counter, $ContentID) = (0, 0);
9028
9029    my $CssStyles = readModule("Styles", "SymbolsList.css");
9030    my $JScripts = readModule("Scripts", "Sections.js");
9031    $SYMBOLS_LIST = "<a name='Top'></a>".$SYMBOLS_LIST.$TOP_REF."<br/>\n";
9032    my $Title = "$LName: public symbols";
9033    my $Keywords = "$LName, API, symbols";
9034    my $Des = "List of symbols in $LName ($LVersion) on ".showArch($ArchName);
9035    $SYMBOLS_LIST = composeHTML_Head($Title, $Keywords, $Des, $CssStyles, $JScripts, 1)."
9036    <body><div>\n$SYMBOLS_LIST</div>
9037    <br/><br/>\n".getReportFooter()."
9038    </body></html>";
9039    writeFile($SaveTo, $SYMBOLS_LIST);
9040}
9041
9042sub dumpSorting($)
9043{
9044    my $Hash = $_[0];
9045    if(not $Hash) {
9046        return [];
9047    }
9048
9049    my @Keys = keys(%{$Hash});
9050    if($#Keys<0) {
9051        return [];
9052    }
9053
9054    if($Keys[0]=~/\A\d+\Z/)
9055    { # numbers
9056        return [sort {$a<=>$b} @Keys];
9057    }
9058    else
9059    { # strings
9060        return [sort {$a cmp $b} @Keys];
9061    }
9062}
9063
9064sub exitReport()
9065{ # the tool has run without any errors
9066    printReport();
9067    if($In::Opt{"CompileError"})
9068    { # errors in headers may add false positives/negatives
9069        exit(getErrorCode("Compile_Error"));
9070    }
9071    if($In::Opt{"BinOnly"} and $RESULT{"Binary"}{"Problems"})
9072    { # --binary
9073        exit(getErrorCode("Incompatible"));
9074    }
9075    elsif($In::Opt{"SrcOnly"}
9076    and $RESULT{"Source"}{"Problems"})
9077    { # --source
9078        exit(getErrorCode("Incompatible"));
9079    }
9080    elsif($RESULT{"Source"}{"Problems"}
9081    or $RESULT{"Binary"}{"Problems"})
9082    { # default
9083        exit(getErrorCode("Incompatible"));
9084    }
9085    else {
9086        exit(getErrorCode("Compatible"));
9087    }
9088}
9089
9090sub readRules($)
9091{
9092    my $Kind = $_[0];
9093    if(not -f $RULES_PATH{$Kind}) {
9094        exitStatus("Module_Error", "can't access \'".$RULES_PATH{$Kind}."\'");
9095    }
9096    my $Content = readFile($RULES_PATH{$Kind});
9097    while(my $Rule = parseTag(\$Content, "rule"))
9098    {
9099        my $RId = parseTag(\$Rule, "id");
9100        my @Properties = ("Severity", "Change", "Effect", "Overcome", "Kind");
9101        foreach my $Prop (@Properties) {
9102            if(my $Value = parseTag(\$Rule, lc($Prop)))
9103            {
9104                $Value=~s/\n[ ]*//;
9105                $CompatRules{$Kind}{$RId}{$Prop} = $Value;
9106            }
9107        }
9108        if($CompatRules{$Kind}{$RId}{"Kind"}=~/\A(Symbols|Parameters)\Z/) {
9109            $CompatRules{$Kind}{$RId}{"Kind"} = "Symbols";
9110        }
9111        else {
9112            $CompatRules{$Kind}{$RId}{"Kind"} = "Types";
9113        }
9114    }
9115}
9116
9117sub getReportPath($)
9118{
9119    my $Level = $_[0];
9120    my $Dir;
9121
9122    if($In::Desc{1}{"Version"} == "" || $In::Desc{2}{"Version"} == "")
9123    { # String version might contain \, / or spaces, which are hassles in making a path, so we just use a simpiler V1_to_V2.
9124      # Note we do not change Desc{}{"Version"}. So in the generated html file, the version description is clearly shown.
9125      $Dir = "compat_reports/".$In::Opt{"TargetLib"}."/V1_to_V2";
9126    }
9127    else
9128    {
9129      $Dir = "compat_reports/".$In::Opt{"TargetLib"}."/".$In::Desc{1}{"Version"}."_to_".$In::Desc{2}{"Version"};
9130    }
9131
9132    if($Level eq "Binary")
9133    {
9134        if($In::Opt{"BinReportPath"})
9135        { # --bin-report-path
9136            return $In::Opt{"BinReportPath"};
9137        }
9138        elsif($In::Opt{"OutputReportPath"})
9139        { # --report-path
9140            return $In::Opt{"OutputReportPath"};
9141        }
9142        else
9143        { # default
9144            return $Dir."/abi_compat_report.".$In::Opt{"ReportFormat"};
9145        }
9146    }
9147    elsif($Level eq "Source")
9148    {
9149        if($In::Opt{"SrcReportPath"})
9150        { # --src-report-path
9151            return $In::Opt{"SrcReportPath"};
9152        }
9153        elsif($In::Opt{"OutputReportPath"})
9154        { # --report-path
9155            return $In::Opt{"OutputReportPath"};
9156        }
9157        else
9158        { # default
9159            return $Dir."/src_compat_report.".$In::Opt{"ReportFormat"};
9160        }
9161    }
9162    else
9163    {
9164        if($In::Opt{"OutputReportPath"})
9165        { # --report-path
9166            return $In::Opt{"OutputReportPath"};
9167        }
9168        else
9169        { # default
9170            return $Dir."/compat_report.".$In::Opt{"ReportFormat"};
9171        }
9172    }
9173}
9174
9175sub printStatMsg($)
9176{
9177    my $Level = $_[0];
9178    printMsg("INFO", "Total ".lc($Level)." compatibility problems: ".$RESULT{$Level}{"Problems"}.", warnings: ".$RESULT{$Level}{"Warnings"});
9179}
9180
9181sub listAffected($)
9182{
9183    my $Level = $_[0];
9184    my $List = "";
9185    foreach (keys(%{$TotalAffected{$Level}}))
9186    {
9187        if($In::Opt{"StrictCompat"} and $TotalAffected{$Level}{$_} eq "Low")
9188        { # skip "Low"-severity problems
9189            next;
9190        }
9191        $List .= "$_\n";
9192    }
9193    my $Dir = getDirname(getReportPath($Level));
9194    if($Level eq "Binary") {
9195        writeFile($Dir."/abi_affected.txt", $List);
9196    }
9197    elsif($Level eq "Source") {
9198        writeFile($Dir."/src_affected.txt", $List);
9199    }
9200}
9201
9202sub printReport()
9203{
9204    printMsg("INFO", "Creating compatibility report ...");
9205
9206    createReport();
9207
9208    if($In::Opt{"JoinReport"} or $In::Opt{"DoubleReport"})
9209    {
9210        if($RESULT{"Binary"}{"Problems"}
9211        or $RESULT{"Source"}{"Problems"})
9212        {
9213            printMsg("INFO", "Binary compatibility: ".(100-$RESULT{"Binary"}{"Affected"})."\%");
9214            printMsg("INFO", "Source compatibility: ".(100-$RESULT{"Source"}{"Affected"})."\%");
9215        }
9216        else
9217        {
9218            printMsg("INFO", "Binary compatibility: 100\%");
9219            printMsg("INFO", "Source compatibility: 100\%");
9220        }
9221        printStatMsg("Binary");
9222        printStatMsg("Source");
9223
9224        if($In::Opt{"ListAffected"})
9225        { # --list-affected
9226            listAffected("Binary");
9227            listAffected("Source");
9228        }
9229    }
9230    elsif($In::Opt{"BinOnly"})
9231    {
9232        if($RESULT{"Binary"}{"Problems"}) {
9233            printMsg("INFO", "Binary compatibility: ".(100-$RESULT{"Binary"}{"Affected"})."\%");
9234        }
9235        else {
9236            printMsg("INFO", "Binary compatibility: 100\%");
9237        }
9238        printStatMsg("Binary");
9239
9240        if($In::Opt{"ListAffected"})
9241        { # --list-affected
9242            listAffected("Binary");
9243        }
9244    }
9245    elsif($In::Opt{"SrcOnly"})
9246    {
9247        if($RESULT{"Source"}{"Problems"}) {
9248            printMsg("INFO", "Source compatibility: ".(100-$RESULT{"Source"}{"Affected"})."\%");
9249        }
9250        else {
9251            printMsg("INFO", "Source compatibility: 100\%");
9252        }
9253        printStatMsg("Source");
9254
9255        if($In::Opt{"ListAffected"})
9256        { # --list-affected
9257            listAffected("Source");
9258        }
9259    }
9260
9261    if($In::Opt{"StdOut"})
9262    {
9263        if($In::Opt{"JoinReport"} or not $In::Opt{"DoubleReport"})
9264        { # --binary or --source
9265            printMsg("INFO", "Compatibility report has been generated to stdout");
9266        }
9267        else
9268        { # default
9269            printMsg("INFO", "Compatibility reports have been generated to stdout");
9270        }
9271    }
9272    else
9273    {
9274        if($In::Opt{"JoinReport"})
9275        { # default
9276            printMsg("INFO", "Report: ".pathFmt(getReportPath("Join")));
9277        }
9278        elsif($In::Opt{"DoubleReport"})
9279        {
9280            printMsg("INFO", "Report (BC): ".pathFmt(getReportPath("Binary")));
9281            printMsg("INFO", "Report (SC): ".pathFmt(getReportPath("Source")));
9282        }
9283        elsif($In::Opt{"BinOnly"})
9284        { # --binary
9285            printMsg("INFO", "Report: ".pathFmt(getReportPath("Binary")));
9286        }
9287        elsif($In::Opt{"SrcOnly"})
9288        { # --source
9289            printMsg("INFO", "Report: ".pathFmt(getReportPath("Source")));
9290        }
9291    }
9292}
9293
9294sub unpackDump($)
9295{
9296    my $Path = $_[0];
9297
9298    $Path = getAbsPath($Path);
9299    my ($Dir, $FileName) = sepPath($Path);
9300
9301    my $TmpDir = $In::Opt{"Tmp"};
9302    my $UnpackDir = $TmpDir."/unpack";
9303    rmtree($UnpackDir);
9304    mkpath($UnpackDir);
9305
9306    if($FileName=~s/\Q.zip\E\Z//g)
9307    { # *.zip
9308        my $UnzipCmd = getCmdPath("unzip");
9309        if(not $UnzipCmd) {
9310            exitStatus("Not_Found", "can't find \"unzip\" command");
9311        }
9312        chdir($UnpackDir);
9313        system("$UnzipCmd \"$Path\" >\"$TmpDir/null\"");
9314        if($?) {
9315            exitStatus("Error", "can't extract \'$Path\' ($?): $!");
9316        }
9317        chdir($In::Opt{"OrigDir"});
9318        my @Contents = cmdFind($UnpackDir, "f");
9319        if(not @Contents) {
9320            exitStatus("Error", "can't extract \'$Path\'");
9321        }
9322        return $Contents[0];
9323    }
9324    elsif($FileName=~s/\Q.tar.gz\E(\.\w+|)\Z//g)
9325    { # *.tar.gz
9326      # *.tar.gz.amd64 (dh & cdbs)
9327        if($In::Opt{"OS"} eq "windows")
9328        { # -xvzf option is not implemented in tar.exe (2003)
9329          # use "gzip.exe -k -d -f" + "tar.exe -xvf" instead
9330            my $TarCmd = getCmdPath("tar");
9331            if(not $TarCmd) {
9332                exitStatus("Not_Found", "can't find \"tar\" command");
9333            }
9334            my $GzipCmd = getCmdPath("gzip");
9335            if(not $GzipCmd) {
9336                exitStatus("Not_Found", "can't find \"gzip\" command");
9337            }
9338            chdir($UnpackDir);
9339            system("$GzipCmd -k -d -f \"$Path\""); # keep input files (-k)
9340            if($?) {
9341                exitStatus("Error", "can't extract \'$Path\'");
9342            }
9343            system("$TarCmd -xvf \"$Dir\\$FileName.tar\" >\"$TmpDir/null\"");
9344            if($?) {
9345                exitStatus("Error", "can't extract \'$Path\' ($?): $!");
9346            }
9347            chdir($In::Opt{"OrigDir"});
9348            unlink($Dir."/".$FileName.".tar");
9349            my @Contents = cmdFind($UnpackDir, "f");
9350            if(not @Contents) {
9351                exitStatus("Error", "can't extract \'$Path\'");
9352            }
9353            return $Contents[0];
9354        }
9355        else
9356        { # Unix, Mac
9357            my $TarCmd = getCmdPath("tar");
9358            if(not $TarCmd) {
9359                exitStatus("Not_Found", "can't find \"tar\" command");
9360            }
9361            chdir($UnpackDir);
9362            system("$TarCmd -xvzf \"$Path\" >\"$TmpDir/null\"");
9363            if($?) {
9364                exitStatus("Error", "can't extract \'$Path\' ($?): $!");
9365            }
9366            chdir($In::Opt{"OrigDir"});
9367            my @Contents = cmdFind($UnpackDir, "f");
9368            if(not @Contents) {
9369                exitStatus("Error", "can't extract \'$Path\'");
9370            }
9371            return $Contents[0];
9372        }
9373    }
9374}
9375
9376sub createArchive($$)
9377{
9378    my ($Path, $To) = @_;
9379    if(not $To) {
9380        $To = ".";
9381    }
9382
9383    my ($From, $Name) = sepPath($Path);
9384    if($In::Opt{"OS"} eq "windows")
9385    { # *.zip
9386        my $ZipCmd = getCmdPath("zip");
9387        if(not $ZipCmd) {
9388            exitStatus("Not_Found", "can't find \"zip\"");
9389        }
9390        my $Pkg = $To."/".$Name.".zip";
9391        unlink($Pkg);
9392        chdir($To);
9393        system("$ZipCmd -j \"$Name.zip\" \"$Path\" >\"".$In::Opt{"Tmp"}."/null\"");
9394        if($?)
9395        { # cannot allocate memory (or other problems with "zip")
9396            chdir($In::Opt{"OrigDir"});
9397            exitStatus("Error", "can't pack the ABI dump: ".$!);
9398        }
9399        chdir($In::Opt{"OrigDir"});
9400        unlink($Path);
9401        return $Pkg;
9402    }
9403    else
9404    { # *.tar.gz
9405        my $TarCmd = getCmdPath("tar");
9406        if(not $TarCmd) {
9407            exitStatus("Not_Found", "can't find \"tar\"");
9408        }
9409        my $GzipCmd = getCmdPath("gzip");
9410        if(not $GzipCmd) {
9411            exitStatus("Not_Found", "can't find \"gzip\"");
9412        }
9413        my $Pkg = abs_path($To)."/".$Name.".tar.gz";
9414        if(-e $Pkg) {
9415            unlink($Pkg);
9416        }
9417        system($TarCmd, "-C", $From, "-czf", $Pkg, $Name);
9418        if($?)
9419        { # cannot allocate memory (or other problems with "tar")
9420            exitStatus("Error", "can't pack the ABI dump: ".$!);
9421        }
9422        unlink($Path);
9423        return $To."/".$Name.".tar.gz";
9424    }
9425}
9426
9427sub defaultDumpPath($$)
9428{
9429    my ($N, $V) = @_;
9430    return "abi_dumps/".$N."/".$V."/ABI.dump";
9431}
9432
9433sub createABIFile($$)
9434{
9435    my ($LVer, $DescPath) = @_;
9436
9437    if(not -e $DescPath) {
9438        exitStatus("Access_Error", "can't access \'$DescPath\'");
9439    }
9440
9441    detectDefaultPaths(undef, undef, "bin", undef);
9442
9443    if(isDump($DescPath))
9444    {
9445        $In::ABI{$LVer} = readABIDump($LVer, $DescPath);
9446        initAliases($LVer);
9447
9448        if(my $V = $In::Desc{$LVer}{"TargetVersion"}) {
9449            $In::Desc{$LVer}{"Version"} = $V;
9450        }
9451        else {
9452            $In::Desc{$LVer}{"Version"} = $In::ABI{$LVer}{"LibraryVersion"};
9453        }
9454    }
9455    else
9456    {
9457        loadModule("ABIDump");
9458        readDesc(createDesc($DescPath, $LVer), $LVer);
9459
9460        initLogging($LVer);
9461
9462        if($In::Opt{"Debug"})
9463        {
9464            if(not $In::Opt{"ExtraInfo"}) {
9465                $In::Opt{"ExtraInfo"} = getExtraDir($LVer);
9466            }
9467        }
9468
9469        detectDefaultPaths("inc", "lib", undef, "gcc");
9470        createABIDump($LVer);
9471    }
9472
9473    clearSysFilesCache($LVer);
9474
9475    printMsg("INFO", "Creating library ABI dump ...");
9476
9477    $In::ABI{$LVer}{"ABI_DUMP_VERSION"} = $ABI_DUMP_VERSION;
9478    $In::ABI{$LVer}{"ABI_COMPLIANCE_CHECKER_VERSION"} = $TOOL_VERSION;
9479
9480    if($In::Opt{"UseXML"}) {
9481        $In::ABI{$LVer}{"XML_ABI_DUMP_VERSION"} = $XML_ABI_DUMP_VERSION;
9482    }
9483
9484    $In::ABI{$LVer}{"TargetHeaders"} = $In::Desc{$LVer}{"TargetHeader"};
9485
9486    foreach ("SymLib", "DepSymLib", "TName_Tid", "TypeTypedef",
9487    "TypedefBase", "Class_SubClasses", "ClassVTable") {
9488        delete($In::ABI{$LVer}{$_});
9489    }
9490
9491    my $DumpPath = defaultDumpPath($In::Opt{"TargetLib"}, $In::Desc{1}{"Version"});
9492    if($In::Opt{"OutputDumpPath"})
9493    { # user defined path
9494        $DumpPath = $In::Opt{"OutputDumpPath"};
9495    }
9496
9497    my $ArExt = $In::Opt{"Ar"};
9498    my $Archive = ($DumpPath=~s/\Q.$ArExt\E\Z//g);
9499
9500    if($Archive and not $In::Opt{"StdOut"})
9501    { # check archive utilities
9502        if($In::Opt{"OS"} eq "windows")
9503        { # using zip
9504            my $ZipCmd = getCmdPath("zip");
9505            if(not $ZipCmd) {
9506                exitStatus("Not_Found", "can't find \"zip\"");
9507            }
9508        }
9509        else
9510        { # using tar and gzip
9511            my $TarCmd = getCmdPath("tar");
9512            if(not $TarCmd) {
9513                exitStatus("Not_Found", "can't find \"tar\"");
9514            }
9515            my $GzipCmd = getCmdPath("gzip");
9516            if(not $GzipCmd) {
9517                exitStatus("Not_Found", "can't find \"gzip\"");
9518            }
9519        }
9520    }
9521
9522    my $ABI_DUMP = "";
9523    if($In::Opt{"UseXML"})
9524    {
9525        loadModule("XmlDump");
9526        $ABI_DUMP = createXmlDump($LVer);
9527    }
9528    else
9529    { # default
9530        $ABI_DUMP = Dumper($In::ABI{$LVer});
9531    }
9532    if($In::Opt{"StdOut"})
9533    {
9534        print STDOUT $ABI_DUMP;
9535        printMsg("INFO", "ABI dump has been generated to stdout");
9536        return;
9537    }
9538    else
9539    { # to file
9540        my ($DDir, $DName) = sepPath($DumpPath);
9541        my $DPath = $In::Opt{"Tmp"}."/".$DName;
9542        if(not $Archive) {
9543            $DPath = $DumpPath;
9544        }
9545
9546        mkpath($DDir);
9547
9548        open(DUMP, ">", $DPath) || die ("can't open file \'$DPath\': $!\n");
9549        print DUMP $ABI_DUMP;
9550        close(DUMP);
9551
9552        # Ensure contents of archive have deterministic contents
9553        chmod(0644, $DPath);
9554        utime(0, 0, $DPath);
9555
9556        if(not -s $DPath) {
9557            exitStatus("Error", "can't create ABI dump because something is going wrong with the Data::Dumper module");
9558        }
9559        if($Archive) {
9560            $DumpPath = createArchive($DPath, $DDir);
9561        }
9562
9563        printMsg("INFO", "Dump path: ".pathFmt($DumpPath));
9564    }
9565}
9566
9567sub readABIDump($$)
9568{
9569    my ($LVer, $Path) = @_;
9570
9571    my $FilePath = "";
9572    if(isDump_U($Path))
9573    { # input *.abi
9574        $FilePath = $Path;
9575    }
9576    else
9577    { # input *.abi.tar.gz
9578        $FilePath = unpackDump($Path);
9579        if(not isDump_U($FilePath)) {
9580            exitStatus("Invalid_Dump", "specified ABI dump \'$Path\' is not valid, try to recreate it");
9581        }
9582    }
9583
9584    my $ABIRef = {};
9585
9586    my $Line = readLineNum($FilePath, 0);
9587    if($Line=~/xml/)
9588    { # XML format
9589        loadModule("XmlDump");
9590        $ABIRef = readXmlDump($FilePath);
9591    }
9592    else
9593    { # Perl Data::Dumper format (default)
9594        open(DUMP, $FilePath);
9595        local $/ = undef;
9596        my $Content = <DUMP>;
9597        close(DUMP);
9598
9599        if(getDirname($FilePath) eq $In::Opt{"Tmp"}."/unpack")
9600        { # remove temp file
9601            unlink($FilePath);
9602        }
9603        if($Content!~/};\s*\Z/) {
9604            exitStatus("Invalid_Dump", "specified ABI dump \'$Path\' is not valid, try to recreate it");
9605        }
9606        $ABIRef = eval($Content);
9607        if(not $ABIRef) {
9608            exitStatus("Error", "internal error - eval() procedure seem to not working correctly, try to remove 'use strict' and try again");
9609        }
9610    }
9611
9612    my $ABIVer = $ABIRef->{"ABI_DUMP_VERSION"};
9613
9614    if($ABIVer)
9615    {
9616        if(cmpVersions($ABIVer, $ABI_DUMP_VERSION)>0)
9617        { # future formats
9618            exitStatus("Dump_Version", "the versions of the ABI dump is newer than version of the tool");
9619        }
9620    }
9621
9622    if(cmpVersions($ABIVer, $ABI_DUMP_VERSION_MIN)<0 and not $In::Opt{"CountSymbols"}) {
9623        exitStatus("Dump_Version", "the version of the ABI dump is too old and unsupported anymore, please regenerate it");
9624    }
9625
9626    if(defined $ABIRef->{"ABI_DUMPER_VERSION"})
9627    { # DWARF ABI Dump
9628        $UseConv_Real{$LVer}{"P"} = 1;
9629        $UseConv_Real{$LVer}{"R"} = 0; # not implemented yet
9630
9631        $UsedDump{$LVer}{"DWARF"} = 1;
9632
9633        if($ABIRef->{"LibraryName"}=~/\.ko(\.|\Z)/) {
9634            $In::Opt{"TargetComponent"} = "module";
9635        }
9636        else {
9637            $In::Opt{"TargetComponent"} = "object";
9638        }
9639    }
9640
9641    if(index($ABIRef->{"LibraryName"}, "libstdc++")==0
9642    or index($ABIRef->{"LibraryName"}, "libc++")==0) {
9643        $In::Opt{"StdcxxTesting"} = 1;
9644    }
9645
9646    if($ABIRef->{"BinOnly"})
9647    { # ABI dump created with --binary option
9648        $UsedDump{$LVer}{"BinOnly"} = 1;
9649    }
9650    else
9651    { # default
9652        $UsedDump{$LVer}{"SrcBin"} = 1;
9653    }
9654
9655    if(defined $ABIRef->{"Mode"}
9656    and $ABIRef->{"Mode"} eq "Extended")
9657    { # --ext option
9658        $In::Opt{"ExtendedCheck"} = 1;
9659    }
9660
9661    if($ABIRef->{"Extra"}) {
9662        $In::Opt{"ExtraDump"} = 1;
9663    }
9664
9665    if(not keys(%{$ABIRef->{"SymbolInfo"}}))
9666    { # validation of old-version dumps
9667        if(not $In::Opt{"ExtendedCheck"}) {
9668            exitStatus("Invalid_Dump", "no symbols info in the ABI dump");
9669        }
9670    }
9671
9672    if(defined $ABIRef->{"GccConstants"})
9673    { # support for 3.0
9674        foreach my $Name (keys(%{$ABIRef->{"GccConstants"}})) {
9675            $ABIRef->{"Constants"}{$Name}{"Value"} = $ABIRef->{"GccConstants"}{$Name};
9676        }
9677    }
9678    elsif(defined $ABIRef->{"CompilerConstants"})
9679    {
9680        foreach my $Name (keys(%{$ABIRef->{"CompilerConstants"}})) {
9681            $ABIRef->{"Constants"}{$Name}{"Value"} = $ABIRef->{"CompilerConstants"}{$Name};
9682        }
9683    }
9684
9685    if(not $ABIRef->{"SymbolVersion"}) {
9686        $ABIRef->{"SymbolVersion"} = $ABIRef->{"SymVer"};
9687    }
9688
9689    if(defined $ABIRef->{"TargetHeaders"}) {
9690        $In::Desc{$LVer}{"TargetHeader"} = $ABIRef->{"TargetHeaders"};
9691    }
9692
9693    foreach my $LName (keys(%{$ABIRef->{"Symbols"}}))
9694    {
9695        foreach my $Symbol (keys(%{$ABIRef->{"Symbols"}{$LName}})) {
9696            $ABIRef->{"SymLib"}{$Symbol} = $LName;
9697        }
9698    }
9699
9700    foreach my $LName (keys(%{$ABIRef->{"DepSymbols"}}))
9701    {
9702        foreach my $Symbol (keys(%{$ABIRef->{"DepSymbols"}{$LName}})) {
9703            $ABIRef->{"DepSymLib"}{$Symbol} = $LName;
9704        }
9705    }
9706
9707    $In::Opt{"Target"} = $ABIRef->{"Target"};
9708    $In::Desc{$LVer}{"Dump"} = 1;
9709
9710    return $ABIRef;
9711}
9712
9713sub prepareCompare($)
9714{
9715    my $LVer = $_[0];
9716
9717    foreach my $Lib_Name (keys(%{$In::ABI{$LVer}{"Symbols"}}))
9718    {
9719        foreach my $Symbol (keys(%{$In::ABI{$LVer}{"Symbols"}{$Lib_Name}}))
9720        {
9721            if($In::ABI{$LVer}{"Symbols"}{$Lib_Name}{$Symbol}<0)
9722            { # data marked as -size in the dump
9723                $GlobalDataObject{$LVer}{$Symbol} = -$In::ABI{$LVer}{"Symbols"}{$Lib_Name}{$Symbol};
9724
9725                if($Symbol=~/\A(.+?)\@.+/) {
9726                    $GlobalDataObject{$LVer}{$1} = $GlobalDataObject{$LVer}{$Symbol};
9727                }
9728            }
9729        }
9730    }
9731
9732    foreach my $TypeId (sort {$a<=>$b} keys(%{$TypeInfo{$LVer}}))
9733    { # NOTE: order is important
9734        if(not defined $TypeInfo{$LVer}{$TypeId}{"Tid"}) {
9735            $TypeInfo{$LVer}{$TypeId}{"Tid"} = $TypeId;
9736        }
9737
9738        my $TInfo = $TypeInfo{$LVer}{$TypeId};
9739        if(defined $TInfo->{"Base"})
9740        {
9741            foreach my $SubId (keys(%{$TInfo->{"Base"}}))
9742            {
9743                if($SubId eq $TypeId)
9744                { # Fix erroneus ABI dump
9745                    delete($TypeInfo{$LVer}{$TypeId}{"Base"}{$SubId});
9746                    next;
9747                }
9748
9749                $In::ABI{$LVer}{"Class_SubClasses"}{$SubId}{$TypeId} = 1;
9750            }
9751        }
9752
9753        if($TInfo->{"BaseType"} eq $TypeId)
9754        { # fix ABI dump
9755            delete($TypeInfo{$LVer}{$TypeId}{"BaseType"});
9756        }
9757
9758        if($TInfo->{"Type"} eq "Typedef" and not $TInfo->{"Artificial"})
9759        {
9760            if(my $BTid = $TInfo->{"BaseType"})
9761            {
9762                my $BName = $TypeInfo{$LVer}{$BTid}{"Name"};
9763                if(not $BName)
9764                { # broken type
9765                    next;
9766                }
9767                if($TInfo->{"Name"} eq $BName)
9768                { # typedef to "class Class"
9769                  # should not be registered in TName_Tid
9770                    next;
9771                }
9772                if(not $In::ABI{$LVer}{"TypedefBase"}{$TInfo->{"Name"}}) {
9773                    $In::ABI{$LVer}{"TypedefBase"}{$TInfo->{"Name"}} = $BName;
9774                }
9775            }
9776        }
9777        if(not $TName_Tid{$LVer}{$TInfo->{"Name"}})
9778        { # classes: class (id1), typedef (artificial, id2 > id1)
9779            $TName_Tid{$LVer}{$TInfo->{"Name"}} = $TypeId;
9780        }
9781    }
9782}
9783
9784sub compareABIDumps($$)
9785{
9786    my ($V1, $V2) = @_;
9787    my $DumpPath1 = defaultDumpPath($In::Opt{"TargetLib"}, $V1);
9788    my $DumpPath2 = defaultDumpPath($In::Opt{"TargetLib"}, $V2);
9789
9790    unlink($DumpPath1);
9791    unlink($DumpPath2);
9792
9793    my $Pid = fork();
9794    if($Pid)
9795    { # dump on two CPU cores
9796        my @PARAMS = ("-dump", $In::Desc{1}{"Path"}, "-l", $In::Opt{"TargetLib"});
9797        @PARAMS = (@PARAMS, "-vnum", $V1);
9798
9799        if($In::Desc{1}{"RelativeDirectory"}) {
9800            @PARAMS = (@PARAMS, "-relpath", $In::Desc{1}{"RelativeDirectory"});
9801        }
9802        if($In::Desc{1}{"OutputLogPath"}) {
9803            @PARAMS = (@PARAMS, "-log-path", $In::Desc{1}{"OutputLogPath"});
9804        }
9805        if($In::Opt{"CrossGcc"}) {
9806            @PARAMS = (@PARAMS, "-cross-gcc", $In::Opt{"CrossGcc"});
9807        }
9808        if($In::Opt{"Quiet"})
9809        {
9810            @PARAMS = (@PARAMS, "-quiet");
9811            @PARAMS = (@PARAMS, "-logging-mode", "a");
9812        }
9813        elsif($In::Opt{"LogMode"} and $In::Opt{"LogMode"} ne "w")
9814        { # "w" is default
9815            @PARAMS = (@PARAMS, "-logging-mode", $In::Opt{"LogMode"});
9816        }
9817        if($In::Opt{"ExtendedCheck"}) {
9818            @PARAMS = (@PARAMS, "-extended");
9819        }
9820        if($In::Opt{"UserLang"}) {
9821            @PARAMS = (@PARAMS, "-lang", $In::Opt{"UserLang"});
9822        }
9823        if($In::Opt{"BinOnly"}) {
9824            @PARAMS = (@PARAMS, "-binary");
9825        }
9826        if($In::Opt{"SrcOnly"}) {
9827            @PARAMS = (@PARAMS, "-source");
9828        }
9829        if($In::Opt{"SortDump"}) {
9830            @PARAMS = (@PARAMS, "-sort");
9831        }
9832        if($In::Opt{"DumpFormat"} and $In::Opt{"DumpFormat"} ne "perl") {
9833            @PARAMS = (@PARAMS, "-dump-format", $In::Opt{"DumpFormat"});
9834        }
9835        if($In::Opt{"CheckHeadersOnly"}) {
9836            @PARAMS = (@PARAMS, "-headers-only");
9837        }
9838        if($In::Opt{"CxxIncompat"}) {
9839            @PARAMS = (@PARAMS, "-cxx-incompatible");
9840        }
9841        if($In::Opt{"Debug"})
9842        {
9843            @PARAMS = (@PARAMS, "-debug");
9844            printMsg("INFO", "Executing perl $0 @PARAMS");
9845        }
9846        system("perl", $0, @PARAMS);
9847        if(not -f $DumpPath1) {
9848            exit(1);
9849        }
9850    }
9851    else
9852    { # child
9853        my @PARAMS = ("-dump", $In::Desc{2}{"Path"}, "-l", $In::Opt{"TargetLib"});
9854        @PARAMS = (@PARAMS, "-vnum", $V2);
9855
9856        if($In::Desc{2}{"RelativeDirectory"}) {
9857            @PARAMS = (@PARAMS, "-relpath", $In::Desc{2}{"RelativeDirectory"});
9858        }
9859        if($In::Desc{2}{"OutputLogPath"}) {
9860            @PARAMS = (@PARAMS, "-log-path", $In::Desc{2}{"OutputLogPath"});
9861        }
9862        if($In::Opt{"CrossGcc"}) {
9863            @PARAMS = (@PARAMS, "-cross-gcc", $In::Opt{"CrossGcc"});
9864        }
9865        if($In::Opt{"Quiet"})
9866        {
9867            @PARAMS = (@PARAMS, "-quiet");
9868            @PARAMS = (@PARAMS, "-logging-mode", "a");
9869        }
9870        elsif($In::Opt{"LogMode"} and $In::Opt{"LogMode"} ne "w")
9871        { # "w" is default
9872            @PARAMS = (@PARAMS, "-logging-mode", $In::Opt{"LogMode"});
9873        }
9874        if($In::Opt{"ExtendedCheck"}) {
9875            @PARAMS = (@PARAMS, "-extended");
9876        }
9877        if($In::Opt{"UserLang"}) {
9878            @PARAMS = (@PARAMS, "-lang", $In::Opt{"UserLang"});
9879        }
9880        if($In::Opt{"BinOnly"}) {
9881            @PARAMS = (@PARAMS, "-binary");
9882        }
9883        if($In::Opt{"SrcOnly"}) {
9884            @PARAMS = (@PARAMS, "-source");
9885        }
9886        if($In::Opt{"SortDump"}) {
9887            @PARAMS = (@PARAMS, "-sort");
9888        }
9889        if($In::Opt{"DumpFormat"} and $In::Opt{"DumpFormat"} ne "perl") {
9890            @PARAMS = (@PARAMS, "-dump-format", $In::Opt{"DumpFormat"});
9891        }
9892        if($In::Opt{"CheckHeadersOnly"}) {
9893            @PARAMS = (@PARAMS, "-headers-only");
9894        }
9895        if($In::Opt{"CxxIncompat"}) {
9896            @PARAMS = (@PARAMS, "-cxx-incompatible");
9897        }
9898        if($In::Opt{"Debug"})
9899        {
9900            @PARAMS = (@PARAMS, "-debug");
9901            printMsg("INFO", "Executing perl $0 @PARAMS");
9902        }
9903        system("perl", $0, @PARAMS);
9904        if(not -f $DumpPath2) {
9905            exit(1);
9906        }
9907        else {
9908            exit(0);
9909        }
9910    }
9911    waitpid($Pid, 0);
9912
9913    my @CMP_PARAMS = ("-l", $In::Opt{"TargetLib"});
9914    @CMP_PARAMS = (@CMP_PARAMS, "-d1", $DumpPath1);
9915    @CMP_PARAMS = (@CMP_PARAMS, "-d2", $DumpPath2);
9916    if($In::Opt{"TargetTitle"} ne $In::Opt{"TargetLib"}) {
9917        @CMP_PARAMS = (@CMP_PARAMS, "-title", $In::Opt{"TargetTitle"});
9918    }
9919    if($In::Opt{"ShowRetVal"}) {
9920        @CMP_PARAMS = (@CMP_PARAMS, "-show-retval");
9921    }
9922    if($In::Opt{"CrossGcc"}) {
9923        @CMP_PARAMS = (@CMP_PARAMS, "-cross-gcc", $In::Opt{"CrossGcc"});
9924    }
9925    @CMP_PARAMS = (@CMP_PARAMS, "-logging-mode", "a");
9926    if($In::Opt{"Quiet"}) {
9927        @CMP_PARAMS = (@CMP_PARAMS, "-quiet");
9928    }
9929    if($In::Opt{"ReportFormat"}
9930    and $In::Opt{"ReportFormat"} ne "html")
9931    { # HTML is default format
9932        @CMP_PARAMS = (@CMP_PARAMS, "-report-format", $In::Opt{"ReportFormat"});
9933    }
9934    if($In::Opt{"OutputReportPath"}) {
9935        @CMP_PARAMS = (@CMP_PARAMS, "-report-path", $In::Opt{"OutputReportPath"});
9936    }
9937    if($In::Opt{"BinReportPath"}) {
9938        @CMP_PARAMS = (@CMP_PARAMS, "-bin-report-path", $In::Opt{"BinReportPath"});
9939    }
9940    if($In::Opt{"SrcReportPath"}) {
9941        @CMP_PARAMS = (@CMP_PARAMS, "-src-report-path", $In::Opt{"SrcReportPath"});
9942    }
9943    if($In::Opt{"LoggingPath"}) {
9944        @CMP_PARAMS = (@CMP_PARAMS, "-log-path", $In::Opt{"LoggingPath"});
9945    }
9946    if($In::Opt{"CheckHeadersOnly"}) {
9947        @CMP_PARAMS = (@CMP_PARAMS, "-headers-only");
9948    }
9949    if($In::Opt{"BinOnly"}) {
9950        @CMP_PARAMS = (@CMP_PARAMS, "-binary");
9951    }
9952    if($In::Opt{"SrcOnly"}) {
9953        @CMP_PARAMS = (@CMP_PARAMS, "-source");
9954    }
9955    if($In::Opt{"FilterPath"}) {
9956        @CMP_PARAMS = (@CMP_PARAMS, "-filter", $In::Opt{"FilterPath"});
9957    }
9958    if($In::Opt{"SkipInternalSymbols"}) {
9959        @CMP_PARAMS = (@CMP_PARAMS, "-skip-internal-symbols", $In::Opt{"SkipInternalSymbols"});
9960    }
9961    if($In::Opt{"SkipInternalTypes"}) {
9962        @CMP_PARAMS = (@CMP_PARAMS, "-skip-internal-types", $In::Opt{"SkipInternalTypes"});
9963    }
9964    if($In::Opt{"Debug"})
9965    {
9966        @CMP_PARAMS = (@CMP_PARAMS, "-debug");
9967        printMsg("INFO", "Executing perl $0 @CMP_PARAMS");
9968    }
9969    system("perl", $0, @CMP_PARAMS);
9970    exit($?>>8);
9971}
9972
9973sub compareInit()
9974{
9975    # read input XML descriptors or ABI dumps
9976    if(not $In::Desc{1}{"Path"}) {
9977        exitStatus("Error", "-old option is not specified");
9978    }
9979    if(not -e $In::Desc{1}{"Path"}) {
9980        exitStatus("Access_Error", "can't access \'".$In::Desc{1}{"Path"}."\'");
9981    }
9982
9983    if(not $In::Desc{2}{"Path"}) {
9984        exitStatus("Error", "-new option is not specified");
9985    }
9986    if(not -e $In::Desc{2}{"Path"}) {
9987        exitStatus("Access_Error", "can't access \'".$In::Desc{2}{"Path"}."\'");
9988    }
9989
9990    detectDefaultPaths(undef, undef, "bin", undef); # to extract dumps
9991
9992    printMsg("INFO", "Preparing, please wait ...");
9993
9994    if($In::Opt{"UseDumps"})
9995    { # --use-dumps
9996      # parallel processing
9997        if(isDump($In::Desc{1}{"Path"})
9998        or isDump($In::Desc{2}{"Path"})) {
9999            exitStatus("Error", "please specify input XML descriptors instead of ABI dumps to use with -use-dumps option.");
10000        }
10001
10002        readDesc(createDesc($In::Desc{1}{"Path"}, 1), 1);
10003        readDesc(createDesc($In::Desc{2}{"Path"}, 2), 2);
10004
10005        compareABIDumps($In::Desc{1}{"Version"}, $In::Desc{2}{"Version"});
10006    }
10007
10008    if(isDump($In::Desc{1}{"Path"}))
10009    {
10010        $In::ABI{1} = readABIDump(1, $In::Desc{1}{"Path"});
10011        initAliases(1);
10012
10013        if(my $V = $In::Desc{1}{"TargetVersion"}) {
10014            $In::Desc{1}{"Version"} = $V;
10015        }
10016        else {
10017            $In::Desc{1}{"Version"} = $In::ABI{1}{"LibraryVersion"};
10018        }
10019
10020        if(not defined $In::Desc{1}{"Version"}) {
10021            $In::Desc{1}{"Version"} = "X";
10022        }
10023    }
10024    else
10025    {
10026        loadModule("ABIDump");
10027        readDesc(createDesc($In::Desc{1}{"Path"}, 1), 1);
10028
10029        initLogging(1);
10030        detectDefaultPaths("inc", "lib", undef, "gcc");
10031        createABIDump(1);
10032    }
10033
10034    if(isDump($In::Desc{2}{"Path"}))
10035    {
10036        $In::ABI{2} = readABIDump(2, $In::Desc{2}{"Path"});
10037        initAliases(2);
10038
10039        if(my $V = $In::Desc{2}{"TargetVersion"}) {
10040            $In::Desc{2}{"Version"} = $V;
10041        }
10042        else {
10043            $In::Desc{2}{"Version"} = $In::ABI{2}{"LibraryVersion"};
10044        }
10045
10046        if(not defined $In::Desc{2}{"Version"}) {
10047            $In::Desc{2}{"Version"} = "Y";
10048        }
10049    }
10050    else
10051    {
10052        loadModule("ABIDump");
10053        readDesc(createDesc($In::Desc{2}{"Path"}, 2), 2);
10054
10055        initLogging(2);
10056        detectDefaultPaths("inc", "lib", undef, "gcc");
10057        createABIDump(2);
10058    }
10059
10060    clearSysFilesCache(1);
10061    clearSysFilesCache(2);
10062
10063    if(my $FPath = $In::Opt{"FilterPath"})
10064    {
10065        if(not -f $FPath) {
10066            exitStatus("Access_Error", "can't access \'".$FPath."\'");
10067        }
10068
10069        if(my $Filt = readFile($FPath))
10070        {
10071            readFilter($Filt, 1);
10072            readFilter($Filt, 2);
10073        }
10074    }
10075
10076    prepareCompare(1);
10077    prepareCompare(2);
10078
10079    if($In::Opt{"AppPath"} and not keys(%{$In::ABI{1}{"SymLib"}})) {
10080        printMsg("WARNING", "the application ".getFilename($In::Opt{"AppPath"})." has no symbols imported from libraries");
10081    }
10082
10083    prepareSymbols(1);
10084    prepareSymbols(2);
10085
10086    # Virtual Tables
10087    registerVTable(1);
10088    registerVTable(2);
10089
10090    registerOverriding(1);
10091    registerOverriding(2);
10092
10093    setVirtFuncPositions(1);
10094    setVirtFuncPositions(2);
10095
10096    # Other
10097    addParamNames(1);
10098    addParamNames(2);
10099
10100    detectChangedTypedefs();
10101}
10102
10103sub compareAPIs($)
10104{
10105    my $Level = $_[0];
10106
10107    readRules($Level);
10108    loadModule("CallConv");
10109
10110    if($Level eq "Binary") {
10111        printMsg("INFO", "Comparing ABIs ...");
10112    }
10113    else {
10114        printMsg("INFO", "Comparing APIs ...");
10115    }
10116
10117    if($In::Opt{"CheckHeadersOnly"}
10118    or $Level eq "Source")
10119    { # added/removed in headers
10120        detectAdded_H($Level);
10121        detectRemoved_H($Level);
10122    }
10123    else
10124    { # added/removed in libs
10125        detectAdded($Level);
10126        detectRemoved($Level);
10127    }
10128
10129    mergeSymbols($Level);
10130
10131    if(not defined $In::Opt{"DisableConstantsCheck"})
10132    {
10133        if(keys(%{$CheckedSymbols{$Level}})) {
10134            mergeConstants($Level);
10135        }
10136    }
10137
10138    $Cache{"mergeTypes"} = (); # free memory
10139
10140    if($In::Opt{"CheckHeadersOnly"}
10141    or $Level eq "Source")
10142    { # added/removed in headers
10143        mergeHeaders($Level);
10144    }
10145    else
10146    { # added/removed in libs
10147        mergeLibs($Level);
10148    }
10149}
10150
10151sub initAliases($)
10152{
10153    my $LVer = $_[0];
10154
10155    initABI($LVer);
10156
10157    $SymbolInfo{$LVer} = $In::ABI{$LVer}{"SymbolInfo"};
10158    $TypeInfo{$LVer} = $In::ABI{$LVer}{"TypeInfo"};
10159    $TName_Tid{$LVer} = $In::ABI{$LVer}{"TName_Tid"};
10160    $Constants{$LVer} = $In::ABI{$LVer}{"Constants"};
10161
10162    initAliases_TypeAttr($LVer);
10163}
10164
10165sub scenario()
10166{
10167    setTarget("default");
10168
10169    initAliases(1);
10170    initAliases(2);
10171
10172    $In::Opt{"Locale"} = "C.UTF-8";
10173    $In::Opt{"OrigDir"} = cwd();
10174    $In::Opt{"Tmp"} = tempdir(CLEANUP=>1);
10175    $In::Opt{"TargetLibShort"} = libPart($In::Opt{"TargetLib"}, "shortest");
10176
10177    $In::Opt{"DoubleReport"} = 0;
10178    $In::Opt{"JoinReport"} = 1;
10179
10180    $In::Opt{"SysPaths"}{"include"} = [];
10181    $In::Opt{"SysPaths"}{"lib"} = [];
10182    $In::Opt{"SysPaths"}{"bin"} = [];
10183
10184    $In::Opt{"CompileError"} = 0;
10185
10186    if($In::Opt{"TargetComponent"}) {
10187        $In::Opt{"TargetComponent"} = lc($In::Opt{"TargetComponent"});
10188    }
10189    else
10190    { # default: library
10191        $In::Opt{"TargetComponent"} = "library";
10192    }
10193
10194    foreach (keys(%{$In::Desc{0}}))
10195    { # common options
10196        $In::Desc{1}{$_} = $In::Desc{0}{$_};
10197        $In::Desc{2}{$_} = $In::Desc{0}{$_};
10198    }
10199
10200    $In::Opt{"AddTemplateInstances"} = 1;
10201    $In::Opt{"GccMissedMangling"} = 0;
10202
10203    if($In::Opt{"StdOut"})
10204    { # enable quiet mode
10205        $In::Opt{"Quiet"} = 1;
10206        $In::Opt{"JoinReport"} = 1;
10207    }
10208    if(not $In::Opt{"LogMode"})
10209    { # default
10210        $In::Opt{"LogMode"} = "w";
10211    }
10212
10213    if($In::Opt{"UserLang"}) {
10214        $In::Opt{"UserLang"} = uc($In::Opt{"UserLang"});
10215    }
10216
10217    if(my $LoggingPath = $In::Opt{"LoggingPath"})
10218    {
10219        $In::Desc{1}{"OutputLogPath"} = $LoggingPath;
10220        $In::Desc{2}{"OutputLogPath"} = $LoggingPath;
10221        if($In::Opt{"Quiet"}) {
10222            $In::Opt{"DefaultLog"} = $LoggingPath;
10223        }
10224    }
10225
10226    if($In::Opt{"Force"}) {
10227        $In::Opt{"GccMissedMangling"} = 1;
10228    }
10229
10230    if($In::Opt{"Quick"}) {
10231        $In::Opt{"AddTemplateInstances"} = 0;
10232    }
10233    if(my $DP = $In::Opt{"OutputDumpPath"})
10234    { # validate
10235        if(not isDump($DP)) {
10236            exitStatus("Error", "the dump path should be a path to *.dump or *.dump.".$In::Opt{"Ar"}." file");
10237        }
10238    }
10239    if($In::Opt{"BinOnly"}
10240    and $In::Opt{"SrcOnly"})
10241    { # both --binary and --source
10242      # is the default mode
10243        if(not $In::Opt{"CmpSystems"})
10244        {
10245            $In::Opt{"BinOnly"} = 0;
10246            $In::Opt{"SrcOnly"} = 0;
10247        }
10248
10249        $In::Opt{"DoubleReport"} = 1;
10250        $In::Opt{"JoinReport"} = 0;
10251
10252        if($In::Opt{"OutputReportPath"})
10253        { # --report-path
10254            $In::Opt{"DoubleReport"} = 0;
10255            $In::Opt{"JoinReport"} = 1;
10256        }
10257    }
10258    elsif($In::Opt{"BinOnly"}
10259    or $In::Opt{"SrcOnly"})
10260    { # --binary or --source
10261        $In::Opt{"DoubleReport"} = 0;
10262        $In::Opt{"JoinReport"} = 0;
10263    }
10264    if($In::Opt{"UseXML"})
10265    { # --xml option
10266        $In::Opt{"ReportFormat"} = "xml";
10267        $In::Opt{"DumpFormat"} = "xml";
10268    }
10269    if($In::Opt{"ReportFormat"})
10270    { # validate
10271        $In::Opt{"ReportFormat"} = lc($In::Opt{"ReportFormat"});
10272        if($In::Opt{"ReportFormat"}!~/\A(xml|html|htm)\Z/) {
10273            exitStatus("Error", "unknown report format \'".$In::Opt{"ReportFormat"}."\'");
10274        }
10275        if($In::Opt{"ReportFormat"} eq "htm")
10276        { # HTM == HTML
10277            $In::Opt{"ReportFormat"} = "html";
10278        }
10279        elsif($In::Opt{"ReportFormat"} eq "xml")
10280        { # --report-format=XML equal to --xml
10281            $In::Opt{"UseXML"} = 1;
10282        }
10283    }
10284    else
10285    { # default: HTML
10286        $In::Opt{"ReportFormat"} = "html";
10287    }
10288    if($In::Opt{"DumpFormat"})
10289    { # validate
10290        $In::Opt{"DumpFormat"} = lc($In::Opt{"DumpFormat"});
10291        if($In::Opt{"DumpFormat"}!~/\A(xml|perl)\Z/) {
10292            exitStatus("Error", "unknown ABI dump format \'".$In::Opt{"DumpFormat"}."\'");
10293        }
10294        if($In::Opt{"DumpFormat"} eq "xml")
10295        { # --dump-format=XML equal to --xml
10296            $In::Opt{"UseXML"} = 1;
10297        }
10298    }
10299    else
10300    { # default: Perl Data::Dumper
10301        $In::Opt{"DumpFormat"} = "perl";
10302    }
10303    if($In::Opt{"Quiet"} and $In::Opt{"LogMode"}!~/a|n/)
10304    { # --quiet log
10305        if(-f $In::Opt{"DefaultLog"}) {
10306            unlink($In::Opt{"DefaultLog"});
10307        }
10308    }
10309    if($In::Opt{"ExtraInfo"}) {
10310        $In::Opt{"CheckUndefined"} = 1;
10311    }
10312
10313    if($In::Opt{"TestTool"} and $In::Opt{"UseDumps"})
10314    { # --test && --use-dumps == --test-dump
10315        $In::Opt{"TestDump"} = 1;
10316    }
10317    if($In::Opt{"Tolerant"})
10318    { # enable all
10319        $In::Opt{"Tolerance"} = 1234;
10320    }
10321    if($In::Opt{"Help"})
10322    {
10323        helpMsg();
10324        exit(0);
10325    }
10326    if($In::Opt{"InfoMsg"})
10327    {
10328        infoMsg();
10329        exit(0);
10330    }
10331    if($In::Opt{"ShowVersion"})
10332    {
10333        printMsg("INFO", "ABI Compliance Checker (ABICC) $TOOL_VERSION\nCopyright (C) 2019 Andrey Ponomarenko's ABI Laboratory\nLicense: GNU LGPL 2.1 <http://www.gnu.org/licenses/>\nThis program is free software: you can redistribute it and/or modify it.\n\nWritten by Andrey Ponomarenko.");
10334        exit(0);
10335    }
10336    if($In::Opt{"DumpVersion"})
10337    {
10338        printMsg("INFO", $TOOL_VERSION);
10339        exit(0);
10340    }
10341    if($In::Opt{"ExtendedCheck"}) {
10342        $In::Opt{"CheckHeadersOnly"} = 1;
10343    }
10344    if($In::Opt{"SystemRoot"})
10345    { # user defined root
10346        if(not -e $In::Opt{"SystemRoot"}) {
10347            exitStatus("Access_Error", "can't access \'".$In::Opt{"SystemRoot"}."\'");
10348        }
10349        $In::Opt{"SystemRoot"}=~s/[\/]+\Z//g;
10350        if($In::Opt{"SystemRoot"}) {
10351            $In::Opt{"SystemRoot"} = getAbsPath($In::Opt{"SystemRoot"});
10352        }
10353    }
10354    $Data::Dumper::Sortkeys = 1;
10355
10356    if($In::Opt{"SortDump"})
10357    {
10358        $Data::Dumper::Useperl = 1;
10359        $Data::Dumper::Sortkeys = \&dump_sorting;
10360    }
10361
10362    if(my $TargetLibsPath = $In::Opt{"TargetLibsPath"})
10363    {
10364        if(not -f $TargetLibsPath) {
10365            exitStatus("Access_Error", "can't access file \'$TargetLibsPath\'");
10366        }
10367        foreach my $Lib (split(/\s*\n\s*/, readFile($TargetLibsPath)))
10368        {
10369            if($In::Opt{"OS"} eq "windows") {
10370                $In::Opt{"TargetLibs"}{lc($Lib)} = 1;
10371            }
10372            else {
10373                $In::Opt{"TargetLibs"}{$Lib} = 1;
10374            }
10375        }
10376    }
10377    if(my $TPath = $In::Opt{"TargetHeadersPath"})
10378    { # --headers-list
10379        if(not -f $TPath) {
10380            exitStatus("Access_Error", "can't access file \'$TPath\'");
10381        }
10382
10383        $In::Desc{1}{"TargetHeader"} = {};
10384        $In::Desc{2}{"TargetHeader"} = {};
10385
10386        foreach my $Header (split(/\s*\n\s*/, readFile($TPath)))
10387        {
10388            my $Name = getFilename($Header);
10389            $In::Desc{1}{"TargetHeader"}{$Name} = 1;
10390            $In::Desc{2}{"TargetHeader"}{$Name} = 1;
10391        }
10392    }
10393    if($In::Opt{"TargetHeader"})
10394    { # --header
10395        $In::Desc{1}{"TargetHeader"} = {};
10396        $In::Desc{2}{"TargetHeader"} = {};
10397
10398        my $Name = getFilename($In::Opt{"TargetHeader"});
10399        $In::Desc{1}{"TargetHeader"}{$Name} = 1;
10400        $In::Desc{2}{"TargetHeader"}{$Name} = 1;
10401    }
10402    if($In::Opt{"TestABIDumper"})
10403    {
10404        if($In::Opt{"OS"} ne "linux") {
10405            exitStatus("Error", "-test-abi-dumper option is available on Linux only");
10406        }
10407    }
10408    if($In::Opt{"TestTool"}
10409    or $In::Opt{"TestDump"}
10410    or $In::Opt{"TestABIDumper"})
10411    { # --test, --test-dump
10412        detectDefaultPaths(undef, undef, "bin", "gcc"); # to compile libs
10413        loadModule("RegTests");
10414        testTool();
10415        exit(0);
10416    }
10417    if($In::Opt{"DumpSystem"})
10418    { # --dump-system
10419        if(not $In::Opt{"TargetSysInfo"})
10420        {
10421            if(-d $MODULES_DIR."/Targets/"
10422            and -d $MODULES_DIR."/Targets/".$In::Opt{"Target"}) {
10423                $In::Opt{"TargetSysInfo"} = $MODULES_DIR."/Targets/".$In::Opt{"Target"};
10424            }
10425        }
10426
10427        if(not $In::Opt{"TargetSysInfo"}) {
10428            exitStatus("Error", "-sysinfo option should be specified to dump system ABI");
10429        }
10430
10431        if(not -d $In::Opt{"TargetSysInfo"}) {
10432            exitStatus("Access_Error", "can't access \'".$In::Opt{"TargetSysInfo"}."\'");
10433        }
10434
10435        loadModule("SysCheck");
10436        if($In::Opt{"DumpSystem"}=~/\.(xml|desc)\Z/)
10437        { # system XML descriptor
10438            if(not -f $In::Opt{"DumpSystem"}) {
10439                exitStatus("Access_Error", "can't access file \'".$In::Opt{"DumpSystem"}."\'");
10440            }
10441
10442            my $SDesc = readFile($In::Opt{"DumpSystem"});
10443            if(my $RelDir = $In::Desc{1}{"RelativeDirectory"}) {
10444                $SDesc=~s/\{RELPATH\}/$RelDir/g;
10445            }
10446
10447            readSysDesc($SDesc);
10448        }
10449        elsif(defined $In::Opt{"SystemRoot"})
10450        { # -sysroot "/" option
10451          # default target: /usr/lib, /usr/include
10452          # search libs: /usr/lib and /lib
10453            my $SystemRoot = $In::Opt{"SystemRoot"};
10454
10455            if(not -e $SystemRoot."/usr/lib") {
10456                exitStatus("Access_Error", "can't access '".$SystemRoot."/usr/lib'");
10457            }
10458            if(not -e $SystemRoot."/lib") {
10459                exitStatus("Access_Error", "can't access '".$SystemRoot."/lib'");
10460            }
10461            if(not -e $SystemRoot."/usr/include") {
10462                exitStatus("Access_Error", "can't access '".$SystemRoot."/usr/include'");
10463            }
10464            readSysDesc("
10465                <name>
10466                    ".$In::Opt{"DumpSystem"}."
10467                </name>
10468                <headers>
10469                    $SystemRoot/usr/include
10470                </headers>
10471                <libs>
10472                    $SystemRoot/usr/lib
10473                </libs>
10474                <search_libs>
10475                    $SystemRoot/lib
10476                </search_libs>");
10477        }
10478        else {
10479            exitStatus("Error", "-sysroot <dirpath> option should be specified, usually it's \"/\"");
10480        }
10481        detectDefaultPaths(undef, undef, "bin", "gcc"); # to check symbols
10482        if($In::Opt{"Target"} eq "windows")
10483        { # to run dumpbin.exe
10484          # and undname.exe
10485            checkWin32Env();
10486        }
10487        dumpSystem();
10488        exit(0);
10489    }
10490
10491    if($In::Opt{"CmpSystems"})
10492    { # --cmp-systems
10493        detectDefaultPaths(undef, undef, "bin", undef); # to extract dumps
10494        loadModule("SysCheck");
10495
10496        if(not $In::Opt{"BinOnly"}
10497        and not $In::Opt{"SrcOnly"})
10498        { # default
10499            $In::Opt{"BinOnly"} = 1;
10500        }
10501
10502        cmpSystems($In::Desc{1}{"Path"}, $In::Desc{2}{"Path"});
10503        exit(0);
10504    }
10505
10506    if(not $In::Opt{"CountSymbols"})
10507    {
10508        if(not $In::Opt{"TargetLib"}) {
10509            exitStatus("Error", "library name is not selected (-l option)");
10510        }
10511        else
10512        { # validate library name
10513            if($In::Opt{"TargetLib"}=~/[\*\/\\]/) {
10514                exitStatus("Error", "\"\\\", \"\/\" and \"*\" symbols are not allowed in the library name");
10515            }
10516        }
10517    }
10518
10519    if(not $In::Opt{"TargetTitle"}) {
10520        $In::Opt{"TargetTitle"} = $In::Opt{"TargetLib"};
10521    }
10522
10523    if(my $SymbolsListPath = $In::Opt{"SymbolsListPath"})
10524    {
10525        if(not -f $SymbolsListPath) {
10526            exitStatus("Access_Error", "can't access file \'$SymbolsListPath\'");
10527        }
10528        foreach my $S (split(/\s*\n\s*/, readFile($SymbolsListPath)))
10529        {
10530            $In::Desc{1}{"SymbolsList"}{$S} = 1;
10531            $In::Desc{2}{"SymbolsList"}{$S} = 1;
10532        }
10533    }
10534    if(my $TypesListPath = $In::Opt{"TypesListPath"})
10535    {
10536        if(not -f $TypesListPath) {
10537            exitStatus("Access_Error", "can't access file \'$TypesListPath\'");
10538        }
10539        foreach my $Type (split(/\s*\n\s*/, readFile($TypesListPath)))
10540        {
10541            $In::Desc{1}{"TypesList"}{$Type} = 1;
10542            $In::Desc{2}{"TypesList"}{$Type} = 1;
10543        }
10544    }
10545    if(my $SymbolsListPath = $In::Opt{"SkipSymbolsListPath"})
10546    {
10547        if(not -f $SymbolsListPath) {
10548            exitStatus("Access_Error", "can't access file \'$SymbolsListPath\'");
10549        }
10550        foreach my $Interface (split(/\s*\n\s*/, readFile($SymbolsListPath)))
10551        {
10552            $In::Desc{1}{"SkipSymbols"}{$Interface} = 1;
10553            $In::Desc{2}{"SkipSymbols"}{$Interface} = 1;
10554        }
10555    }
10556    if(my $TypesListPath = $In::Opt{"SkipTypesListPath"})
10557    {
10558        if(not -f $TypesListPath) {
10559            exitStatus("Access_Error", "can't access file \'$TypesListPath\'");
10560        }
10561        foreach my $Type (split(/\s*\n\s*/, readFile($TypesListPath)))
10562        {
10563            $In::Desc{1}{"SkipTypes"}{$Type} = 1;
10564            $In::Desc{2}{"SkipTypes"}{$Type} = 1;
10565        }
10566    }
10567    if(my $HeadersList = $In::Opt{"SkipHeadersPath"})
10568    {
10569        if(not -f $HeadersList) {
10570            exitStatus("Access_Error", "can't access file \'$HeadersList\'");
10571        }
10572        foreach my $Path (split(/\s*\n\s*/, readFile($HeadersList)))
10573        {
10574            my ($CPath, $Type) = classifyPath($Path);
10575            $In::Desc{1}{"SkipHeaders"}{$Type}{$CPath} = 1;
10576            $In::Desc{2}{"SkipHeaders"}{$Type}{$CPath} = 1;
10577        }
10578    }
10579    if(my $ParamNamesPath = $In::Opt{"ParamNamesPath"})
10580    {
10581        if(not -f $ParamNamesPath) {
10582            exitStatus("Access_Error", "can't access file \'$ParamNamesPath\'");
10583        }
10584        foreach my $Line (split(/\n/, readFile($ParamNamesPath)))
10585        {
10586            if($Line=~s/\A(\w+)\;//)
10587            {
10588                my $Interface = $1;
10589                if($Line=~/;(\d+);/)
10590                {
10591                    while($Line=~s/(\d+);(\w+)//) {
10592                        $AddSymbolParams{$Interface}{$1}=$2;
10593                    }
10594                }
10595                else
10596                {
10597                    my $Num = 0;
10598                    foreach my $Name (split(/;/, $Line)) {
10599                        $AddSymbolParams{$Interface}{$Num++}=$Name;
10600                    }
10601                }
10602            }
10603        }
10604    }
10605
10606    if(my $AppPath = $In::Opt{"AppPath"})
10607    {
10608        if(not -f $AppPath) {
10609            exitStatus("Access_Error", "can't access file \'$AppPath\'");
10610        }
10611
10612        detectDefaultPaths(undef, undef, "bin", "gcc");
10613        foreach my $Symbol (readSymbols_App($AppPath)) {
10614            $In::Opt{"SymbolsList_App"}{$Symbol} = 1;
10615        }
10616    }
10617
10618    if(my $Path = $In::Opt{"CountSymbols"})
10619    {
10620        if(not -e $Path) {
10621            exitStatus("Access_Error", "can't access \'$Path\'");
10622        }
10623
10624        $In::ABI{1} = readABIDump(1, $Path);
10625        initAliases(1);
10626
10627        foreach my $Id (keys(%{$SymbolInfo{1}}))
10628        {
10629            my $MnglName = $SymbolInfo{1}{$Id}{"MnglName"};
10630            if(not $MnglName) {
10631                $MnglName = $SymbolInfo{1}{$Id}{"ShortName"}
10632            }
10633
10634            if(my $SV = $In::ABI{1}{"SymbolVersion"}{$MnglName}) {
10635                $CompSign{1}{$SV} = $SymbolInfo{1}{$Id};
10636            }
10637            else {
10638                $CompSign{1}{$MnglName} = $SymbolInfo{1}{$Id};
10639            }
10640
10641            if(my $Alias = $CompSign{1}{$MnglName}{"Alias"}) {
10642                $CompSign{1}{$Alias} = $SymbolInfo{1}{$Id};
10643            }
10644        }
10645
10646        my $Count = 0;
10647        foreach my $Symbol (sort keys(%{$CompSign{1}}))
10648        {
10649            if($CompSign{1}{$Symbol}{"PureVirt"}) {
10650                next;
10651            }
10652
10653            if(not $CompSign{1}{$Symbol}{"Header"})
10654            {
10655                if(index($CompSign{1}{$Symbol}{"Source"}, ".f")==-1)
10656                { # Fortran
10657                    next;
10658                }
10659            }
10660
10661            $Count += symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected + InlineVirt", "Binary", 1);
10662        }
10663
10664        printMsg("INFO", $Count);
10665        exit(0);
10666    }
10667
10668    if($In::Opt{"DumpABI"})
10669    {
10670        createABIFile(1, $In::Opt{"DumpABI"});
10671
10672        if($In::Opt{"CompileError"}) {
10673            exit(getErrorCode("Compile_Error"));
10674        }
10675
10676        exit(0);
10677    }
10678
10679    # default: compare APIs
10680    compareInit();
10681    if($In::Opt{"JoinReport"} or $In::Opt{"DoubleReport"})
10682    {
10683        compareAPIs("Binary");
10684        compareAPIs("Source");
10685    }
10686    elsif($In::Opt{"BinOnly"}) {
10687        compareAPIs("Binary");
10688    }
10689    elsif($In::Opt{"SrcOnly"}) {
10690        compareAPIs("Source");
10691    }
10692    exitReport();
10693}
10694
10695scenario();
10696