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 .= " "; 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 .= "( "; 1307 } 1308 1309 $Name .= $Params[$Pos]; 1310 1311 $Name = "<span>".$Name."</span>"; 1312 1313 if($Pos==$#Params) { 1314 $Name .= " )"; 1315 } 1316 else { 1317 $Name .= ", "; 1318 } 1319 1320 $Signature .= $Name; 1321 } 1322 } 1323 else { 1324 $Signature .= "( )"; 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'>  <b>:</b>  ".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'> $VSpec $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!\[\]![ ]!g; 1401 $Signature=~s!operator=!operator =!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'> ".($#Headers+1)." </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'> ".($#Sources+1)." </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'> ".keys(%{$In::ABI{1}{"Symbols"}})." </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 (not 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)."> $ProblemsNum </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)."> $Added_Number </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)."> $Removed_Number </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)."> $ProblemNum </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'>⇣</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)."> $ProblemsNum </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)."> $ProblemNum </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)."> $ProblemsNum </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  </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