From e24dfddae01bcf0648def2d4486a2ac87c070a2d Mon Sep 17 00:00:00 2001 From: guoqinglan Date: Tue, 26 Oct 2021 10:53:14 +0800 Subject: [PATCH 1/2] add abi-compliance-checker-2.4 and abi-dumper-2.1 modules --- .../.github/FUNDING.yml | 4 + abi-compliance-checker-2.4/INSTALL | 143 + abi-compliance-checker-2.4/LICENSE | 504 + abi-compliance-checker-2.4/Makefile | 15 + abi-compliance-checker-2.4/Makefile.pl | 295 + abi-compliance-checker-2.4/README.md | 93 + .../abi-compliance-checker.pl | 10684 ++++++++++++++++ abi-compliance-checker-2.4/doc/Changelog.html | 2571 ++++ .../doc/Xml-Descriptor.html | 602 + abi-compliance-checker-2.4/doc/index.html | 748 ++ .../modules/Internals/ABIDump.pm | 1427 +++ .../modules/Internals/Basic.pm | 746 ++ .../modules/Internals/CallConv.pm | 1354 ++ .../modules/Internals/Descriptor.pm | 291 + .../modules/Internals/ElfTools.pm | 286 + .../modules/Internals/Filter.pm | 849 ++ .../modules/Internals/GccAst.pm | 3945 ++++++ .../modules/Internals/Input.pm | 34 + .../modules/Internals/Logging.pm | 174 + .../modules/Internals/Mangling.pm | 1052 ++ .../modules/Internals/Path.pm | 91 + .../modules/Internals/RegTests.pm | 5201 ++++++++ .../modules/Internals/Scripts/Sections.js | 16 + .../modules/Internals/Scripts/Tabs.js | 61 + .../modules/Internals/Styles/CmpSystems.css | 84 + .../modules/Internals/Styles/HeadersDiff.css | 49 + .../modules/Internals/Styles/Report.css | 254 + .../modules/Internals/Styles/SymbolsList.css | 79 + .../modules/Internals/Styles/Tabs.css | 34 + .../modules/Internals/SysCheck.pm | 2487 ++++ .../modules/Internals/SysFiles.pm | 2549 ++++ .../modules/Internals/TUDump.pm | 992 ++ .../modules/Internals/TypeAttr.pm | 268 + .../modules/Internals/Utils.pm | 493 + .../modules/Internals/XmlDump.pm | 863 ++ .../modules/RulesBin.xml | 3563 ++++++ .../modules/RulesSrc.xml | 1792 +++ abi-dumper-2.1/.github/FUNDING.yml | 4 + abi-dumper-2.1/Dockerfile | 11 + abi-dumper-2.1/INSTALL | 64 + abi-dumper-2.1/LICENSE | 504 + abi-dumper-2.1/Makefile | 8 + abi-dumper-2.1/Makefile.pl | 262 + abi-dumper-2.1/README.md | 80 + abi-dumper-2.1/abi-dumper.pl | 6802 ++++++++++ 45 files changed, 52428 insertions(+) create mode 100644 abi-compliance-checker-2.4/.github/FUNDING.yml create mode 100644 abi-compliance-checker-2.4/INSTALL create mode 100644 abi-compliance-checker-2.4/LICENSE create mode 100644 abi-compliance-checker-2.4/Makefile create mode 100644 abi-compliance-checker-2.4/Makefile.pl create mode 100644 abi-compliance-checker-2.4/README.md create mode 100755 abi-compliance-checker-2.4/abi-compliance-checker.pl create mode 100644 abi-compliance-checker-2.4/doc/Changelog.html create mode 100644 abi-compliance-checker-2.4/doc/Xml-Descriptor.html create mode 100644 abi-compliance-checker-2.4/doc/index.html create mode 100644 abi-compliance-checker-2.4/modules/Internals/ABIDump.pm create mode 100644 abi-compliance-checker-2.4/modules/Internals/Basic.pm create mode 100644 abi-compliance-checker-2.4/modules/Internals/CallConv.pm create mode 100644 abi-compliance-checker-2.4/modules/Internals/Descriptor.pm create mode 100644 abi-compliance-checker-2.4/modules/Internals/ElfTools.pm create mode 100644 abi-compliance-checker-2.4/modules/Internals/Filter.pm create mode 100644 abi-compliance-checker-2.4/modules/Internals/GccAst.pm create mode 100644 abi-compliance-checker-2.4/modules/Internals/Input.pm create mode 100644 abi-compliance-checker-2.4/modules/Internals/Logging.pm create mode 100644 abi-compliance-checker-2.4/modules/Internals/Mangling.pm create mode 100644 abi-compliance-checker-2.4/modules/Internals/Path.pm create mode 100644 abi-compliance-checker-2.4/modules/Internals/RegTests.pm create mode 100644 abi-compliance-checker-2.4/modules/Internals/Scripts/Sections.js create mode 100644 abi-compliance-checker-2.4/modules/Internals/Scripts/Tabs.js create mode 100644 abi-compliance-checker-2.4/modules/Internals/Styles/CmpSystems.css create mode 100644 abi-compliance-checker-2.4/modules/Internals/Styles/HeadersDiff.css create mode 100644 abi-compliance-checker-2.4/modules/Internals/Styles/Report.css create mode 100644 abi-compliance-checker-2.4/modules/Internals/Styles/SymbolsList.css create mode 100644 abi-compliance-checker-2.4/modules/Internals/Styles/Tabs.css create mode 100644 abi-compliance-checker-2.4/modules/Internals/SysCheck.pm create mode 100644 abi-compliance-checker-2.4/modules/Internals/SysFiles.pm create mode 100644 abi-compliance-checker-2.4/modules/Internals/TUDump.pm create mode 100644 abi-compliance-checker-2.4/modules/Internals/TypeAttr.pm create mode 100644 abi-compliance-checker-2.4/modules/Internals/Utils.pm create mode 100644 abi-compliance-checker-2.4/modules/Internals/XmlDump.pm create mode 100644 abi-compliance-checker-2.4/modules/RulesBin.xml create mode 100644 abi-compliance-checker-2.4/modules/RulesSrc.xml create mode 100644 abi-dumper-2.1/.github/FUNDING.yml create mode 100644 abi-dumper-2.1/Dockerfile create mode 100644 abi-dumper-2.1/INSTALL create mode 100644 abi-dumper-2.1/LICENSE create mode 100644 abi-dumper-2.1/Makefile create mode 100644 abi-dumper-2.1/Makefile.pl create mode 100644 abi-dumper-2.1/README.md create mode 100644 abi-dumper-2.1/abi-dumper.pl diff --git a/abi-compliance-checker-2.4/.github/FUNDING.yml b/abi-compliance-checker-2.4/.github/FUNDING.yml new file mode 100644 index 0000000..5773b1d --- /dev/null +++ b/abi-compliance-checker-2.4/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +patreon: LINUXABI +custom: https://abi-laboratory.pro/?view=donate diff --git a/abi-compliance-checker-2.4/INSTALL b/abi-compliance-checker-2.4/INSTALL new file mode 100644 index 0000000..e0dc1f7 --- /dev/null +++ b/abi-compliance-checker-2.4/INSTALL @@ -0,0 +1,143 @@ + +Copyright (C) 2009-2011 Institute for System Programming, RAS +Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies) +Copyright (C) 2012-2018 Andrey Ponomarenko's ABI Laboratory +All rights reserved. + + +RELEASE INFORMATION + +Project: ABI Compliance Checker (ABICC) +Version: 2.3 +Date: May 15, 2018 + + +This file explains how to install and setup environment +for the tool in your computer. + + +Content: + + 1. Requirements for Linux and FreeBSD + 2. Requirements for Mac OS X + 3. Requirements for MS Windows + 4. Configure and Install + 5. Usage (with ABI Dumper) + 6. Usage (Original) + + +1. REQUIREMENTS FOR LINUX AND FREEBSD +===================================== + + 1. G++ (3.0 or newer) + 2. GNU Binutils (c++filt, readelf, objdump) + 3. Perl 5 + 4. Ctags + 5. ABI Dumper (1.1 or newer) + + + +2. REQUIREMENTS FOR MAC OS X +============================ + + 1. Xcode (g++, c++filt, otool, nm) + 2. Perl 5 + 3. Ctags + +2.1 Setup environment + + 1. If /usr/bin/g++ points to clang, then please + specify GCC path by the -gcc-path option + + 2. You can install GCC by the command: + + brew install homebrew/versions/gcc49 + + And then specify its path: + + abi-compliance-checker --gcc-path=/usr/local/bin/gcc-4.9 ... + + + +3. REQUIREMENTS FOR MS WINDOWS +============================== + + 1. MinGW (3.0 or newer) + 2. MS Visual C++ (dumpbin, undname, cl) + 3. Active Perl 5 (5.8 or newer) + 4. Sigcheck v1.71 or newer + 5. Info-ZIP 3.0 (zip, unzip) + 6. Ctags + +3.1 Setup environment + + 1. Add tool locations to the PATH environment variable + 2. Run vcvars64.bat script (C:\Microsoft Visual Studio 9.0\VC\bin\) + + + +4. CONFIGURE AND INSTALL +======================== + + This command will install the abi-compliance-checker program into the + PREFIX/bin system directory and private modules into the PREFIX/share: + + sudo make install prefix=PREFIX [/usr, /usr/local, ...] + +4.1 Remove + + sudo make uninstall prefix=PREFIX + + + +5. USAGE (WITH ABI DUMPER) +========================== + + Library should be compiled with -g -Og + options to contain DWARF debug info. + + Create ABI dumps for both library versions + using the ABI Dumper tool (https://github.com/lvc/abi-dumper): + + abi-dumper OLD.so -o ABI-0.dump -lver 0 + abi-dumper NEW.so -o ABI-1.dump -lver 1 + + You can filter public ABI symbols with the help of + additional -public-headers option of the ABI Dumper tool. + + Compare ABI dumps to create report: + + abi-compliance-checker -l NAME -old ABI-0.dump -new ABI-1.dump + + + +6. USAGE (ORIGINAL) +=================== + + Create XML-descriptors for two versions + of a library (OLD.xml and NEW.xml): + + + 1.0 + + + + /path1/to/header(s)/ + /path2/to/header(s)/ + ... + + + + /path1/to/library(ies)/ + /path2/to/library(ies)/ + ... + + + Check compatibility: + + abi-compliance-checker -lib NAME -old OLD.xml -new NEW.xml + + For advanced usage, see doc/index.html or -help option. + + +Enjoy! diff --git a/abi-compliance-checker-2.4/LICENSE b/abi-compliance-checker-2.4/LICENSE new file mode 100644 index 0000000..8000a6f --- /dev/null +++ b/abi-compliance-checker-2.4/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/abi-compliance-checker-2.4/Makefile b/abi-compliance-checker-2.4/Makefile new file mode 100644 index 0000000..daf10ad --- /dev/null +++ b/abi-compliance-checker-2.4/Makefile @@ -0,0 +1,15 @@ +prefix ?= /usr + +.PHONY: all + +all: + echo "Nothing to build." + +install: + perl Makefile.pl -install -prefix "$(prefix)" + +uninstall: + perl Makefile.pl -remove -prefix "$(prefix)" + +clean: + echo "Nothing to clean up." diff --git a/abi-compliance-checker-2.4/Makefile.pl b/abi-compliance-checker-2.4/Makefile.pl new file mode 100644 index 0000000..69a1322 --- /dev/null +++ b/abi-compliance-checker-2.4/Makefile.pl @@ -0,0 +1,295 @@ +#!/usr/bin/perl +########################################################################### +# A makefile to install the tool +# Install/remove the tool for GNU/Linux, FreeBSD and Mac OS X +# +# Copyright (C) 2009-2011 Institute for System Programming, RAS +# Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies) +# Copyright (C) 2012-2018 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; +use Getopt::Long; +Getopt::Long::Configure ("posix_default", "no_ignore_case"); +use File::Path qw(mkpath rmtree); +use File::Spec qw(catfile file_name_is_absolute); +use File::Copy qw(copy); +use File::Basename qw(dirname); +use Cwd qw(abs_path); +use File::Find; +use Config; + +my $TOOL_SNAME = "abi-compliance-checker"; +my $ARCHIVE_DIR = abs_path(dirname($0)); + +my $HELP_MSG = " +NAME: + Makefile for ABI Compliance Checker + +DESCRIPTION: + Install $TOOL_SNAME command and private modules. + +USAGE: + sudo perl $0 -install -prefix /usr + sudo perl $0 -remove -prefix /usr + +OPTIONS: + -h|-help + Print this help. + + --prefix=PREFIX + Install files in PREFIX [/usr]. + + -install + Command to install the tool. + + -remove + Command to remove the tool. + +EXTRA OPTIONS: + --destdir=DESTDIR + This option is for maintainers to build + RPM or DEB packages inside the build root. + The environment variable DESTDIR is also + supported. +\n"; + +if(not @ARGV) +{ + print $HELP_MSG; + exit(0); +} + +my ($PREFIX, $DESTDIR, $Help, $Install, $Remove); + +GetOptions( + "h|help!" => \$Help, + "prefix=s" => \$PREFIX, + "destdir=s" => \$DESTDIR, + "install!" => \$Install, + "remove!" => \$Remove +) or exit(1); + +sub scenario() +{ + if($Help) + { + print $HELP_MSG; + exit(0); + } + if(not $Install and not $Remove) + { + print STDERR "ERROR: command is not selected (-install or -remove)\n"; + exit(1); + } + + if($Install) + { # remove old version first + $Remove = 1; + } + + if($PREFIX ne "/") { + $PREFIX=~s/[\/]+\Z//g; + } + if(not $PREFIX) + { # default prefix + if($Config{"osname"}!~/win/i) { + $PREFIX = "/usr"; + } + } + if(my $Var = $ENV{"DESTDIR"}) + { + print "Using DESTDIR environment variable\n"; + $DESTDIR = $Var; + } + if($DESTDIR) + { + if($DESTDIR ne "/") { + $DESTDIR=~s/[\/]+\Z//g; + } + if(not isAbs($DESTDIR)) + { + print STDERR "ERROR: destdir is not absolute path\n"; + exit(1); + } + if(not -d $DESTDIR) + { + print STDERR "ERROR: you should create destdir directory first\n"; + exit(1); + } + $PREFIX = $DESTDIR.$PREFIX; + if(not -d $PREFIX) + { + print STDERR "ERROR: you should create installation directory first (destdir + prefix):\n mkdir -p $PREFIX\n"; + exit(1); + } + } + else + { + if(not isAbs($PREFIX)) + { + print STDERR "ERROR: prefix is not absolute path\n"; + exit(1); + } + if(not -d $PREFIX) + { + print STDERR "ERROR: you should create prefix directory first\n"; + exit(1); + } + } + + print "INSTALL PREFIX: $PREFIX\n"; + + # paths + my $EXE_PATH = catFile($PREFIX, "bin"); + my $MODULES_PATH = catFile($PREFIX, "share", $TOOL_SNAME); + my $REL_PATH = catFile("..", "share", $TOOL_SNAME); + my $TOOL_PATH = catFile($EXE_PATH, $TOOL_SNAME); + + if(not -w $PREFIX) + { + print STDERR "ERROR: you should be root\n"; + exit(1); + } + if($Remove) + { + if(-e $EXE_PATH."/".$TOOL_SNAME) + { # remove executable + print "-- Removing $TOOL_PATH\n"; + unlink($EXE_PATH."/".$TOOL_SNAME); + } + elsif(not $Install) { + print "The tool is not installed\n"; + } + + if(-d $MODULES_PATH) + { # remove modules + print "-- Removing $MODULES_PATH\n"; + rmtree($MODULES_PATH); + } + elsif(not $Install) { + print "The modules of the tool are not installed\n"; + } + } + if($Install) + { + # configure + my $Content = readFile($ARCHIVE_DIR."/".$TOOL_SNAME.".pl"); + if($DESTDIR) { # relative path + $Content=~s/MODULES_INSTALL_PATH/$REL_PATH/; + } + else { # absolute path + $Content=~s/MODULES_INSTALL_PATH/$MODULES_PATH/; + } + + # copy executable + print "-- Installing $TOOL_PATH\n"; + mkpath($EXE_PATH); + writeFile($EXE_PATH."/".$TOOL_SNAME, $Content); + chmod(0755, $EXE_PATH."/".$TOOL_SNAME); + + if($Config{"osname"}=~/win/i) { + writeFile($EXE_PATH."/".$TOOL_SNAME.".cmd", "\@perl \"$TOOL_PATH\" \%*"); + } + + # copy modules + if(-d $ARCHIVE_DIR."/modules") + { + print "-- Installing $MODULES_PATH\n"; + mkpath($MODULES_PATH); + copyDir($ARCHIVE_DIR."/modules", $MODULES_PATH); + } + + # check PATH + my $Warn = "WARNING: your PATH variable doesn't include \'$EXE_PATH\'\n"; + + if($Config{"osname"}=~/win/i) + { + if($ENV{"PATH"}!~/(\A|[:;])\Q$EXE_PATH\E[\/\\]?(\Z|[:;])/i) { + print $Warn; + } + } + else + { + if($ENV{"PATH"}!~/(\A|[:;])\Q$EXE_PATH\E[\/\\]?(\Z|[:;])/) { + print $Warn; + } + } + } + exit(0); +} + +sub catFile(@) { + return File::Spec->catfile(@_); +} + +sub isAbs($) { + return File::Spec->file_name_is_absolute($_[0]); +} + +sub copyDir($$) +{ + my ($From, $To) = @_; + my %Files; + find(\&wanted, $From); + sub wanted { + $Files{$File::Find::dir."/$_"} = 1 if($_ ne "."); + } + foreach my $Path (sort keys(%Files)) + { + if($Path=~/Targets\//) + { # Do not install descriptors + next; + } + my $Inst = $Path; + $Inst=~s/\A\Q$ARCHIVE_DIR\E/$To/; + if(-d $Path) + { # directories + mkpath($Inst); + } + else + { # files + mkpath(dirname($Inst)); + copy($Path, $Inst); + } + } +} + +sub readFile($) +{ + my $Path = $_[0]; + + open(FILE, $Path) || die ("can't open file \'$Path\': $!\n"); + local $/ = undef; + my $Content = ; + close(FILE); + + return $Content; +} + +sub writeFile($$) +{ + my ($Path, $Content) = @_; + + open(FILE, ">".$Path) || die ("can't open file \'$Path\': $!\n"); + print FILE $Content; + close(FILE); +} + +scenario(); diff --git a/abi-compliance-checker-2.4/README.md b/abi-compliance-checker-2.4/README.md new file mode 100644 index 0000000..d305d43 --- /dev/null +++ b/abi-compliance-checker-2.4/README.md @@ -0,0 +1,93 @@ +ABICC 2.3 +========= + +ABI Compliance Checker (ABICC) — a tool for checking backward binary and source-level compatibility of a C/C++ software library. + +Contents +-------- + +1. [ About ](#about) +2. [ Install ](#install) +3. [ Usage ](#usage) +4. [ Test suite ](#test-suite) + +About +----- + +The tool analyzes changes in API/ABI (ABI=API+compiler ABI) that may break binary compatibility and/or source compatibility: changes in calling stack, v-table changes, removed symbols, renamed fields, etc. + +The tool can create and compare ABI dumps for header files and shared objects of a library. The ABI dump for a library can also be created by the ABI Dumper tool (https://github.com/lvc/abi-dumper) if shared objects include debug-info. + +The tool is intended for developers of software libraries and Linux maintainers who are interested in ensuring backward compatibility, i.e. allow old applications to run or to be recompiled with newer library versions. + +The tool is a core of the ABI Tracker and Upstream Tracker projects: https://abi-laboratory.pro/tracker/ + +The tool is developed by Andrey Ponomarenko. + +Install +------- + + sudo make install prefix=/usr + +###### Requires + +* Perl 5 +* GCC C++ (3.0 or newer) +* GNU Binutils +* Ctags +* ABI Dumper (1.1 or newer) + +###### Platforms + +* Linux +* Mac OS X +* Windows + +Usage +----- + +###### With ABI Dumper + +1. Library should be compiled with `-g -Og` GCC options to contain DWARF debug info + +2. Create ABI dumps for both library versions using the ABI Dumper (https://github.com/lvc/abi-dumper) tool: + + abi-dumper OLD.so -o ABI-1.dump -lver 1 + abi-dumper NEW.so -o ABI-2.dump -lver 2 + +3. You can filter public ABI with the help of additional `-public-headers` option of the ABI Dumper tool + +4. Compare ABI dumps to create report: + + abi-compliance-checker -l NAME -old ABI-1.dump -new ABI-2.dump + +###### Compile headers + + abi-compliance-checker -lib NAME -old OLD.xml -new NEW.xml + +`OLD.xml` and `NEW.xml` are XML-descriptors: + + + 1.0 + + + + /path/to/headers/ + + + + /path/to/libraries/ + + +###### Adv. usage + +For advanced usage, see `doc/index.html` or output of `-help` option. + +Test suite +---------- + +The tool is tested properly in the ABI Tracker and Upstream Tracker projects, by the community and by the internal test suite: + + abi-compliance-checker -test + +There are about 100 test cases for C and 200 test cases for C++ API/ABI breaks. diff --git a/abi-compliance-checker-2.4/abi-compliance-checker.pl b/abi-compliance-checker-2.4/abi-compliance-checker.pl new file mode 100755 index 0000000..98117bb --- /dev/null +++ b/abi-compliance-checker-2.4/abi-compliance-checker.pl @@ -0,0 +1,10684 @@ +#!/usr/bin/perl +######################################################################## +# ABI Compliance Checker (ABICC) 2.3 +# A tool for checking backward compatibility of a C/C++ library API +# +# Copyright (C) 2009-2011 Institute for System Programming, RAS +# Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies) +# Copyright (C) 2012-2019 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# PLATFORMS +# ========= +# Linux, FreeBSD, Solaris, Mac OS X, MS Windows, Symbian, Haiku +# +# REQUIREMENTS +# ============ +# Linux +# - G++ (3.0 or newer) +# - GNU Binutils (readelf, c++filt, objdump) +# - Perl 5 +# - Ctags +# - ABI Dumper >= 1.1 +# +# Mac OS X +# - Xcode (g++, c++filt, otool, nm) +# - Ctags +# +# MS Windows +# - MinGW (3.0 or newer) +# - MS Visual C++ (dumpbin, undname, cl) +# - Active Perl 5 (5.8 or newer) +# - Sigcheck v2.52 or newer +# - GnuWin Zip and UnZip +# - Ctags (Exuberant or Universal) +# - Add tool locations to the PATH environment variable +# - Run vcvars64.bat (C:\Microsoft Visual Studio 9.0\VC\bin\) +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +######################################################################## +use Getopt::Long; +Getopt::Long::Configure ("posix_default", "no_ignore_case"); +use File::Path qw(mkpath rmtree); +use File::Temp qw(tempdir); +use File::Copy qw(copy); +use File::Basename qw(dirname); +use Cwd qw(abs_path cwd); +use Data::Dumper; + +my $TOOL_VERSION = "2.3"; +my $ABI_DUMP_VERSION = "3.5"; +my $ABI_DUMP_VERSION_MIN = "3.5"; + +my $XML_REPORT_VERSION = "1.2"; +my $XML_ABI_DUMP_VERSION = "1.2"; + +# Internal modules +my $MODULES_DIR = getModules(); +push(@INC, dirname($MODULES_DIR)); + +# Basic modules +my %LoadedModules = (); +loadModule("Basic"); +loadModule("Input"); +loadModule("Path"); +loadModule("Logging"); +loadModule("Utils"); +loadModule("TypeAttr"); +loadModule("Filter"); +loadModule("SysFiles"); +loadModule("Descriptor"); +loadModule("Mangling"); + +# Rules DB +my %RULES_PATH = ( + "Binary" => $MODULES_DIR."/RulesBin.xml", + "Source" => $MODULES_DIR."/RulesSrc.xml"); + +my $BYTE = 8; +my $CmdName = getFilename($0); + +my %HomePage = ( + "Dev"=>"https://github.com/lvc/abi-compliance-checker", + "Doc"=>"https://lvc.github.io/abi-compliance-checker/" +); + +my $ShortUsage = "ABI Compliance Checker (ABICC) $TOOL_VERSION +A tool for checking backward compatibility of a C/C++ library API +Copyright (C) 2019 Andrey Ponomarenko's ABI Laboratory +License: GNU LGPL 2.1 + +Usage: $CmdName [options] +Example: $CmdName -l NAME -old ABI-0.dump -new ABI-1.dump + +ABI-0.dump and ABI-1.dump are ABI dumps generated +by the ABI Dumper or ABICC tools. + +More info: $CmdName --help\n"; + +if($#ARGV==-1) +{ + printMsg("INFO", $ShortUsage); + exit(0); +} + +GetOptions( + "h|help!" => \$In::Opt{"Help"}, + "i|info!" => \$In::Opt{"InfoMsg"}, + "v|version!" => \$In::Opt{"ShowVersion"}, + "dumpversion!" => \$In::Opt{"DumpVersion"}, +# General + "l|lib|library=s" => \$In::Opt{"TargetLib"}, + "d1|old|o=s" => \$In::Desc{1}{"Path"}, + "d2|new|n=s" => \$In::Desc{2}{"Path"}, + "dump|dump-abi|dump_abi=s" => \$In::Opt{"DumpABI"}, + "d|f|filter=s" => \$In::Opt{"FilterPath"}, +# Extra + "debug!" => \$In::Opt{"Debug"}, + "debug-mangling!" => \$In::Opt{"DebugMangling"}, + "ext|extended!" => \$In::Opt{"ExtendedCheck"}, + "static|static-libs!" => \$In::Opt{"UseStaticLibs"}, + "gcc-path|cross-gcc=s" => \$In::Opt{"CrossGcc"}, + "gcc-prefix|cross-prefix=s" => \$In::Opt{"CrossPrefix"}, + "gcc-options=s" => \$In::Opt{"GccOptions"}, + "count-symbols=s" => \$In::Opt{"CountSymbols"}, + "use-dumps!" => \$In::Opt{"UseDumps"}, + "xml!" => \$In::Opt{"UseXML"}, + "app|application=s" => \$In::Opt{"AppPath"}, + "headers-only!" => \$In::Opt{"CheckHeadersOnly"}, + "v1|vnum1|version1=s" => \$In::Desc{1}{"TargetVersion"}, + "v2|vnum2|version2=s" => \$In::Desc{2}{"TargetVersion"}, + "relpath1=s" => \$In::Desc{1}{"RelativeDirectory"}, + "relpath2=s" => \$In::Desc{2}{"RelativeDirectory"}, +# Test + "test!" => \$In::Opt{"TestTool"}, + "test-dump!" => \$In::Opt{"TestDump"}, + "test-abi-dumper!" => \$In::Opt{"TestABIDumper"}, +# Report + "s|strict!" => \$In::Opt{"StrictCompat"}, + "binary|bin|abi!" => \$In::Opt{"BinOnly"}, + "source|src|api!" => \$In::Opt{"SrcOnly"}, + "warn-newsym!" => \$In::Opt{"WarnNewSym"}, +# Report path + "report-path=s" => \$In::Opt{"OutputReportPath"}, + "bin-report-path=s" => \$In::Opt{"BinReportPath"}, + "src-report-path=s" => \$In::Opt{"SrcReportPath"}, +# Report format + "show-retval!" => \$In::Opt{"ShowRetVal"}, + "stdout!" => \$In::Opt{"StdOut"}, + "report-format=s" => \$In::Opt{"ReportFormat"}, + "old-style!" => \$In::Opt{"OldStyle"}, + "title=s" => \$In::Opt{"TargetTitle"}, + "component=s" => \$In::Opt{"TargetComponent"}, + "p|params=s" => \$In::Opt{"ParamNamesPath"}, + "limit-affected|affected-limit=s" => \$In::Opt{"AffectLimit"}, + "all-affected!" => \$In::Opt{"AllAffected"}, + "list-affected!" => \$In::Opt{"ListAffected"}, +# ABI dump + "dump-path=s" => \$In::Opt{"OutputDumpPath"}, + "dump-format=s" => \$In::Opt{"DumpFormat"}, + "check!" => \$In::Opt{"CheckInfo"}, + "extra-info=s" => \$In::Opt{"ExtraInfo"}, + "extra-dump!" => \$In::Opt{"ExtraDump"}, + "relpath=s" => \$In::Desc{1}{"RelativeDirectory"}, + "vnum=s" => \$In::Desc{1}{"TargetVersion"}, + "sort!" => \$In::Opt{"SortDump"}, +# Filter symbols and types + "symbols-list=s" => \$In::Opt{"SymbolsListPath"}, + "types-list=s" => \$In::Opt{"TypesListPath"}, + "skip-symbols=s" => \$In::Opt{"SkipSymbolsListPath"}, + "skip-types=s" => \$In::Opt{"SkipTypesListPath"}, + "skip-internal-symbols|skip-internal=s" => \$In::Opt{"SkipInternalSymbols"}, + "skip-internal-types=s" => \$In::Opt{"SkipInternalTypes"}, + "keep-cxx!" => \$In::Opt{"KeepCxx"}, + "keep-reserved!" => \$In::Opt{"KeepReserved"}, +# Filter header files + "skip-headers=s" => \$In::Opt{"SkipHeadersPath"}, + "headers-list=s" => \$In::Opt{"TargetHeadersPath"}, + "header=s" => \$In::Opt{"TargetHeader"}, + "nostdinc!" => \$In::Opt{"NoStdInc"}, + "tolerance=s" => \$In::Opt{"Tolerance"}, + "tolerant!" => \$In::Opt{"Tolerant"}, + "skip-unidentified!" => \$In::Opt{"SkipUnidentified"}, +# Filter rules + "skip-typedef-uncover!" => \$In::Opt{"SkipTypedefUncover"}, + "check-private-abi!" => \$In::Opt{"CheckPrivateABI"}, + "disable-constants-check!" => \$In::Opt{"DisableConstantsCheck"}, + "skip-added-constants!" => \$In::Opt{"SkipAddedConstants"}, + "skip-removed-constants!" => \$In::Opt{"SkipRemovedConstants"}, +# Other + "lang=s" => \$In::Opt{"UserLang"}, + "arch=s" => \$In::Opt{"TargetArch"}, + "mingw-compatible!" => \$In::Opt{"MinGWCompat"}, + "cxx-incompatible|cpp-incompatible!" => \$In::Opt{"CxxIncompat"}, + "cpp-compatible!" => \$In::Opt{"CxxCompat"}, + "quick!" => \$In::Opt{"Quick"}, + "force!" => \$In::Opt{"Force"}, +# OS analysis + "dump-system=s" => \$In::Opt{"DumpSystem"}, + "cmp-systems!" => \$In::Opt{"CmpSystems"}, + "sysroot=s" => \$In::Opt{"SystemRoot"}, + "sysinfo=s" => \$In::Opt{"TargetSysInfo"}, + "libs-list=s" => \$In::Opt{"TargetLibsPath"}, +# Logging + "log-path=s" => \$In::Opt{"LoggingPath"}, + "log1-path=s" => \$In::Desc{1}{"OutputLogPath"}, + "log2-path=s" => \$In::Desc{2}{"OutputLogPath"}, + "logging-mode=s" => \$In::Opt{"LogMode"}, + "q|quiet!" => \$In::Opt{"Quiet"} +) or errMsg(); + +sub errMsg() +{ + printMsg("INFO", "\n".$ShortUsage); + exit(getErrorCode("Error")); +} + +# Default log path +$In::Opt{"DefaultLog"} = "logs/run.log"; + +my $HelpMessage = " +NAME: + ABI Compliance Checker ($CmdName) + Check backward compatibility of a C/C++ library API + +DESCRIPTION: + ABI Compliance Checker (ABICC) is a tool for checking backward binary + compatibility and backward source compatibility of a C/C++ library API. + + The tool analyzes changes in API and ABI (ABI=API+compiler ABI) that may + break binary compatibility and/or source compatibility: changes in calling + stack, v-table changes, removed symbols, renamed fields, etc. + + Binary incompatibility may result in crashing or incorrect behavior of + applications built with an old version of a library if they run on a new + one. Source incompatibility may result in recompilation errors with a new + library version. + + The tool can create and compare ABI dumps for header files and shared + objects of a library. The ABI dump for a library can also be created by + the ABI Dumper tool (https://github.com/lvc/abi-dumper) if shared objects + include debug-info. + + The tool is intended for developers of software libraries and maintainers + of operating systems who are interested in ensuring backward compatibility, + i.e. allow old applications to run or to be recompiled with newer library + versions. + + Also the tool can be used by ISVs for checking applications portability to + new library versions. Found issues can be taken into account when adapting + the application to a new library version. + + This tool is free software: you can redistribute it and/or modify it + under the terms of the GNU LGPL 2.1. + +USAGE #1 (WITH ABI DUMPER): + + 1. Library should be compiled with \"-g -Og\" GCC options + to contain DWARF debug info + + 2. Create ABI dumps for both library versions + using the ABI Dumper (https://github.com/lvc/abi-dumper) tool: + + abi-dumper OLD.so -o ABI-0.dump -lver 0 + abi-dumper NEW.so -o ABI-1.dump -lver 1 + + 3. You can filter public ABI with the help of + additional -public-headers option of the ABI Dumper tool. + + 4. Compare ABI dumps to create report: + + abi-compliance-checker -l NAME -old ABI-0.dump -new ABI-1.dump + +USAGE #2 (ORIGINAL): + + 1. Create XML-descriptors for two versions + of a library (OLD.xml and NEW.xml): + + + 1.0 + + + + /path/to/headers/ + + + + /path/to/libraries/ + + + 2. Compare Xml-descriptors to create report: + + abi-compliance-checker -lib NAME -old OLD.xml -new NEW.xml + +USAGE #3 (CREATE ABI DUMPS): + + 1. Create XML-descriptors for two versions + of a library (OLD.xml and NEW.xml): + + + 1.0 + + + + /path/to/headers/ + + + + /path/to/libraries/ + + + 2. Create ABI dumps: + + abi-compliance-checker -lib NAME -dump OLD.xml -dump-path ./ABI-0.dump + abi-compliance-checker -lib NAME -dump NEW.xml -dump-path ./ABI-1.dump + + 3. Compare ABI dumps to create report: + + abi-compliance-checker -l NAME -old ABI-0.dump -new ABI-1.dump + +INFO OPTIONS: + -h|-help + Print this help. + + -i|-info + Print complete info including all options. + + -v|-version + Print version information. + + -dumpversion + Print the tool version ($TOOL_VERSION) and don't do anything else. + +GENERAL OPTIONS: + -l|-library NAME + Any name of the library. + + -old|-d1 PATH + Descriptor of the 1st (old) library version. + It may be one of the following: + + 1. ABI dump generated by the ABI Dumper tool + 2. XML-descriptor (*.xml file): + + + 1.0 + + + + /path1/to/header(s)/ + /path2/to/header(s)/ + ... + + + + /path1/to/library(ies)/ + /path2/to/library(ies)/ + ... + + + ... + + 3. ABI dump generated by -dump option + 4. Directory with headers and libraries + 5. Single header file + + If you are using 4-5 descriptor types then you should + specify version numbers with -v1 and -v2 options. + + For more information, please see: + https://lvc.github.io/abi-compliance-checker/Xml-Descriptor.html + + -new|-d2 PATH + Descriptor of the 2nd (new) library version. + + -dump PATH + Create library ABI dump for the input XML descriptor. You can + transfer it anywhere and pass instead of the descriptor. Also + it can be used for debugging the tool. + + -filter PATH + A path to XML descriptor with skip_* rules to filter + analyzed symbols in the report. +"; + +sub helpMsg() { + printMsg("INFO", $HelpMessage." +MORE OPTIONS: + $CmdName --info\n"); +} + +sub infoMsg() +{ + printMsg("INFO", "$HelpMessage +EXTRA OPTIONS: + -debug + Debugging mode. Print debug info on the screen. Save intermediate + analysis stages in the debug directory: + debug/LIB_NAME/VERSION/ + + Also consider using -dump option for debugging the tool. + + -ext|-extended + If your library A is supposed to be used by other library B and you + want to control the ABI of B, then you should enable this option. The + tool will check for changes in all data types, even if they are not + used by any function in the library A. Such data types are not part + of the A library ABI, but may be a part of the ABI of the B library. + + The short scheme is: + app C (broken) -> lib B (broken ABI) -> lib A (stable ABI) + + -static + Check static libraries instead of the shared ones. The section + of the XML-descriptor should point to static libraries location. + + -gcc-path PATH + Path to the cross GCC compiler to use instead of the usual (host) GCC. + + -gcc-prefix PREFIX + GCC toolchain prefix. + + -gcc-options OPTS + Additional compiler options. + + -count-symbols PATH + Count total public symbols in the ABI dump. + + -use-dumps + Make dumps for two versions of a library and compare dumps. This should + increase the performance of the tool and decrease the system memory usage. + + -xml + Alias for: --report-format=xml or --dump-format=xml + + -app|-application PATH + This option allows one to specify the application that should be checked + for portability to the new library version. + + -headers-only + Check header files without libraries. It is easy to run, but may + provide a low quality compatibility report with false positives and + without detecting of added/removed symbols. + + -v1|-vnum1 NUM + Specify 1st library version outside the descriptor. This option is needed + if you have preferred an alternative descriptor type (see -d1 option). + + In general case you should specify it in the XML-descriptor: + + VERSION + + + -v2|-vnum2 NUM + Specify 2nd library version outside the descriptor. + + -relpath1 PATH + Replace {RELPATH} macros to PATH in the 1st XML-descriptor (-d1). + + -relpath2 PATH + Replace {RELPATH} macros to PATH in the 2nd XML-descriptor (-d2). + +TEST OPTIONS: + -test + Run internal tests. Create two binary incompatible versions of a sample + library and run the tool to check them for compatibility. This option + allows one to check if the tool works correctly in the current environment. + + -test-dump + Test ability to create, read and compare ABI dumps. + + -test-abi-dumper + Compare ABI dumps created by the ABI Dumper tool. + +REPORT OPTIONS: + -binary|-bin|-abi + Show binary compatibility problems only. + Generate report to: + compat_reports/LIB_NAME/V1_to_V2/abi_compat_report.html + + -source|-src|-api + Show source compatibility problems only. + Generate report to: + compat_reports/LIB_NAME/V1_to_V2/src_compat_report.html + + -s|-strict + Treat all compatibility warnings as problems. Add a number of \"Low\" + severity problems to the return value of the tool. + + -warn-newsym + Treat new symbols as problems, because that breaks semantic versioning. + For more info please visit https://semver.org/ + +REPORT PATH OPTIONS: + -report-path PATH + Path to compatibility report. + Default: + compat_reports/LIB_NAME/V1_to_V2/compat_report.html + + -bin-report-path PATH + Path to binary compatibility report. + Default: + compat_reports/LIB_NAME/V1_to_V2/abi_compat_report.html + + -src-report-path PATH + Path to source compatibility report. + Default: + compat_reports/LIB_NAME/V1_to_V2/src_compat_report.html + +REPORT FORMAT OPTIONS: + -show-retval + Show the symbol's return type in the report. + + -stdout + Print analysis results (compatibility reports and ABI dumps) to stdout + instead of creating a file. This would allow piping data to other programs. + + -report-format FMT + Change format of compatibility report. + Formats: + htm - HTML format (default) + xml - XML format + + -old-style + Generate old-style report. + + -title NAME + Change library name in the report title to NAME. By default + will be displayed a name specified by -l option. + + -component NAME + The component name in the title and summary of the HTML report. + Default: + library + + -p|-params PATH + Path to file with the function parameter names. It can be used + for improving report view if the library header files have no + parameter names. File format: + + func1;param1;param2;param3 ... + func2;param1;param2;param3 ... + ... + + -limit-affected LIMIT + The maximum number of affected symbols listed under the description + of the changed type in the report. + + -list-affected + Generate file with the list of incompatible + symbols beside the HTML compatibility report. + Use 'c++filt \@file' command from GNU binutils + to unmangle C++ symbols in the generated file. + Default names: + abi_affected.txt + src_affected.txt + +ABI DUMP OPTIONS: + -dump-path PATH + Specify a *.dump file path where to generate an ABI dump. + Default: + abi_dumps/LIB_NAME/VERSION/ABI.dump + + -dump-format FMT + Change format of ABI dump. + Formats: + perl - Data::Dumper format (default) + xml - XML format + + -check + Check completeness of the ABI dump. + + -extra-info DIR + Dump extra info to DIR. + + -extra-dump + Create extended ABI dump containing all symbols + from the translation unit. + + -relpath PATH + Replace {RELPATH} macros to PATH in the XML-descriptor used + for dumping the library ABI (see -dump option). + + -vnum NUM + Specify the library version in the generated ABI dump. The section + of the input XML descriptor will be overwritten in this case. + + -sort + Enable sorting of data in ABI dumps. + +FILTER SYMBOLS OPTIONS: + -symbols-list PATH + This option allows one to specify a file with a list of symbols (mangled + names in C++) that should be checked. Other symbols will not be checked. + + -types-list PATH + This option allows one to specify a file with a list of types that should + be checked. Other types will not be checked. + + -skip-symbols PATH + The list of symbols that should not be checked. + + -skip-types PATH + The list of types that should not be checked. + + -skip-internal-symbols PATTERN + Do not check symbols matched by the regular expression. + + -skip-internal-types PATTERN + Do not check types matched by the regular expression. + It's matched against full qualified type names (e.g. 'struct xyz::Name'). + It has to match any part of type name. + + -keep-cxx + Check _ZS*, _ZNS* and _ZNKS* symbols. + + -keep-reserved + Report changes in reserved fields. + +FILTER HEADERS OPTIONS: + -skip-headers PATH + The file with the list of header files, that should not be checked. + + -headers-list PATH + The file with a list of headers, that should be checked/dumped. + + -header NAME + Check/Dump ABI of this header only. + + -nostdinc + Do not search in GCC standard system directories for header files. + + -tolerance LEVEL + Apply a set of heuristics to successfully compile input + header files. You can enable several tolerance levels by + joining them into one string (e.g. 13, 124, etc.). + Levels: + 1 - skip non-Linux headers (e.g. win32_*.h, etc.) + 2 - skip internal headers (e.g. *_p.h, impl/*.h, etc.) + 3 - skip headers that include non-Linux headers + 4 - skip headers included by others + + -tolerant + Enable highest tolerance level [1234]. + + -skip-unidentified + Skip header files in 'headers' and 'include_preamble' sections + of the XML descriptor that cannot be found. This is useful if + you are trying to use the same descriptor for different targets. + +FILTER RULES OPTIONS: + -skip-typedef-uncover + Do not report a problem if type is covered or + uncovered by typedef (useful for broken debug info). + + -check-private-abi + Check data types from the private part of the ABI when + comparing ABI dumps created by the ABI Dumper tool with + use of the -public-headers option. + + Requires ABI Dumper >= 0.99.14 + + -disable-constants-check + Do not check for changes in constants. + + -skip-added-constants + Do not detect added constants. + + -skip-removed-constants + Do not detect removed constants. + +OTHER OPTIONS: + -lang LANG + Set library language (C or C++). You can use this option if the tool + cannot auto-detect a language. This option may be useful for checking + C-library headers (--lang=C) in --headers-only or --extended modes. + + -arch ARCH + Set library architecture (x86, x86_64, ia64, arm, ppc32, ppc64, s390, + etc.). The option is useful if the tool cannot detect correct architecture + of the input objects. + + -mingw-compatible + If input header files are compatible with the MinGW GCC compiler, + then you can tell the tool about this and speedup the analysis. + + -cxx-incompatible + Set this option if input C header files use C++ keywords. The tool + will try to replace such keywords at preprocessor stage and replace + them back in the final TU dump. + + -cpp-compatible + Do nothing. + + -quick + Quick analysis. Disable check of some template instances. + + -force + Try to enable this option if the tool checked not all + types and symbols in header files. + +OS ANALYSIS OPTIONS: + -dump-system NAME -sysroot DIR + Find all the shared libraries and header files in DIR directory, + create XML descriptors and make ABI dumps for each library. The result + set of ABI dumps can be compared (--cmp-systems) with the other one + created for other version of operating system in order to check them for + compatibility. Do not forget to specify -cross-gcc option if your target + system requires some specific version of GCC compiler (different from + the host GCC). The system ABI dump will be generated to: + sys_dumps/NAME/ARCH + + -dump-system DESCRIPTOR.xml + The same as the previous option but takes an XML descriptor of the target + system as input, where you should describe it: + + /* Primary sections */ + + + /* Name of the system */ + + + + /* The list of paths to header files and/or + directories with header files, one per line */ + + + + /* The list of paths to shared libraries and/or + directories with shared libraries, one per line */ + + + /* Optional sections */ + + + /* List of directories to be searched + for header files to automatically + generate include paths, one per line */ + + + + /* List of directories to be searched + for shared libraries to resolve + dependencies, one per line */ + + + + /* List of directories with tools used + for analysis (GCC toolchain), one per line */ + + + + /* GCC toolchain prefix. + Examples: + arm-linux-gnueabi + arm-none-symbianelf */ + + + + /* Additional GCC options, one per line */ + + + -cmp-systems -d1 sys_dumps/NAME1/ARCH -d2 sys_dumps/NAME2/ARCH + Compare two ABI dumps of a system. Create compatibility reports for + each system library and the common HTML report including the summary + of test results for all checked libraries. + + Summary report will be generated to: + sys_compat_reports/NAME1_to_NAME2/ARCH + + -sysroot DIR + Specify the alternative root directory. The tool will search for include + paths in the DIR/usr/include and DIR/usr/lib directories. + + -sysinfo DIR + This option should be used with -dump-system option to dump + ABI of operating systems and configure the dumping process. + + -libs-list PATH + The file with a list of libraries, that should be dumped by + the -dump-system option or should be checked by the -cmp-systems option. + +LOGGING OPTIONS: + -log-path PATH + Log path for all messages. + Default: + logs/LIB_NAME/VERSION/log.txt + + -log1-path PATH + Log path for 1st version of a library. + Default: + logs/LIB_NAME/V1/log.txt + + -log2-path PATH + Log path for 2nd version of a library. + Default: + logs/LIB_NAME/V2/log.txt + + -logging-mode MODE + Change logging mode. + Modes: + w - overwrite old logs (default) + a - append old logs + n - do not write any logs + + -q|-quiet + Print all messages to the file instead of stdout and stderr. + Default path (can be changed by -log-path option): + ".$In::Opt{"DefaultLog"}." + +REPORT PATH: + Compatibility report will be generated to: + compat_reports/LIB_NAME/V1_to_V2/compat_report.html + +LOG PATH: + Log will be generated to: + logs/LIB_NAME/V1/log.txt + logs/LIB_NAME/V2/log.txt + +EXIT CODES: + 0 - Compatible. The tool has run without any errors. + non-zero - Incompatible or the tool has run with errors. + +MORE INFO: + ".$HomePage{"Doc"}." + ".$HomePage{"Dev"}."\n\n"); +} + +# Aliases +my (%SymbolInfo, %TypeInfo, %TName_Tid, %Constants) = (); + +# Global +my %Cache; +my $TOP_REF = "to the top"; +my %RESULT; + +# Counter +my %CheckedTypes; +my %CheckedSymbols; + +# Classes +my %VirtualTable; +my %VirtualTable_Model; +my %VTableClass; +my %AllocableClass; +my %ClassMethods; +my %ClassNames; +my %OverriddenMethods; + +# Symbols +my %Func_ShortName; +my %AddSymbolParams; +my %GlobalDataObject; + +# Merging +my %CompSign; +my %AddedInt; +my %RemovedInt; +my %AddedInt_Virt; +my %RemovedInt_Virt; +my %VirtualReplacement; +my %ChangedTypedef; +my %CompatRules; +my %IncompleteRules; +my %UnknownRules; +my %VTableChanged_M; +my %ReturnedClass; +my %ParamClass; +my %SourceAlternative; +my %SourceAlternative_B; +my %SourceReplacement; + +# Extra +my %ExtendedSymbols; + +#Report +my %TypeChanges; + +#Speedup +my %TypeProblemsIndex; + +# Calling Conventions +my %UseConv_Real = ( + 1=>{ "R"=>0, "P"=>0 }, + 2=>{ "R"=>0, "P"=>0 } +); + +# ABI Dump +my %UsedDump; + +# Recursion locks +my @RecurTypes; +my @RecurTypes_Diff; +my @RecurConstant; + +# Problem descriptions +my %CompatProblems; +my %CompatProblems_Constants; +my %TotalAffected; + +# Reports +my $ContentID = 1; +my $ContentSpanStart = "\n"; +my $ContentSpanStart_Affected = "\n"; +my $ContentSpanStart_Info = "\n"; +my $ContentSpanEnd = "\n"; +my $ContentDivStart = "
\n"; +my $ContentDivEnd = "
\n"; +my $Content_Counter = 0; + +my %Severity_Val=( + "High"=>3, + "Medium"=>2, + "Low"=>1, + "Safe"=>-1 +); + +sub getModules() +{ + my $TOOL_DIR = dirname($0); + if(not $TOOL_DIR) + { # patch for MS Windows + $TOOL_DIR = "."; + } + my @SEARCH_DIRS = ( + # tool's directory + abs_path($TOOL_DIR), + # relative path to modules + abs_path($TOOL_DIR)."/../share/abi-compliance-checker", + # install path + 'MODULES_INSTALL_PATH' + ); + foreach my $DIR (@SEARCH_DIRS) + { + if($DIR!~/\A(\/|\w+:[\/\\])/) + { # relative path + $DIR = abs_path($TOOL_DIR)."/".$DIR; + } + if(-d $DIR."/modules") { + return $DIR."/modules"; + } + } + + print STDERR "ERROR: can't find modules (Did you installed the tool by 'make install' command?)\n"; + exit(9); # Module_Error +} + +sub loadModule($) +{ + my $Name = $_[0]; + if(defined $LoadedModules{$Name}) { + return; + } + my $Path = $MODULES_DIR."/Internals/$Name.pm"; + if(not -f $Path) + { + print STDERR "can't access \'$Path\'\n"; + exit(2); + } + require $Path; + $LoadedModules{$Name} = 1; +} + +sub readModule($$) +{ + my ($Module, $Name) = @_; + my $Path = $MODULES_DIR."/Internals/$Module/".$Name; + if(not -f $Path) { + exitStatus("Module_Error", "can't access \'$Path\'"); + } + return readFile($Path); +} + +sub selectSymbolNs($$) +{ + my ($Symbol, $LVer) = @_; + + my $NS = $CompSign{$LVer}{$Symbol}{"NameSpace"}; + if(not $NS) + { + if(my $Class = $CompSign{$LVer}{$Symbol}{"Class"}) { + $NS = $TypeInfo{$LVer}{$Class}{"NameSpace"}; + } + } + if($NS) + { + if(defined $In::ABI{$LVer}{"NameSpaces"}{$NS}) { + return $NS; + } + else + { + while($NS=~s/::[^:]+\Z//) + { + if(defined $In::ABI{$LVer}{"NameSpaces"}{$NS}) { + return $NS; + } + } + } + } + + return ""; +} + +sub selectTypeNs($$) +{ + my ($TypeName, $LVer) = @_; + + my $Tid = $TName_Tid{$LVer}{$TypeName}; + + if(my $NS = $TypeInfo{$LVer}{$Tid}{"NameSpace"}) + { + if(defined $In::ABI{$LVer}{"NameSpaces"}{$NS}) { + return $NS; + } + else + { + while($NS=~s/::[^:]+\Z//) + { + if(defined $In::ABI{$LVer}{"NameSpaces"}{$NS}) { + return $NS; + } + } + } + } + return ""; +} + +sub getChargeLevel($$) +{ + my ($Symbol, $LVer) = @_; + + if(defined $CompSign{$LVer}{$Symbol} + and $CompSign{$LVer}{$Symbol}{"ShortName"}) + { + if($CompSign{$LVer}{$Symbol}{"Constructor"}) + { + if($Symbol=~/C([1-4])[EI]/) + { # [in-charge] + # [not-in-charge] + return "[C".$1."]"; + } + } + elsif($CompSign{$LVer}{$Symbol}{"Destructor"}) + { + if($Symbol=~/D([0-4])[EI]/) + { # [in-charge] + # [not-in-charge] + # [in-charge-deleting] + return "[D".$1."]"; + } + } + } + + return undef; +} + +sub blackName($) +{ + my $N = $_[0]; + return "".$N.""; +} + +sub highLight_ItalicColor($$) +{ + my ($Symbol, $LVer) = @_; + return getSignature($Symbol, $LVer, "Full|HTML|Italic|Color"); +} + +sub getSignature($$$) +{ + my ($Symbol, $LVer, $Kind) = @_; + if($Cache{"getSignature"}{$LVer}{$Symbol}{$Kind}) { + return $Cache{"getSignature"}{$LVer}{$Symbol}{$Kind}; + } + + # settings + my ($Html, $Simple, $Italic, $Color, $Full, $ShowClass, $ShowName, + $ShowParams, $ShowQuals, $ShowAttr, $Desc, $Target) = (); + + if($Kind=~/HTML/) { + $Html = 1; + } + if($Kind=~/Simple/) { + $Simple = 1; + } + if($Kind=~/Italic/) { + $Italic = 1; + } + if($Kind=~/Color/) { + $Color = 1; + } + + if($Kind=~/Full/) { + $Full = 1; + } + if($Kind=~/Class/) { + $ShowClass = 1; + } + if($Kind=~/Name/) { + $ShowName = 1; + } + if($Kind=~/Param/) { + $ShowParams = 1; + } + if($Kind=~/Qual/) { + $ShowQuals = 1; + } + if($Kind=~/Attr/) { + $ShowAttr = 1; + } + if($Kind=~/Desc/) { + $Desc = 1; + } + + if($Kind=~/Target=(\d+)/) { + $Target = $1; + } + + if($Full) + { + $ShowName = 1; + $ShowClass = 1; + } + + my ($MnglName, $VSpec, $SVer) = symbolParts($Symbol); + + if(index($Symbol, "_ZTV")==0) + { + if(my $ClassId = $CompSign{$LVer}{$Symbol}{"Class"}) + { + my $ClassName = $TypeInfo{$LVer}{$ClassId}{"Name"}; + $ClassName=~s/\bstruct //g; + + if($Html) { + return "vtable for ".specChars($ClassName)." [data]"; + } + + return "vtable for $ClassName [data]"; + } + else + { # failure + return undef; + } + } + + my $Mngl = (index($Symbol, "_Z")==0 or index($Symbol, "?")==0); + + my $Signature = ""; + if($ShowName) + { + my $ShortName = $CompSign{$LVer}{$Symbol}{"ShortName"}; + + if(not $Mngl and defined $CompSign{$LVer}{$Symbol}{"Alias"}) + { # alias symbol + $ShortName = $MnglName; + } + + if($Html) { + $ShortName = specChars($ShortName); + } + + $Signature .= $ShortName; + + my $ClassId = $CompSign{$LVer}{$Symbol}{"Class"}; + + if($Mngl or $ClassId) + { + if($CompSign{$LVer}{$Symbol}{"Destructor"}) { + $Signature = "~".$Signature; + } + + if($ShowClass) + { + if($ClassId) + { + my $Class = $TypeInfo{$LVer}{$ClassId}{"Name"}; + $Class=~s/\bstruct //g; + + if($Html) { + $Class = specChars($Class); + } + + $Signature = $Class."::".$Signature; + } + elsif(my $NameSpace = $CompSign{$LVer}{$Symbol}{"NameSpace"}) { + $Signature = $NameSpace."::".$Signature; + } + } + } + } + + my @Params = (); + if(defined $CompSign{$LVer}{$Symbol}{"Param"}) + { + foreach my $PPos (sort {$a<=>$b} keys(%{$CompSign{$LVer}{$Symbol}{"Param"}})) + { + my $PTid = $CompSign{$LVer}{$Symbol}{"Param"}{$PPos}{"type"}; + if(not $PTid) { + next; + } + + if(my $PTName = $TypeInfo{$LVer}{$PTid}{"Name"}) + { + foreach my $Typedef (keys(%ChangedTypedef)) + { + if(index($PTName, $Typedef)!=-1) + { + if($PTName=~/\b\Q$Typedef\E\b/) + { + $PTName = uncoverTypedefs($PTName, $LVer); + last; + } + } + } + + $PTName=~s/\(kind=\d+\)//g; + + if($Html) { + $PTName = specChars($PTName); + } + + my $PName = $CompSign{$LVer}{$Symbol}{"Param"}{$PPos}{"name"}; + if($Mngl and ($PName eq "this" or $PName eq "__in_chrg" or $PName eq "__vtt_parm")) + { # do NOT show first hidden "this"-parameter + next; + } + + if($PName and ($Full or $ShowParams)) + { + if($Simple) { + $PName = "$PName"; + } + elsif($Html) + { + if(defined $Target + and $Target==adjustParamPos($PPos, $Symbol, $LVer)) { + $PName = "$PName"; + } + elsif($Color) { + $PName = "$PName"; + } + elsif($Italic) { + $PName = "$PName"; + } + } + + push(@Params, createMemDecl($PTName, $PName)); + } + else { + push(@Params, $PTName); + } + } + } + } + + if($Simple) { + $Signature = "".$Signature.""; + } + + if($CompSign{$LVer}{$Symbol}{"Data"}) + { + $Signature .= " [data]"; + } + else + { + if($Full or $ShowAttr) + { + if($Mngl) + { + if(my $ChargeLevel = getChargeLevel($Symbol, $LVer)) { + $Signature .= " ".$ChargeLevel; + } + } + } + + if($Html and not $Simple) + { + $Signature .= " "; + + if($Desc) { + $Signature .= ""; + } + else { + $Signature .= ""; + } + if(@Params) + { + foreach my $Pos (0 .. $#Params) + { + my $Name = ""; + + if($Pos==0) { + $Name .= "( "; + } + + $Name .= $Params[$Pos]; + + $Name = "".$Name.""; + + if($Pos==$#Params) { + $Name .= " )"; + } + else { + $Name .= ", "; + } + + $Signature .= $Name; + } + } + else { + $Signature .= "( )"; + } + $Signature .= ""; + } + else + { + if(@Params) { + $Signature .= " ( ".join(", ", @Params)." )"; + } + else { + $Signature .= " ( )"; + } + } + + + if($Full or $ShowQuals) + { + if($CompSign{$LVer}{$Symbol}{"Const"} + or $Symbol=~/\A_ZN(V|)K/) { + $Signature .= " const"; + } + + if($CompSign{$LVer}{$Symbol}{"Volatile"} + or $Symbol=~/\A_ZN(K|)V/) { + $Signature .= " volatile"; + } + } + + if($Full or $ShowAttr) + { + if($CompSign{$LVer}{$Symbol}{"Static"} + and $Mngl) { + $Signature .= " [static]"; + } + } + } + + if($Full) + { + if(defined $In::Opt{"ShowRetVal"}) + { + if(my $ReturnId = $CompSign{$LVer}{$Symbol}{"Return"}) + { + my $RName = $TypeInfo{$LVer}{$ReturnId}{"Name"}; + if($Simple) { + $Signature .= " : ".specChars($RName); + } + elsif($Html) { + $Signature .= "  :  ".specChars($RName).""; + } + else { + $Signature .= " : ".$RName; + } + } + } + + if($SVer) + { + if($Html) { + $Signature .= " $VSpec $SVer"; + } + else { + $Signature .= $VSpec.$SVer; + } + } + } + + if($Html) { + $Signature=~s!(\[C\d\]|\[D\d\]|\[static\]|\[data\])!$1!g; + } + + if($Simple) { + $Signature=~s/\[\]/\[ \]/g; + } + elsif($Html) + { + $Signature=~s!\[\]![ ]!g; + $Signature=~s!operator=!operator =!g; + } + + return ($Cache{"getSignature"}{$LVer}{$Symbol}{$Kind} = $Signature); +} + +sub createMemDecl($$) +{ + my ($TName, $Member) = @_; + if($TName=~/\([\*]+\)/) + { + $TName=~s/\(([\*]+)\)/\($1$Member\)/; + return $TName; + } + else + { + my @ArraySizes = (); + while($TName=~s/(\[[^\[\]]*\])\Z//) { + push(@ArraySizes, $1); + } + return $TName." ".$Member.join("", @ArraySizes); + } +} + +sub prepareSymbols($) +{ + my $LVer = $_[0]; + + if(not keys(%{$SymbolInfo{$LVer}})) + { # check if input is valid + if(not $In::Opt{"ExtendedCheck"}) + { + if($In::Opt{"CheckHeadersOnly"}) { + exitStatus("Empty_Set", "the set of public symbols is empty"); + } + else { + exitStatus("Empty_Intersection", "the sets of public symbols in headers and libraries have empty intersection"); + } + } + } + + foreach my $InfoId (sort {$b<=>$a} keys(%{$SymbolInfo{$LVer}})) + { + my $MnglName = $SymbolInfo{$LVer}{$InfoId}{"MnglName"}; + my $ShortName = $SymbolInfo{$LVer}{$InfoId}{"ShortName"}; + + if(not $MnglName) + { + $SymbolInfo{$LVer}{$InfoId}{"MnglName"} = $ShortName; + $MnglName = $ShortName; + } + + # symbol and its symlink have same signatures + if(my $SVer = $In::ABI{$LVer}{"SymbolVersion"}{$MnglName}) { + $CompSign{$LVer}{$SVer} = $SymbolInfo{$LVer}{$InfoId}; + } + + if(my $Alias = $SymbolInfo{$LVer}{$InfoId}{"Alias"}) + { + $CompSign{$LVer}{$Alias} = $SymbolInfo{$LVer}{$InfoId}; + + if(my $SAVer = $In::ABI{$LVer}{"SymbolVersion"}{$Alias}) { + $CompSign{$LVer}{$SAVer} = $SymbolInfo{$LVer}{$InfoId}; + } + } + + if(defined $CompSign{$LVer}{$MnglName}) + { # NOTE: duplicated entries in the ABI Dump + if(defined $SymbolInfo{$LVer}{$InfoId}{"Param"}) + { + if($SymbolInfo{$LVer}{$InfoId}{"Param"}{0}{"name"} eq "p1") { + next; + } + } + } + + if(not $CompSign{$LVer}{$MnglName}{"MnglName"}) + { # NOTE: global data may enter here twice + $CompSign{$LVer}{$MnglName} = $SymbolInfo{$LVer}{$InfoId}; + } + + if(not defined $SymbolInfo{$LVer}{$InfoId}{"Unmangled"}) + { + if($MnglName eq $ShortName) { + $SymbolInfo{$LVer}{$InfoId}{"Unmangled"} = $ShortName; + } + else { + $SymbolInfo{$LVer}{$InfoId}{"Unmangled"} = getSignature($MnglName, $LVer, "Class|Name|Qual"); + } + } + } + + if($In::ABI{$LVer}{"Language"} eq "C++" + and getCmdPath("c++filt")) + { + my @VTables = (); + foreach my $S (keys(%{$In::ABI{$LVer}{"SymLib"}})) + { + if(index($S, "_ZTV")==0) { + push(@VTables, $S); + } + } + translateSymbols(@VTables, $LVer); + } + + if($In::Opt{"ExtendedCheck"}) { + addExtension($LVer); + } + + foreach my $Symbol (keys(%{$CompSign{$LVer}})) + { # detect allocable classes with public exported constructors + # or classes with auto-generated or inline-only constructors + # and other temp info + if(my $ClassId = $CompSign{$LVer}{$Symbol}{"Class"}) + { + my $ClassName = $TypeInfo{$LVer}{$ClassId}{"Name"}; + if($CompSign{$LVer}{$Symbol}{"Constructor"} + and not $CompSign{$LVer}{$Symbol}{"InLine"}) + { # Class() { ... } will not be exported + if(not $CompSign{$LVer}{$Symbol}{"Private"}) + { + if($In::Opt{"CheckHeadersOnly"} or linkSymbol($Symbol, $LVer, "-Deps")) { + $AllocableClass{$LVer}{$ClassName} = 1; + } + } + } + if(not $CompSign{$LVer}{$Symbol}{"Private"}) + { # all imported class methods + if(symbolFilter($Symbol, $CompSign{$LVer}{$Symbol}, "Affected", "Binary", $LVer)) + { + if($In::Opt{"CheckHeadersOnly"}) + { + if(not $CompSign{$LVer}{$Symbol}{"InLine"} + or $CompSign{$LVer}{$Symbol}{"Virt"}) + { # all symbols except non-virtual inline + $ClassMethods{"Binary"}{$LVer}{$ClassName}{$Symbol} = 1; + } + } + else { + $ClassMethods{"Binary"}{$LVer}{$ClassName}{$Symbol} = 1; + } + } + if(symbolFilter($Symbol, $CompSign{$LVer}{$Symbol}, "Affected", "Source", $LVer)) { + $ClassMethods{"Source"}{$LVer}{$ClassName}{$Symbol} = 1; + } + } + $ClassNames{$LVer}{$ClassName} = 1; + } + if(my $RetId = $CompSign{$LVer}{$Symbol}{"Return"}) + { + my %Base = getBaseType($RetId, $LVer); + if(defined $Base{"Type"} + and $Base{"Type"}=~/Struct|Class/) + { + my $Name = $TypeInfo{$LVer}{$Base{"Tid"}}{"Name"}; + if($Name=~/<([^<>\s]+)>/) + { + if(my $Tid = getTypeIdByName($1, $LVer)) { + $ReturnedClass{$LVer}{$Tid} = 1; + } + } + else { + $ReturnedClass{$LVer}{$Base{"Tid"}} = 1; + } + } + } + foreach my $Num (keys(%{$CompSign{$LVer}{$Symbol}{"Param"}})) + { + my $PId = $CompSign{$LVer}{$Symbol}{"Param"}{$Num}{"type"}; + if(getPLevel($PId, $LVer)>=1) + { + if(my %Base = getBaseType($PId, $LVer)) + { + if($Base{"Type"}=~/Struct|Class/) + { + $ParamClass{$LVer}{$Base{"Tid"}}{$Symbol} = 1; + foreach my $SubId (getSubClasses($Base{"Tid"}, $LVer, 1)) + { # mark all derived classes + $ParamClass{$LVer}{$SubId}{$Symbol} = 1; + } + } + } + } + } + + # mapping {short name => symbols} + $Func_ShortName{$LVer}{$CompSign{$LVer}{$Symbol}{"ShortName"}}{$Symbol} = 1; + } + + foreach my $ClassName (keys(%{$In::ABI{$LVer}{"ClassVTable"}})) + { + my $MnglName = $In::ABI{$LVer}{"ClassVTable"}{$ClassName}; + + if(my $ClassId = $TName_Tid{$LVer}{$ClassName}) + { + if(my $H = $TypeInfo{$LVer}{$ClassId}{"Header"}) { + $CompSign{$LVer}{$MnglName}{"Header"} = $H; + } + if(my $S = $TypeInfo{$LVer}{$ClassId}{"Source"}) { + $CompSign{$LVer}{$MnglName}{"Source"} = $S; + } + $CompSign{$LVer}{$MnglName}{"Class"} = $ClassId; + $CompSign{$LVer}{$MnglName}{"Unmangled"} = getSignature($MnglName, $LVer, "Class|Name"); + } + + $VTableClass{$LVer}{$MnglName} = $ClassName; + } + + # types + foreach my $TypeId (keys(%{$TypeInfo{$LVer}})) + { + if(my $TName = $TypeInfo{$LVer}{$TypeId}{"Name"}) + { + if(defined $TypeInfo{$LVer}{$TypeId}{"VTable"}) { + $ClassNames{$LVer}{$TName} = 1; + } + if(defined $TypeInfo{$LVer}{$TypeId}{"Base"}) + { + $ClassNames{$LVer}{$TName} = 1; + foreach my $Bid (keys(%{$TypeInfo{$LVer}{$TypeId}{"Base"}})) + { + if(my $BName = $TypeInfo{$LVer}{$Bid}{"Name"}) { + $ClassNames{$LVer}{$BName} = 1; + } + } + } + } + } +} + +sub addExtension($) +{ + my $LVer = $_[0]; + foreach my $Tid (sort {$a<=>$b} keys(%{$TypeInfo{$LVer}})) + { + if(pickType($Tid, $LVer)) + { + my $TName = $TypeInfo{$LVer}{$Tid}{"Name"}; + $TName=~s/\A(struct|union|class|enum) //; + my $Symbol = "external_func_".$TName; + $Symbol=~s/\W+/_/g; + + %{$CompSign{$LVer}{$Symbol}} = ( + "Header" => "extended.h", + "ShortName" => $Symbol, + "MnglName" => $Symbol, + "Param" => { 0 => { "type"=>$Tid, "name"=>"p1" } } + ); + + $ExtendedSymbols{$Symbol} = 1; + $CheckedSymbols{"Binary"}{$Symbol} = 1; + $CheckedSymbols{"Source"}{$Symbol} = 1; + } + } + + $ExtendedSymbols{"external_func_0"} = 1; + $CheckedSymbols{"Binary"}{"external_func_0"} = 1; + $CheckedSymbols{"Source"}{"external_func_0"} = 1; +} + +sub findMethod($$$) +{ + my ($VirtFunc, $ClassId, $LVer) = @_; + foreach my $BaseClass_Id (keys(%{$TypeInfo{$LVer}{$ClassId}{"Base"}})) + { + if(my $VirtMethodInClass = findMethod_Class($VirtFunc, $BaseClass_Id, $LVer)) { + return $VirtMethodInClass; + } + elsif(my $VirtMethodInBaseClasses = findMethod($VirtFunc, $BaseClass_Id, $LVer)) { + return $VirtMethodInBaseClasses; + } + } + return undef; +} + +sub findMethod_Class($$$) +{ + my ($VirtFunc, $ClassId, $LVer) = @_; + + my $ClassName = $TypeInfo{$LVer}{$ClassId}{"Name"}; + if(not defined $VirtualTable{$LVer}{$ClassName}) { + return undef; + } + my $TargetSuffix = getSignature($VirtFunc, $LVer, "Qual"); + my $TargetShortName = $CompSign{$LVer}{$VirtFunc}{"ShortName"}; + + foreach my $Candidate (keys(%{$VirtualTable{$LVer}{$ClassName}})) + { # search for interface with the same parameters suffix (overridden) + if($TargetSuffix eq getSignature($Candidate, $LVer, "Qual")) + { + if($CompSign{$LVer}{$VirtFunc}{"Destructor"}) + { + if($CompSign{$LVer}{$Candidate}{"Destructor"}) + { + if(($VirtFunc=~/D0E/ and $Candidate=~/D0E/) + or ($VirtFunc=~/D1E/ and $Candidate=~/D1E/) + or ($VirtFunc=~/D2E/ and $Candidate=~/D2E/)) { + return $Candidate; + } + } + } + else + { + if($TargetShortName eq $CompSign{$LVer}{$Candidate}{"ShortName"}) { + return $Candidate; + } + } + } + } + return undef; +} + +sub registerVTable($) +{ + my $LVer = $_[0]; + foreach my $Symbol (keys(%{$CompSign{$LVer}})) + { + if($CompSign{$LVer}{$Symbol}{"Virt"} + or $CompSign{$LVer}{$Symbol}{"PureVirt"}) + { + my $ClassName = $TypeInfo{$LVer}{$CompSign{$LVer}{$Symbol}{"Class"}}{"Name"}; + if(not $In::Opt{"StdcxxTesting"} and $ClassName=~/\A(std::|__cxxabi)/) { + next; + } + if($CompSign{$LVer}{$Symbol}{"Destructor"} + and $Symbol=~/D2E/) + { # pure virtual D2-destructors are marked as "virt" in the dump + # virtual D2-destructors are NOT marked as "virt" in the dump + # both destructors are not presented in the v-table + next; + } + my ($MnglName, $VersionSpec, $SymbolVersion) = symbolParts($Symbol); + $VirtualTable{$LVer}{$ClassName}{$MnglName} = 1; + } + } +} + +sub registerOverriding($) +{ + my $LVer = $_[0]; + my @Classes = keys(%{$VirtualTable{$LVer}}); + @Classes = sort {$TName_Tid{$LVer}{$a}<=>$TName_Tid{$LVer}{$b}} @Classes; + foreach my $ClassName (@Classes) + { + foreach my $VirtFunc (keys(%{$VirtualTable{$LVer}{$ClassName}})) + { + if($CompSign{$LVer}{$VirtFunc}{"PureVirt"}) + { # pure virtuals + next; + } + my $ClassId = $TName_Tid{$LVer}{$ClassName}; + if(my $Overridden = findMethod($VirtFunc, $ClassId, $LVer)) + { + if($CompSign{$LVer}{$Overridden}{"Virt"} + or $CompSign{$LVer}{$Overridden}{"PureVirt"}) + { # both overridden virtual methods + # and implemented pure virtual methods + $CompSign{$LVer}{$VirtFunc}{"Override"} = $Overridden; + $OverriddenMethods{$LVer}{$Overridden}{$VirtFunc} = 1; + + # remove from v-table model + delete($VirtualTable{$LVer}{$ClassName}{$VirtFunc}); + } + } + } + if(not keys(%{$VirtualTable{$LVer}{$ClassName}})) { + delete($VirtualTable{$LVer}{$ClassName}); + } + } +} + +sub setVirtFuncPositions($) +{ + my $LVer = $_[0]; + foreach my $ClassName (keys(%{$VirtualTable{$LVer}})) + { + my ($Num, $Rel) = (1, 0); + + if(my @Funcs = sort keys(%{$VirtualTable{$LVer}{$ClassName}})) + { + if($UsedDump{$LVer}{"DWARF"}) { + @Funcs = sort {$CompSign{$LVer}{$a}{"VirtPos"}<=>$CompSign{$LVer}{$b}{"VirtPos"}} @Funcs; + } + else { + @Funcs = sort {$CompSign{$LVer}{$a}{"Line"}<=>$CompSign{$LVer}{$b}{"Line"}} @Funcs; + } + foreach my $VirtFunc (@Funcs) + { + if($UsedDump{$LVer}{"DWARF"}) + { + if(defined $CompSign{$LVer}{$VirtFunc}{"VirtPos"}) { + $VirtualTable{$LVer}{$ClassName}{$VirtFunc} = $CompSign{$LVer}{$VirtFunc}{"VirtPos"}; + } + else { + $VirtualTable{$LVer}{$ClassName}{$VirtFunc} = 1; + } + } + else { + $VirtualTable{$LVer}{$ClassName}{$VirtFunc} = $Num++; + } + + # set relative positions + if(defined $VirtualTable{1}{$ClassName} and defined $VirtualTable{1}{$ClassName}{$VirtFunc} + and defined $VirtualTable{2}{$ClassName} and defined $VirtualTable{2}{$ClassName}{$VirtFunc}) + { # relative position excluding added and removed virtual functions + if(not $CompSign{1}{$VirtFunc}{"Override"} + and not $CompSign{2}{$VirtFunc}{"Override"}) { + $CompSign{$LVer}{$VirtFunc}{"RelPos"} = $Rel++; + } + } + } + } + } + foreach my $ClassName (keys(%{$ClassNames{$LVer}})) + { + my $AbsNum = 1; + foreach my $VirtFunc (getVTable_Model($ClassName, $LVer)) { + $VirtualTable_Model{$LVer}{$ClassName}{$VirtFunc} = $AbsNum++; + } + } +} + +sub getVTable_Model($$) +{ # return an ordered list of v-table elements + my ($ClassName, $LVer) = @_; + + my $ClassId = $TName_Tid{$LVer}{$ClassName}; + my @Bases = getBaseClasses($ClassId, $LVer, 1); + my @Elements = (); + foreach my $BaseId (@Bases, $ClassId) + { + if(my $BName = $TypeInfo{$LVer}{$BaseId}{"Name"}) + { + if(defined $VirtualTable{$LVer}{$BName}) + { + my @VFuncs = sort keys(%{$VirtualTable{$LVer}{$BName}}); + if($UsedDump{$LVer}{"DWARF"}) { + @VFuncs = sort {$CompSign{$LVer}{$a}{"VirtPos"} <=> $CompSign{$LVer}{$b}{"VirtPos"}} @VFuncs; + } + else { + @VFuncs = sort {$CompSign{$LVer}{$a}{"Line"} <=> $CompSign{$LVer}{$b}{"Line"}} @VFuncs; + } + + foreach my $VFunc (@VFuncs) { + push(@Elements, $VFunc); + } + } + } + } + return @Elements; +} + +sub getVShift($$) +{ + my ($ClassId, $LVer) = @_; + my @Bases = getBaseClasses($ClassId, $LVer, 1); + my $VShift = 0; + foreach my $BaseId (@Bases) + { + if(my $BName = $TypeInfo{$LVer}{$BaseId}{"Name"}) + { + if(defined $VirtualTable{$LVer}{$BName}) { + $VShift+=keys(%{$VirtualTable{$LVer}{$BName}}); + } + } + } + return $VShift; +} + +sub getShift($$) +{ + my ($ClassId, $LVer) = @_; + my @Bases = getBaseClasses($ClassId, $LVer, 0); + my $Shift = 0; + foreach my $BaseId (@Bases) + { + if(my $Size = $TypeInfo{$LVer}{$BaseId}{"Size"}) + { + if($Size!=1) + { # not empty base class + $Shift+=$Size; + } + } + } + return $Shift; +} + +sub getVTable_Size($$) +{ # number of v-table elements + my ($ClassName, $LVer) = @_; + my $Size = 0; + # three approaches + if(not $Size) + { # real size + if(my %VTable = getVTable_Real($ClassName, $LVer)) { + $Size = keys(%VTable); + } + } + if(not $Size) + { # shared library symbol size + if(my $VTSym = $In::ABI{$LVer}{"ClassVTable"}{$ClassName}) + { + if($Size = getSymbolSize($VTSym, $LVer)) { + $Size /= $In::ABI{$LVer}{"WordSize"}; + } + } + } + if(not $Size) + { # model size + if(defined $VirtualTable_Model{$LVer}{$ClassName}) { + $Size = keys(%{$VirtualTable_Model{$LVer}{$ClassName}}) + 2; + } + } + return $Size; +} + +sub isLeafClass($$) +{ + my ($ClassId, $LVer) = @_; + if(not defined $In::ABI{$LVer}{"Class_SubClasses"}{$ClassId} + or not keys(%{$In::ABI{$LVer}{"Class_SubClasses"}{$ClassId}})) { + return 1; + } + + return 0; +} + +sub havePubFields($) +{ # check structured type for public fields + return isAccessible($_[0], {}, 0, -1); +} + +sub isAccessible($$$$) +{ # check interval in structured type for public fields + my ($TypePtr, $Skip, $Start, $End) = @_; + if(not $TypePtr) { + return 0; + } + if($End==-1) { + $End = keys(%{$TypePtr->{"Memb"}})-1; + } + foreach my $MemPos (sort {$a<=>$b} keys(%{$TypePtr->{"Memb"}})) + { + if($Skip and $Skip->{$MemPos}) + { # skip removed/added fields + next; + } + if(int($MemPos)>=$Start and int($MemPos)<=$End) + { + if(isPublic($TypePtr, $MemPos)) { + return ($MemPos+1); + } + } + } + return 0; +} + +sub isPublic($$) +{ + my ($TypePtr, $FieldPos) = @_; + + return 0 if(not $TypePtr); + return 0 if(not defined $TypePtr->{"Memb"}{$FieldPos}); + return 0 if(not defined $TypePtr->{"Memb"}{$FieldPos}{"name"}); + + my $Access = $TypePtr->{"Memb"}{$FieldPos}{"access"}; + if($Access eq "private") + { # by access in C++ language + return 0; + } + + # by name in C language + # TODO: add other methods to detect private members + my $MName = $TypePtr->{"Memb"}{$FieldPos}{"name"}; + if($MName=~/priv|abidata|parent_object|impl/i) + { # C-styled private data + return 0; + } + if(lc($MName) eq "abi") + { # ABI information/reserved field + return 0; + } + if(isReserved($MName)) + { # reserved fields + return 0; + } + + return 1; +} + +sub getVTable_Real($$) +{ + my ($ClassName, $LVer) = @_; + if(my $ClassId = $TName_Tid{$LVer}{$ClassName}) + { + my %Type = getType($ClassId, $LVer); + if(defined $Type{"VTable"}) { + return %{$Type{"VTable"}}; + } + } + return (); +} + +sub cmpVTables($) +{ + my $ClassName = $_[0]; + my $Res = cmpVTables_Real($ClassName, 1); + if($Res==-1) { + $Res = cmpVTables_Model($ClassName); + } + return $Res; +} + +sub cmpVTables_Model($) +{ + my $ClassName = $_[0]; + foreach my $Symbol (keys(%{$VirtualTable_Model{1}{$ClassName}})) + { + if(not defined $VirtualTable_Model{2}{$ClassName}{$Symbol}) { + return 1; + } + } + return 0; +} + +sub cmpVTables_Real($$) +{ + my ($ClassName, $Strong) = @_; + if(defined $Cache{"cmpVTables_Real"}{$Strong}{$ClassName}) { + return $Cache{"cmpVTables_Real"}{$Strong}{$ClassName}; + } + my %VTable_Old = getVTable_Real($ClassName, 1); + my %VTable_New = getVTable_Real($ClassName, 2); + if(not %VTable_Old or not %VTable_New) + { # old ABI dumps + return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = -1); + } + my %Indexes = map {$_=>1} (keys(%VTable_Old), keys(%VTable_New)); + foreach my $Offset (sort {$a<=>$b} keys(%Indexes)) + { + if(not defined $VTable_Old{$Offset}) + { # v-table v.1 < v-table v.2 + return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = $Strong); + } + my $Entry1 = $VTable_Old{$Offset}; + if(not defined $VTable_New{$Offset}) + { # v-table v.1 > v-table v.2 + return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = ($Strong or $Entry1!~/__cxa_pure_virtual/)); + } + my $Entry2 = $VTable_New{$Offset}; + + $Entry1 = simpleVEntry($Entry1); + $Entry2 = simpleVEntry($Entry2); + + if($Entry1=~/ 0x/ or $Entry2=~/ 0x/) + { # NOTE: problem with vtable-dumper + next; + } + + if($Entry1 ne $Entry2) + { # register as changed + if($Entry1=~/::([^:]+)\Z/) + { + my $M1 = $1; + if($Entry2=~/::([^:]+)\Z/) + { + my $M2 = $1; + if($M1 eq $M2) + { # overridden + next; + } + } + } + if($In::ABI{1}{"GccVersion"} ne $In::ABI{2}{"GccVersion"}) + { + if($Entry1=~/\A\-(0x|\d+)/ and $Entry2=~/\A\-(0x|\d+)/) + { + # GCC 4.6.1: -0x00000000000000010 + # GCC 4.7.0: -16 + next; + } + } + return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = 1); + } + } + return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = 0); +} + +sub mergeVTables($) +{ # merging v-tables without diagnostics + my $Level = $_[0]; + foreach my $ClassName (keys(%{$VirtualTable{1}})) + { + my $ClassId = $TName_Tid{1}{$ClassName}; + if(isPrivateABI($ClassId, 1)) { + next; + } + + if($VTableChanged_M{$ClassName}) + { # already registered + next; + } + if(cmpVTables_Real($ClassName, 0)==1) + { + my @Affected = (keys(%{$ClassMethods{$Level}{1}{$ClassName}})); + foreach my $Symbol (@Affected) + { + %{$CompatProblems{$Level}{$Symbol}{"Virtual_Table_Changed_Unknown"}{$ClassName}}=( + "Type_Name"=>$ClassName, + "Target"=>$ClassName); + } + } + } +} + +sub mergeBases($) +{ + my $Level = $_[0]; + foreach my $ClassName (sort keys(%{$ClassNames{1}})) + { # detect added and removed virtual functions + my $ClassId = $TName_Tid{1}{$ClassName}; + next if(not $ClassId); + + if(isPrivateABI($ClassId, 1)) { + next; + } + + if(defined $VirtualTable{2}{$ClassName}) + { + foreach my $Symbol (keys(%{$VirtualTable{2}{$ClassName}})) + { + if($TName_Tid{1}{$ClassName} + and not defined $VirtualTable{1}{$ClassName}{$Symbol}) + { # added to v-table + if(defined $CompSign{1}{$Symbol}) + { + if($CompSign{1}{$Symbol}{"Virt"}) + { # override some method in v.1 + next; + } + } + else + { + if(linkSymbol($Symbol, 1, "+Deps")) { + next; + } + } + + $AddedInt_Virt{$Level}{$ClassName}{$Symbol} = 1; + } + } + } + if(defined $VirtualTable{1}{$ClassName}) + { + foreach my $Symbol (keys(%{$VirtualTable{1}{$ClassName}})) + { + if($TName_Tid{2}{$ClassName} + and not defined $VirtualTable{2}{$ClassName}{$Symbol}) + { # removed from v-table + if(defined $CompSign{2}{$Symbol}) + { + if($CompSign{2}{$Symbol}{"Virt"}) + { # override some method in v.2 + next; + } + } + else + { + if(linkSymbol($Symbol, 2, "+Deps")) { + next; + } + } + + $RemovedInt_Virt{$Level}{$ClassName}{$Symbol} = 1; + } + } + } + + if($Level eq "Binary") + { # Binary-level + my %Class_Type = getType($ClassId, 1); + foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$Level}{$ClassName}})) + { # check replacements, including pure virtual methods + my $AddedPos = $VirtualTable{2}{$ClassName}{$AddedVFunc}; + foreach my $RemovedVFunc (keys(%{$RemovedInt_Virt{$Level}{$ClassName}})) + { + my $RemovedPos = $VirtualTable{1}{$ClassName}{$RemovedVFunc}; + if($AddedPos==$RemovedPos) + { + $VirtualReplacement{$AddedVFunc} = $RemovedVFunc; + $VirtualReplacement{$RemovedVFunc} = $AddedVFunc; + last; # other methods will be reported as "added" or "removed" + } + } + if(my $RemovedVFunc = $VirtualReplacement{$AddedVFunc}) + { + if(lc($AddedVFunc) eq lc($RemovedVFunc)) + { # skip: DomUi => DomUI parameter (Qt 4.2.3 to 4.3.0) + next; + } + + my $ProblemType = "Virtual_Replacement"; + my @Affected = (); + + if($CompSign{1}{$RemovedVFunc}{"PureVirt"}) + { # pure methods + if(not isUsedClass($ClassId, 1, $Level)) + { # not a parameter of some exported method + next; + } + $ProblemType = "Pure_Virtual_Replacement"; + + # affected all methods (both virtual and non-virtual ones) + @Affected = (keys(%{$ClassMethods{$Level}{1}{$ClassName}})); + } + else { + @Affected = ($RemovedVFunc); + } + + push(@Affected, keys(%{$OverriddenMethods{1}{$RemovedVFunc}})); + + $VTableChanged_M{$ClassName} = 1; + + foreach my $AffectedInt (@Affected) + { + if($CompSign{1}{$AffectedInt}{"PureVirt"}) + { # affected exported methods only + next; + } + if(not symbolFilter($AffectedInt, $CompSign{1}{$AffectedInt}, "Affected", $Level, 1)) { + next; + } + %{$CompatProblems{$Level}{$AffectedInt}{$ProblemType}{$AddedVFunc}}=( + "Type_Name"=>$Class_Type{"Name"}, + "Target"=>$AddedVFunc, + "Old_Value"=>$RemovedVFunc); + } + } + } + } + } + + foreach my $ClassName (sort keys(%{$ClassNames{1}})) + { + my $ClassId_Old = $TName_Tid{1}{$ClassName}; + next if(not $ClassId_Old); + + if(isPrivateABI($ClassId_Old, 1)) { + next; + } + + if(not isCreatable($ClassId_Old, 1)) + { # skip classes without public constructors (including auto-generated) + # example: class has only a private exported or private inline constructor + next; + } + + my %Class_Old = getType($ClassId_Old, 1); + my $ClassId_New = $TName_Tid{2}{$ClassName}; + if(not $ClassId_New) { + next; + } + my %Class_New = getType($ClassId_New, 2); + if($Class_New{"Type"}!~/Class|Struct/) + { # became typedef + if($Level eq "Binary") { + next; + } + if($Level eq "Source") + { + %Class_New = getPureType($ClassId_New, 2); + if($Class_New{"Type"}!~/Class|Struct/) { + next; + } + $ClassId_New = $Class_New{"Tid"}; + } + } + + if(not $Class_New{"Size"} or not $Class_Old{"Size"}) + { # incomplete info in the ABI dump + next; + } + + if($Level eq "Binary" and cmpVTables_Real($ClassName, 1)!=0) + { + foreach my $Symbol (sort keys(%{$RemovedInt_Virt{$Level}{$ClassName}})) + { + if($VirtualReplacement{$Symbol}) { + next; + } + + if(symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected", $Level, 1)) + { + my $ProblemType = "Removed_Virtual_Method"; + if($CompSign{1}{$Symbol}{"PureVirt"}) { + $ProblemType = "Removed_Pure_Virtual_Method"; + } + + %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{getSignature($Symbol, 1, "Class|Name|Qual")}}=( + "Type_Name"=>$ClassName, + "Target"=>$Symbol); + } + + $VTableChanged_M{$ClassName} = 1; + foreach my $SubId (getSubClasses($ClassId_Old, 1, 1)) + { + if(my $SubName = $TypeInfo{1}{$SubId}{"Name"}) { + $VTableChanged_M{$SubName} = 1; + } + } + } + } + + if(index($ClassName, ">")!=-1) + { # skip affected template instances + next; + } + + my @Bases_Old = sort {$Class_Old{"Base"}{$a}{"pos"}<=>$Class_Old{"Base"}{$b}{"pos"}} keys(%{$Class_Old{"Base"}}); + my @Bases_New = sort {$Class_New{"Base"}{$a}{"pos"}<=>$Class_New{"Base"}{$b}{"pos"}} keys(%{$Class_New{"Base"}}); + + my %Tr_Old = map {$TypeInfo{1}{$_}{"Name"} => uncoverTypedefs($TypeInfo{1}{$_}{"Name"}, 1)} @Bases_Old; + my %Tr_New = map {$TypeInfo{2}{$_}{"Name"} => uncoverTypedefs($TypeInfo{2}{$_}{"Name"}, 2)} @Bases_New; + + my ($BNum1, $BNum2) = (1, 1); + my %BasePos_Old = map {$Tr_Old{$TypeInfo{1}{$_}{"Name"}} => $BNum1++} @Bases_Old; + my %BasePos_New = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $BNum2++} @Bases_New; + my %ShortBase_Old = map {getShortClass($_, 1) => 1} @Bases_Old; + my %ShortBase_New = map {getShortClass($_, 2) => 1} @Bases_New; + my $Shift_Old = getShift($ClassId_Old, 1); + my $Shift_New = getShift($ClassId_New, 2); + my %BaseId_New = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $_} @Bases_New; + my @StableBases_Old = (); + foreach my $BaseId (@Bases_Old) + { + my $BaseName = $TypeInfo{1}{$BaseId}{"Name"}; + if($BasePos_New{$Tr_Old{$BaseName}}) { + push(@StableBases_Old, $BaseId); + } + elsif(not $ShortBase_New{$Tr_Old{$BaseName}} + and not $ShortBase_New{getShortClass($BaseId, 1)}) + { # removed base + # excluding namespace::SomeClass to SomeClass renaming + my $ProblemKind = "Removed_Base_Class"; + if($Level eq "Binary") + { # Binary-level + if($Shift_Old ne $Shift_New) + { # affected fields + if(havePubFields(\%Class_Old)) { + $ProblemKind .= "_And_Shift"; + } + elsif($Class_Old{"Size"} ne $Class_New{"Size"}) { + $ProblemKind .= "_And_Size"; + } + } + if(keys(%{$VirtualTable_Model{1}{$BaseName}}) + and cmpVTables($ClassName)==1) + { # affected v-table + $ProblemKind .= "_And_VTable"; + $VTableChanged_M{$ClassName} = 1; + } + } + my @Affected = keys(%{$ClassMethods{$Level}{1}{$ClassName}}); + foreach my $SubId (getSubClasses($ClassId_Old, 1, 1)) + { + if(my $SubName = $TypeInfo{1}{$SubId}{"Name"}) + { + push(@Affected, keys(%{$ClassMethods{$Level}{1}{$SubName}})); + if($ProblemKind=~/VTable/) { + $VTableChanged_M{$SubName} = 1; + } + } + } + foreach my $Interface (@Affected) + { + if(symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1)) + { + %{$CompatProblems{$Level}{$Interface}{$ProblemKind}{"this"}}=( + "Type_Name"=>$ClassName, + "Target"=>$BaseName, + "Old_Size"=>$Class_Old{"Size"}*$BYTE, + "New_Size"=>$Class_New{"Size"}*$BYTE, + "Shift"=>abs($Shift_New-$Shift_Old)); + } + } + } + } + my @StableBases_New = (); + foreach my $BaseId (@Bases_New) + { + my $BaseName = $TypeInfo{2}{$BaseId}{"Name"}; + if($BasePos_Old{$Tr_New{$BaseName}}) { + push(@StableBases_New, $BaseId); + } + elsif(not $ShortBase_Old{$Tr_New{$BaseName}} + and not $ShortBase_Old{getShortClass($BaseId, 2)}) + { # added base + # excluding namespace::SomeClass to SomeClass renaming + my $ProblemKind = "Added_Base_Class"; + if($Level eq "Binary") + { # Binary-level + if($Shift_Old ne $Shift_New) + { # affected fields + if(havePubFields(\%Class_Old)) { + $ProblemKind .= "_And_Shift"; + } + elsif($Class_Old{"Size"} ne $Class_New{"Size"}) { + $ProblemKind .= "_And_Size"; + } + } + if(keys(%{$VirtualTable_Model{2}{$BaseName}}) + and cmpVTables($ClassName)==1) + { # affected v-table + $ProblemKind .= "_And_VTable"; + $VTableChanged_M{$ClassName} = 1; + } + } + my @Affected = keys(%{$ClassMethods{$Level}{1}{$ClassName}}); + foreach my $SubId (getSubClasses($ClassId_Old, 1, 1)) + { + if(my $SubName = $TypeInfo{1}{$SubId}{"Name"}) + { + push(@Affected, keys(%{$ClassMethods{$Level}{1}{$SubName}})); + if($ProblemKind=~/VTable/) { + $VTableChanged_M{$SubName} = 1; + } + } + } + foreach my $Interface (@Affected) + { + if(symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1)) + { + %{$CompatProblems{$Level}{$Interface}{$ProblemKind}{"this"}}=( + "Type_Name"=>$ClassName, + "Target"=>$BaseName, + "Old_Size"=>$Class_Old{"Size"}*$BYTE, + "New_Size"=>$Class_New{"Size"}*$BYTE, + "Shift"=>abs($Shift_New-$Shift_Old)); + } + } + } + } + if($Level eq "Binary") + { # Binary-level + ($BNum1, $BNum2) = (1, 1); + my %BaseRelPos_Old = map {$Tr_Old{$TypeInfo{1}{$_}{"Name"}} => $BNum1++} @StableBases_Old; + my %BaseRelPos_New = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $BNum2++} @StableBases_New; + foreach my $BaseId (@Bases_Old) + { + my $BaseName = $TypeInfo{1}{$BaseId}{"Name"}; + if(my $NewPos = $BaseRelPos_New{$Tr_Old{$BaseName}}) + { + my $BaseNewId = $BaseId_New{$Tr_Old{$BaseName}}; + my $OldPos = $BaseRelPos_Old{$Tr_Old{$BaseName}}; + if($NewPos!=$OldPos) + { # changed position of the base class + foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}})) + { + if(symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1)) + { + %{$CompatProblems{$Level}{$Interface}{"Base_Class_Position"}{"this"}}=( + "Type_Name"=>$ClassName, + "Target"=>$BaseName, + "Old_Value"=>$OldPos-1, + "New_Value"=>$NewPos-1); + } + } + } + if($Class_Old{"Base"}{$BaseId}{"virtual"} + and not $Class_New{"Base"}{$BaseNewId}{"virtual"}) + { # became non-virtual base + foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}})) + { + if(symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1)) + { + %{$CompatProblems{$Level}{$Interface}{"Base_Class_Became_Non_Virtually_Inherited"}{"this->".$BaseName}}=( + "Type_Name"=>$ClassName, + "Target"=>$BaseName ); + } + } + } + elsif(not $Class_Old{"Base"}{$BaseId}{"virtual"} + and $Class_New{"Base"}{$BaseNewId}{"virtual"}) + { # became virtual base + foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}})) + { + if(not symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1)) { + next; + } + %{$CompatProblems{$Level}{$Interface}{"Base_Class_Became_Virtually_Inherited"}{"this->".$BaseName}}=( + "Type_Name"=>$ClassName, + "Target"=>$BaseName ); + } + } + } + } + # detect size changes in base classes + if($Shift_Old!=$Shift_New) + { # size of allocable class + foreach my $BaseId (@StableBases_Old) + { # search for changed base + my %BaseType = getType($BaseId, 1); + my $Size_Old = $TypeInfo{1}{$BaseId}{"Size"}; + my $Size_New = $TypeInfo{2}{$BaseId_New{$Tr_Old{$BaseType{"Name"}}}}{"Size"}; + if($Size_Old ne $Size_New + and $Size_Old and $Size_New) + { + my $ProblemType = undef; + if(isCopyingClass($BaseId, 1)) { + $ProblemType = "Size_Of_Copying_Class"; + } + elsif($AllocableClass{1}{$BaseType{"Name"}}) + { + if($Size_New>$Size_Old) + { # increased size + $ProblemType = "Size_Of_Allocable_Class_Increased"; + } + else + { # decreased size + $ProblemType = "Size_Of_Allocable_Class_Decreased"; + if(not havePubFields(\%Class_Old)) + { # affected class has no public members + next; + } + } + } + next if(not $ProblemType); + foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}})) + { # base class size changes affecting current class + if(not symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1)) { + next; + } + %{$CompatProblems{$Level}{$Interface}{$ProblemType}{"this->".$BaseType{"Name"}}}=( + "Type_Name"=>$BaseType{"Name"}, + "Target"=>$BaseType{"Name"}, + "Old_Size"=>$Size_Old*$BYTE, + "New_Size"=>$Size_New*$BYTE ); + } + } + } + } + + if(defined $VirtualTable_Model{1}{$ClassName} + and cmpVTables_Real($ClassName, 1)==1 + and my @VFunctions = keys(%{$VirtualTable_Model{1}{$ClassName}})) + { # compare virtual tables size in base classes + my $VShift_Old = getVShift($ClassId_Old, 1); + my $VShift_New = getVShift($ClassId_New, 2); + if($VShift_Old ne $VShift_New) + { # changes in the base class or changes in the list of base classes + my @AllBases_Old = getBaseClasses($ClassId_Old, 1, 1); + my @AllBases_New = getBaseClasses($ClassId_New, 2, 1); + ($BNum1, $BNum2) = (1, 1); + my %StableBase = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $_} @AllBases_New; + foreach my $BaseId (@AllBases_Old) + { + my %BaseType = getType($BaseId, 1); + if(not $StableBase{$Tr_Old{$BaseType{"Name"}}}) + { # lost base + next; + } + my $VSize_Old = getVTable_Size($BaseType{"Name"}, 1); + my $VSize_New = getVTable_Size($BaseType{"Name"}, 2); + if($VSize_Old!=$VSize_New) + { + foreach my $Symbol (@VFunctions) + { # TODO: affected non-virtual methods? + if(not defined $VirtualTable_Model{2}{$ClassName}{$Symbol}) + { # Removed_Virtual_Method, will be registered in mergeVirtualTables() + next; + } + + if($VirtualTable_Model{2}{$ClassName}{$Symbol}-$VirtualTable_Model{1}{$ClassName}{$Symbol}==0) + { # skip interfaces that have not changed the absolute virtual position + next; + } + + if(not symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected", $Level, 1)) { + next; + } + + $VTableChanged_M{$BaseType{"Name"}} = 1; + $VTableChanged_M{$ClassName} = 1; + + foreach my $VirtFunc (keys(%{$AddedInt_Virt{$Level}{$BaseType{"Name"}}})) + { # the reason of the layout change: added virtual functions + next if($VirtualReplacement{$VirtFunc}); + my $ProblemType = "Added_Virtual_Method"; + if($CompSign{2}{$VirtFunc}{"PureVirt"}) { + $ProblemType = "Added_Pure_Virtual_Method"; + } + %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{getSignature($VirtFunc, 2, "Class|Name|Qual")}}=( + "Type_Name"=>$BaseType{"Name"}, + "Target"=>$VirtFunc ); + } + + foreach my $VirtFunc (keys(%{$RemovedInt_Virt{$Level}{$BaseType{"Name"}}})) + { # the reason of the layout change: removed virtual functions + next if($VirtualReplacement{$VirtFunc}); + my $ProblemType = "Removed_Virtual_Method"; + if($CompSign{1}{$VirtFunc}{"PureVirt"}) { + $ProblemType = "Removed_Pure_Virtual_Method"; + } + %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{getSignature($VirtFunc, 1, "Class|Name|Qual")}}=( + "Type_Name"=>$BaseType{"Name"}, + "Target"=>$VirtFunc ); + } + } + } + } + } + } + } + } +} + +sub isCreatable($$) +{ + my ($ClassId, $LVer) = @_; + if($AllocableClass{$LVer}{$TypeInfo{$LVer}{$ClassId}{"Name"}} + or isCopyingClass($ClassId, $LVer)) { + return 1; + } + if(keys(%{$In::ABI{$LVer}{"Class_SubClasses"}{$ClassId}})) + { # Fix for incomplete data: if this class has + # a base class then it should also has a constructor + return 1; + } + if($ReturnedClass{$LVer}{$ClassId}) + { # returned by some method of this class + # or any other class + return 1; + } + return 0; +} + +sub isUsedClass($$$) +{ + my ($ClassId, $LVer, $Level) = @_; + if(keys(%{$ParamClass{$LVer}{$ClassId}})) + { # parameter of some exported method + return 1; + } + my $CName = $TypeInfo{$LVer}{$ClassId}{"Name"}; + if(keys(%{$ClassMethods{$Level}{$LVer}{$CName}})) + { # method from target class + return 1; + } + return 0; +} + +sub mergeVirtualTables($$) +{ # check for changes in the virtual table + my ($Interface, $Level) = @_; + # affected methods: + # - virtual + # - pure-virtual + # - non-virtual + if($CompSign{1}{$Interface}{"Data"}) + { # global data is not affected + return; + } + my $Class_Id = $CompSign{1}{$Interface}{"Class"}; + if(not $Class_Id) { + return; + } + my $CName = $TypeInfo{1}{$Class_Id}{"Name"}; + if(cmpVTables_Real($CName, 1)==0) + { # no changes + return; + } + $CheckedTypes{$Level}{$CName} = 1; + if($Level eq "Binary") + { # Binary-level + if($CompSign{1}{$Interface}{"PureVirt"} + and not isUsedClass($Class_Id, 1, $Level)) + { # pure virtuals should not be affected + # if there are no exported methods using this class + return; + } + } + foreach my $Func (keys(%{$VirtualTable{1}{$CName}})) + { + if(defined $VirtualTable{2}{$CName}{$Func} + and defined $CompSign{2}{$Func}) + { + if(not $CompSign{1}{$Func}{"PureVirt"} + and $CompSign{2}{$Func}{"PureVirt"}) + { # became pure virtual + %{$CompatProblems{$Level}{$Interface}{"Virtual_Method_Became_Pure"}{$Func}}=( + "Type_Name"=>$CName, + "Target"=>$Func ); + $VTableChanged_M{$CName} = 1; + } + elsif($CompSign{1}{$Func}{"PureVirt"} + and not $CompSign{2}{$Func}{"PureVirt"}) + { # became non-pure virtual + %{$CompatProblems{$Level}{$Interface}{"Virtual_Method_Became_Non_Pure"}{$Func}}=( + "Type_Name"=>$CName, + "Target"=>$Func ); + $VTableChanged_M{$CName} = 1; + } + } + } + if($Level eq "Binary") + { # Binary-level + # check virtual table structure + foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$Level}{$CName}})) + { + next if($Interface eq $AddedVFunc); + next if($VirtualReplacement{$AddedVFunc}); + my $VPos_Added = $VirtualTable{2}{$CName}{$AddedVFunc}; + if($CompSign{2}{$AddedVFunc}{"PureVirt"}) + { # pure virtual methods affect all others (virtual and non-virtual) + %{$CompatProblems{$Level}{$Interface}{"Added_Pure_Virtual_Method"}{$AddedVFunc}}=( + "Type_Name"=>$CName, + "Target"=>$AddedVFunc ); + $VTableChanged_M{$CName} = 1; + } + elsif(not defined $VirtualTable{1}{$CName} + or $VPos_Added>keys(%{$VirtualTable{1}{$CName}})) + { # added virtual function at the end of v-table + if(not keys(%{$VirtualTable_Model{1}{$CName}})) + { # became polymorphous class, added v-table pointer + %{$CompatProblems{$Level}{$Interface}{"Added_First_Virtual_Method"}{$AddedVFunc}}=( + "Type_Name"=>$CName, + "Target"=>$AddedVFunc ); + $VTableChanged_M{$CName} = 1; + } + else + { + my $VSize_Old = getVTable_Size($CName, 1); + my $VSize_New = getVTable_Size($CName, 2); + next if($VSize_Old==$VSize_New); # exception: register as removed and added virtual method + if(isCopyingClass($Class_Id, 1)) + { # class has no constructors and v-table will be copied by applications, this may affect all methods + my $ProblemType = "Added_Virtual_Method"; + if(isLeafClass($Class_Id, 1)) { + $ProblemType = "Added_Virtual_Method_At_End_Of_Leaf_Copying_Class"; + } + %{$CompatProblems{$Level}{$Interface}{$ProblemType}{getSignature($AddedVFunc, 2, "Class|Name|Qual")}}=( + "Type_Name"=>$CName, + "Target"=>$AddedVFunc ); + $VTableChanged_M{$CName} = 1; + } + else + { + my $ProblemType = "Added_Virtual_Method"; + if(isLeafClass($Class_Id, 1)) { + $ProblemType = "Added_Virtual_Method_At_End_Of_Leaf_Allocable_Class"; + } + %{$CompatProblems{$Level}{$Interface}{$ProblemType}{getSignature($AddedVFunc, 2, "Class|Name|Qual")}}=( + "Type_Name"=>$CName, + "Target"=>$AddedVFunc ); + $VTableChanged_M{$CName} = 1; + } + } + } + elsif($CompSign{1}{$Interface}{"Virt"} + or $CompSign{1}{$Interface}{"PureVirt"}) + { + if(defined $VirtualTable{1}{$CName} + and defined $VirtualTable{2}{$CName}) + { + my $VPos_Old = $VirtualTable{1}{$CName}{$Interface}; + my $VPos_New = $VirtualTable{2}{$CName}{$Interface}; + + if($VPos_Added<=$VPos_Old and $VPos_Old!=$VPos_New) + { + my @Affected = ($Interface, keys(%{$OverriddenMethods{1}{$Interface}})); + foreach my $ASymbol (@Affected) + { + if(not $CompSign{1}{$ASymbol}{"PureVirt"}) + { + if(not symbolFilter($ASymbol, $CompSign{1}{$ASymbol}, "Affected", $Level, 1)) { + next; + } + } + %{$CompatProblems{$Level}{$ASymbol}{"Added_Virtual_Method"}{getSignature($AddedVFunc, 2, "Class|Name|Qual")}}=( + "Type_Name"=>$CName, + "Target"=>$AddedVFunc ); + $VTableChanged_M{$TypeInfo{1}{$CompSign{1}{$ASymbol}{"Class"}}{"Name"}} = 1; + } + } + } + } + else { + # safe + } + } + + foreach my $RemovedVFunc (sort keys(%{$RemovedInt_Virt{$Level}{$CName}})) + { + next if($VirtualReplacement{$RemovedVFunc}); + if($RemovedVFunc eq $Interface + and $CompSign{1}{$RemovedVFunc}{"PureVirt"}) + { # This case is for removed virtual methods + # implemented in both versions of a library + next; + } + + if(not keys(%{$VirtualTable_Model{2}{$CName}})) + { # became non-polymorphous class, removed v-table pointer + %{$CompatProblems{$Level}{$Interface}{"Removed_Last_Virtual_Method"}{getSignature($RemovedVFunc, 1, "Class|Name|Qual")}}=( + "Type_Name"=>$CName, + "Target"=>$RemovedVFunc); + $VTableChanged_M{$CName} = 1; + } + elsif($CompSign{1}{$Interface}{"Virt"} + or $CompSign{1}{$Interface}{"PureVirt"}) + { + if(defined $VirtualTable{1}{$CName} + and defined $VirtualTable{2}{$CName}) + { + if(not defined $VirtualTable{1}{$CName}{$Interface}) { + next; + } + my $VPos_New = -1; + if(defined $VirtualTable{2}{$CName}{$Interface}) + { + $VPos_New = $VirtualTable{2}{$CName}{$Interface}; + } + else + { + if($Interface ne $RemovedVFunc) { + next; + } + } + my $VPos_Removed = $VirtualTable{1}{$CName}{$RemovedVFunc}; + my $VPos_Old = $VirtualTable{1}{$CName}{$Interface}; + if($VPos_Removed<=$VPos_Old and $VPos_Old!=$VPos_New) + { + my @Affected = ($Interface, keys(%{$OverriddenMethods{1}{$Interface}})); + foreach my $ASymbol (@Affected) + { + if(not $CompSign{1}{$ASymbol}{"PureVirt"}) + { + if(not symbolFilter($ASymbol, $CompSign{1}{$ASymbol}, "Affected", $Level, 1)) { + next; + } + } + my $ProblemType = "Removed_Virtual_Method"; + if($CompSign{1}{$RemovedVFunc}{"PureVirt"}) { + $ProblemType = "Removed_Pure_Virtual_Method"; + } + + %{$CompatProblems{$Level}{$ASymbol}{$ProblemType}{getSignature($RemovedVFunc, 1, "Class|Name|Qual")}}=( + "Type_Name"=>$CName, + "Target"=>$RemovedVFunc); + $VTableChanged_M{$TypeInfo{1}{$CompSign{1}{$ASymbol}{"Class"}}{"Name"}} = 1; + } + } + } + } + } + } + else + { # Source-level + foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$Level}{$CName}})) + { + next if($Interface eq $AddedVFunc); + if($CompSign{2}{$AddedVFunc}{"PureVirt"}) + { + %{$CompatProblems{$Level}{$Interface}{"Added_Pure_Virtual_Method"}{$AddedVFunc}}=( + "Type_Name"=>$CName, + "Target"=>$AddedVFunc ); + } + } + foreach my $RemovedVFunc (keys(%{$RemovedInt_Virt{$Level}{$CName}})) + { + if($CompSign{1}{$RemovedVFunc}{"PureVirt"}) + { + %{$CompatProblems{$Level}{$Interface}{"Removed_Pure_Virtual_Method"}{$RemovedVFunc}}=( + "Type_Name"=>$CName, + "Target"=>$RemovedVFunc ); + } + } + } +} + +sub findMemPairByName($$) +{ + my ($Mem, $PairType) = @_; + $Mem=~s/\A[_]+|[_]+\Z//g; + foreach my $Pair (sort {$a<=>$b} keys(%{$PairType->{"Memb"}})) + { + if(defined $PairType->{"Memb"}{$Pair}) + { + my $Name = $PairType->{"Memb"}{$Pair}{"name"}; + + if(index($Name, "_")!=-1) { + $Name=~s/\A[_]+|[_]+\Z//g; + } + + if($Name eq $Mem) { + return $Pair; + } + } + } + return "lost"; +} + +sub findMemPairByVal($$) +{ + my ($Member_Value, $Pair_Type) = @_; + foreach my $MemberPair_Pos (sort {$a<=>$b} keys(%{$Pair_Type->{"Memb"}})) + { + if(defined $Pair_Type->{"Memb"}{$MemberPair_Pos} + and $Pair_Type->{"Memb"}{$MemberPair_Pos}{"value"} eq $Member_Value) { + return $MemberPair_Pos; + } + } + return "lost"; +} + +sub isRenamed($$$$$) +{ + my ($MemPos, $Type1, $V1, $Type2, $V2) = @_; + my $Member_Name = $Type1->{"Memb"}{$MemPos}{"name"}; + my $MemberType_Id = $Type1->{"Memb"}{$MemPos}{"type"}; + my %MemberType_Pure = getPureType($MemberType_Id, $V1); + if(not defined $Type2->{"Memb"}{$MemPos}) { + return ""; + } + my $PairType_Id = $Type2->{"Memb"}{$MemPos}{"type"}; + my %PairType_Pure = getPureType($PairType_Id, $V2); + + my $Pair_Name = $Type2->{"Memb"}{$MemPos}{"name"}; + my $MemberPair_Pos_Rev = ($Member_Name eq $Pair_Name)?$MemPos:findMemPairByName($Pair_Name, $Type1); + if($MemberPair_Pos_Rev eq "lost") + { + if($MemberType_Pure{"Name"} eq $PairType_Pure{"Name"}) + { # base type match + return $Pair_Name; + } + if($TypeInfo{$V1}{$MemberType_Id}{"Name"} eq $TypeInfo{$V2}{$PairType_Id}{"Name"}) + { # exact type match + return $Pair_Name; + } + if($MemberType_Pure{"Size"} eq $PairType_Pure{"Size"}) + { # size match + return $Pair_Name; + } + if(isReserved($Pair_Name)) + { # reserved fields + return $Pair_Name; + } + } + return ""; +} + +sub isLastElem($$) +{ + my ($Pos, $TypeRef) = @_; + my $Name = $TypeRef->{"Memb"}{$Pos}{"name"}; + if($Name=~/last|count|max|total|num/i) + { # GST_LEVEL_COUNT, GST_RTSP_ELAST + return 1; + } + elsif($Name=~/END|NLIMITS\Z/) + { # __RLIMIT_NLIMITS + return 1; + } + elsif($Name=~/\AN[A-Z](.+)[a-z]+s\Z/ + and $Pos+1==keys(%{$TypeRef->{"Memb"}})) + { # NImageFormats, NColorRoles + return 1; + } + return 0; +} + +sub nonComparable($$) +{ + my ($T1, $T2) = @_; + + my $N1 = $T1->{"Name"}; + my $N2 = $T2->{"Name"}; + + $N1=~s/\A(struct|union|enum) //; + $N2=~s/\A(struct|union|enum) //; + + if($N1 ne $N2 and $N2!~/\A\Q$N1\E_v\d+\Z/ + and not isAnon($N1) + and not isAnon($N2)) + { # different names + # NOTE: compare versioned types (type vs type_v30) + if($T1->{"Type"} ne "Pointer" + or $T2->{"Type"} ne "Pointer") + { # compare base types + return 1; + } + + if($N1!~/\Avoid\s*\*/ + and $N2=~/\Avoid\s*\*/) + { + return 1; + } + } + elsif($T1->{"Type"} ne $T2->{"Type"}) + { # different types + if($T1->{"Type"} eq "Class" + and $T2->{"Type"} eq "Struct") + { # "class" to "struct" + return 0; + } + elsif($T2->{"Type"} eq "Class" + and $T1->{"Type"} eq "Struct") + { # "struct" to "class" + return 0; + } + else + { # "class" to "enum" + # "union" to "class" + # ... + return 1; + } + } + return 0; +} + +sub mergeTypes($$$) +{ + my ($Type1_Id, $Type2_Id, $Level) = @_; + return {} if(not $Type1_Id or not $Type2_Id); + + if(defined $Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id}) + { # already merged + return $Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id}; + } + + my %Type1 = getType($Type1_Id, 1); + my %Type2 = getType($Type2_Id, 2); + if(not $Type1{"Name"} or not $Type2{"Name"}) { + return {}; + } + + my %Type1_Pure = getPureType($Type1_Id, 1); + my %Type2_Pure = getPureType($Type2_Id, 2); + + if(defined $UsedDump{1}{"DWARF"}) + { + if($Type1_Pure{"Name"} eq "__unknown__" + or $Type2_Pure{"Name"} eq "__unknown__") + { # Error ABI dump + return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = {}); + } + } + + if(isPrivateABI($Type1_Id, 1)) { + return {}; + } + + if($Type1{"Type"}=~/Intrinsic|Class|Struct|Union|Enum|Ptr|Typedef/) { + $CheckedTypes{$Level}{$Type1{"Name"}} = 1; + } + + if($Type1_Pure{"Type"}=~/Intrinsic|Class|Struct|Union|Enum|Ptr|Typedef/) { + $CheckedTypes{$Level}{$Type1_Pure{"Name"}} = 1; + } + + my %SubProblems = (); + + if($Type1_Pure{"Name"} eq $Type2_Pure{"Name"}) + { + if($Type1_Pure{"Type"}=~/Struct|Union/ + and $Type2_Pure{"Type"}=~/Struct|Union/) + { + if(isOpaque(\%Type2_Pure) and not isOpaque(\%Type1_Pure)) + { + if(not defined $UsedDump{1}{"DWARF"} + and not defined $UsedDump{2}{"DWARF"}) + { + %{$SubProblems{"Type_Became_Opaque"}{$Type1_Pure{"Name"}}}=( + "Target"=>$Type1_Pure{"Name"}, + "Type_Name"=>$Type1_Pure{"Name"} ); + } + + return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems); + } + } + } + + if($Type1_Pure{"Size"} eq "" + or $Type2_Pure{"Size"} eq "") + { # NOTE: size of struct may be 0 bytes + if($Type1_Pure{"Type"}=~/Class|Struct|Union/) + { # including a case when "class Class { ... };" changed to "class Class;" + if(not defined $Type1_Pure{"Memb"} or not defined $Type2_Pure{"Memb"} + or index($Type1_Pure{"Name"}, "<")==-1 or index($Type2_Pure{"Name"}, "<")==-1) + { # NOTE: template instances have no size + return {}; + } + } + } + + if(isRecurType($Type1_Pure{"Tid"}, $Type2_Pure{"Tid"}, \@RecurTypes)) + { # skip recursive declarations + return {}; + } + return {} if(not $Type1_Pure{"Name"} or not $Type2_Pure{"Name"}); + return {} if($In::Desc{1}{"SkipTypes"}{$Type1_Pure{"Name"}}); + return {} if($In::Desc{1}{"SkipTypes"}{$Type1{"Name"}}); + + if($Type1_Pure{"Type"}=~/Class|Struct|Union|Enum|Typedef/ + and not isTargetType($Type1_Pure{"Tid"}, 1)) { + return {}; + } + + my %Typedef_1 = goToFirst($Type1{"Tid"}, 1, "Typedef"); + my %Typedef_2 = goToFirst($Type2{"Tid"}, 2, "Typedef"); + + if(%Typedef_1 and %Typedef_2 + and $Typedef_1{"Type"} eq "Typedef" and $Typedef_2{"Type"} eq "Typedef" + and $Typedef_1{"Name"} eq $Typedef_2{"Name"}) + { + my %Base_1 = getOneStepBaseType($Typedef_1{"Tid"}, 1); + my %Base_2 = getOneStepBaseType($Typedef_2{"Tid"}, 2); + if($Base_1{"Name"} ne $Base_2{"Name"}) + { + if($In::ABI{1}{"GccVersion"} ne $In::ABI{2}{"GccVersion"} + or $In::Opt{"SkipTypedefUncover"}) + { # different GCC versions or different dumps + $Base_1{"Name"} = uncoverTypedefs($Base_1{"Name"}, 1); + $Base_2{"Name"} = uncoverTypedefs($Base_2{"Name"}, 2); + # std::__va_list and __va_list + $Base_1{"Name"}=~s/\A(\w+::)+//; + $Base_2{"Name"}=~s/\A(\w+::)+//; + $Base_1{"Name"} = formatName($Base_1{"Name"}, "T"); + $Base_2{"Name"} = formatName($Base_2{"Name"}, "T"); + } + } + if($Base_1{"Name"}!~/anon\-/ and $Base_2{"Name"}!~/anon\-/ + and $Base_1{"Name"} ne $Base_2{"Name"}) + { + if($Level eq "Binary" + and $Type1{"Size"} and $Type2{"Size"} + and $Type1{"Size"} ne $Type2{"Size"}) + { + %{$SubProblems{"DataType_Size"}{$Typedef_1{"Name"}}}=( + "Target"=>$Typedef_1{"Name"}, + "Type_Name"=>$Typedef_1{"Name"}, + "Old_Size"=>$Type1{"Size"}*$BYTE, + "New_Size"=>$Type2{"Size"}*$BYTE ); + } + my %Base1_Pure = getPureType($Base_1{"Tid"}, 1); + my %Base2_Pure = getPureType($Base_2{"Tid"}, 2); + + if(defined $UsedDump{1}{"DWARF"}) + { + if($Base1_Pure{"Name"}=~/\b__unknown__\b/ + or $Base2_Pure{"Name"}=~/\b__unknown__\b/) + { # Error ABI dump + return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = {}); + } + } + + if(tNameLock($Base_1{"Tid"}, $Base_2{"Tid"})) + { + if(diffTypes($Base1_Pure{"Tid"}, $Base2_Pure{"Tid"}, $Level)) + { + %{$SubProblems{"Typedef_BaseType_Format"}{$Typedef_1{"Name"}}}=( + "Target"=>$Typedef_1{"Name"}, + "Type_Name"=>$Typedef_1{"Name"}, + "Old_Value"=>$Base_1{"Name"}, + "New_Value"=>$Base_2{"Name"} ); + } + else + { + %{$SubProblems{"Typedef_BaseType"}{$Typedef_1{"Name"}}}=( + "Target"=>$Typedef_1{"Name"}, + "Type_Name"=>$Typedef_1{"Name"}, + "Old_Value"=>$Base_1{"Name"}, + "New_Value"=>$Base_2{"Name"} ); + } + } + } + } + if(nonComparable(\%Type1_Pure, \%Type2_Pure)) + { # different types (reported in detectTypeChange(...)) + my $TT1 = $Type1_Pure{"Type"}; + my $TT2 = $Type2_Pure{"Type"}; + + if($TT1 ne $TT2 + and $TT1!~/Intrinsic|Pointer|Ref|Typedef/) + { # different type of the type + my $Short1 = $Type1_Pure{"Name"}; + my $Short2 = $Type2_Pure{"Name"}; + + $Short1=~s/\A\Q$TT1\E //ig; + $Short2=~s/\A\Q$TT2\E //ig; + + if($Short1 eq $Short2) + { + %{$SubProblems{"DataType_Type"}{$Type1_Pure{"Name"}}}=( + "Target"=>$Type1_Pure{"Name"}, + "Type_Name"=>$Type1_Pure{"Name"}, + "Old_Value"=>lc($Type1_Pure{"Type"}), + "New_Value"=>lc($Type2_Pure{"Type"}) ); + } + } + return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems); + } + + pushType($Type1_Pure{"Tid"}, $Type2_Pure{"Tid"}, \@RecurTypes); + + if(($Type1_Pure{"Name"} eq $Type2_Pure{"Name"} + or (isAnon($Type1_Pure{"Name"}) and isAnon($Type2_Pure{"Name"}))) + and $Type1_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/) + { # checking size + if($Level eq "Binary" + and $Type1_Pure{"Size"} and $Type2_Pure{"Size"} + and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) + { + my $ProblemKind = "DataType_Size"; + if($Type1_Pure{"Type"} eq "Class" + and keys(%{$ClassMethods{$Level}{1}{$Type1_Pure{"Name"}}})) + { + if(isCopyingClass($Type1_Pure{"Tid"}, 1)) { + $ProblemKind = "Size_Of_Copying_Class"; + } + elsif($AllocableClass{1}{$Type1_Pure{"Name"}}) + { + if(int($Type2_Pure{"Size"})>int($Type1_Pure{"Size"})) { + $ProblemKind = "Size_Of_Allocable_Class_Increased"; + } + else + { + # descreased size of allocable class + # it has no special effects + } + } + } + %{$SubProblems{$ProblemKind}{$Type1_Pure{"Name"}}}=( + "Target"=>$Type1_Pure{"Name"}, + "Type_Name"=>$Type1_Pure{"Name"}, + "Old_Size"=>$Type1_Pure{"Size"}*$BYTE, + "New_Size"=>$Type2_Pure{"Size"}*$BYTE); + } + } + if(defined $Type1_Pure{"BaseType"} + and defined $Type2_Pure{"BaseType"}) + { # checking base types + my $Sub_SubProblems = mergeTypes($Type1_Pure{"BaseType"}, $Type2_Pure{"BaseType"}, $Level); + foreach my $Sub_SubProblemType (keys(%{$Sub_SubProblems})) + { + foreach my $Sub_SubLocation (keys(%{$Sub_SubProblems->{$Sub_SubProblemType}})) { + $SubProblems{$Sub_SubProblemType}{$Sub_SubLocation} = $Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}; + } + } + } + my (%AddedField, %RemovedField, %RenamedField, %RenamedField_Rev, %RelatedField, %RelatedField_Rev) = (); + my %NameToPosA = map {$Type1_Pure{"Memb"}{$_}{"name"}=>$_} keys(%{$Type1_Pure{"Memb"}}); + my %NameToPosB = map {$Type2_Pure{"Memb"}{$_}{"name"}=>$_} keys(%{$Type2_Pure{"Memb"}}); + foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type1_Pure{"Memb"}})) + { # detect removed and renamed fields + my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"}; + next if(not $Member_Name); + 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); + if($MemberPair_Pos eq "lost") + { + if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/) + { + if(my $RenamedTo = isRenamed($Member_Pos, \%Type1_Pure, 1, \%Type2_Pure, 2)) + { # renamed + $RenamedField{$Member_Pos} = $RenamedTo; + $RenamedField_Rev{$NameToPosB{$RenamedTo}} = $Member_Name; + } + else + { # removed + $RemovedField{$Member_Pos} = 1; + } + } + elsif($Type1_Pure{"Type"} eq "Enum") + { + my $Member_Value1 = $Type1_Pure{"Memb"}{$Member_Pos}{"value"}; + next if($Member_Value1 eq ""); + $MemberPair_Pos = findMemPairByVal($Member_Value1, \%Type2_Pure); + if($MemberPair_Pos ne "lost") + { # renamed + my $RenamedTo = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"name"}; + my $MemberPair_Pos_Rev = findMemPairByName($RenamedTo, \%Type1_Pure); + if($MemberPair_Pos_Rev eq "lost") + { + $RenamedField{$Member_Pos} = $RenamedTo; + $RenamedField_Rev{$NameToPosB{$RenamedTo}} = $Member_Name; + } + else { + $RemovedField{$Member_Pos} = 1; + } + } + else + { # removed + $RemovedField{$Member_Pos} = 1; + } + } + } + else + { # related + $RelatedField{$Member_Pos} = $MemberPair_Pos; + $RelatedField_Rev{$MemberPair_Pos} = $Member_Pos; + } + } + foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type2_Pure{"Memb"}})) + { # detect added fields + my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"}; + next if(not $Member_Name); + 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); + if($MemberPair_Pos eq "lost") + { + if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union|Enum)\Z/) + { + if(not $RenamedField_Rev{$Member_Pos}) + { # added + $AddedField{$Member_Pos}=1; + } + } + } + } + if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/) + { # detect moved fields + my (%RelPos, %RelPosName, %AbsPos) = (); + my $Pos = 0; + foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type1_Pure{"Memb"}})) + { # relative positions in 1st version + my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"}; + next if(not $Member_Name); + if(not $RemovedField{$Member_Pos}) + { # old type without removed fields + $RelPos{1}{$Member_Name} = $Pos; + $RelPosName{1}{$Pos} = $Member_Name; + $AbsPos{1}{$Pos++} = $Member_Pos; + } + } + $Pos = 0; + foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type2_Pure{"Memb"}})) + { # relative positions in 2nd version + my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"}; + next if(not $Member_Name); + if(not $AddedField{$Member_Pos}) + { # new type without added fields + $RelPos{2}{$Member_Name} = $Pos; + $RelPosName{2}{$Pos} = $Member_Name; + $AbsPos{2}{$Pos++} = $Member_Pos; + } + } + foreach my $Member_Name (keys(%{$RelPos{1}})) + { + my $RPos1 = $RelPos{1}{$Member_Name}; + my $AbsPos1 = $NameToPosA{$Member_Name}; + my $Member_Name2 = $Member_Name; + if(my $RenamedTo = $RenamedField{$AbsPos1}) + { # renamed + $Member_Name2 = $RenamedTo; + } + my $RPos2 = $RelPos{2}{$Member_Name2}; + if($RPos2 ne "" and $RPos1 ne $RPos2) + { # different relative positions + my $AbsPos2 = $NameToPosB{$Member_Name2}; + if($AbsPos1 ne $AbsPos2) + { # different absolute positions + my $ProblemType = "Moved_Field"; + if(not isPublic(\%Type1_Pure, $AbsPos1)) + { # may change layout and size of type + if($Level eq "Source") { + next; + } + $ProblemType = "Moved_Private_Field"; + } + if($Level eq "Binary" + and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) + { # affected size + my $MemSize1 = $TypeInfo{1}{$Type1_Pure{"Memb"}{$AbsPos1}{"type"}}{"Size"}; + my $MovedAbsPos = $AbsPos{1}{$RPos2}; + my $MemSize2 = $TypeInfo{1}{$Type1_Pure{"Memb"}{$MovedAbsPos}{"type"}}{"Size"}; + if($MemSize1 ne $MemSize2) { + $ProblemType .= "_And_Size"; + } + } + if($ProblemType eq "Moved_Private_Field") { + next; + } + %{$SubProblems{$ProblemType}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"}, + "Old_Value"=>$RPos1, + "New_Value"=>$RPos2 ); + } + } + } + } + foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type1_Pure{"Memb"}})) + { # check older fields, public and private + my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"}; + next if(not $Member_Name); + next if($Member_Name eq "_vptr"); + if(my $RenamedTo = $RenamedField{$Member_Pos}) + { # renamed + if(defined $Constants{2}{$Member_Name}) + { + if($Constants{2}{$Member_Name}{"Value"} eq $RenamedTo) + { # define OLD NEW + next; # Safe + } + } + + if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/) + { + if(isPublic(\%Type1_Pure, $Member_Pos)) + { + %{$SubProblems{"Renamed_Field"}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"}, + "Old_Value"=>$Member_Name, + "New_Value"=>$RenamedTo ); + } + elsif(isReserved($Member_Name)) + { + %{$SubProblems{"Used_Reserved_Field"}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"}, + "Old_Value"=>$Member_Name, + "New_Value"=>$RenamedTo ); + } + } + elsif($Type1_Pure{"Type"} eq "Enum") + { + %{$SubProblems{"Enum_Member_Name"}{$Type1_Pure{"Memb"}{$Member_Pos}{"value"}}}=( + "Target"=>$Type1_Pure{"Memb"}{$Member_Pos}{"value"}, + "Type_Name"=>$Type1_Pure{"Name"}, + "Old_Value"=>$Member_Name, + "New_Value"=>$RenamedTo ); + } + } + elsif($RemovedField{$Member_Pos}) + { # removed + if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/) + { + my $ProblemType = "Removed_Field"; + if(not isPublic(\%Type1_Pure, $Member_Pos) + or isUnnamed($Member_Name)) + { + if($Level eq "Source") { + next; + } + $ProblemType = "Removed_Private_Field"; + } + if($Level eq "Binary" + and not isMemPadded($Member_Pos, -1, \%Type1_Pure, \%RemovedField, 1)) + { + if(my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1)) + { # affected fields + if(getOffset($MNum-1, \%Type1_Pure, 1)!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, 2)) + { # changed offset + $ProblemType .= "_And_Layout"; + } + } + if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) + { # affected size + $ProblemType .= "_And_Size"; + } + } + if($ProblemType eq "Removed_Private_Field") { + next; + } + %{$SubProblems{$ProblemType}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"} ); + } + elsif($Type2_Pure{"Type"} eq "Union") + { + if($Level eq "Binary" + and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) + { + %{$SubProblems{"Removed_Union_Field_And_Size"}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"} ); + } + else + { + %{$SubProblems{"Removed_Union_Field"}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"} ); + } + } + elsif($Type1_Pure{"Type"} eq "Enum") + { + %{$SubProblems{"Enum_Member_Removed"}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"}, + "Old_Value"=>$Member_Name ); + } + } + else + { # changed + my $MemberPair_Pos = $RelatedField{$Member_Pos}; + if($Type1_Pure{"Type"} eq "Enum") + { + my $Member_Value1 = $Type1_Pure{"Memb"}{$Member_Pos}{"value"}; + next if($Member_Value1 eq ""); + my $Member_Value2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"value"}; + next if($Member_Value2 eq ""); + if($Member_Value1 ne $Member_Value2) + { + my $ProblemType = "Enum_Member_Value"; + if(isLastElem($Member_Pos, \%Type1_Pure)) { + $ProblemType = "Enum_Last_Member_Value"; + } + if($In::Desc{1}{"SkipConstants"}{$Member_Name}) { + $ProblemType = "Enum_Private_Member_Value"; + } + %{$SubProblems{$ProblemType}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"}, + "Old_Value"=>$Member_Value1, + "New_Value"=>$Member_Value2 ); + } + } + elsif($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/) + { + my $Access1 = $Type1_Pure{"Memb"}{$Member_Pos}{"access"}; + my $Access2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"access"}; + + if($Access1 ne "private" + and $Access2 eq "private") + { + %{$SubProblems{"Field_Became_Private"}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"}); + } + elsif($Access1 ne "protected" + and $Access1 ne "private" + and $Access2 eq "protected") + { + %{$SubProblems{"Field_Became_Protected"}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"}); + } + + my $MemberType1_Id = $Type1_Pure{"Memb"}{$Member_Pos}{"type"}; + my $MemberType2_Id = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"type"}; + my $SizeV1 = $TypeInfo{1}{$MemberType1_Id}{"Size"}*$BYTE; + if(my $BSize1 = $Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"}) { + $SizeV1 = $BSize1; + } + my $SizeV2 = $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE; + if(my $BSize2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"}) { + $SizeV2 = $BSize2; + } + + my $MemberType1_Name = $TypeInfo{1}{$MemberType1_Id}{"Name"}; + my $MemberType2_Name = $TypeInfo{2}{$MemberType2_Id}{"Name"}; + + if($Level eq "Binary" + and $SizeV1 ne "" and $SizeV2 ne "" + and $SizeV1 ne $SizeV2) + { + if($MemberType1_Name eq $MemberType2_Name or (isAnon($MemberType1_Name) and isAnon($MemberType2_Name)) + or ($Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"} and $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"})) + { # field size change (including anon-structures and unions) + # - same types + # - unnamed types + # - bitfields + my $ProblemType = "Field_Size"; + if(not isPublic(\%Type1_Pure, $Member_Pos) + or isUnnamed($Member_Name)) + { # should not be accessed by applications, goes to "Low Severity" + # example: "abidata" members in GStreamer types + $ProblemType = "Private_".$ProblemType; + } + if(not isMemPadded($Member_Pos, $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE, \%Type1_Pure, \%RemovedField, 1)) + { # check an effect + if($Type2_Pure{"Type"} ne "Union" + and my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1)) + { # public fields after the current + if(getOffset($MNum-1, \%Type1_Pure, 1)!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, 2)) + { # changed offset + $ProblemType .= "_And_Layout"; + } + } + if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) { + $ProblemType .= "_And_Type_Size"; + } + } + if($ProblemType eq "Private_Field_Size") + { # private field size with no effect + } + if($ProblemType eq "Field_Size") + { + if($Type1_Pure{"Type"}=~/Union|Struct/ and $SizeV1<$SizeV2) + { # Low severity + $ProblemType = "Struct_Field_Size_Increased"; + } + } + if($ProblemType) + { # register a problem + %{$SubProblems{$ProblemType}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"}, + "Old_Size"=>$SizeV1, + "New_Size"=>$SizeV2); + } + } + } + if($Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"} + or $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"}) + { # do NOT check bitfield type changes + next; + } + + if(not $Type1_Pure{"Memb"}{$Member_Pos}{"mutable"} + and $Type2_Pure{"Memb"}{$MemberPair_Pos}{"mutable"}) + { + %{$SubProblems{"Field_Became_Mutable"}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"}); + } + elsif($Type1_Pure{"Memb"}{$Member_Pos}{"mutable"} + and not $Type2_Pure{"Memb"}{$MemberPair_Pos}{"mutable"}) + { + %{$SubProblems{"Field_Became_Non_Mutable"}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"}); + } + + my %Sub_SubChanges = detectTypeChange($MemberType1_Id, $MemberType2_Id, "Field", $Level); + foreach my $ProblemType (keys(%Sub_SubChanges)) + { + my $Old_Value = $Sub_SubChanges{$ProblemType}{"Old_Value"}; + my $New_Value = $Sub_SubChanges{$ProblemType}{"New_Value"}; + + # quals + if($ProblemType eq "Field_Type" + or $ProblemType eq "Field_Type_And_Size" + or $ProblemType eq "Field_Type_Format") + { + if(addedQual($Old_Value, $New_Value, "volatile")) { + %{$Sub_SubChanges{"Field_Became_Volatile"}} = %{$Sub_SubChanges{$ProblemType}}; + } + elsif(removedQual($Old_Value, $New_Value, "volatile")) { + %{$Sub_SubChanges{"Field_Became_Non_Volatile"}} = %{$Sub_SubChanges{$ProblemType}}; + } + + if(my $RA = addedQual($Old_Value, $New_Value, "const")) + { + if($RA==2) { + %{$Sub_SubChanges{"Field_Added_Const"}} = %{$Sub_SubChanges{$ProblemType}}; + } + else { + %{$Sub_SubChanges{"Field_Became_Const"}} = %{$Sub_SubChanges{$ProblemType}}; + } + } + elsif(my $RR = removedQual($Old_Value, $New_Value, "const")) + { + if($RR==2) { + %{$Sub_SubChanges{"Field_Removed_Const"}} = %{$Sub_SubChanges{$ProblemType}}; + } + else { + %{$Sub_SubChanges{"Field_Became_Non_Const"}} = %{$Sub_SubChanges{$ProblemType}}; + } + } + } + } + + if($Level eq "Source") + { + foreach my $ProblemType (keys(%Sub_SubChanges)) + { + my $Old_Value = $Sub_SubChanges{$ProblemType}{"Old_Value"}; + my $New_Value = $Sub_SubChanges{$ProblemType}{"New_Value"}; + + if($ProblemType eq "Field_Type") + { + if(cmpBTypes($Old_Value, $New_Value, 1, 2)) { + delete($Sub_SubChanges{$ProblemType}); + } + } + } + } + + foreach my $ProblemType (keys(%Sub_SubChanges)) + { + my $ProblemType_Init = $ProblemType; + if($ProblemType eq "Field_Type_And_Size") + { # Binary + if(not isPublic(\%Type1_Pure, $Member_Pos) + or isUnnamed($Member_Name)) { + $ProblemType = "Private_".$ProblemType; + } + if(not isMemPadded($Member_Pos, $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE, \%Type1_Pure, \%RemovedField, 1)) + { # check an effect + if($Type2_Pure{"Type"} ne "Union" + and my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1)) + { # public fields after the current + if(getOffset($MNum-1, \%Type1_Pure, 1)!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, 2)) + { # changed offset + $ProblemType .= "_And_Layout"; + } + } + if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) { + $ProblemType .= "_And_Type_Size"; + } + } + } + else + { + # TODO: Private_Field_Type rule? + + if(not isPublic(\%Type1_Pure, $Member_Pos) + or isUnnamed($Member_Name)) { + next; + } + } + if($ProblemType eq "Private_Field_Type_And_Size") + { # private field change with no effect + } + %{$SubProblems{$ProblemType}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"}); + + foreach my $Attr (keys(%{$Sub_SubChanges{$ProblemType_Init}})) + { # other properties + $SubProblems{$ProblemType}{$Member_Name}{$Attr} = $Sub_SubChanges{$ProblemType_Init}{$Attr}; + } + } + if(not isPublic(\%Type1_Pure, $Member_Pos)) + { # do NOT check internal type changes + next; + } + if($MemberType1_Id and $MemberType2_Id) + { # checking member type changes + my $Sub_SubProblems = mergeTypes($MemberType1_Id, $MemberType2_Id, $Level); + + my %DupProblems = (); + + foreach my $Sub_SubProblemType (sort keys(%{$Sub_SubProblems})) + { + foreach my $Sub_SubLocation (sort {length($a)<=>length($b)} sort keys(%{$Sub_SubProblems->{$Sub_SubProblemType}})) + { + if(not defined $In::Opt{"AllAffected"}) + { + if(defined $DupProblems{$Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}}) { + next; + } + } + + my $NewLocation = ($Sub_SubLocation)?$Member_Name."->".$Sub_SubLocation:$Member_Name; + $SubProblems{$Sub_SubProblemType}{$NewLocation} = $Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}; + + if(not defined $In::Opt{"AllAffected"}) + { + $DupProblems{$Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}} = 1; + } + } + } + + %DupProblems = (); + } + } + } + } + foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type2_Pure{"Memb"}})) + { # checking added members, public and private + my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"}; + next if(not $Member_Name); + next if($Member_Name eq "_vptr"); + if($AddedField{$Member_Pos}) + { # added + if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/) + { + my $ProblemType = "Added_Field"; + if(not isPublic(\%Type2_Pure, $Member_Pos) + or isUnnamed($Member_Name)) + { + if($Level eq "Source") { + next; + } + $ProblemType = "Added_Private_Field"; + } + if($Level eq "Binary" + and not isMemPadded($Member_Pos, -1, \%Type2_Pure, \%AddedField, 2)) + { + if(my $MNum = isAccessible(\%Type2_Pure, \%AddedField, $Member_Pos, -1)) + { # public fields after the current + if(getOffset($MNum-1, \%Type2_Pure, 2)!=getOffset($RelatedField_Rev{$MNum-1}, \%Type1_Pure, 1)) + { # changed offset + $ProblemType .= "_And_Layout"; + } + } + if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) { + $ProblemType .= "_And_Size"; + } + } + if($ProblemType eq "Added_Private_Field") + { # skip added private fields + next; + } + %{$SubProblems{$ProblemType}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"}); + } + elsif($Type2_Pure{"Type"} eq "Union") + { + if($Level eq "Binary" + and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) + { + %{$SubProblems{"Added_Union_Field_And_Size"}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"}); + } + else + { + %{$SubProblems{"Added_Union_Field"}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"}); + } + } + elsif($Type2_Pure{"Type"} eq "Enum") + { + my $Member_Value = $Type2_Pure{"Memb"}{$Member_Pos}{"value"}; + next if($Member_Value eq ""); + %{$SubProblems{"Added_Enum_Member"}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type2_Pure{"Name"}, + "New_Value"=>$Member_Value); + } + } + } + + if($Type1_Pure{"Type"} eq "FuncPtr") + { + foreach my $PPos (sort {$a<=>$b} keys(%{$Type1_Pure{"Param"}})) + { + if(not defined $Type2_Pure{"Param"}{$PPos}) { + next; + } + + my $PT1 = $Type1_Pure{"Param"}{$PPos}{"type"}; + my $PT2 = $Type2_Pure{"Param"}{$PPos}{"type"}; + + my $PName = "p".$PPos; + + my $FP_SubProblems = mergeTypes($PT1, $PT2, $Level); + my %DupProblems = (); + + foreach my $FP_SubProblemType (keys(%{$FP_SubProblems})) + { + foreach my $FP_SubLocation (keys(%{$FP_SubProblems->{$FP_SubProblemType}})) + { + if(not defined $In::Opt{"AllAffected"}) + { + if(defined $DupProblems{$FP_SubProblems->{$FP_SubProblemType}{$FP_SubLocation}}) { + next; + } + } + + my $NewLocation = ($FP_SubLocation)?$PName."->".$FP_SubLocation:$PName; + $SubProblems{$FP_SubProblemType}{$NewLocation} = $FP_SubProblems->{$FP_SubProblemType}{$FP_SubLocation}; + + if(not defined $In::Opt{"AllAffected"}) + { + $DupProblems{$FP_SubProblems->{$FP_SubProblemType}{$FP_SubLocation}} = 1; + } + } + } + + %DupProblems = (); + } + } + + pop(@RecurTypes); + return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems); +} + +sub isUnnamed($) { + return $_[0]=~/\Aunnamed\d+\Z/; +} + +sub detectAdded($) +{ + my $Level = $_[0]; + foreach my $Symbol (keys(%{$In::ABI{2}{"SymLib"}})) + { + if(linkSymbol($Symbol, 1, "+Deps")) + { # linker can find a new symbol + # in the old-version library + # So, it's not a new symbol + next; + } + if(my $VSym = $In::ABI{2}{"SymbolVersion"}{$Symbol} + and index($Symbol,"\@")==-1) { + next; + } + $AddedInt{$Level}{$Symbol} = 1; + } +} + +sub detectRemoved($) +{ + my $Level = $_[0]; + foreach my $Symbol (keys(%{$In::ABI{1}{"SymLib"}})) + { + if(linkSymbol($Symbol, 2, "+Deps")) + { # linker can find an old symbol + # in the new-version library + next; + } + if(my $VSym = $In::ABI{1}{"SymbolVersion"}{$Symbol} + and index($Symbol,"\@")==-1) { + next; + } + $RemovedInt{$Level}{$Symbol} = 1; + } +} + +sub checkVtable($$$) +{ + my ($Level, $Symbol, $V) = @_; + + # skip v-tables for templates, that should not be imported by applications + if(my $CName = $VTableClass{$V}{$Symbol}) + { + if(index($CName, "<")!=-1) { + return 0; + } + + if(not keys(%{$ClassMethods{$Level}{$V}{$CName}})) + { # do not show vtables for "private" classes + # use case: vtable for QDragManager (Qt 4.5.3 to 4.6.0) became HIDDEN symbol + return 0; + } + } + + if($In::Desc{$V}{"SkipSymbols"}{$Symbol}) + { # user defined symbols to ignore + return 0; + } + + return 1; +} + +sub mergeLibs($) +{ + my $Level = $_[0]; + foreach my $Symbol (sort keys(%{$AddedInt{$Level}})) + { # checking added symbols + next if(not $CompSign{2}{$Symbol}{"Header"} and not $CompSign{2}{$Symbol}{"Source"}); + + if(index($Symbol, "_ZTV")==0) + { + if(not checkVtable($Level, $Symbol, 2)) { + next; + } + } + else { + next if(not symbolFilter($Symbol, $CompSign{2}{$Symbol}, "Affected + InlineVirt", $Level, 2)); + } + + if($CompSign{2}{$Symbol}{"PureVirt"}) + { # symbols for pure virtual methods cannot be called by clients + next; + } + + %{$CompatProblems{$Level}{$Symbol}{"Added_Symbol"}{""}} = (); + } + foreach my $Symbol (sort keys(%{$RemovedInt{$Level}})) + { # checking removed symbols + next if(not $CompSign{1}{$Symbol}{"Header"} and not $CompSign{1}{$Symbol}{"Source"}); + + if(index($Symbol, "_ZTV")==0) + { + if(not checkVtable($Level, $Symbol, 1)) { + next; + } + } + else { + next if(not symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected + InlineVirt", $Level, 1)); + } + + if($CompSign{1}{$Symbol}{"PureVirt"}) + { # symbols for pure virtual methods cannot be called by clients + next; + } + + %{$CompatProblems{$Level}{$Symbol}{"Removed_Symbol"}{""}} = (); + } +} + +sub detectAdded_H($) +{ + my $Level = $_[0]; + foreach my $Symbol (sort keys(%{$CompSign{2}})) + { + if($Level eq "Source") + { # remove symbol version + my ($SN, $SS, $SV) = symbolParts($Symbol); + $Symbol=$SN; + + if($CompSign{2}{$Symbol}{"Artificial"}) + { # skip artificial constructors + next; + } + } + + if(not $CompSign{2}{$Symbol}{"Header"} + and not $CompSign{2}{$Symbol}{"Source"}) { + next; + } + + if(not $CompSign{2}{$Symbol}{"MnglName"}) { + next; + } + + if($ExtendedSymbols{$Symbol}) { + next; + } + + if(not $CompSign{2}{$Symbol}{"Virt"} + and not $CompSign{2}{$Symbol}{"PureVirt"} + and $CompSign{2}{$Symbol}{"InLine"}) + { # sunch symbols are not properly detected + next; + } + + if(not defined $CompSign{1}{$Symbol} + or not $CompSign{1}{$Symbol}{"MnglName"}) + { + if($UsedDump{2}{"SrcBin"}) + { + if($UsedDump{1}{"BinOnly"}) + { # support for different ABI dumps + if(not $CompSign{2}{$Symbol}{"Virt"} + and not $CompSign{2}{$Symbol}{"PureVirt"}) + { + if($In::Opt{"CheckHeadersOnly"}) + { + if(my $Lang = $CompSign{2}{$Symbol}{"Lang"}) + { + if($Lang eq "C") + { # support for old ABI dumps: missed extern "C" functions + next; + } + } + } + else + { + if(not linkSymbol($Symbol, 2, "-Deps")) + { # skip added inline symbols and const global data + next; + } + } + } + } + } + $AddedInt{$Level}{$Symbol} = 1; + } + } +} + +sub detectRemoved_H($) +{ + my $Level = $_[0]; + foreach my $Symbol (sort keys(%{$CompSign{1}})) + { + my $ISymbol = $Symbol; + + if($Level eq "Source") + { # remove symbol version + my ($SN, $SS, $SV) = symbolParts($Symbol); + $Symbol = $SN; + } + + if(not $CompSign{1}{$Symbol}{"Header"} + and not $CompSign{1}{$Symbol}{"Source"}) { + next; + } + + if(not $CompSign{1}{$Symbol}{"MnglName"}) { + next; + } + + if($ExtendedSymbols{$Symbol}) { + next; + } + + if(not $CompSign{1}{$Symbol}{"Virt"} + and not $CompSign{1}{$Symbol}{"PureVirt"} + and $CompSign{1}{$Symbol}{"InLine"}) + { # sunch symbols are not properly detected + next; + } + + if(not defined $CompSign{2}{$Symbol} + or not $CompSign{2}{$Symbol}{"MnglName"}) + { + if(defined $UsedDump{1}{"DWARF"} + and defined $UsedDump{2}{"DWARF"} + and $Level eq "Source") + { # not present in debug-info, + # but present in dynsym + if(linkSymbol($Symbol, 2, "-Deps")) { + next; + } + + if($ISymbol ne $Symbol) + { + if(linkSymbol($ISymbol, 2, "-Deps")) { + next; + } + } + + if(my $SVer = $In::ABI{2}{"SymbolVersion"}{$Symbol}) + { + if(linkSymbol($SVer, 2, "-Deps")) { + next; + } + } + + if(my $Alias = $CompSign{1}{$ISymbol}{"Alias"}) + { + if(linkSymbol($Alias, 2, "-Deps")) { + next; + } + + if(my $SAVer = $In::ABI{2}{"SymbolVersion"}{$Alias}) + { + if(linkSymbol($SAVer, 2, "-Deps")) { + next; + } + } + } + } + if($UsedDump{1}{"SrcBin"}) + { + if($UsedDump{2}{"BinOnly"}) + { # support for different ABI dumps + if(not $CompSign{1}{$Symbol}{"Virt"} + and not $CompSign{1}{$Symbol}{"PureVirt"}) + { + if($In::Opt{"CheckHeadersOnly"}) + { # skip all removed symbols + if(my $Lang = $CompSign{1}{$Symbol}{"Lang"}) + { + if($Lang eq "C") + { # support for old ABI dumps: missed extern "C" functions + next; + } + } + } + else + { + if(not linkSymbol($Symbol, 1, "-Deps")) + { # skip removed inline symbols + next; + } + } + } + } + } + + if(not $CompSign{1}{$Symbol}{"Class"}) + { + if(my $Short = $CompSign{1}{$Symbol}{"ShortName"}) + { + if(defined $Constants{2}{$Short}) + { + my $Val = $Constants{2}{$Short}{"Value"}; + if(defined $Func_ShortName{2}{$Val}) + { # old name defined to new + next; + } + } + } + + } + $RemovedInt{$Level}{$Symbol} = 1; + if($Level eq "Source") + { # search for a source-compatible equivalent + setAlternative($Symbol, $Level); + } + } + } +} + +sub mergeHeaders($) +{ + my $Level = $_[0]; + foreach my $Symbol (sort keys(%{$AddedInt{$Level}})) + { # checking added symbols + next if($CompSign{2}{$Symbol}{"PureVirt"}); + next if(not symbolFilter($Symbol, $CompSign{2}{$Symbol}, "Affected", $Level, 2)); + if($Level eq "Binary") + { + if($CompSign{2}{$Symbol}{"InLine"}) + { + if(not $CompSign{2}{$Symbol}{"Virt"}) + { # skip inline non-virtual functions + next; + } + } + } + else + { # Source + if($SourceAlternative_B{$Symbol}) { + next; + } + } + %{$CompatProblems{$Level}{$Symbol}{"Added_Symbol"}{""}}=(); + } + foreach my $Symbol (sort keys(%{$RemovedInt{$Level}})) + { # checking removed symbols + next if($CompSign{1}{$Symbol}{"PureVirt"}); + next if(not symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected", $Level, 1)); + if($Level eq "Binary") + { + if($CompSign{1}{$Symbol}{"InLine"}) + { + if(not $CompSign{1}{$Symbol}{"Virt"}) + { # skip inline non-virtual functions + next; + } + } + } + else + { # Source + if(my $Alt = $SourceAlternative{$Symbol}) + { + if(defined $CompSign{1}{$Alt} + and $CompSign{1}{$Symbol}{"Const"}) + { + my $Cid = $CompSign{1}{$Symbol}{"Class"}; + %{$CompatProblems{$Level}{$Symbol}{"Removed_Const_Overload"}{"this"}}=( + "Type_Name"=>$TypeInfo{1}{$Cid}{"Name"}, + "Target"=>$Alt); + } + else + { # do NOT show removed symbol + next; + } + } + } + %{$CompatProblems{$Level}{$Symbol}{"Removed_Symbol"}{""}}=(); + } +} + +sub addParamNames($) +{ + my $LibraryVersion = $_[0]; + + if(not keys(%AddSymbolParams)) { + return; + } + + my $SecondVersion = $LibraryVersion==1?2:1; + foreach my $Interface (sort keys(%{$CompSign{$LibraryVersion}})) + { + if(not keys(%{$AddSymbolParams{$Interface}})) { + next; + } + + foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{$LibraryVersion}{$Interface}{"Param"}})) + { # add absent parameter names + my $ParamName = $CompSign{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"}; + if($ParamName=~/\Ap\d+\Z/ and my $NewParamName = $AddSymbolParams{$Interface}{$ParamPos}) + { # names from the external file + if(defined $CompSign{$SecondVersion}{$Interface} + and defined $CompSign{$SecondVersion}{$Interface}{"Param"}{$ParamPos}) + { + if($CompSign{$SecondVersion}{$Interface}{"Param"}{$ParamPos}{"name"}=~/\Ap\d+\Z/) { + $CompSign{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"} = $NewParamName; + } + } + else { + $CompSign{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"} = $NewParamName; + } + } + } + } +} + +sub detectChangedTypedefs() +{ # detect changed typedefs to show + # correct function signatures + foreach my $Typedef (keys(%{$In::ABI{1}{"TypedefBase"}})) + { + if(not $Typedef) { + next; + } + + my $BName1 = $In::ABI{1}{"TypedefBase"}{$Typedef}; + if(not $BName1 or isAnon($BName1)) { + next; + } + + my $BName2 = $In::ABI{2}{"TypedefBase"}{$Typedef}; + if(not $BName2 or isAnon($BName2)) { + next; + } + + $BName1 = uncoverTypedefs($BName1, 1); + $BName2 = uncoverTypedefs($BName2, 2); + + if($BName1 ne $BName2) { + $ChangedTypedef{$Typedef} = 1; + } + } +} + +sub symbolPrefix($$) +{ + my ($Symbol, $LVer) = @_; + my $ShortName = $CompSign{$LVer}{$Symbol}{"ShortName"}; + if(my $ClassId = $CompSign{$LVer}{$Symbol}{"Class"}) + { # methods + $ShortName = $TypeInfo{$LVer}{$ClassId}{"Name"}."::".$ShortName; + } + return $ShortName; +} + +sub setAlternative($) +{ + my $Symbol = $_[0]; + my $PSymbol = $Symbol; + if(not defined $CompSign{2}{$PSymbol} + or (not $CompSign{2}{$PSymbol}{"MnglName"} + and not $CompSign{2}{$PSymbol}{"ShortName"})) + { # search for a pair + if(my $ShortName = $CompSign{1}{$PSymbol}{"ShortName"}) + { + if($CompSign{1}{$PSymbol}{"Data"}) + { + if($PSymbol=~s/L(\d+$ShortName(E)\Z)/$1/ + or $PSymbol=~s/(\d+$ShortName(E)\Z)/L$1/) + { + if(defined $CompSign{2}{$PSymbol} + and $CompSign{2}{$PSymbol}{"MnglName"}) + { + $SourceAlternative{$Symbol} = $PSymbol; + $SourceAlternative_B{$PSymbol} = $Symbol; + if(not defined $CompSign{1}{$PSymbol} + or not $CompSign{1}{$PSymbol}{"MnglName"}) { + $SourceReplacement{$Symbol} = $PSymbol; + } + } + } + } + else + { + foreach my $Sp ("KV", "VK", "K", "V") + { + if($PSymbol=~s/\A_ZN$Sp/_ZN/ + or $PSymbol=~s/\A_ZN/_ZN$Sp/) + { + if(defined $CompSign{2}{$PSymbol} + and $CompSign{2}{$PSymbol}{"MnglName"}) + { + $SourceAlternative{$Symbol} = $PSymbol; + $SourceAlternative_B{$PSymbol} = $Symbol; + if(not defined $CompSign{1}{$PSymbol} + or not $CompSign{1}{$PSymbol}{"MnglName"}) { + $SourceReplacement{$Symbol} = $PSymbol; + } + } + } + $PSymbol = $Symbol; + } + } + } + } + return ""; +} + +sub getSymKind($$) +{ + my ($Symbol, $LVer) = @_; + if($CompSign{$LVer}{$Symbol}{"Data"}) + { + return "Global_Data"; + } + elsif($CompSign{$LVer}{$Symbol}{"Class"}) + { + return "Method"; + } + return "Function"; +} + +sub isConstData($$) +{ + my ($Symbol, $LVer) = @_; + + my $Return = $CompSign{$LVer}{$Symbol}{"Return"}; + my $RTName = uncoverTypedefs($TypeInfo{$LVer}{$Return}{"Name"}, $LVer); + + return ($RTName=~/\bconst\Z/); +} + +sub getConstDataSym($$) +{ + my ($Symbol, $LVer) = @_; + + my $Short = $CompSign{$LVer}{$Symbol}{"ShortName"}; + $Symbol=~s/(\d+$Short)/L$1/; + return $Symbol; +} + +sub getNonConstDataSym($$) +{ + my ($Symbol, $LVer) = @_; + + my $Short = $CompSign{$LVer}{$Symbol}{"ShortName"}; + $Symbol=~s/L(\d+$Short)/$1/; + return $Symbol; +} + +sub mergeSymbols($) +{ + my $Level = $_[0]; + my %SubProblems = (); + + mergeBases($Level); + + my %AddedOverloads = (); + foreach my $Symbol (sort keys(%{$AddedInt{$Level}})) + { # check all added exported symbols + if(not $CompSign{2}{$Symbol}{"Header"} + and not $CompSign{2}{$Symbol}{"Source"}) { + next; + } + if(defined $CompSign{1}{$Symbol} + and ($CompSign{1}{$Symbol}{"Header"} or $CompSign{1}{$Symbol}{"Source"})) + { # double-check added symbol + next; + } + if($Symbol=~/\A(_Z|\?)/) + { # C++ + $AddedOverloads{symbolPrefix($Symbol, 2)}{getSignature($Symbol, 2, "Qual")} = $Symbol; + } + if(my $OverriddenMethod = $CompSign{2}{$Symbol}{"Override"}) + { # register virtual overridings + my $Cid = $CompSign{2}{$Symbol}{"Class"}; + my $AffectedClass_Name = $TypeInfo{2}{$Cid}{"Name"}; + if(defined $CompSign{1}{$OverriddenMethod} and $CompSign{1}{$OverriddenMethod}{"Virt"} + and not $CompSign{1}{$OverriddenMethod}{"Private"}) + { + if($TName_Tid{1}{$AffectedClass_Name}) + { # class should exist in previous version + if(not isCopyingClass($TName_Tid{1}{$AffectedClass_Name}, 1)) + { # old v-table is NOT copied by old applications + if(symbolFilter($OverriddenMethod, $CompSign{1}{$OverriddenMethod}, "Affected", $Level, 1)) + { + %{$CompatProblems{$Level}{$OverriddenMethod}{"Overridden_Virtual_Method"}{$Symbol}}=( + "Type_Name"=>$AffectedClass_Name, + "Target"=>$Symbol, + "Old_Value"=>$OverriddenMethod, + "New_Value"=>$Symbol); + } + } + } + } + } + } + foreach my $Symbol (sort keys(%{$RemovedInt{$Level}})) + { # check all removed exported symbols + if(not $CompSign{1}{$Symbol}{"Header"} + and not $CompSign{1}{$Symbol}{"Source"}) { + next; + } + if(defined $CompSign{2}{$Symbol} + and ($CompSign{2}{$Symbol}{"Header"} or $CompSign{2}{$Symbol}{"Source"})) + { # double-check removed symbol + next; + } + if(not symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected", $Level, 1)) { + next; + } + + $CheckedSymbols{$Level}{$Symbol} = 1; + + if(my $OverriddenMethod = $CompSign{1}{$Symbol}{"Override"}) + { # register virtual overridings + my $Cid = $CompSign{1}{$Symbol}{"Class"}; + my $AffectedClass_Name = $TypeInfo{1}{$Cid}{"Name"}; + if(defined $CompSign{2}{$OverriddenMethod} + and $CompSign{2}{$OverriddenMethod}{"Virt"}) + { + if($TName_Tid{2}{$AffectedClass_Name}) + { # class should exist in newer version + if(not isCopyingClass($CompSign{1}{$Symbol}{"Class"}, 1)) + { # old v-table is NOT copied by old applications + %{$CompatProblems{$Level}{$Symbol}{"Overridden_Virtual_Method_B"}{$OverriddenMethod}}=( + "Type_Name"=>$AffectedClass_Name, + "Target"=>$OverriddenMethod, + "Old_Value"=>$Symbol, + "New_Value"=>$OverriddenMethod); + } + } + } + } + + if($Level eq "Binary" + and $In::Opt{"Target"} eq "windows") + { # register the reason of symbol name change + if(defined $CompSign{1}{$Symbol}{"Unmangled"} + and my $NewSym = getMangled_MSVC($CompSign{1}{$Symbol}{"Unmangled"}, 2)) + { + if($AddedInt{$Level}{$NewSym}) + { + if($CompSign{1}{$Symbol}{"Static"} ne $CompSign{2}{$NewSym}{"Static"}) + { + if($CompSign{2}{$NewSym}{"Static"}) + { + %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Static"}{$Symbol}}=( + "Target"=>$Symbol, + "Old_Value"=>$Symbol, + "New_Value"=>$NewSym ); + } + else + { + %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Non_Static"}{$Symbol}}=( + "Target"=>$Symbol, + "Old_Value"=>$Symbol, + "New_Value"=>$NewSym ); + } + } + if($CompSign{1}{$Symbol}{"Virt"} ne $CompSign{2}{$NewSym}{"Virt"}) + { + if($CompSign{2}{$NewSym}{"Virt"}) + { + %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Virtual"}{$Symbol}}=( + "Target"=>$Symbol, + "Old_Value"=>$Symbol, + "New_Value"=>$NewSym ); + } + else + { + %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Non_Virtual"}{$Symbol}}=( + "Target"=>$Symbol, + "Old_Value"=>$Symbol, + "New_Value"=>$NewSym ); + } + } + my $RTId1 = $CompSign{1}{$Symbol}{"Return"}; + my $RTId2 = $CompSign{2}{$NewSym}{"Return"}; + my $RTName1 = $TypeInfo{1}{$RTId1}{"Name"}; + my $RTName2 = $TypeInfo{2}{$RTId2}{"Name"}; + if($RTName1 ne $RTName2) + { + my $ProblemType = "Symbol_Changed_Return"; + if($CompSign{1}{$Symbol}{"Data"}) { + $ProblemType = "Global_Data_Symbol_Changed_Type"; + } + %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{$Symbol}}=( + "Target"=>$Symbol, + "Old_Type"=>$RTName1, + "New_Type"=>$RTName2, + "Old_Value"=>$Symbol, + "New_Value"=>$NewSym ); + } + } + } + } + + if($Symbol=~/\A(_Z|\?)/) + { # C++ + my $Prefix = symbolPrefix($Symbol, 1); + if(my @Overloads = sort keys(%{$AddedOverloads{$Prefix}}) + and not $AddedOverloads{$Prefix}{getSignature($Symbol, 1, "Qual")}) + { # changed signature: params, "const"-qualifier + my $NewSym = $AddedOverloads{$Prefix}{$Overloads[0]}; + if($CompSign{1}{$Symbol}{"Constructor"}) + { + if($Symbol=~/(C[1-2][EI])/) + { + my $CtorType = $1; + $NewSym=~s/(C[1-2][EI])/$CtorType/g; + } + } + elsif($CompSign{1}{$Symbol}{"Destructor"}) + { + if($Symbol=~/(D[0-2][EI])/) + { + my $DtorType = $1; + $NewSym=~s/(D[0-2][EI])/$DtorType/g; + } + } + my $NS1 = $CompSign{1}{$Symbol}{"NameSpace"}; + my $NS2 = $CompSign{2}{$NewSym}{"NameSpace"}; + if((not $NS1 and not $NS2) or ($NS1 and $NS2 and $NS1 eq $NS2)) + { # from the same class and namespace + if($CompSign{1}{$Symbol}{"Const"} + and not $CompSign{2}{$NewSym}{"Const"}) + { # "const" to non-"const" + %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Const"}{$Symbol}}=( + "Type_Name"=>$TypeInfo{1}{$CompSign{1}{$Symbol}{"Class"}}{"Name"}, + "Target"=>$Symbol, + "New_Signature"=>$NewSym, + "Old_Value"=>$Symbol, + "New_Value"=>$NewSym ); + } + elsif(not $CompSign{1}{$Symbol}{"Const"} + and $CompSign{2}{$NewSym}{"Const"}) + { # non-"const" to "const" + %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Const"}{$Symbol}}=( + "Target"=>$Symbol, + "New_Signature"=>$NewSym, + "Old_Value"=>$Symbol, + "New_Value"=>$NewSym ); + } + if($CompSign{1}{$Symbol}{"Volatile"} + and not $CompSign{2}{$NewSym}{"Volatile"}) + { # "volatile" to non-"volatile" + + %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Volatile"}{$Symbol}}=( + "Target"=>$Symbol, + "New_Signature"=>$NewSym, + "Old_Value"=>$Symbol, + "New_Value"=>$NewSym ); + } + elsif(not $CompSign{1}{$Symbol}{"Volatile"} + and $CompSign{2}{$NewSym}{"Volatile"}) + { # non-"volatile" to "volatile" + %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Volatile"}{$Symbol}}=( + "Target"=>$Symbol, + "New_Signature"=>$NewSym, + "Old_Value"=>$Symbol, + "New_Value"=>$NewSym ); + } + if(getSignature($Symbol, 1, "Param") ne getSignature($NewSym, 2, "Param")) + { # params list + %{$CompatProblems{$Level}{$Symbol}{"Symbol_Changed_Parameters"}{$Symbol}}=( + "Target"=>$Symbol, + "New_Signature"=>$NewSym, + "Old_Value"=>$Symbol, + "New_Value"=>$NewSym ); + } + } + } + } + } + + foreach my $Symbol (sort keys(%{$CompSign{1}})) + { # checking symbols + my ($SN, $SS, $SV) = symbolParts($Symbol); + if($Level eq "Source") + { # remove symbol version + $Symbol = $SN; + } + else + { # Binary + if(not $SV) + { # symbol without version + if(my $VSym = $In::ABI{1}{"SymbolVersion"}{$Symbol}) + { # the symbol is linked with versioned symbol + if($CompSign{2}{$VSym}{"MnglName"}) + { # show report for symbol@ver only + next; + } + elsif(not linkSymbol($VSym, 2, "-Deps")) + { # changed version: sym@v1 to sym@v2 + # do NOT show report for symbol + next; + } + } + } + } + my $PSymbol = $Symbol; + if($Level eq "Source" + and my $S = $SourceReplacement{$Symbol}) + { # take a source-compatible replacement function + $PSymbol = $S; + } + if($CompSign{1}{$Symbol}{"Data"} + and not defined $CompSign{2}{$Symbol}) + { + if(isConstData($Symbol, 1)) + { + if(my $NonConstSymbol = getNonConstDataSym($Symbol, 1)) + { + if($CompSign{2}{$NonConstSymbol}) { + $PSymbol = $NonConstSymbol; + } + } + } + else + { + if(my $ConstSymbol = getConstDataSym($Symbol, 1)) + { + if($CompSign{2}{$ConstSymbol}) { + $PSymbol = $ConstSymbol; + } + } + } + } + + if($CompSign{1}{$Symbol}{"Private"}) + { # private symbols + next; + } + if(not defined $CompSign{1}{$Symbol} + or not defined $CompSign{2}{$PSymbol}) + { # no info + next; + } + if(not $CompSign{1}{$Symbol}{"MnglName"} + or not $CompSign{2}{$PSymbol}{"MnglName"}) + { # no mangled name + next; + } + if((not $CompSign{1}{$Symbol}{"Header"} and not $CompSign{1}{$Symbol}{"Source"}) + or (not $CompSign{2}{$PSymbol}{"Header"} and not $CompSign{2}{$PSymbol}{"Source"})) + { # without a header or source + next; + } + + if(not $CompSign{1}{$Symbol}{"PureVirt"} + and $CompSign{2}{$PSymbol}{"PureVirt"}) + { # became pure + next; + } + if($CompSign{1}{$Symbol}{"PureVirt"} + and not $CompSign{2}{$PSymbol}{"PureVirt"}) + { # became non-pure + next; + } + + if(not symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected + InlineVirt", $Level, 1)) + { # exported, target, inline virtual and pure virtual + next; + } + + if($CompSign{1}{$Symbol}{"Data"} + and $CompSign{2}{$PSymbol}{"Data"}) + { + my $Value1 = $CompSign{1}{$Symbol}{"Value"}; + my $Value2 = $CompSign{2}{$PSymbol}{"Value"}; + if(defined $Value1) + { + $Value1 = showVal($Value1, $CompSign{1}{$Symbol}{"Return"}, 1); + if(defined $Value2) + { + $Value2 = showVal($Value2, $CompSign{2}{$PSymbol}{"Return"}, 2); + if($Value1 ne $Value2) + { + %{$CompatProblems{$Level}{$Symbol}{"Global_Data_Value_Changed"}{""}}=( + "Old_Value"=>$Value1, + "New_Value"=>$Value2, + "Target"=>$Symbol ); + } + } + } + } + + if($CompSign{2}{$PSymbol}{"Private"}) + { + %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Private"}{""}}=( + "Target"=>$PSymbol ); + } + elsif(not $CompSign{1}{$Symbol}{"Protected"} + and $CompSign{2}{$PSymbol}{"Protected"}) + { + %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Protected"}{""}}=( + "Target"=>$PSymbol ); + } + elsif($CompSign{1}{$Symbol}{"Protected"} + and not $CompSign{2}{$PSymbol}{"Protected"}) + { + %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Public"}{""}}=( + "Target"=>$PSymbol ); + } + + # checking virtual table + mergeVirtualTables($Symbol, $Level); + + if($In::Opt{"CompileError"}) + { # if some errors occurred at the compiling stage + # then some false positives can be skipped here + if(not $CompSign{1}{$Symbol}{"Data"} and $CompSign{2}{$PSymbol}{"Data"} + and not $GlobalDataObject{2}{$Symbol}) + { # missed information about parameters in newer version + next; + } + if($CompSign{1}{$Symbol}{"Data"} and not $GlobalDataObject{1}{$Symbol} + and not $CompSign{2}{$PSymbol}{"Data"}) + { # missed information about parameters in older version + next; + } + } + my ($MnglName, $VersionSpec, $SymbolVersion) = symbolParts($Symbol); + + # check attributes + if($CompSign{2}{$PSymbol}{"Static"} + and not $CompSign{1}{$Symbol}{"Static"} and $Symbol=~/\A(_Z|\?)/) + { + %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Static"}{""}}=( + "Target"=>$Symbol + ); + } + elsif(not $CompSign{2}{$PSymbol}{"Static"} + and $CompSign{1}{$Symbol}{"Static"} and $Symbol=~/\A(_Z|\?)/) + { + %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Static"}{""}}=( + "Target"=>$Symbol + ); + } + if(($CompSign{1}{$Symbol}{"Virt"} and $CompSign{2}{$PSymbol}{"Virt"}) + or ($CompSign{1}{$Symbol}{"PureVirt"} and $CompSign{2}{$PSymbol}{"PureVirt"})) + { # relative position of virtual and pure virtual methods + if($Level eq "Binary") + { + if(defined $CompSign{1}{$Symbol}{"RelPos"} and defined $CompSign{2}{$PSymbol}{"RelPos"} + and $CompSign{1}{$Symbol}{"RelPos"}!=$CompSign{2}{$PSymbol}{"RelPos"}) + { # top-level virtual methods only + my $Class_Id = $CompSign{1}{$Symbol}{"Class"}; + my $Class_Name = $TypeInfo{1}{$Class_Id}{"Name"}; + if(defined $VirtualTable{1}{$Class_Name} and defined $VirtualTable{2}{$Class_Name} + and $VirtualTable{1}{$Class_Name}{$Symbol}!=$VirtualTable{2}{$Class_Name}{$Symbol}) + { # check absolute position of a virtual method (including added and removed methods) + my %Class_Type = getType($Class_Id, 1); + my $ProblemType = "Virtual_Method_Position"; + if($CompSign{1}{$Symbol}{"PureVirt"}) { + $ProblemType = "Pure_Virtual_Method_Position"; + } + if(isUsedClass($Class_Id, 1, $Level)) + { + my @Affected = ($Symbol, keys(%{$OverriddenMethods{1}{$Symbol}})); + foreach my $ASymbol (@Affected) + { + if(not symbolFilter($ASymbol, $CompSign{1}{$ASymbol}, "Affected", $Level, 1)) { + next; + } + %{$CompatProblems{$Level}{$ASymbol}{$ProblemType}{$Symbol}}=( + "Type_Name"=>$Class_Type{"Name"}, + "Old_Value"=>$CompSign{1}{$Symbol}{"RelPos"}, + "New_Value"=>$CompSign{2}{$PSymbol}{"RelPos"}, + "Target"=>$Symbol); + } + $VTableChanged_M{$Class_Type{"Name"}} = 1; + } + } + } + } + } + if($CompSign{1}{$Symbol}{"PureVirt"} + or $CompSign{2}{$PSymbol}{"PureVirt"}) + { # do NOT check type changes in pure virtuals + next; + } + + $CheckedSymbols{$Level}{$Symbol} = 1; + + if($Symbol=~/\A(_Z|\?)/ + or keys(%{$CompSign{1}{$Symbol}{"Param"}})==keys(%{$CompSign{2}{$PSymbol}{"Param"}})) + { # C/C++: changes in parameters + foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{1}{$Symbol}{"Param"}})) + { # checking parameters + mergeParameters($Symbol, $PSymbol, $ParamPos, $ParamPos, $Level, 1); + } + } + else + { # C: added/removed parameters + foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{2}{$PSymbol}{"Param"}})) + { # checking added parameters + my $PType2_Id = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos}{"type"}; + my $PType2_Name = $TypeInfo{2}{$PType2_Id}{"Name"}; + last if($PType2_Name eq "..."); + my $PName = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos}{"name"}; + my $PName_Old = (defined $CompSign{1}{$Symbol}{"Param"}{$ParamPos})?$CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"name"}:""; + my $ParamPos_Prev = "-1"; + if($PName=~/\Ap\d+\Z/i) + { # added unnamed parameter ( pN ) + my @Positions1 = findParamPairByTypeAndPos($PType2_Name, $ParamPos, "backward", $Symbol, 1); + my @Positions2 = findParamPairByTypeAndPos($PType2_Name, $ParamPos, "backward", $Symbol, 2); + if($#Positions1==-1 or $#Positions2>$#Positions1) { + $ParamPos_Prev = "lost"; + } + } + else { + $ParamPos_Prev = findParamPairByName($PName, $Symbol, 1); + } + if($ParamPos_Prev eq "lost") + { + if($ParamPos>keys(%{$CompSign{1}{$Symbol}{"Param"}})-1) + { + my $ProblemType = "Added_Parameter"; + if($PName=~/\Ap\d+\Z/) { + $ProblemType = "Added_Unnamed_Parameter"; + } + %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=( + "Target"=>$PName, + "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2), + "Param_Type"=>$PType2_Name, + "New_Signature"=>$Symbol ); + } + else + { + my %ParamType_Pure = getPureType($PType2_Id, 2); + my $PairType_Id = $CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"type"}; + my %PairType_Pure = getPureType($PairType_Id, 1); + if(($ParamType_Pure{"Name"} eq $PairType_Pure{"Name"} or $PType2_Name eq $TypeInfo{1}{$PairType_Id}{"Name"}) + and findParamPairByName($PName_Old, $Symbol, 2) eq "lost") + { + if($PName_Old!~/\Ap\d+\Z/ and $PName!~/\Ap\d+\Z/) + { + %{$CompatProblems{$Level}{$Symbol}{"Renamed_Parameter"}{showPos($ParamPos)." Parameter"}}=( + "Target"=>$PName_Old, + "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2), + "Param_Type"=>$PType2_Name, + "Old_Value"=>$PName_Old, + "New_Value"=>$PName, + "New_Signature"=>$Symbol ); + } + } + else + { + my $ProblemType = "Added_Middle_Parameter"; + if($PName=~/\Ap\d+\Z/) { + $ProblemType = "Added_Middle_Unnamed_Parameter"; + } + %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=( + "Target"=>$PName, + "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2), + "Param_Type"=>$PType2_Name, + "New_Signature"=>$Symbol ); + } + } + } + } + foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{1}{$Symbol}{"Param"}})) + { # check relevant parameters + my $PType1_Id = $CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"type"}; + my $ParamName1 = $CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"name"}; + # FIXME: find relevant parameter by name + if(defined $CompSign{2}{$PSymbol}{"Param"}{$ParamPos}) + { + my $PType2_Id = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos}{"type"}; + my $ParamName2 = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos}{"name"}; + if($TypeInfo{1}{$PType1_Id}{"Name"} eq $TypeInfo{2}{$PType2_Id}{"Name"} + or ($ParamName1!~/\Ap\d+\Z/i and $ParamName1 eq $ParamName2)) { + mergeParameters($Symbol, $PSymbol, $ParamPos, $ParamPos, $Level, 0); + } + } + } + foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{1}{$Symbol}{"Param"}})) + { # checking removed parameters + my $PType1_Id = $CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"type"}; + my $PType1_Name = $TypeInfo{1}{$PType1_Id}{"Name"}; + last if($PType1_Name eq "..."); + my $PName = $CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"name"}; + my $PName_New = (defined $CompSign{2}{$PSymbol}{"Param"}{$ParamPos})?$CompSign{2}{$PSymbol}{"Param"}{$ParamPos}{"name"}:""; + my $ParamPos_New = "-1"; + if($PName=~/\Ap\d+\Z/i) + { # removed unnamed parameter ( pN ) + my @Positions1 = findParamPairByTypeAndPos($PType1_Name, $ParamPos, "forward", $Symbol, 1); + my @Positions2 = findParamPairByTypeAndPos($PType1_Name, $ParamPos, "forward", $Symbol, 2); + if($#Positions2==-1 or $#Positions2<$#Positions1) { + $ParamPos_New = "lost"; + } + } + else { + $ParamPos_New = findParamPairByName($PName, $Symbol, 2); + } + if($ParamPos_New eq "lost") + { + if($ParamPos>keys(%{$CompSign{2}{$PSymbol}{"Param"}})-1) + { + my $ProblemType = "Removed_Parameter"; + if($PName=~/\Ap\d+\Z/) { + $ProblemType = "Removed_Unnamed_Parameter"; + } + %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=( + "Target"=>$PName, + "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2), + "Param_Type"=>$PType1_Name, + "New_Signature"=>$Symbol); + } + elsif($ParamPos$PName, + "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2), + "Param_Type"=>$PType1_Name, + "Old_Value"=>$PName, + "New_Value"=>$PName_New, + "New_Signature"=>$Symbol); + } + } + else + { + my $ProblemType = "Removed_Middle_Parameter"; + if($PName=~/\Ap\d+\Z/) { + $ProblemType = "Removed_Middle_Unnamed_Parameter"; + } + %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=( + "Target"=>$PName, + "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2), + "Param_Type"=>$PType1_Name, + "New_Signature"=>$Symbol); + } + } + } + } + } + # checking return type + my $ReturnType1_Id = $CompSign{1}{$Symbol}{"Return"}; + my $ReturnType2_Id = $CompSign{2}{$PSymbol}{"Return"}; + my %RC_SubProblems = detectTypeChange($ReturnType1_Id, $ReturnType2_Id, "Return", $Level); + + foreach my $SubProblemType (keys(%RC_SubProblems)) + { + my $New_Value = $RC_SubProblems{$SubProblemType}{"New_Value"}; + my $Old_Value = $RC_SubProblems{$SubProblemType}{"Old_Value"}; + my %ProblemTypes = (); + + if($CompSign{1}{$Symbol}{"Data"}) + { + if($SubProblemType eq "Return_Type_And_Size") { + $ProblemTypes{"Global_Data_Type_And_Size"} = 1; + } + elsif($SubProblemType eq "Return_Type_Format") { + $ProblemTypes{"Global_Data_Type_Format"} = 1; + } + else { + $ProblemTypes{"Global_Data_Type"} = 1; + } + + # quals + if($SubProblemType eq "Return_Type" + or $SubProblemType eq "Return_Type_And_Size" + or $SubProblemType eq "Return_Type_Format") + { + if(my $RR = removedQual($Old_Value, $New_Value, "const")) + { # const to non-const + if($RR==2) { + $ProblemTypes{"Global_Data_Removed_Const"} = 1; + } + else { + $ProblemTypes{"Global_Data_Became_Non_Const"} = 1; + } + $ProblemTypes{"Global_Data_Type"} = 1; + } + elsif(my $RA = addedQual($Old_Value, $New_Value, "const")) + { # non-const to const + if($RA==2) { + $ProblemTypes{"Global_Data_Added_Const"} = 1; + } + else { + $ProblemTypes{"Global_Data_Became_Const"} = 1; + } + $ProblemTypes{"Global_Data_Type"} = 1; + } + } + } + else + { + # quals + if($SubProblemType eq "Return_Type" + or $SubProblemType eq "Return_Type_And_Size" + or $SubProblemType eq "Return_Type_Format") + { + if(addedQual($Old_Value, $New_Value, "volatile")) + { + $ProblemTypes{"Return_Value_Became_Volatile"} = 1; + if($Level ne "Source" + or not cmpBTypes($Old_Value, $New_Value, 1, 2)) { + $ProblemTypes{"Return_Type"} = 1; + } + } + + if(my $RA = addedQual($Old_Value, $New_Value, "const")) + { + if($RA==2) { + $ProblemTypes{"Return_Type_Added_Const"} = 1; + } + else { + $ProblemTypes{"Return_Type_Became_Const"} = 1; + } + if($Level ne "Source" + or not cmpBTypes($Old_Value, $New_Value, 1, 2)) { + $ProblemTypes{"Return_Type"} = 1; + } + } + } + } + if($Level eq "Binary" + and not $CompSign{1}{$Symbol}{"Data"}) + { + my (%Conv1, %Conv2) = (); + + if($UseConv_Real{1}{"R"} and $UseConv_Real{2}{"R"}) + { + %Conv1 = callingConvention_R_Real($CompSign{1}{$Symbol}); + %Conv2 = callingConvention_R_Real($CompSign{2}{$PSymbol}); + } + else + { + %Conv1 = callingConvention_R_Model($CompSign{1}{$Symbol}, 1); + %Conv2 = callingConvention_R_Model($CompSign{2}{$PSymbol}, 2); + } + + if($SubProblemType eq "Return_Type_Became_Void") + { + if(keys(%{$CompSign{1}{$Symbol}{"Param"}})) + { # parameters stack has been affected + if($Conv1{"Method"} eq "stack") { + $ProblemTypes{"Return_Type_Became_Void_And_Stack_Layout"} = 1; + } + elsif($Conv1{"Hidden"}) { + $ProblemTypes{"Return_Type_Became_Void_And_Register"} = 1; + } + } + } + elsif($SubProblemType eq "Return_Type_From_Void") + { + if(keys(%{$CompSign{1}{$Symbol}{"Param"}})) + { # parameters stack has been affected + if($Conv2{"Method"} eq "stack") { + $ProblemTypes{"Return_Type_From_Void_And_Stack_Layout"} = 1; + } + elsif($Conv2{"Hidden"}) { + $ProblemTypes{"Return_Type_From_Void_And_Register"} = 1; + } + } + } + elsif($SubProblemType eq "Return_Type" + or $SubProblemType eq "Return_Type_And_Size" + or $SubProblemType eq "Return_Type_Format") + { + if($Conv1{"Method"} ne $Conv2{"Method"}) + { + if($Conv1{"Method"} eq "stack") + { # returns in a register instead of a hidden first parameter + $ProblemTypes{"Return_Type_From_Stack_To_Register"} = 1; + } + else { + $ProblemTypes{"Return_Type_From_Register_To_Stack"} = 1; + } + } + else + { + if($Conv1{"Method"} eq "reg") + { + if($Conv1{"Registers"} ne $Conv2{"Registers"}) + { + if($Conv1{"Hidden"}) { + $ProblemTypes{"Return_Type_And_Register_Was_Hidden_Parameter"} = 1; + } + elsif($Conv2{"Hidden"}) { + $ProblemTypes{"Return_Type_And_Register_Became_Hidden_Parameter"} = 1; + } + else { + $ProblemTypes{"Return_Type_And_Register"} = 1; + } + } + } + } + } + } + + if(not keys(%ProblemTypes)) + { # default + $ProblemTypes{$SubProblemType} = 1; + } + + foreach my $ProblemType (keys(%ProblemTypes)) + { # additional + $CompatProblems{$Level}{$Symbol}{$ProblemType}{"retval"} = $RC_SubProblems{$SubProblemType}; + } + } + if($ReturnType1_Id and $ReturnType2_Id) + { + @RecurTypes = (); + my $Sub_SubProblems = mergeTypes($ReturnType1_Id, $ReturnType2_Id, $Level); + + my $AddProblems = {}; + + if($CompSign{1}{$Symbol}{"Data"}) + { + if($Level eq "Binary") + { + if(getPLevel($ReturnType1_Id, 1)==0) + { + if(defined $Sub_SubProblems->{"DataType_Size"}) + { # add "Global_Data_Size" problem + + foreach my $Loc (keys(%{$Sub_SubProblems->{"DataType_Size"}})) + { + if(index($Loc,"->")==-1) + { + if($Loc eq $Sub_SubProblems->{"DataType_Size"}{$Loc}{"Type_Name"}) + { + $AddProblems->{"Global_Data_Size"}{$Loc} = $Sub_SubProblems->{"DataType_Size"}{$Loc}; # add a new problem + last; + } + } + } + } + } + if(not defined $AddProblems->{"Global_Data_Size"}) + { + if(defined $GlobalDataObject{1}{$Symbol} + and defined $GlobalDataObject{2}{$Symbol}) + { + my $Old_Size = $GlobalDataObject{1}{$Symbol}; + my $New_Size = $GlobalDataObject{2}{$Symbol}; + if($Old_Size!=$New_Size) + { + $AddProblems->{"Global_Data_Size"}{"retval"} = { + "Old_Size"=>$Old_Size*$BYTE, + "New_Size"=>$New_Size*$BYTE }; + } + } + } + } + } + + foreach my $SubProblemType (keys(%{$AddProblems})) + { + foreach my $SubLocation (keys(%{$AddProblems->{$SubProblemType}})) + { + my $NewLocation = "retval"; + if($SubLocation and $SubLocation ne "retval") { + $NewLocation = "retval->".$SubLocation; + } + $CompatProblems{$Level}{$Symbol}{$SubProblemType}{$NewLocation} = $AddProblems->{$SubProblemType}{$SubLocation}; + } + } + + foreach my $SubProblemType (keys(%{$Sub_SubProblems})) + { + foreach my $SubLocation (keys(%{$Sub_SubProblems->{$SubProblemType}})) + { + my $NewLocation = "retval"; + if($SubLocation and $SubLocation ne "retval") { + $NewLocation = "retval->".$SubLocation; + } + $CompatProblems{$Level}{$Symbol}{$SubProblemType}{$NewLocation} = $Sub_SubProblems->{$SubProblemType}{$SubLocation}; + } + } + } + + # checking object type + my $ObjTId1 = $CompSign{1}{$Symbol}{"Class"}; + my $ObjTId2 = $CompSign{2}{$PSymbol}{"Class"}; + if($ObjTId1 and $ObjTId2 + and not $CompSign{1}{$Symbol}{"Static"} + and not $CompSign{1}{$Symbol}{"Data"}) + { + my ($ThisPtr1, $ThisPtr2) = (undef, undef); + if($CompSign{1}{$Symbol}{"Const"}) + { + $ThisPtr1 = getTypeIdByName($TypeInfo{1}{$ObjTId1}{"Name"}." const*const", 1); + $ThisPtr2 = getTypeIdByName($TypeInfo{2}{$ObjTId2}{"Name"}." const*const", 2); + } + else + { + $ThisPtr1 = getTypeIdByName($TypeInfo{1}{$ObjTId1}{"Name"}."*const", 1); + $ThisPtr2 = getTypeIdByName($TypeInfo{2}{$ObjTId2}{"Name"}."*const", 2); + } + + if($ThisPtr1 and $ThisPtr2) + { + @RecurTypes = (); + my $Sub_SubProblems = mergeTypes($ThisPtr1, $ThisPtr2, $Level); + foreach my $SubProblemType (keys(%{$Sub_SubProblems})) + { + foreach my $SubLocation (keys(%{$Sub_SubProblems->{$SubProblemType}})) + { + my $NewLocation = ($SubLocation)?"this->".$SubLocation:"this"; + $CompatProblems{$Level}{$Symbol}{$SubProblemType}{$NewLocation} = $Sub_SubProblems->{$SubProblemType}{$SubLocation}; + } + } + } + } + } + + if($Level eq "Binary") { + mergeVTables($Level); + } + + # mark all affected symbols as "checked" + foreach my $Symbol (keys(%{$CompatProblems{$Level}})) { + $CheckedSymbols{$Level}{$Symbol} = 1; + } +} + +sub rmQuals($$) +{ + my ($Value, $Qual) = @_; + if(not $Qual) { + return $Value; + } + if($Qual eq "all") + { # all quals + $Qual = "const|volatile|restrict"; + } + while($Value=~s/\b$Qual\b//) { + $Value = formatName($Value, "T"); + } + return $Value; +} + +sub cmpBTypes($$$$) +{ + my ($T1, $T2, $V1, $V2) = @_; + $T1 = uncoverTypedefs($T1, $V1); + $T2 = uncoverTypedefs($T2, $V2); + return (rmQuals($T1, "all") eq rmQuals($T2, "all")); +} + +sub addedQual($$$) +{ + my ($Old_Value, $New_Value, $Qual) = @_; + return removedQual_I($New_Value, $Old_Value, 2, 1, $Qual); +} + +sub removedQual($$$) +{ + my ($Old_Value, $New_Value, $Qual) = @_; + return removedQual_I($Old_Value, $New_Value, 1, 2, $Qual); +} + +sub removedQual_I($$$$$) +{ + my ($Old_Value, $New_Value, $V1, $V2, $Qual) = @_; + $Old_Value = uncoverTypedefs($Old_Value, $V1); + $New_Value = uncoverTypedefs($New_Value, $V2); + + if($Old_Value eq $New_Value) + { # equal types + return 0; + } + if($Old_Value!~/\b$Qual\b/) + { # without a qual + return 0; + } + elsif($New_Value!~/\b$Qual\b/) + { # became non-qual + return 1; + } + else + { + my @BQ1 = getQualModel($Old_Value, $Qual); + my @BQ2 = getQualModel($New_Value, $Qual); + foreach (0 .. $#BQ1) + { # removed qual + if($BQ1[$_]==1 + and $BQ2[$_]!=1) + { + return 2; + } + } + } + return 0; +} + +sub getQualModel($$) +{ + my ($Value, $Qual) = @_; + if(not $Qual) { + return $Value; + } + + # cleaning + while($Value=~/(\w+)/) + { + my $W = $1; + + if($W eq $Qual) { + $Value=~s/\b$W\b/\@/g; + } + else { + $Value=~s/\b$W\b//g; + } + } + + $Value=~s/\@/$Qual/g; + $Value=~s/[^\*\&\w]+//g; + + # modeling + # int*const*const == 011 + # int**const == 001 + my @Model = (); + my @Elems = split(/[\*\&]/, $Value); + if(not @Elems) { + return (0); + } + foreach (@Elems) + { + if($_ eq $Qual) { + push(@Model, 1); + } + else { + push(@Model, 0); + } + } + + return @Model; +} + +my %StringTypes = map {$_=>1} ( + "char*", + "char const*" +); + +my %CharTypes = map {$_=>1} ( + "char", + "char const" +); + +sub showVal($$$) +{ + my ($Value, $TypeId, $LVer) = @_; + my %PureType = getPureType($TypeId, $LVer); + my $TName = uncoverTypedefs($PureType{"Name"}, $LVer); + + if(defined $StringTypes{$TName} or $TName=~/string/i) + { # strings + return "\"$Value\""; + } + elsif(defined $CharTypes{$TName}) + { # characters + return "\'$Value\'"; + } + if($Value eq "") + { # other + return "\'\'"; + } + + return $Value; +} + +sub getRegs($$$) +{ + my ($LVer, $Symbol, $Pos) = @_; + + if(defined $CompSign{$LVer}{$Symbol}{"Reg"}) + { + my %Regs = (); + foreach my $Elem (sort keys(%{$CompSign{$LVer}{$Symbol}{"Reg"}})) + { + if(index($Elem, $Pos)==0 + and $Elem=~/\A$Pos([\.\+]|\Z)/) { + $Regs{$CompSign{$LVer}{$Symbol}{"Reg"}{$Elem}} = 1; + } + } + + return join(", ", sort keys(%Regs)); + } + elsif(defined $CompSign{$LVer}{$Symbol}{"Param"} + and defined $CompSign{$LVer}{$Symbol}{"Param"}{0} + and not defined $CompSign{$LVer}{$Symbol}{"Param"}{0}{"offset"}) + { + return "unknown"; + } + + return undef; +} + +sub mergeParameters($$$$$$) +{ + my ($Symbol, $PSymbol, $ParamPos1, $ParamPos2, $Level, $CheckRenamed) = @_; + + my $PTid1 = $CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"type"}; + my $PTid2 = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"type"}; + + if(not $PTid1 + or not $PTid2) { + return; + } + + my $PName1 = $CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"name"}; + my $PName2 = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"name"}; + + if(index($Symbol, "_Z")==0 + or index($Symbol, "?")==0) + { # do not merge "this" + if($PName1 eq "this" or $PName2 eq "this") { + return; + } + } + + my %Type1 = getType($PTid1, 1); + my %Type2 = getType($PTid2, 2); + + my %PureType1 = getPureType($PTid1, 1); + + my %BaseType1 = getBaseType($PTid1, 1); + my %BaseType2 = getBaseType($PTid2, 2); + + my $ParamLoc = ($PName1)?$PName1:showPos($ParamPos1)." Parameter"; + + if($Level eq "Binary") + { + if($CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"reg"} + and not $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"reg"}) + { + %{$CompatProblems{$Level}{$Symbol}{"Parameter_Became_Non_Register"}{$ParamLoc}}=( + "Target"=>$PName1, + "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1) ); + } + elsif(not $CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"reg"} + and $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"reg"}) + { + %{$CompatProblems{$Level}{$Symbol}{"Parameter_Became_Register"}{$ParamLoc}}=( + "Target"=>$PName1, + "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1) ); + } + + if(defined $UsedDump{1}{"DWARF"} + and defined $UsedDump{2}{"DWARF"}) + { + my $Old_Regs = getRegs(1, $Symbol, $ParamPos1); + my $New_Regs = getRegs(2, $PSymbol, $ParamPos2); + + my $Old_Offset = $CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"offset"}; + my $New_Offset = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"offset"}; + + if($Old_Regs ne "unknown" + and $New_Regs ne "unknown") + { + if($Old_Regs and $New_Regs) + { + if($Old_Regs ne $New_Regs) + { + %{$CompatProblems{$Level}{$Symbol}{"Parameter_Changed_Register"}{$ParamLoc}}=( + "Target"=>$PName1, + "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), + "Old_Value"=>$Old_Regs, + "New_Value"=>$New_Regs ); + } + } + elsif($Old_Regs and not $New_Regs and $New_Offset ne "") + { + %{$CompatProblems{$Level}{$Symbol}{"Parameter_From_Register"}{$ParamLoc}}=( + "Target"=>$PName1, + "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), + "Old_Value"=>$Old_Regs ); + } + elsif(not $Old_Regs and $Old_Offset ne "" and $New_Regs) + { + %{$CompatProblems{$Level}{$Symbol}{"Parameter_To_Register"}{$ParamLoc}}=( + "Target"=>$PName1, + "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), + "New_Value"=>$New_Regs ); + } + } + + if($Old_Offset ne "" + and $New_Offset ne "") + { + if($Old_Offset ne $New_Offset) + { + my $Start1 = $CompSign{1}{$Symbol}{"Param"}{0}{"offset"}; + my $Start2 = $CompSign{2}{$PSymbol}{"Param"}{0}{"offset"}; + + $Old_Offset = $Old_Offset - $Start1; + $New_Offset = $New_Offset - $Start2; + + if($Old_Offset ne $New_Offset) + { + %{$CompatProblems{$Level}{$Symbol}{"Parameter_Changed_Offset"}{$ParamLoc}}=( + "Target"=>$PName1, + "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), + "Old_Value"=>$Old_Offset, + "New_Value"=>$New_Offset ); + } + } + } + } + } + + my $Value_Old = $CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"default"}; + my $Value_New = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"default"}; + + if(defined $Value_Old) + { + $Value_Old = showVal($Value_Old, $PTid1, 1); + if(defined $Value_New) + { + $Value_New = showVal($Value_New, $PTid2, 2); + if($Value_Old ne $Value_New) + { # FIXME: how to distinguish "0" and 0 (NULL) + %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Changed"}{$ParamLoc}}=( + "Target"=>$PName1, + "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), + "Old_Value"=>$Value_Old, + "New_Value"=>$Value_New ); + } + } + else + { + %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Removed"}{$ParamLoc}}=( + "Target"=>$PName1, + "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), + "Old_Value"=>$Value_Old ); + } + } + elsif(defined $Value_New) + { + $Value_New = showVal($Value_New, $PTid2, 2); + %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Added"}{$ParamLoc}}=( + "Target"=>$PName1, + "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), + "New_Value"=>$Value_New ); + } + + if($CheckRenamed) + { + if($PName1 and $PName2 and $PName1 ne $PName2 + and $PTid1!=-1 and $PTid2!=-1 + and $PName1!~/\Ap\d+\Z/ and $PName2!~/\Ap\d+\Z/) + { # except unnamed "..." value list (Id=-1) + %{$CompatProblems{$Level}{$Symbol}{"Renamed_Parameter"}{showPos($ParamPos1)." Parameter"}}=( + "Target"=>$PName1, + "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), + "Param_Type"=>$TypeInfo{1}{$PTid1}{"Name"}, + "Old_Value"=>$PName1, + "New_Value"=>$PName2, + "New_Signature"=>$Symbol); + } + } + + # checking type change (replace) + my %SubProblems = detectTypeChange($PTid1, $PTid2, "Parameter", $Level); + + foreach my $SubProblemType (keys(%SubProblems)) + { # add new problems, remove false alarms + my $New_Value = $SubProblems{$SubProblemType}{"New_Value"}; + my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"}; + + # quals + if($SubProblemType eq "Parameter_Type" + or $SubProblemType eq "Parameter_Type_And_Size" + or $SubProblemType eq "Parameter_Type_Format") + { + if(addedQual($Old_Value, $New_Value, "restrict")) { + %{$SubProblems{"Parameter_Became_Restrict"}} = %{$SubProblems{$SubProblemType}}; + } + elsif(removedQual($Old_Value, $New_Value, "restrict")) { + %{$SubProblems{"Parameter_Became_Non_Restrict"}} = %{$SubProblems{$SubProblemType}}; + } + + if(removedQual($Old_Value, $New_Value, "volatile")) { + %{$SubProblems{"Parameter_Became_Non_Volatile"}} = %{$SubProblems{$SubProblemType}}; + } + + if($Type2{"Type"} eq "Const" and $BaseType2{"Name"} eq $Type1{"Name"} + and $Type1{"Type"}=~/Intrinsic|Class|Struct|Union|Enum/) + { # int to "int const" + delete($SubProblems{$SubProblemType}); + } + elsif($Type1{"Type"} eq "Const" and $BaseType1{"Name"} eq $Type2{"Name"} + and $Type2{"Type"}=~/Intrinsic|Class|Struct|Union|Enum/) + { # "int const" to int + delete($SubProblems{$SubProblemType}); + } + elsif(my $RR = removedQual($Old_Value, $New_Value, "const")) + { # "const" to non-"const" + if($RR==2) { + %{$SubProblems{"Parameter_Removed_Const"}} = %{$SubProblems{$SubProblemType}}; + } + else { + %{$SubProblems{"Parameter_Became_Non_Const"}} = %{$SubProblems{$SubProblemType}}; + } + } + } + } + + if($Level eq "Source") + { + foreach my $SubProblemType (keys(%SubProblems)) + { + my $New_Value = $SubProblems{$SubProblemType}{"New_Value"}; + my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"}; + + if($SubProblemType eq "Parameter_Type") + { + if(cmpBTypes($Old_Value, $New_Value, 1, 2)) { + delete($SubProblems{$SubProblemType}); + } + } + } + } + + foreach my $SubProblemType (keys(%SubProblems)) + { # modify/register problems + my $New_Value = $SubProblems{$SubProblemType}{"New_Value"}; + my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"}; + my $New_Size = $SubProblems{$SubProblemType}{"New_Size"}; + my $Old_Size = $SubProblems{$SubProblemType}{"Old_Size"}; + + my $NewProblemType = $SubProblemType; + if($Old_Value eq "..." and $New_Value ne "...") + { # change from "..." to "int" + if($ParamPos1==0) + { # ISO C requires a named argument before "..." + next; + } + $NewProblemType = "Parameter_Became_Non_VaList"; + } + elsif($New_Value eq "..." and $Old_Value ne "...") + { # change from "int" to "..." + if($ParamPos2==0) + { # ISO C requires a named argument before "..." + next; + } + $NewProblemType = "Parameter_Became_VaList"; + } + elsif($Level eq "Binary" and ($SubProblemType eq "Parameter_Type_And_Size" + or $SubProblemType eq "Parameter_Type" or $SubProblemType eq "Parameter_Type_Format")) + { + my (%Conv1, %Conv2) = (); + + if($UseConv_Real{1}{"P"} and $UseConv_Real{2}{"P"}) + { # real + %Conv1 = callingConvention_P_Real($CompSign{1}{$Symbol}, $ParamPos1); + %Conv2 = callingConvention_P_Real($CompSign{2}{$PSymbol}, $ParamPos2); + } + else + { # model + %Conv1 = callingConvention_P_Model($CompSign{1}{$Symbol}, $ParamPos1, 1); + %Conv2 = callingConvention_P_Model($CompSign{2}{$PSymbol}, $ParamPos2, 2); + } + + my $ArrayType = ($Type1{"Type"} eq "Array" and $Type2{"Type"} eq "Array"); # Fortran + + if($Conv1{"Method"} eq $Conv2{"Method"}) + { + if($Conv1{"Method"} eq "stack") + { + if($Old_Size ne $New_Size and not $ArrayType) { # FIXME: isMemPadded, getOffset + $NewProblemType = "Parameter_Type_And_Stack"; + } + } + elsif($Conv1{"Method"} eq "reg") + { + if($Conv1{"Registers"} ne $Conv2{"Registers"}) { + $NewProblemType = "Parameter_Type_And_Register"; + } + } + } + elsif($Conv1{"Method"} ne "unknown" + and $Conv2{"Method"} ne "unknown") + { + if($Conv1{"Method"} eq "stack") { + $NewProblemType = "Parameter_Type_From_Stack_To_Register"; + } + elsif($Conv1{"Method"} eq "register") { + $NewProblemType = "Parameter_Type_From_Register_To_Stack"; + } + } + $SubProblems{$SubProblemType}{"Old_Reg"} = $Conv1{"Registers"}; + $SubProblems{$SubProblemType}{"New_Reg"} = $Conv2{"Registers"}; + } + %{$CompatProblems{$Level}{$Symbol}{$NewProblemType}{$ParamLoc}}=( + "Target"=>$PName1, + "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), + "New_Signature"=>$Symbol); + @{$CompatProblems{$Level}{$Symbol}{$NewProblemType}{$ParamLoc}}{keys(%{$SubProblems{$SubProblemType}})} = values %{$SubProblems{$SubProblemType}}; + } + + @RecurTypes = (); + + # checking type definition changes + my $Sub_SubProblems = mergeTypes($PTid1, $PTid2, $Level); + foreach my $SubProblemType (keys(%{$Sub_SubProblems})) + { + foreach my $SubLoc (keys(%{$Sub_SubProblems->{$SubProblemType}})) + { + my $NewProblemType = $SubProblemType; + if($SubProblemType eq "DataType_Size") + { + if($PureType1{"Type"} ne "Pointer" + and $PureType1{"Type"} ne "Ref" + and index($SubLoc, "->")==-1) + { # stack has been affected + $NewProblemType = "DataType_Size_And_Stack"; + } + } + my $NewLoc = $ParamLoc; + if($SubLoc) { + $NewLoc .= "->".$SubLoc; + } + $CompatProblems{$Level}{$Symbol}{$NewProblemType}{$NewLoc} = $Sub_SubProblems->{$SubProblemType}{$SubLoc}; + } + } +} + +sub findParamPairByName($$$) +{ + my ($Name, $Symbol, $LVer) = @_; + foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{$LVer}{$Symbol}{"Param"}})) + { + next if(not defined $CompSign{$LVer}{$Symbol}{"Param"}{$ParamPos}); + if($CompSign{$LVer}{$Symbol}{"Param"}{$ParamPos}{"name"} eq $Name) + { + return $ParamPos; + } + } + return "lost"; +} + +sub findParamPairByTypeAndPos($$$$$) +{ + my ($TypeName, $MediumPos, $Order, $Symbol, $LVer) = @_; + my @Positions = (); + foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{$LVer}{$Symbol}{"Param"}})) + { + next if($Order eq "backward" and $ParamPos>$MediumPos); + next if($Order eq "forward" and $ParamPos<$MediumPos); + next if(not defined $CompSign{$LVer}{$Symbol}{"Param"}{$ParamPos}); + my $PTypeId = $CompSign{$LVer}{$Symbol}{"Param"}{$ParamPos}{"type"}; + if($TypeInfo{$LVer}{$PTypeId}{"Name"} eq $TypeName) { + push(@Positions, $ParamPos); + } + } + return @Positions; +} + +sub diffTypes($$$) +{ + if(defined $Cache{"diffTypes"}{$_[2]}{$_[0]}{$_[1]}) { + return $Cache{"diffTypes"}{$_[2]}{$_[0]}{$_[1]}; + } + if(isRecurType($_[0], $_[1], \@RecurTypes_Diff)) + { # skip recursive declarations + return 0; + } + + pushType($_[0], $_[1], \@RecurTypes_Diff); + my $Diff = diffTypes_I(@_); + pop(@RecurTypes_Diff); + + return ($Cache{"diffTypes"}{$_[2]}{$_[0]}{$_[1]} = $Diff); +} + +sub diffTypes_I($$$) +{ + my ($Type1_Id, $Type2_Id, $Level) = @_; + + my %Type1_Pure = getPureType($Type1_Id, 1); + my %Type2_Pure = getPureType($Type2_Id, 2); + + if($Type1_Pure{"Name"} eq $Type2_Pure{"Name"}) + { # equal types + return 0; + } + if($Type1_Pure{"Name"} eq "void") + { # from void* to something + return 0; + } + if($Type2_Pure{"Name"} eq "void") + { # from something to void* + return 0; + } + if($Type1_Pure{"Name"}=~/\*/ + or $Type2_Pure{"Name"}=~/\*/) + { # compared in detectTypeChange() + return 0; + } + + my %FloatType = map {$_=>1} ( + "float", + "double", + "long double" + ); + + my $T1 = $Type1_Pure{"Type"}; + my $T2 = $Type2_Pure{"Type"}; + + if($T1 eq "Struct" + and $T2 eq "Class") + { # compare as data structures + $T2 = "Struct"; + } + + if($T1 eq "Class" + and $T2 eq "Struct") + { # compare as data structures + $T1 = "Struct"; + } + + if($T1 ne $T2) + { # different types + if($T1 eq "Intrinsic" + and $T2 eq "Enum") + { # "int" to "enum" + return 0; + } + elsif($T2 eq "Intrinsic" + and $T1 eq "Enum") + { # "enum" to "int" + return 0; + } + else + { # union to struct + # ... + return 1; + } + } + else + { + if($T1 eq "Intrinsic") + { + if($FloatType{$Type1_Pure{"Name"}} + or $FloatType{$Type2_Pure{"Name"}}) + { # "float" to "double" + # "float" to "int" + if($Level eq "Source") + { # Safe + return 0; + } + else { + return 1; + } + } + } + elsif($T1=~/Class|Struct|Union|Enum/) + { + my @Membs1 = keys(%{$Type1_Pure{"Memb"}}); + my @Membs2 = keys(%{$Type2_Pure{"Memb"}}); + if(not @Membs1 + or not @Membs2) + { # private + return 0; + } + if($#Membs1!=$#Membs2) + { # different number of elements + return 1; + } + if($T1 eq "Enum") + { + foreach my $Pos (@Membs1) + { # compare elements by name and value + if($Type1_Pure{"Memb"}{$Pos}{"name"} ne $Type2_Pure{"Memb"}{$Pos}{"name"} + or $Type1_Pure{"Memb"}{$Pos}{"value"} ne $Type2_Pure{"Memb"}{$Pos}{"value"}) + { # different names + return 1; + } + } + } + else + { + foreach my $Pos (@Membs1) + { + if($Level eq "Source") + { + if($Type1_Pure{"Memb"}{$Pos}{"name"} ne $Type2_Pure{"Memb"}{$Pos}{"name"}) + { # different names + return 1; + } + } + + my %MT1 = %{$TypeInfo{1}{$Type1_Pure{"Memb"}{$Pos}{"type"}}}; + my %MT2 = %{$TypeInfo{2}{$Type2_Pure{"Memb"}{$Pos}{"type"}}}; + + if($MT1{"Name"} ne $MT2{"Name"} + or isAnon($MT1{"Name"}) or isAnon($MT2{"Name"})) + { + my $PL1 = getPLevel($MT1{"Tid"}, 1); + my $PL2 = getPLevel($MT2{"Tid"}, 2); + + if($PL1 ne $PL2) + { # different pointer level + return 1; + } + + # compare base types + my %BT1 = getBaseType($MT1{"Tid"}, 1); + my %BT2 = getBaseType($MT2{"Tid"}, 2); + + if(diffTypes($BT1{"Tid"}, $BT2{"Tid"}, $Level)) + { # different types + return 1; + } + } + } + } + } + else + { + # TODO: arrays, etc. + } + } + return 0; +} + +sub detectTypeChange($$$$) +{ + my ($Type1_Id, $Type2_Id, $Prefix, $Level) = @_; + if(not $Type1_Id or not $Type2_Id) { + return (); + } + my %LocalProblems = (); + + my %Type1 = getType($Type1_Id, 1); + my %Type2 = getType($Type2_Id, 2); + + if(not $Type1{"Name"} or not $Type2{"Name"}) { + return (); + } + + my %Type1_Pure = getPureType($Type1_Id, 1); + my %Type2_Pure = getPureType($Type2_Id, 2); + + if(defined $In::Opt{"SkipTypedefUncover"}) + { + if($Type1_Pure{"Name"} eq $Type2_Pure{"Name"}) { + return (); + } + + if(cmpBTypes($Type1_Pure{"Name"}, $Type2_Pure{"Name"}, 1, 2)) { + return (); + } + } + + my %Type1_Base = ($Type1_Pure{"Type"} eq "Array")?getOneStepBaseType($Type1_Pure{"Tid"}, 1):getBaseType($Type1_Id, 1); + my %Type2_Base = ($Type2_Pure{"Type"} eq "Array")?getOneStepBaseType($Type2_Pure{"Tid"}, 2):getBaseType($Type2_Id, 2); + + if(not $Type1_Base{"Name"} or not $Type2_Base{"Name"}) { + return (); + } + + if(defined $UsedDump{1}{"DWARF"}) + { + if($Type1_Pure{"Name"} eq "__unknown__" + or $Type2_Pure{"Name"} eq "__unknown__" + or $Type1_Base{"Name"} eq "__unknown__" + or $Type2_Base{"Name"} eq "__unknown__") + { # Error ABI dump + return (); + } + } + + my $Type1_PLevel = getPLevel($Type1_Id, 1); + my $Type2_PLevel = getPLevel($Type2_Id, 2); + + if($Type1_PLevel eq "" or $Type2_PLevel eq "") { + return (); + } + + if($Type1_Base{"Name"} ne $Type2_Base{"Name"} + and ($Type1{"Name"} eq $Type2{"Name"} or ($Type1_PLevel>=1 and $Type1_PLevel==$Type2_PLevel + and $Type1_Base{"Name"} ne "void" and $Type2_Base{"Name"} ne "void"))) + { # base type change + if($Type1{"Name"} eq $Type2{"Name"}) + { + if($Type1{"Type"} eq "Typedef" and $Type2{"Type"} eq "Typedef") + { # will be reported in mergeTypes() as typedef problem + return (); + } + my %Typedef_1 = goToFirst($Type1{"Tid"}, 1, "Typedef"); + my %Typedef_2 = goToFirst($Type2{"Tid"}, 2, "Typedef"); + if(%Typedef_1 and %Typedef_2) + { + if($Typedef_1{"Name"} eq $Typedef_2{"Name"} + and $Typedef_1{"Type"} eq "Typedef" and $Typedef_2{"Type"} eq "Typedef") + { # const Typedef + return (); + } + } + } + if($Type1_Base{"Name"}!~/anon\-/ and $Type2_Base{"Name"}!~/anon\-/) + { + if($Level eq "Binary" + and $Type1_Base{"Size"} and $Type2_Base{"Size"} + and $Type1_Base{"Size"} ne $Type2_Base{"Size"}) + { + %{$LocalProblems{$Prefix."_BaseType_And_Size"}}=( + "Old_Value"=>$Type1_Base{"Name"}, + "New_Value"=>$Type2_Base{"Name"}, + "Old_Size"=>$Type1_Base{"Size"}*$BYTE, + "New_Size"=>$Type2_Base{"Size"}*$BYTE); + } + else + { + if(diffTypes($Type1_Base{"Tid"}, $Type2_Base{"Tid"}, $Level)) + { # format change + %{$LocalProblems{$Prefix."_BaseType_Format"}}=( + "Old_Value"=>$Type1_Base{"Name"}, + "New_Value"=>$Type2_Base{"Name"}, + "Old_Size"=>$Type1_Base{"Size"}*$BYTE, + "New_Size"=>$Type2_Base{"Size"}*$BYTE); + } + elsif(tNameLock($Type1_Base{"Tid"}, $Type2_Base{"Tid"})) + { + %{$LocalProblems{$Prefix."_BaseType"}}=( + "Old_Value"=>$Type1_Base{"Name"}, + "New_Value"=>$Type2_Base{"Name"}, + "Old_Size"=>$Type1_Base{"Size"}*$BYTE, + "New_Size"=>$Type2_Base{"Size"}*$BYTE); + } + } + } + } + elsif($Type1{"Name"} ne $Type2{"Name"}) + { # type change + if($Type1{"Name"}!~/anon\-/ and $Type2{"Name"}!~/anon\-/) + { + my $ArrayType = ($Type1{"Type"} eq "Array" and $Type2{"Type"} eq "Array"); # Fortran + + if($Prefix eq "Return" + and $Type1_Pure{"Name"} eq "void") + { + %{$LocalProblems{"Return_Type_From_Void"}}=( + "New_Value"=>$Type2{"Name"}, + "New_Size"=>$Type2{"Size"}*$BYTE); + } + elsif($Prefix eq "Return" + and $Type2_Pure{"Name"} eq "void") + { + %{$LocalProblems{"Return_Type_Became_Void"}}=( + "Old_Value"=>$Type1{"Name"}, + "Old_Size"=>$Type1{"Size"}*$BYTE); + } + else + { + if($Level eq "Binary" + and $Type1{"Size"} and $Type2{"Size"} + and $Type1{"Size"} ne $Type2{"Size"} + and not $ArrayType) + { + %{$LocalProblems{$Prefix."_Type_And_Size"}}=( + "Old_Value"=>$Type1{"Name"}, + "New_Value"=>$Type2{"Name"}, + "Old_Size"=>$Type1{"Size"}*$BYTE, + "New_Size"=>$Type2{"Size"}*$BYTE); + } + else + { + if(diffTypes($Type1_Id, $Type2_Id, $Level)) + { # format change + %{$LocalProblems{$Prefix."_Type_Format"}}=( + "Old_Value"=>$Type1{"Name"}, + "New_Value"=>$Type2{"Name"}, + "Old_Size"=>$Type1{"Size"}*$BYTE, + "New_Size"=>$Type2{"Size"}*$BYTE); + } + elsif(tNameLock($Type1_Id, $Type2_Id)) + { # FIXME: correct this condition + %{$LocalProblems{$Prefix."_Type"}}=( + "Old_Value"=>$Type1{"Name"}, + "New_Value"=>$Type2{"Name"}, + "Old_Size"=>$Type1{"Size"}*$BYTE, + "New_Size"=>$Type2{"Size"}*$BYTE); + } + } + } + } + } + if($Type1_PLevel!=$Type2_PLevel) + { + if($Type1{"Name"} ne "void" and $Type1{"Name"} ne "..." + and $Type2{"Name"} ne "void" and $Type2{"Name"} ne "...") + { + if($Level eq "Source") + { + %{$LocalProblems{$Prefix."_PointerLevel"}}=( + "Old_Value"=>$Type1_PLevel, + "New_Value"=>$Type2_PLevel); + } + else + { + if($Type2_PLevel>$Type1_PLevel) + { + %{$LocalProblems{$Prefix."_PointerLevel_Increased"}}=( + "Old_Value"=>$Type1_PLevel, + "New_Value"=>$Type2_PLevel); + } + else + { + %{$LocalProblems{$Prefix."_PointerLevel_Decreased"}}=( + "Old_Value"=>$Type1_PLevel, + "New_Value"=>$Type2_PLevel); + } + } + } + } + if($Type1_Pure{"Type"} eq "Array" + and $Type1_Pure{"BaseType"}) + { # base_type[N] -> base_type[N] + # base_type: older_structure -> typedef to newer_structure + my %SubProblems = detectTypeChange($Type1_Base{"Tid"}, $Type2_Base{"Tid"}, $Prefix, $Level); + foreach my $SubProblemType (keys(%SubProblems)) + { + $SubProblemType=~s/_Type/_BaseType/g; + next if(defined $LocalProblems{$SubProblemType}); + foreach my $Attr (keys(%{$SubProblems{$SubProblemType}})) { + $LocalProblems{$SubProblemType}{$Attr} = $SubProblems{$SubProblemType}{$Attr}; + } + } + } + return %LocalProblems; +} + +sub tNameLock($$) +{ + my ($Tid1, $Tid2) = @_; + + my $TN1 = $TypeInfo{1}{$Tid1}{"Name"}; + my $TN2 = $TypeInfo{2}{$Tid2}{"Name"}; + + my $TT1 = $TypeInfo{1}{$Tid1}{"Type"}; + my $TT2 = $TypeInfo{2}{$Tid2}{"Type"}; + + if($In::ABI{1}{"GccVersion"} ne $In::ABI{2}{"GccVersion"}) + { # different formats + my %Base1 = getType($Tid1, 1); + while(defined $Base1{"Type"} and $Base1{"Type"} eq "Typedef") { + %Base1 = getOneStepBaseType($Base1{"Tid"}, 1); + } + my %Base2 = getType($Tid2, 2); + while(defined $Base2{"Type"} and $Base2{"Type"} eq "Typedef") { + %Base2 = getOneStepBaseType($Base2{"Tid"}, 2); + } + my $BName1 = uncoverTypedefs($Base1{"Name"}, 1); + my $BName2 = uncoverTypedefs($Base2{"Name"}, 2); + + if($BName1 eq $BName2) + { # equal base types + return 0; + } + } + else + { + # typedef struct {...} type_t + # typedef struct type_t {...} type_t + if(index($TN1, " ".$TN2)!=-1) + { + if($TN1=~/\A(struct|union|enum) \Q$TN2\E\Z/) { + return 0; + } + } + if(index($TN2, " ".$TN1)!=-1) + { + if($TN2=~/\A(struct|union|enum) \Q$TN1\E\Z/) { + return 0; + } + } + + if($TT1 eq "FuncPtr" + and $TT2 eq "FuncPtr") + { + my $TN1_C = $TN1; + my $TN2_C = $TN2; + + $TN1_C=~s/\b(struct|union) //g; + $TN2_C=~s/\b(struct|union) //g; + + if($TN1_C eq $TN2_C) { + return 0; + } + } + } + + my ($N1, $N2) = ($TN1, $TN2); + $N1=~s/\b(struct|union) //g; + $N2=~s/\b(struct|union) //g; + + if($N1 eq $N2) + { # QList and QList + return 0; + } + + return 1; +} + +sub showArch($) +{ + my $Arch = $_[0]; + if($Arch eq "arm" + or $Arch eq "mips") { + return uc($Arch); + } + return $Arch; +} + +sub getReportTitle($) +{ + my $Level = $_[0]; + + my $ArchInfo = " on ".showArch($In::ABI{1}{"Arch"}).""; + if($In::ABI{1}{"Arch"} ne $In::ABI{2}{"Arch"} + or $Level eq "Source") + { # don't show architecture in the header + $ArchInfo = ""; + } + my $Title = ""; + if($Level eq "Source") { + $Title .= "Source compatibility"; + } + elsif($Level eq "Binary") { + $Title .= "Binary compatibility"; + } + else { + $Title .= "API compatibility"; + } + + my $V1 = $In::Desc{1}{"Version"}; + my $V2 = $In::Desc{2}{"Version"}; + + if($UsedDump{1}{"DWARF"} and $UsedDump{2}{"DWARF"}) + { + my $M1 = $In::ABI{1}{"LibraryName"}; + my $M2 = $In::ABI{2}{"LibraryName"}; + + my $M1S = $M1; + my $M2S = $M2; + + $M1S=~s/(\.so|\.ko)\..+/$1/ig; + $M2S=~s/(\.so|\.ko)\..+/$1/ig; + + if($M1S eq $M2S + and $V1 ne "X" and $V2 ne "Y") + { + $Title .= " report for the $M1S ".$In::Opt{"TargetComponent"}; + $Title .= " between ".$V1." and ".$V2." versions"; + } + else + { + $Title .= " report between $M1 (".$V1.")"; + $Title .= " and $M2 (".$V2.") objects"; + } + } + else + { + $Title .= " report for the ".$In::Opt{"TargetTitle"}." ".$In::Opt{"TargetComponent"}; + $Title .= " between ".$V1." and ".$V2." versions"; + } + + $Title .= $ArchInfo; + + if($In::Opt{"AppPath"}) { + $Title .= " (relating to the portability of application ".getFilename($In::Opt{"AppPath"}).")"; + } + $Title = "

".$Title."

\n"; + return $Title; +} + +sub getCheckedHeaders($) +{ + my $LVer = $_[0]; + + my @Headers = (); + + foreach my $Path (keys(%{$In::ABI{$LVer}{"Headers"}})) + { + my $Name = getFilename($Path); + + if(not isTargetHeader($Name, $LVer)) { + next; + } + + if(skipHeader($Name, $LVer)) { + next; + } + + push(@Headers, $Path); + } + + return @Headers; +} + +sub getSourceInfo() +{ + my ($CheckedHeaders, $CheckedSources, $CheckedLibs) = ("", ""); + + if(my @Headers = getCheckedHeaders(1)) + { + $CheckedHeaders = ""; + if($In::Opt{"OldStyle"}) { + $CheckedHeaders .= "

Header Files (".($#Headers+1).")

"; + } + else { + $CheckedHeaders .= "

Header Files  ".($#Headers+1)." 

"; + } + $CheckedHeaders .= "
\n"; + $CheckedHeaders .= "
\n"; + foreach my $Path (sort {lc($a) cmp lc($b)} @Headers) { + $CheckedHeaders .= getFilename($Path)."
\n"; + } + $CheckedHeaders .= "
\n"; + $CheckedHeaders .= "
$TOP_REF
\n"; + } + + if(my @Sources = keys(%{$In::ABI{1}{"Sources"}})) + { + $CheckedSources = ""; + if($In::Opt{"OldStyle"}) { + $CheckedSources .= "

Source Files (".($#Sources+1).")

"; + } + else { + $CheckedSources .= "

Source Files  ".($#Sources+1)." 

"; + } + $CheckedSources .= "
\n"; + $CheckedSources .= "
\n"; + foreach my $Path (sort {lc($a) cmp lc($b)} @Sources) { + $CheckedSources .= getFilename($Path)."
\n"; + } + $CheckedSources .= "
\n"; + $CheckedSources .= "
$TOP_REF
\n"; + } + + if(not $In::Opt{"CheckHeadersOnly"}) + { + $CheckedLibs = ""; + if($In::Opt{"OldStyle"}) { + $CheckedLibs .= "

".getObjTitle()." (".keys(%{$In::ABI{1}{"Symbols"}}).")

"; + } + else { + $CheckedLibs .= "

".getObjTitle()."  ".keys(%{$In::ABI{1}{"Symbols"}})." 

"; + } + $CheckedLibs .= "
\n"; + $CheckedLibs .= "
\n"; + foreach my $Library (sort {lc($a) cmp lc($b)} keys(%{$In::ABI{1}{"Symbols"}})) { + $CheckedLibs .= $Library."
\n"; + } + $CheckedLibs .= "
\n"; + $CheckedLibs .= "
$TOP_REF
\n"; + } + + return $CheckedHeaders.$CheckedSources.$CheckedLibs; +} + +sub getObjTitle() +{ + if(defined $UsedDump{1}{"DWARF"}) { + return "Objects"; + } + + return "Libraries"; +} + +sub getTypeProblemsCount($$) +{ + my ($TargetSeverity, $Level) = @_; + my $Count = 0; + + foreach my $Type_Name (sort keys(%{$TypeChanges{$Level}})) + { + my %Kinds_Target = (); + foreach my $Kind (keys(%{$TypeChanges{$Level}{$Type_Name}})) + { + if($CompatRules{$Level}{$Kind}{"Severity"} ne $TargetSeverity) { + next; + } + + foreach my $Loc (keys(%{$TypeChanges{$Level}{$Type_Name}{$Kind}})) + { + my $Target = $TypeChanges{$Level}{$Type_Name}{$Kind}{$Loc}{"Target"}; + + if($Kinds_Target{$Kind}{$Target}) { + next; + } + + $Kinds_Target{$Kind}{$Target} = 1; + $Count += 1; + } + } + } + return $Count; +} + +sub getSummary($) +{ + my $Level = $_[0]; + my ($Added, $Removed, $I_Problems_High, $I_Problems_Medium, $I_Problems_Low, $T_Problems_High, + $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); + %{$RESULT{$Level}} = ( + "Problems"=>0, + "Warnings"=>0, + "Affected"=>0); + # check rules + foreach my $Symbol (sort keys(%{$CompatProblems{$Level}})) + { + foreach my $Kind (keys(%{$CompatProblems{$Level}{$Symbol}})) + { + if(not defined $CompatRules{$Level}{$Kind}) + { # unknown rule + if(not $UnknownRules{$Level}{$Kind}) + { # only one warning + printMsg("WARNING", "unknown rule \"$Kind\" (\"$Level\")"); + $UnknownRules{$Level}{$Kind}=1; + } + delete($CompatProblems{$Level}{$Symbol}{$Kind}); + } + } + } + foreach my $Constant (sort keys(%{$CompatProblems_Constants{$Level}})) + { + foreach my $Kind (keys(%{$CompatProblems_Constants{$Level}{$Constant}})) + { + if(not defined $CompatRules{$Level}{$Kind}) + { # unknown rule + if(not $UnknownRules{$Level}{$Kind}) + { # only one warning + printMsg("WARNING", "unknown rule \"$Kind\" (\"$Level\")"); + $UnknownRules{$Level}{$Kind}=1; + } + delete($CompatProblems_Constants{$Level}{$Constant}{$Kind}); + } + } + } + foreach my $Symbol (sort keys(%{$CompatProblems{$Level}})) + { + foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}})) + { + if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols") + { + my $Severity = $CompatRules{$Level}{$Kind}{"Severity"}; + foreach my $Loc (sort keys(%{$CompatProblems{$Level}{$Symbol}{$Kind}})) + { + if($Kind eq "Added_Symbol") { + $Added += 1; + } + elsif($Kind eq "Removed_Symbol") + { + $Removed += 1; + $TotalAffected{$Level}{$Symbol} = $Severity; + } + else + { + if($Severity eq "Safe") { + $I_Other += 1; + } + elsif($Severity eq "High") { + $I_Problems_High += 1; + } + elsif($Severity eq "Medium") { + $I_Problems_Medium += 1; + } + elsif($Severity eq "Low") { + $I_Problems_Low += 1; + } + if(($Severity ne "Low" or $In::Opt{"StrictCompat"}) + and $Severity ne "Safe") { + $TotalAffected{$Level}{$Symbol} = $Severity; + } + } + } + } + } + } + + my %MethodTypeIndex = (); + + foreach my $Symbol (sort keys(%{$CompatProblems{$Level}})) + { + foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}})) + { + if($CompatRules{$Level}{$Kind}{"Kind"} eq "Types") + { + my $Severity = $CompatRules{$Level}{$Kind}{"Severity"}; + if(($Severity ne "Low" or $In::Opt{"StrictCompat"}) + and $Severity ne "Safe") + { + if(my $Sev = $TotalAffected{$Level}{$Symbol}) + { + if($Severity_Val{$Severity}>$Severity_Val{$Sev}) { + $TotalAffected{$Level}{$Symbol} = $Severity; + } + } + else { + $TotalAffected{$Level}{$Symbol} = $Severity; + } + } + + my $LSK = $CompatProblems{$Level}{$Symbol}{$Kind}; + my (@Locs1, @Locs2) = (); + foreach my $Loc (sort keys(%{$LSK})) + { + if(index($Loc, "retval")==0 or index($Loc, "this")==0) { + push(@Locs2, $Loc); + } + else { + push(@Locs1, $Loc); + } + } + + foreach my $Loc (@Locs1, @Locs2) + { + my $Type = $LSK->{$Loc}{"Type_Name"}; + my $Target = $LSK->{$Loc}{"Target"}; + + if(defined $MethodTypeIndex{$Symbol}{$Type}{$Kind}{$Target}) + { # one location for one type and target + next; + } + $MethodTypeIndex{$Symbol}{$Type}{$Kind}{$Target} = 1; + + $TypeChanges{$Level}{$Type}{$Kind}{$Loc} = $LSK->{$Loc}; + $TypeProblemsIndex{$Level}{$Type}{$Kind}{$Loc}{$Symbol} = 1; + } + } + } + } + + # clean memory + %MethodTypeIndex = (); + + $T_Problems_High = getTypeProblemsCount("High", $Level); + $T_Problems_Medium = getTypeProblemsCount("Medium", $Level); + $T_Problems_Low = getTypeProblemsCount("Low", $Level); + $T_Other = getTypeProblemsCount("Safe", $Level); + + # changed and removed public symbols + my $SCount = keys(%{$CheckedSymbols{$Level}}); + if($In::Opt{"ExtendedCheck"}) + { # don't count external_func_0 for constants + $SCount-=1; + } + if($SCount) + { + my %Weight = ( + "High" => 100, + "Medium" => 50, + "Low" => 25 + ); + foreach (keys(%{$TotalAffected{$Level}})) { + $RESULT{$Level}{"Affected"}+=$Weight{$TotalAffected{$Level}{$_}}; + } + $RESULT{$Level}{"Affected"} = $RESULT{$Level}{"Affected"}/$SCount; + } + else { + $RESULT{$Level}{"Affected"} = 0; + } + + $RESULT{$Level}{"Affected"} = showNum($RESULT{$Level}{"Affected"}); + if($RESULT{$Level}{"Affected"}>=100) { + $RESULT{$Level}{"Affected"} = 100; + } + + $RESULT{$Level}{"Problems"} += $Removed; + $RESULT{$Level}{"Problems"} += $T_Problems_High + $I_Problems_High; + $RESULT{$Level}{"Problems"} += $T_Problems_Medium + $I_Problems_Medium; + if($In::Opt{"StrictCompat"}) { + $RESULT{$Level}{"Problems"} += $T_Problems_Low + $I_Problems_Low; + } + else { + $RESULT{$Level}{"Warnings"} += $T_Problems_Low + $I_Problems_Low; + } + if($In::Opt{"WarnNewSym"}) { + $RESULT{$Level}{"Problems"} += $Added; + } + + foreach my $Constant (keys(%{$CompatProblems_Constants{$Level}})) + { + foreach my $Kind (keys(%{$CompatProblems_Constants{$Level}{$Constant}})) + { + my $Severity = $CompatRules{$Level}{$Kind}{"Severity"}; + if($Severity eq "Safe") + { + $C_Other+=1; + } + elsif($Severity eq "Low") + { + $C_Problems_Low+=1; + } + } + } + + if($C_Problems_Low) + { + if($In::Opt{"StrictCompat"}) { + $RESULT{$Level}{"Problems"} += $C_Problems_Low; + } + else { + $RESULT{$Level}{"Warnings"} += $C_Problems_Low; + } + } + if($RESULT{$Level}{"Problems"} + and $RESULT{$Level}{"Affected"}) { + $RESULT{$Level}{"Verdict"} = "incompatible"; + } + else { + $RESULT{$Level}{"Verdict"} = "compatible"; + } + + my $TotalTypes = keys(%{$CheckedTypes{$Level}}); + if(not $TotalTypes) + { # list all the types + $TotalTypes = keys(%{$TName_Tid{1}}); + } + + my $TotalSymbols = keys(%{$CheckedSymbols{$Level}}); + + if($In::Opt{"ExtendedCheck"}) { + $TotalSymbols -= keys(%ExtendedSymbols); + } + + my $AnyChanged = ($Added or $Removed or $I_Problems_High or $I_Problems_Medium or $I_Problems_Low or $T_Problems_High or + $C_Problems_Low or $T_Problems_Medium or $T_Problems_Low or $I_Other or $T_Other or $C_Other); + + my ($Arch1, $Arch2) = ($In::ABI{1}{"Arch"}, $In::ABI{2}{"Arch"}); + my ($GccV1, $GccV2) = ($In::ABI{1}{"GccVersion"}, $In::ABI{2}{"GccVersion"}); + my ($ClangV1, $ClangV2) = ($In::ABI{1}{"ClangVersion"}, $In::ABI{2}{"ClangVersion"}); + + my ($TestInfo, $TestResults, $Problem_Summary) = (); + + if($In::Opt{"ReportFormat"} eq "xml") + { # XML + # test info + $TestInfo .= " ".$In::Opt{"TargetLib"}."\n"; + $TestInfo .= " \n"; + $TestInfo .= " ".$In::Desc{1}{"Version"}."\n"; + $TestInfo .= " $Arch1\n"; + if($GccV1) { + $TestInfo .= " $GccV1\n"; + } + elsif($ClangV1) { + $TestInfo .= " $ClangV1\n"; + } + $TestInfo .= " \n"; + + $TestInfo .= " \n"; + $TestInfo .= " ".$In::Desc{2}{"Version"}."\n"; + $TestInfo .= " $Arch2\n"; + if($GccV2) { + $TestInfo .= " $GccV2\n"; + } + elsif($ClangV2) { + $TestInfo .= " $ClangV2\n"; + } + $TestInfo .= " \n"; + $TestInfo = "\n".$TestInfo."\n\n"; + + # test results + if(my @Headers = keys(%{$In::ABI{1}{"Headers"}})) + { + $TestResults .= " \n"; + foreach my $Name (sort {lc($a) cmp lc($b)} @Headers) { + $TestResults .= " ".getFilename($Name)."\n"; + } + $TestResults .= " \n"; + } + + if(my @Sources = keys(%{$In::ABI{1}{"Sources"}})) + { + $TestResults .= " \n"; + foreach my $Name (sort {lc($a) cmp lc($b)} @Sources) { + $TestResults .= " ".getFilename($Name)."\n"; + } + $TestResults .= " \n"; + } + + $TestResults .= " \n"; + foreach my $Library (sort {lc($a) cmp lc($b)} keys(%{$In::ABI{1}{"Symbols"}})) + { + $TestResults .= " $Library\n"; + } + $TestResults .= " \n"; + + $TestResults .= " ".$TotalSymbols."\n"; + $TestResults .= " ".$TotalTypes."\n"; + + $TestResults .= " ".$RESULT{$Level}{"Verdict"}."\n"; + $TestResults .= " ".$RESULT{$Level}{"Affected"}."\n"; + $TestResults = "\n".$TestResults."\n\n"; + + # problem summary + $Problem_Summary .= " ".$Added."\n"; + $Problem_Summary .= " ".$Removed."\n"; + + $Problem_Summary .= " \n"; + $Problem_Summary .= " $T_Problems_High\n"; + $Problem_Summary .= " $T_Problems_Medium\n"; + $Problem_Summary .= " $T_Problems_Low\n"; + $Problem_Summary .= " $T_Other\n"; + $Problem_Summary .= " \n"; + + $Problem_Summary .= " \n"; + $Problem_Summary .= " $I_Problems_High\n"; + $Problem_Summary .= " $I_Problems_Medium\n"; + $Problem_Summary .= " $I_Problems_Low\n"; + $Problem_Summary .= " $I_Other\n"; + $Problem_Summary .= " \n"; + + $Problem_Summary .= " \n"; + $Problem_Summary .= " $C_Problems_Low\n"; + $Problem_Summary .= " \n"; + + $Problem_Summary = "\n".$Problem_Summary."\n\n"; + + return ($TestInfo.$TestResults.$Problem_Summary, "", $AnyChanged); + } + else + { # HTML + # test info + $TestInfo = "

Test Info


\n"; + $TestInfo .= "\n"; + + if($In::Opt{"TargetComponent"} eq "library") { + $TestInfo .= "\n"; + } + else { + $TestInfo .= "\n"; + } + + my (@VInf1, @VInf2, $AddTestInfo) = (); + + # CPU arch + if($Arch1 eq $Arch2) + { # go to the separate section + $AddTestInfo .= "\n"; + } + else + { # go to the version number + push(@VInf1, showArch($Arch1)); + push(@VInf2, showArch($Arch2)); + } + + if($Level eq "Binary" + and $In::Opt{"Target"} ne "windows") + { + if($GccV1 and $GccV2) + { # GCC version + if($GccV1 eq $GccV2) + { # go to the separate section + $AddTestInfo .= "\n"; + } + else + { # go to the version number + push(@VInf1, "gcc ".$GccV1); + push(@VInf2, "gcc ".$GccV2); + } + } + elsif($ClangV1 and $ClangV2) + { # Clang version + if($ClangV1 eq $ClangV2) + { # go to the separate section + $AddTestInfo .= "\n"; + } + else + { # go to the version number + push(@VInf1, "clang ".$ClangV1); + push(@VInf2, "clang ".$ClangV2); + } + } + elsif($GccV1 and $ClangV2) + { + push(@VInf1, "gcc ".$GccV1); + push(@VInf2, "clang ".$ClangV2); + } + elsif($ClangV1 and $GccV2) + { + push(@VInf1, "clang ".$ClangV1); + push(@VInf2, "gcc ".$GccV2); + } + } + # show long version names with GCC version and CPU architecture name (if different) + $TestInfo .= "\n"; + $TestInfo .= "\n"; + $TestInfo .= $AddTestInfo; + + if($In::Opt{"ExtendedCheck"}) { + $TestInfo .= "\n"; + } + if($In::Opt{"JoinReport"}) + { + if($Level eq "Binary") { + $TestInfo .= "\n"; # Run-time + } + elsif($Level eq "Source") { + $TestInfo .= "\n"; # Build-time + } + } + $TestInfo .= "
Library Name".$In::Opt{"TargetTitle"}."
Module Name".$In::Opt{"TargetTitle"}."
Arch".showArch($Arch1)."
GCC Version$GccV1
Clang Version$ClangV1
Version #1".$In::Desc{1}{"Version"}.(@VInf1?" (".join(", ", reverse(@VInf1)).")":"")."
Version #2".$In::Desc{2}{"Version"}.(@VInf2?" (".join(", ", reverse(@VInf2)).")":"")."
ModeExtended
SubjectBinary Compatibility
SubjectSource Compatibility
\n"; + + # test results + $TestResults = "

Test Results


\n"; + $TestResults .= ""; + + if(my @Headers = getCheckedHeaders(1)) + { + my $Headers_Link = "".($#Headers + 1).""; + $TestResults .= "\n"; + } + + if(my @Sources = keys(%{$In::ABI{1}{"Sources"}})) + { + my $Src_Link = "".($#Sources + 1).""; + $TestResults .= "\n"; + } + + if(not $In::Opt{"ExtendedCheck"}) + { + my $Libs_Link = "0"; + $Libs_Link = "".keys(%{$In::ABI{1}{"Symbols"}})."" if(keys(%{$In::ABI{1}{"Symbols"}})>0); + $TestResults .= "\n"; + } + + $TestResults .= "\n"; + + my $META_DATA = "verdict:".$RESULT{$Level}{"Verdict"}.";"; + if($In::Opt{"JoinReport"}) { + $META_DATA = "kind:".lc($Level).";".$META_DATA; + } + + my $BC_Rate = showNum(100 - $RESULT{$Level}{"Affected"}); + + $TestResults .= "\n"; + if($RESULT{$Level}{"Verdict"} eq "incompatible") + { + my $Cl = "incompatible"; + if($BC_Rate>=90) { + $Cl = "warning"; + } + elsif($BC_Rate>=80) { + $Cl = "almost_compatible"; + } + + $TestResults .= "\n"; + } + else { + $TestResults .= "\n"; + } + $TestResults .= "\n"; + $TestResults .= "
Total Header Files".$Headers_Link."
Total Source Files".$Src_Link."
Total ".getObjTitle()."".($In::Opt{"CheckHeadersOnly"}?"0 (not analyzed)":$Libs_Link)."
Total Symbols / Types".$TotalSymbols." / ".$TotalTypes."
Compatibility".$BC_Rate."%100%
\n"; + + $META_DATA .= "affected:".$RESULT{$Level}{"Affected"}.";";# in percents + # problem summary + $Problem_Summary = "

Problem Summary


\n"; + $Problem_Summary .= ""; + $Problem_Summary .= ""; + + my $Added_Link = "0"; + if($Added>0) + { + if($In::Opt{"JoinReport"}) { + $Added_Link = "$Added"; + } + else { + $Added_Link = "$Added"; + } + } + $META_DATA .= "added:$Added;"; + $Problem_Summary .= "$Added_Link\n"; + + my $Removed_Link = "0"; + if($Removed>0) + { + if($In::Opt{"JoinReport"}) { + $Removed_Link = "$Removed" + } + else { + $Removed_Link = "$Removed" + } + } + $META_DATA .= "removed:$Removed;"; + $Problem_Summary .= ""; + $Problem_Summary .= "$Removed_Link\n"; + + my $TH_Link = "0"; + $TH_Link = "$T_Problems_High" if($T_Problems_High>0); + $META_DATA .= "type_problems_high:$T_Problems_High;"; + $Problem_Summary .= ""; + $Problem_Summary .= "$TH_Link\n"; + + my $TM_Link = "0"; + $TM_Link = "$T_Problems_Medium" if($T_Problems_Medium>0); + $META_DATA .= "type_problems_medium:$T_Problems_Medium;"; + $Problem_Summary .= "$TM_Link\n"; + + my $TL_Link = "0"; + $TL_Link = "$T_Problems_Low" if($T_Problems_Low>0); + $META_DATA .= "type_problems_low:$T_Problems_Low;"; + $Problem_Summary .= "$TL_Link\n"; + + my $IH_Link = "0"; + $IH_Link = "$I_Problems_High" if($I_Problems_High>0); + $META_DATA .= "interface_problems_high:$I_Problems_High;"; + $Problem_Summary .= ""; + $Problem_Summary .= "$IH_Link\n"; + + my $IM_Link = "0"; + $IM_Link = "$I_Problems_Medium" if($I_Problems_Medium>0); + $META_DATA .= "interface_problems_medium:$I_Problems_Medium;"; + $Problem_Summary .= "$IM_Link\n"; + + my $IL_Link = "0"; + $IL_Link = "$I_Problems_Low" if($I_Problems_Low>0); + $META_DATA .= "interface_problems_low:$I_Problems_Low;"; + $Problem_Summary .= "$IL_Link\n"; + + my $ChangedConstants_Link = "0"; + if(keys(%{$CheckedSymbols{$Level}}) and $C_Problems_Low) { + $ChangedConstants_Link = "$C_Problems_Low"; + } + $META_DATA .= "changed_constants:$C_Problems_Low;"; + $Problem_Summary .= "$ChangedConstants_Link\n"; + + # Safe Changes + if($T_Other) + { + my $TS_Link = "$T_Other"; + $Problem_Summary .= "$TS_Link\n"; + $META_DATA .= "type_changes_other:$T_Other;"; + } + + if($I_Other) + { + my $IS_Link = "$I_Other"; + $Problem_Summary .= "$IS_Link\n"; + $META_DATA .= "interface_changes_other:$I_Other;"; + } + + if($C_Other) + { + my $CS_Link = "$C_Other"; + $Problem_Summary .= "$CS_Link\n"; + $META_DATA .= "constant_changes_other:$C_Other;"; + } + + $META_DATA .= "tool_version:$TOOL_VERSION"; + $Problem_Summary .= "
SeverityCount
Added Symbols-
Removed SymbolsHigh
Problems with
Data Types
High
Medium
Low
Problems with
Symbols
High
Medium
Low
Problems with
Constants
Low
Other Changes
in Data Types
-
Other Changes
in Symbols
-
Other Changes
in Constants
-
\n"; + + return ($TestInfo.$TestResults.$Problem_Summary, $META_DATA, $AnyChanged); + } +} + +sub getStyle($$$) +{ + my ($Subj, $Act, $Num) = @_; + my %Style = ( + "Added"=>"new", + "Removed"=>"failed", + "Safe"=>"passed", + "Low"=>"warning", + "Medium"=>"failed", + "High"=>"failed" + ); + + if($Num>0) { + return " class='".$Style{$Act}."'"; + } + + return ""; +} + +sub getReportChangedConstants($$) +{ + my ($TargetSeverity, $Level) = @_; + my $CHANGED_CONSTANTS = ""; + + my %ReportMap = (); + foreach my $Constant (keys(%{$CompatProblems_Constants{$Level}})) + { + my $Header = $Constants{1}{$Constant}{"Header"}; + if(not $Header) { + $Header = $Constants{1}{$Constant}{"Source"}; + } + if(not $Header) + { # added + $Header = $Constants{2}{$Constant}{"Header"}; + if(not $Header) { + $Header = $Constants{2}{$Constant}{"Source"} + } + } + + foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$CompatProblems_Constants{$Level}{$Constant}})) + { + if(not defined $CompatRules{$Level}{$Kind}) { + next; + } + if($TargetSeverity ne $CompatRules{$Level}{$Kind}{"Severity"}) { + next; + } + $ReportMap{$Header}{$Constant}{$Kind} = 1; + } + } + + if($In::Opt{"ReportFormat"} eq "xml") + { # XML + foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap)) + { + $CHANGED_CONSTANTS .= "
\n"; + foreach my $Constant (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}})) + { + $CHANGED_CONSTANTS .= " \n"; + foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$Constant}})) + { + my $Change = $CompatRules{$Level}{$Kind}{"Change"}; + my $Effect = $CompatRules{$Level}{$Kind}{"Effect"}; + my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"}; + + $CHANGED_CONSTANTS .= " \n"; + $CHANGED_CONSTANTS .= " $Change\n"; + $CHANGED_CONSTANTS .= " $Effect\n"; + if($Overcome) { + $CHANGED_CONSTANTS .= " $Overcome\n"; + } + $CHANGED_CONSTANTS .= " \n"; + } + $CHANGED_CONSTANTS .= " \n"; + } + $CHANGED_CONSTANTS .= "
\n"; + } + $CHANGED_CONSTANTS = "\n".$CHANGED_CONSTANTS."\n\n"; + } + else + { # HTML + my $ProblemsNum = 0; + foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap)) + { + $CHANGED_CONSTANTS .= "$HeaderName
\n"; + foreach my $Constant (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}})) + { + my $Report = ""; + + foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$Constant}})) + { + my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $CompatProblems_Constants{$Level}{$Constant}{$Kind}); + my $Effect = $CompatRules{$Level}{$Kind}{"Effect"}; + $Report .= "\n1\n".$Change."\n$Effect\n\n"; + $ProblemsNum += 1; + } + if($Report) + { + $Report = $ContentDivStart."\n\n\n\n\n\n".$Report."
ChangeEffect
\n
\n$ContentDivEnd\n"; + $Report = $ContentSpanStart."[+] ".$Constant.$ContentSpanEnd."
\n".$Report; + $Report = insertIDs($Report); + } + $CHANGED_CONSTANTS .= $Report; + } + $CHANGED_CONSTANTS .= "
\n"; + } + if($CHANGED_CONSTANTS) + { + my $Title = "Problems with Constants, $TargetSeverity Severity"; + if($TargetSeverity eq "Safe") + { # Safe Changes + $Title = "Other Changes in Constants"; + } + if($In::Opt{"OldStyle"}) { + $CHANGED_CONSTANTS = "

$Title ($ProblemsNum)


\n".$CHANGED_CONSTANTS; + } + else { + $CHANGED_CONSTANTS = "

$Title  $ProblemsNum 


\n".$CHANGED_CONSTANTS; + } + $CHANGED_CONSTANTS = "\n".$CHANGED_CONSTANTS.$TOP_REF."
\n"; + } + } + return $CHANGED_CONSTANTS; +} + +sub getTitle($$$) +{ + my ($Header, $Library, $NameSpace) = @_; + my $Title = ""; + + if($Header and $Library) + { + $Title .= "$Header"; + $Title .= ", $Library
\n"; + } + elsif($Library) { + $Title .= "$Library
\n"; + } + elsif($Header) { + $Title .= "$Header
\n"; + } + + if($NameSpace) { + $Title .= "namespace $NameSpace
\n"; + } + + return $Title; +} + +sub getReportAdded($) +{ + my $Level = $_[0]; + my $ADDED_INTERFACES = ""; + my %ReportMap = (); + foreach my $Symbol (sort keys(%{$CompatProblems{$Level}})) + { + foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}})) + { + if($Kind eq "Added_Symbol") + { + my $HeaderName = $CompSign{2}{$Symbol}{"Header"}; + if(not $HeaderName) { + $HeaderName = $CompSign{2}{$Symbol}{"Source"}; + } + + my $DyLib = $In::ABI{2}{"SymLib"}{$Symbol}; + if($Level eq "Source" and $In::Opt{"ReportFormat"} eq "html") + { # do not show library name in the HTML report + $DyLib = ""; + } + $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1; + } + } + } + if($In::Opt{"ReportFormat"} eq "xml") + { # XML + foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap)) + { + $ADDED_INTERFACES .= "
\n"; + foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}})) + { + $ADDED_INTERFACES .= " \n"; + foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) { + $ADDED_INTERFACES .= " $Symbol\n"; + } + $ADDED_INTERFACES .= " \n"; + } + $ADDED_INTERFACES .= "
\n"; + } + $ADDED_INTERFACES = "\n".$ADDED_INTERFACES."\n\n"; + } + else + { # HTML + my $Added_Number = 0; + foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap)) + { + foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}})) + { + my %NameSpaceSymbols = (); + foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) { + $NameSpaceSymbols{selectSymbolNs($Symbol, 2)}{$Symbol} = 1; + } + foreach my $NameSpace (sort keys(%NameSpaceSymbols)) + { + $ADDED_INTERFACES .= getTitle($HeaderName, $DyLib, $NameSpace); + my @SortedInterfaces = sort {lc($CompSign{2}{$a}{"Unmangled"}) cmp lc($CompSign{2}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$NameSpaceSymbols{$NameSpace}}); + foreach my $Symbol (@SortedInterfaces) + { + $Added_Number += 1; + my $Signature = highLight_ItalicColor($Symbol, 2); + if($NameSpace) { + $Signature = cutNs($Signature, $NameSpace); + } + + if($Symbol=~/\A(_Z|\?)/) { + $ADDED_INTERFACES .= insertIDs($ContentSpanStart.$Signature.$ContentSpanEnd."
\n".$ContentDivStart."$Symbol\n
\n
\n".$ContentDivEnd."\n"); + } + else { + $ADDED_INTERFACES .= "".$Signature."
\n"; + } + } + $ADDED_INTERFACES .= "
\n"; + } + } + } + if($ADDED_INTERFACES) + { + my $Anchor = ""; + if($In::Opt{"JoinReport"}) { + $Anchor = ""; + } + if($In::Opt{"OldStyle"}) { + $ADDED_INTERFACES = "

Added Symbols ($Added_Number)


\n".$ADDED_INTERFACES; + } + else { + $ADDED_INTERFACES = "

Added Symbols  $Added_Number 


\n".$ADDED_INTERFACES; + } + $ADDED_INTERFACES = $Anchor.$ADDED_INTERFACES.$TOP_REF."
\n"; + } + } + return $ADDED_INTERFACES; +} + +sub getReportRemoved($) +{ + my $Level = $_[0]; + my $REMOVED_INTERFACES = ""; + my %ReportMap = (); + foreach my $Symbol (sort keys(%{$CompatProblems{$Level}})) + { + foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}})) + { + if($Kind eq "Removed_Symbol") + { + my $HeaderName = $CompSign{1}{$Symbol}{"Header"}; + if(not $HeaderName) { + $HeaderName = $CompSign{1}{$Symbol}{"Source"}; + } + + my $DyLib = $In::ABI{1}{"SymLib"}{$Symbol}; + if($Level eq "Source" and $In::Opt{"ReportFormat"} eq "html") + { # do not show library name in HTML report + $DyLib = ""; + } + $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1; + } + } + } + if($In::Opt{"ReportFormat"} eq "xml") + { # XML + foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap)) + { + $REMOVED_INTERFACES .= "
\n"; + foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}})) + { + $REMOVED_INTERFACES .= " \n"; + foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) { + $REMOVED_INTERFACES .= " $Symbol\n"; + } + $REMOVED_INTERFACES .= " \n"; + } + $REMOVED_INTERFACES .= "
\n"; + } + $REMOVED_INTERFACES = "\n".$REMOVED_INTERFACES."\n\n"; + } + else + { # HTML + my $Removed_Number = 0; + foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap)) + { + foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}})) + { + my %NameSpaceSymbols = (); + foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) { + $NameSpaceSymbols{selectSymbolNs($Interface, 1)}{$Interface} = 1; + } + foreach my $NameSpace (sort keys(%NameSpaceSymbols)) + { + $REMOVED_INTERFACES .= getTitle($HeaderName, $DyLib, $NameSpace); + my @SortedInterfaces = sort {lc($CompSign{1}{$a}{"Unmangled"}) cmp lc($CompSign{1}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$NameSpaceSymbols{$NameSpace}}); + foreach my $Symbol (@SortedInterfaces) + { + $Removed_Number += 1; + my $Signature = highLight_ItalicColor($Symbol, 1); + if($NameSpace) { + $Signature = cutNs($Signature, $NameSpace); + } + if($Symbol=~/\A(_Z|\?)/) { + $REMOVED_INTERFACES .= insertIDs($ContentSpanStart.$Signature.$ContentSpanEnd."
\n".$ContentDivStart."$Symbol\n
\n
\n".$ContentDivEnd."\n"); + } + else { + $REMOVED_INTERFACES .= "".$Signature."
\n"; + } + } + } + $REMOVED_INTERFACES .= "
\n"; + } + } + if($REMOVED_INTERFACES) + { + my $Anchor = ""; + if($In::Opt{"JoinReport"}) { + $Anchor = ""; + } + if($In::Opt{"OldStyle"}) { + $REMOVED_INTERFACES = "

Removed Symbols ($Removed_Number)


\n".$REMOVED_INTERFACES; + } + else { + $REMOVED_INTERFACES = "

Removed Symbols  $Removed_Number 


\n".$REMOVED_INTERFACES; + } + + $REMOVED_INTERFACES = $Anchor.$REMOVED_INTERFACES.$TOP_REF."
\n"; + } + } + return $REMOVED_INTERFACES; +} + +sub getXmlParams($$) +{ + my ($Content, $Problem) = @_; + + my %XMLparams = (); + foreach my $Attr (sort {$b cmp $a} keys(%{$Problem})) + { + my $Macro = "\@".lc($Attr); + if($Content=~/\Q$Macro\E/) + { + my $Value = $Problem->{$Attr}; + + if($Attr eq "Param_Pos") { + $Value = showPos($Value); + } + + $XMLparams{lc($Attr)} = $Value; + } + } + my @PString = (); + foreach my $P (sort {$b cmp $a} keys(%XMLparams)) { + push(@PString, $P."=\"".xmlSpecChars($XMLparams{$P})."\""); + } + if(@PString) { + return " ".join(" ", @PString); + } + return ""; +} + +sub addMarkup($) +{ + my $Content = $_[0]; + + # auto-markup + $Content=~s/\n[ ]*//; # spaces + $Content=~s!(\@\w+\s*\(\@\w+\))!$1!g; # @old_type (@old_size) + $Content=~s!(... \(\w+\))!$1!g; # ... (va_list) + $Content=~s!(.+?)!$1!g; + $Content=~s!([2-9]\))!
$1!g; # 1), 2), ... + + if($Content=~/\ANOTE:/) + { # notes + $Content=~s!(NOTE):!$1:!g; + } + else { + $Content=~s!(NOTE):!

$1:!g; + } + $Content=~s! (out)-! $1-!g; # out-parameters + + my @Keywords = ( + "void", + "const", + "static", + "restrict", + "volatile", + "register", + "virtual" + ); + my $MKeys = join("|", @Keywords); + foreach (@Keywords) { + $MKeys .= "|non-".$_; + } + $Content=~s!(added\s*|to\s*|from\s*|became\s*)($MKeys)([^\w-]|\Z)!$1$2$3!ig; # intrinsic types, modifiers + + # Markdown + $Content=~s!\*\*([\w\-]+?)\*\*!$1!ig; + $Content=~s!\*([\w\-]+?)\*!$1!ig; + + return $Content; +} + +sub applyMacroses($$$$) +{ + my ($Level, $Kind, $Content, $Problem) = @_; + + $Problem->{"Word_Size"} = $In::ABI{2}{"WordSize"}; + $Content = addMarkup($Content); + + # macros + foreach my $Attr (sort {$b cmp $a} keys(%{$Problem})) + { + my $Macro = "\@".lc($Attr); + my $Value = $Problem->{$Attr}; + if(not defined $Value + or $Value eq "") { + next; + } + + if(index($Content, $Macro)==-1) { + next; + } + + if($Attr eq "Param_Pos") { + $Value = showPos($Value); + } + + if($Value=~/\s/) { + $Value = "".specChars($Value).""; + } + elsif($Value=~/\A\d+\Z/ + and ($Attr eq "Old_Size" or $Attr eq "New_Size")) + { # bits to bytes + if($Value % $BYTE) + { # bits + if($Value==1) { + $Value = "".$Value." bit"; + } + else { + $Value = "".$Value." bits"; + } + } + else + { # bytes + $Value /= $BYTE; + if($Value==1) { + $Value = "".$Value." byte"; + } + else { + $Value = "".$Value." bytes"; + } + } + } + else + { + my $Fmt = "Class|Name|Qual|HTML|Desc"; + if($Kind!~/Overridden/) { + $Fmt = "Name|Qual|HTML|Desc"; + } + + my $V1 = (defined $CompSign{1}{$Value} and defined $CompSign{1}{$Value}{"ShortName"}); + my $V2 = (defined $CompSign{2}{$Value} and defined $CompSign{2}{$Value}{"ShortName"}); + + if($Kind!~/Symbol_Became|Symbol_Changed|Method_Became/ + and ($V1 or $V2)) + { # symbols + if($V1) { + $Value = blackName(getSignature($Value, 1, $Fmt)); + } + else { + $Value = blackName(getSignature($Value, 2, $Fmt)); + } + } + else + { + $Value = "".specChars($Value).""; + } + } + $Content=~s/\Q$Macro\E/$Value/g; + } + + if($Content=~/(\A|[^\@\w])\@\w/) + { + if(not $IncompleteRules{$Level}{$Kind}) + { # only one warning + printMsg("WARNING", "incomplete rule \"$Kind\" (\"$Level\")"); + $IncompleteRules{$Level}{$Kind} = 1; + } + } + return $Content; +} + +sub getReportSymbolProblems($$) +{ + my ($TargetSeverity, $Level) = @_; + my $INTERFACE_PROBLEMS = ""; + my (%ReportMap, %SymbolChanges) = (); + + foreach my $Symbol (sort keys(%{$CompatProblems{$Level}})) + { + my ($SN, $SS, $SV) = symbolParts($Symbol); + if($SV and defined $CompatProblems{$Level}{$SN}) { + next; + } + + if(not defined $CompSign{1}{$Symbol}) + { # added symbols + next; + } + + my $HeaderName = $CompSign{1}{$Symbol}{"Header"}; + if(not $HeaderName) { + $HeaderName = $CompSign{1}{$Symbol}{"Source"}; + } + + my $DyLib = $In::ABI{1}{"SymLib"}{$Symbol}; + if(not $DyLib and my $VSym = $In::ABI{1}{"SymbolVersion"}{$Symbol}) + { # Symbol with Version + $DyLib = $In::ABI{1}{"SymLib"}{$VSym}; + } + if(not $DyLib) + { # const global data + $DyLib = ""; + } + if($Level eq "Source" and $In::Opt{"ReportFormat"} eq "html") + { # do not show library name in HTML report + $DyLib = ""; + } + + foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}})) + { + if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols" + and $Kind ne "Added_Symbol" and $Kind ne "Removed_Symbol") + { + my $Severity = $CompatRules{$Level}{$Kind}{"Severity"}; + if($Severity eq $TargetSeverity) + { + $SymbolChanges{$Symbol}{$Kind} = $CompatProblems{$Level}{$Symbol}{$Kind}; + $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1; + } + } + } + } + + if($In::Opt{"ReportFormat"} eq "xml") + { # XML + foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap)) + { + $INTERFACE_PROBLEMS .= "
\n"; + foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}})) + { + $INTERFACE_PROBLEMS .= " \n"; + + my @SortedInterfaces = sort {lc($CompSign{1}{$a}{"Unmangled"}) cmp lc($CompSign{1}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$DyLib}}); + foreach my $Symbol (@SortedInterfaces) + { + $INTERFACE_PROBLEMS .= " \n"; + foreach my $Kind (sort keys(%{$SymbolChanges{$Symbol}})) + { + foreach my $Loc (sort keys(%{$SymbolChanges{$Symbol}{$Kind}})) + { + my $ProblemAttr = $SymbolChanges{$Symbol}{$Kind}{$Loc}; + + $INTERFACE_PROBLEMS .= " \n"; + my $Change = $CompatRules{$Level}{$Kind}{"Change"}; + $INTERFACE_PROBLEMS .= " $Change\n"; + my $Effect = $CompatRules{$Level}{$Kind}{"Effect"}; + $INTERFACE_PROBLEMS .= " $Effect\n"; + if(my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"}) { + $INTERFACE_PROBLEMS .= " $Overcome\n"; + } + $INTERFACE_PROBLEMS .= " \n"; + } + } + $INTERFACE_PROBLEMS .= " \n"; + } + $INTERFACE_PROBLEMS .= " \n"; + } + $INTERFACE_PROBLEMS .= "
\n"; + } + $INTERFACE_PROBLEMS = "\n".$INTERFACE_PROBLEMS."\n\n"; + } + else + { # HTML + my $ProblemsNum = 0; + foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap)) + { + foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}})) + { + my (%NameSpaceSymbols, %NewSignature) = (); + foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) { + $NameSpaceSymbols{selectSymbolNs($Symbol, 1)}{$Symbol} = 1; + } + foreach my $NameSpace (sort keys(%NameSpaceSymbols)) + { + $INTERFACE_PROBLEMS .= getTitle($HeaderName, $DyLib, $NameSpace); + + my @SortedInterfaces = sort {lc($CompSign{1}{$a}{"Unmangled"}) cmp lc($CompSign{1}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$NameSpaceSymbols{$NameSpace}}); + foreach my $Symbol (@SortedInterfaces) + { + my $SYMBOL_REPORT = ""; + my $ProblemNum = 1; + foreach my $Kind (sort keys(%{$SymbolChanges{$Symbol}})) + { + foreach my $Loc (sort keys(%{$SymbolChanges{$Symbol}{$Kind}})) + { + my $ProblemAttr = $SymbolChanges{$Symbol}{$Kind}{$Loc}; + + if(my $NSign = $ProblemAttr->{"New_Signature"}) { + $NewSignature{$Symbol} = $NSign; + } + + if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $ProblemAttr)) + { + my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, $ProblemAttr); + $SYMBOL_REPORT .= "\n$ProblemNum\n".$Change."\n".$Effect."\n\n"; + $ProblemNum += 1; + $ProblemsNum += 1; + } + } + } + $ProblemNum -= 1; + if($SYMBOL_REPORT) + { + my $ShowSymbol = highLight_ItalicColor($Symbol, 1); + + if($NameSpace) + { + $SYMBOL_REPORT = cutNs($SYMBOL_REPORT, $NameSpace); + $ShowSymbol = cutNs($ShowSymbol, $NameSpace); + } + + $INTERFACE_PROBLEMS .= $ContentSpanStart."[+] ".$ShowSymbol; + if($In::Opt{"OldStyle"}) { + $INTERFACE_PROBLEMS .= " ($ProblemNum)"; + } + else { + $INTERFACE_PROBLEMS .= "  $ProblemNum 
"; + } + $INTERFACE_PROBLEMS .= $ContentSpanEnd."
\n"; + $INTERFACE_PROBLEMS .= $ContentDivStart."\n"; + + if(my $NSign = $NewSignature{$Symbol}) + { # argument list changed to + $NSign = highLight_ItalicColor($NSign, 2); + if($NameSpace) { + $NSign = cutNs($NSign, $NameSpace); + } + $INTERFACE_PROBLEMS .= "\n\n
\n".$NSign."
\n"; + } + + if($Symbol=~/\A(_Z|\?)/) { + $INTERFACE_PROBLEMS .= "$Symbol
\n"; + } + + $INTERFACE_PROBLEMS .= "\n\n\n\n\n\n$SYMBOL_REPORT
ChangeEffect
\n
\n"; + $INTERFACE_PROBLEMS .= $ContentDivEnd; + } + } + $INTERFACE_PROBLEMS .= "
\n"; + } + } + } + + if($INTERFACE_PROBLEMS) + { + $INTERFACE_PROBLEMS = insertIDs($INTERFACE_PROBLEMS); + my $Title = "Problems with Symbols, $TargetSeverity Severity"; + if($TargetSeverity eq "Safe") + { # Safe Changes + $Title = "Other Changes in Symbols"; + } + if($In::Opt{"OldStyle"}) { + $INTERFACE_PROBLEMS = "

$Title ($ProblemsNum)


\n".$INTERFACE_PROBLEMS; + } + else { + $INTERFACE_PROBLEMS = "

$Title  $ProblemsNum 


\n".$INTERFACE_PROBLEMS; + } + $INTERFACE_PROBLEMS = "\n".$INTERFACE_PROBLEMS.$TOP_REF."
\n"; + } + } + return $INTERFACE_PROBLEMS; +} + +sub cutNs($$) +{ + my ($N, $Ns) = @_; + $N=~s/\b\Q$Ns\E:://g; + return $N; +} + +sub getReportTypeProblems($$) +{ + my ($TargetSeverity, $Level) = @_; + my $TYPE_PROBLEMS = ""; + + my %ReportMap = (); + my %TypeChanges_Sev = (); + + foreach my $TypeName (keys(%{$TypeChanges{$Level}})) + { + my $Tid = $TName_Tid{1}{$TypeName}; + my $HeaderName = $TypeInfo{1}{$Tid}{"Header"}; + if(not $HeaderName) { + $HeaderName = $TypeInfo{1}{$Tid}{"Source"}; + } + + foreach my $Kind (keys(%{$TypeChanges{$Level}{$TypeName}})) + { + if($CompatRules{$Level}{$Kind}{"Severity"} ne $TargetSeverity) { + next; + } + + foreach my $Loc (keys(%{$TypeChanges{$Level}{$TypeName}{$Kind}})) + { + $ReportMap{$HeaderName}{$TypeName} = 1; + $TypeChanges_Sev{$TypeName}{$Kind}{$Loc} = $TypeChanges{$Level}{$TypeName}{$Kind}{$Loc}; + } + } + } + + if($In::Opt{"ReportFormat"} eq "xml") + { # XML + foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap)) + { + $TYPE_PROBLEMS .= "
\n"; + foreach my $TypeName (keys(%{$ReportMap{$HeaderName}})) + { + my (%Kinds_Locations, %Kinds_Target) = (); + $TYPE_PROBLEMS .= " \n"; + foreach my $Kind (sort {$b=~/Size/ <=> $a=~/Size/} sort keys(%{$TypeChanges_Sev{$TypeName}})) + { + foreach my $Loc (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges_Sev{$TypeName}{$Kind}})) + { + $Kinds_Locations{$Kind}{$Loc} = 1; + + my $Target = $TypeChanges_Sev{$TypeName}{$Kind}{$Loc}{"Target"}; + if($Kinds_Target{$Kind}{$Target}) { + next; + } + $Kinds_Target{$Kind}{$Target} = 1; + + my $ProblemAttr = $TypeChanges_Sev{$TypeName}{$Kind}{$Loc}; + $TYPE_PROBLEMS .= " \n"; + my $Change = $CompatRules{$Level}{$Kind}{"Change"}; + $TYPE_PROBLEMS .= " $Change\n"; + my $Effect = $CompatRules{$Level}{$Kind}{"Effect"}; + $TYPE_PROBLEMS .= " $Effect\n"; + if(my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"}) { + $TYPE_PROBLEMS .= " $Overcome\n"; + } + $TYPE_PROBLEMS .= " \n"; + } + } + $TYPE_PROBLEMS .= getAffectedSymbols($Level, $TypeName, \%Kinds_Locations); + if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%Kinds_Locations)) { + $TYPE_PROBLEMS .= showVTables($TypeName); + } + $TYPE_PROBLEMS .= " \n"; + } + $TYPE_PROBLEMS .= "
\n"; + } + $TYPE_PROBLEMS = "\n".$TYPE_PROBLEMS."\n\n"; + } + else + { # HTML + my $ProblemsNum = 0; + foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap)) + { + my (%NameSpace_Type) = (); + foreach my $TypeName (keys(%{$ReportMap{$HeaderName}})) { + $NameSpace_Type{selectTypeNs($TypeName, 1)}{$TypeName} = 1; + } + foreach my $NameSpace (sort keys(%NameSpace_Type)) + { + $TYPE_PROBLEMS .= getTitle($HeaderName, "", $NameSpace); + my @SortedTypes = sort {lc(showType($a, 0, 1)) cmp lc(showType($b, 0, 1))} keys(%{$NameSpace_Type{$NameSpace}}); + foreach my $TypeName (@SortedTypes) + { + my $ProblemNum = 1; + my $TYPE_REPORT = ""; + my (%Kinds_Locations, %Kinds_Target) = (); + + foreach my $Kind (sort {(index($b, "Size")!=-1) cmp (index($a, "Size")!=-1)} sort keys(%{$TypeChanges_Sev{$TypeName}})) + { + foreach my $Loc (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges_Sev{$TypeName}{$Kind}})) + { + $Kinds_Locations{$Kind}{$Loc} = 1; + + my $Target = $TypeChanges_Sev{$TypeName}{$Kind}{$Loc}{"Target"}; + if($Kinds_Target{$Kind}{$Target}) { + next; + } + $Kinds_Target{$Kind}{$Target} = 1; + + my $ProblemAttr = $TypeChanges_Sev{$TypeName}{$Kind}{$Loc}; + if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $ProblemAttr)) + { + my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, $ProblemAttr); + $TYPE_REPORT .= "\n$ProblemNum\n".$Change."\n$Effect\n\n"; + $ProblemNum += 1; + $ProblemsNum += 1; + } + } + } + $ProblemNum -= 1; + if($TYPE_REPORT) + { + my $Affected = getAffectedSymbols($Level, $TypeName, \%Kinds_Locations); + my $ShowVTables = ""; + if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%Kinds_Locations)) { + $ShowVTables = showVTables($TypeName); + } + + my $ShowType = showType($TypeName, 1, 1); + + if($NameSpace) + { + $TYPE_REPORT = cutNs($TYPE_REPORT, $NameSpace); + $ShowType = cutNs($ShowType, $NameSpace); + $Affected = cutNs($Affected, $NameSpace); + $ShowVTables = cutNs($ShowVTables, $NameSpace); + } + + $TYPE_PROBLEMS .= $ContentSpanStart."[+] ".$ShowType; + if($In::Opt{"OldStyle"}) { + $TYPE_PROBLEMS .= " ($ProblemNum)"; + } + else { + $TYPE_PROBLEMS .= "  $ProblemNum 
"; + } + $TYPE_PROBLEMS .= $ContentSpanEnd; + $TYPE_PROBLEMS .= "
\n".$ContentDivStart."\n"; + $TYPE_PROBLEMS .= "\n\n"; + $TYPE_PROBLEMS .= "".$TYPE_REPORT."
ChangeEffect
\n"; + $TYPE_PROBLEMS .= $ShowVTables.$Affected."

".$ContentDivEnd."\n"; + } + } + $TYPE_PROBLEMS .= "
\n"; + } + } + + if($TYPE_PROBLEMS) + { + $TYPE_PROBLEMS = insertIDs($TYPE_PROBLEMS); + my $Title = "Problems with Data Types, $TargetSeverity Severity"; + if($TargetSeverity eq "Safe") + { # Safe Changes + $Title = "Other Changes in Data Types"; + } + if($In::Opt{"OldStyle"}) { + $TYPE_PROBLEMS = "

$Title ($ProblemsNum)


\n".$TYPE_PROBLEMS; + } + else { + $TYPE_PROBLEMS = "

$Title  $ProblemsNum 


\n".$TYPE_PROBLEMS; + } + $TYPE_PROBLEMS = "\n".$TYPE_PROBLEMS.$TOP_REF."
\n"; + } + } + return $TYPE_PROBLEMS; +} + +sub showType($$$) +{ + my ($Name, $Html, $LVer) = @_; + my $TType = $TypeInfo{$LVer}{$TName_Tid{$LVer}{$Name}}{"Type"}; + $TType = lc($TType); + if($TType=~/struct|union|enum/) { + $Name=~s/\A\Q$TType\E //g; + } + if($Html) { + $Name = "".$TType." ".specChars($Name); + } + else { + $Name = $TType." ".$Name; + } + return $Name; +} + +sub getAnchor($$$) +{ + my ($Kind, $Level, $Severity) = @_; + if($In::Opt{"JoinReport"}) + { + if($Severity eq "Safe") { + return "Other_".$Level."_Changes_In_".$Kind."s"; + } + else { + return $Kind."_".$Level."_Problems_".$Severity; + } + } + else + { + if($Severity eq "Safe") { + return "Other_Changes_In_".$Kind."s"; + } + else { + return $Kind."_Problems_".$Severity; + } + } +} + +sub showVTables($) +{ + my $TypeName = $_[0]; + my $TypeId1 = $TName_Tid{1}{$TypeName}; + my %Type1 = getType($TypeId1, 1); + if(defined $Type1{"VTable"} + and keys(%{$Type1{"VTable"}})) + { + my $TypeId2 = $TName_Tid{2}{$TypeName}; + my %Type2 = getType($TypeId2, 2); + if(defined $Type2{"VTable"} + and keys(%{$Type2{"VTable"}})) + { + my %Indexes = map {$_=>1} (keys(%{$Type1{"VTable"}}), keys(%{$Type2{"VTable"}})); + my %Entries = (); + foreach my $Index (sort {$a<=>$b} (keys(%Indexes))) + { + $Entries{$Index}{"E1"} = simpleVEntry($Type1{"VTable"}{$Index}); + $Entries{$Index}{"E2"} = simpleVEntry($Type2{"VTable"}{$Index}); + } + my $VTABLES = ""; + if($In::Opt{"ReportFormat"} eq "xml") + { # XML + $VTABLES .= " \n"; + foreach my $Index (sort {$a<=>$b} (keys(%Entries))) + { + $VTABLES .= " \n"; + $VTABLES .= " ".xmlSpecChars($Entries{$Index}{"E1"})."\n"; + $VTABLES .= " ".xmlSpecChars($Entries{$Index}{"E2"})."\n"; + $VTABLES .= " \n"; + } + $VTABLES .= " \n\n"; + } + else + { # HTML + $VTABLES .= ""; + $VTABLES .= ""; + $VTABLES .= ""; + $VTABLES .= ""; + foreach my $Index (sort {$a<=>$b} (keys(%Entries))) + { + my ($Color1, $Color2) = ("", ""); + + my $E1 = $Entries{$Index}{"E1"}; + my $E2 = $Entries{$Index}{"E2"}; + + if($E1 ne $E2 + and $E1!~/ 0x/ + and $E2!~/ 0x/) + { + if($Entries{$Index}{"E1"}) + { + $Color1 = " class='failed'"; + $Color2 = " class='failed'"; + } + else { + $Color2 = " class='warning'"; + } + } + $VTABLES .= "\n"; + $VTABLES .= "".specChars($Entries{$Index}{"E1"})."\n"; + $VTABLES .= "".specChars($Entries{$Index}{"E2"})."\n"; + } + $VTABLES .= "
OffsetVirtual Table (Old) - ".(keys(%{$Type1{"VTable"}}))." entriesVirtual Table (New) - ".(keys(%{$Type2{"VTable"}}))." entries
".$Index."

\n"; + $VTABLES = $ContentDivStart.$VTABLES.$ContentDivEnd; + $VTABLES = $ContentSpanStart_Info."[+] show v-table (old and new)".$ContentSpanEnd."
\n".$VTABLES; + } + return $VTABLES; + } + } + return ""; +} + +sub simpleVEntry($) +{ + my $VEntry = $_[0]; + if(not defined $VEntry + or $VEntry eq "") { + return ""; + } + + $VEntry=~s/ \[.+?\]\Z//; # support for ABI Dumper + $VEntry=~s/\A(.+)::(_ZThn.+)\Z/$2/; # thunks + $VEntry=~s/_ZTI\w+/typeinfo/g; # typeinfo + if($VEntry=~/\A_ZThn.+\Z/) { + $VEntry = "non-virtual thunk"; + } + $VEntry=~s/\A\(int \(\*\)\(...\)\)\s*([a-z_])/$1/i; + # support for old GCC versions + $VEntry=~s/\A0u\Z/(int (*)(...))0/; + $VEntry=~s/\A4294967268u\Z/(int (*)(...))-0x000000004/; + $VEntry=~s/\A&_Z\Z/& _Z/; + $VEntry=~s/([^:]+)::\~([^:]+)\Z/~$1/; # destructors + return $VEntry; +} + +sub adjustParamPos($$$) +{ + my ($Pos, $Symbol, $LVer) = @_; + if(defined $CompSign{$LVer}{$Symbol}) + { + if(not $CompSign{$LVer}{$Symbol}{"Static"} + and $CompSign{$LVer}{$Symbol}{"Class"}) + { + return $Pos-1; + } + + return $Pos; + } + + return undef; +} + +sub getParamPos($$$) +{ + my ($Name, $Symbol, $LVer) = @_; + + if(defined $CompSign{$LVer}{$Symbol} + and defined $CompSign{$LVer}{$Symbol}{"Param"}) + { + my $Info = $CompSign{$LVer}{$Symbol}; + foreach (keys(%{$Info->{"Param"}})) + { + if($Info->{"Param"}{$_}{"name"} eq $Name) + { + return $_; + } + } + } + + return undef; +} + +sub getParamName($) +{ + my $Loc = $_[0]; + $Loc=~s/\->.*//g; + return $Loc; +} + +sub getAffectedSymbols($$$) +{ + my ($Level, $Target_TypeName, $Kinds_Locations) = @_; + + my $LIMIT = 10; + if(defined $In::Opt{"AffectLimit"}) { + $LIMIT = $In::Opt{"AffectLimit"}; + } + + my %SymSel = (); + + foreach my $Kind (sort keys(%{$Kinds_Locations})) + { + my @Locs = sort {(index($a, "retval")!=-1) cmp (index($b, "retval")!=-1)} sort {length($a)<=>length($b)} sort keys(%{$Kinds_Locations->{$Kind}}); + + foreach my $Loc (@Locs) + { + foreach my $Symbol (keys(%{$TypeProblemsIndex{$Level}{$Target_TypeName}{$Kind}{$Loc}})) + { + if(index($Symbol, "_Z")==0 + and $Symbol=~/(C4|C2|D4|D2|D0)[EI]/) + { # duplicated problems for C2/C4 constructors, D2/D4 and D0 destructors + next; + } + + if(index($Symbol, "\@")!=-1 + or index($Symbol, "\$")!=-1) + { + my ($SN, $SS, $SV) = symbolParts($Symbol); + + if($Level eq "Source") + { # remove symbol version + $Symbol = $SN; + } + + if($SV and defined $CompatProblems{$Level}{$SN}) + { # duplicated problems for versioned symbols + next; + } + } + + if(not defined $SymSel{$Symbol}) + { + $SymSel{$Symbol}{"Kind"} = $Kind; + $SymSel{$Symbol}{"Loc"} = $Loc; + } + } + } + } + + my $Affected = ""; + my $SNum = 0; + + if($In::Opt{"ReportFormat"} eq "xml") + { # XML + $Affected .= " \n"; + + foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymSel)) + { + my $Kind = $SymSel{$Symbol}{"Kind"}; + my $Loc = $SymSel{$Symbol}{"Loc"}; + + my $PName = getParamName($Loc); + my $Des = getAffectDesc($Level, $Symbol, $Kind, $Loc); + + my $Target = ""; + if($PName) + { + $Target .= " param=\"$PName\""; + $Des=~s/parameter $PName /parameter \@param /; + } + elsif($Loc=~/\Aretval(\-|\Z)/i) { + $Target .= " affected=\"retval\""; + } + elsif($Loc=~/\Athis(\-|\Z)/i) { + $Target .= " affected=\"this\""; + } + + if($Des=~s/\AField ([^\s]+) /Field \@field /) { + $Target .= " field=\"$1\""; + } + + $Affected .= " \n"; + $Affected .= " ".xmlSpecChars($Des)."\n"; + $Affected .= " \n"; + + if(++$SNum>=$LIMIT) { + last; + } + } + $Affected .= " \n"; + } + else + { # HTML + foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymSel)) + { + my $Kind = $SymSel{$Symbol}{"Kind"}; + my $Loc = $SymSel{$Symbol}{"Loc"}; + + my $Des = getAffectDesc($Level, $Symbol, $Kind, $Loc); + my $PName = getParamName($Loc); + my $Pos = adjustParamPos(getParamPos($PName, $Symbol, 1), $Symbol, 1); + + $Affected .= "".getSignature($Symbol, 1, "Class|Name|Param|HTML|Italic|Target=".$Pos)."
\n"; + $Affected .= "
".specChars($Des)."
\n"; + + if(++$SNum>=$LIMIT) { + last; + } + } + + my $Total = keys(%SymSel); + + if($Total>$LIMIT) { + $Affected .= " ...\n
\n"; # and others ... + } + + $Affected = "
".$Affected."
\n"; + if($Affected) + { + + my $Per = showNum($Total*100/keys(%{$CheckedSymbols{$Level}})); + $Affected = $ContentDivStart.$Affected.$ContentDivEnd; + $Affected = $ContentSpanStart_Affected."[+] affected symbols: $Total ($Per\%)".$ContentSpanEnd.$Affected; + } + } + + return $Affected; +} + +sub cmpLocations($$) +{ + my ($L1, $L2) = @_; + if((index($L2, "retval")==0 or index($L2, "this")==0) + and (index($L1, "retval")!=0 and index($L1, "this")!=0)) + { + if(index($L1, "->")==-1) { + return 1; + } + elsif(index($L2, "->")!=-1) { + return 1; + } + } + return 0; +} + +sub getAffectDesc($$$$) +{ + my ($Level, $Symbol, $Kind, $Loc) = @_; + + if($ExtendedSymbols{$Symbol}) { + return "This is a symbol from an external library that may use subject library and change the ABI after recompiling."; + } + + my $PAttr = $CompatProblems{$Level}{$Symbol}{$Kind}{$Loc}; + + $Loc=~s/\A(.*)\-\>(.+?)\Z/$1/; # without the latest affected field + + my @Sentence = (); + + if($Kind eq "Overridden_Virtual_Method" + or $Kind eq "Overridden_Virtual_Method_B") { + push(@Sentence, "The method '".$PAttr->{"New_Value"}."' will be called instead of this method."); + } + elsif($CompatRules{$Level}{$Kind}{"Kind"} eq "Types") + { + my %SymInfo = %{$CompSign{1}{$Symbol}}; + + if($Loc eq "this" or $Kind=~/(\A|_)Virtual(_|\Z)/) + { + my $MType = "method"; + if($SymInfo{"Constructor"}) { + $MType = "constructor"; + } + elsif($SymInfo{"Destructor"}) { + $MType = "destructor"; + } + + my $ClassName = $TypeInfo{1}{$SymInfo{"Class"}}{"Name"}; + + if($ClassName eq $PAttr->{"Type_Name"}) { + push(@Sentence, "This $MType is from \'".$PAttr->{"Type_Name"}."\' class."); + } + else { + push(@Sentence, "This $MType is from derived class \'".$ClassName."\'."); + } + } + else + { + my $TypeID = undef; + + if($Loc=~/retval/) + { # return value + if(index($Loc, "->")!=-1) { + push(@Sentence, "Field \'".$Loc."\' in the return value"); + } + else { + push(@Sentence, "Return value"); + } + + $TypeID = $SymInfo{"Return"}; + } + elsif($Loc=~/this/) + { # "this" pointer + if(index($Loc, "->")!=-1) { + push(@Sentence, "Field \'".$Loc."\' in the object of this method"); + } + else { + push(@Sentence, "\'this\' pointer"); + } + + $TypeID = $SymInfo{"Class"}; + } + else + { # parameters + my $PName = getParamName($Loc); + my $PPos = getParamPos($PName, $Symbol, 1); + + if(index($Loc, "->")!=-1) { + push(@Sentence, "Field \'".$Loc."\' in ".showPos(adjustParamPos($PPos, $Symbol, 1))." parameter"); + } + else { + push(@Sentence, showPos(adjustParamPos($PPos, $Symbol, 1))." parameter"); + } + if($PName) { + push(@Sentence, "\'".$PName."\'"); + } + + $TypeID = $SymInfo{"Param"}{$PPos}{"type"}; + } + + if($Loc!~/this/) + { + if(my %PureType = getPureType($TypeID, 1)) + { + if($PureType{"Type"} eq "Pointer") { + push(@Sentence, "(pointer)"); + } + elsif($PureType{"Type"} eq "Ref") { + push(@Sentence, "(reference)"); + } + } + } + + if($Loc eq "this") { + push(@Sentence, "has base type \'".$PAttr->{"Type_Name"}."\'."); + } + else + { + my $Loc_T = $Loc; + $Loc_T=~s/\A\w+(\->|\Z)//; # location in type + + my $TypeID_Problem = $TypeID; + if($Loc_T) { + $TypeID_Problem = getFieldType($Loc_T, $TypeID, 1); + } + + if($TypeInfo{1}{$TypeID_Problem}{"Name"} eq $PAttr->{"Type_Name"}) { + push(@Sentence, "is of type \'".$PAttr->{"Type_Name"}."\'."); + } + else { + push(@Sentence, "has base type \'".$PAttr->{"Type_Name"}."\'."); + } + } + } + } + + my $Sent = join(" ", @Sentence); + + $Sent=~s/->/./g; + + if($In::Opt{"ReportFormat"} eq "xml") { + $Sent=~s/'//g; + } + + return $Sent; +} + +sub getFieldType($$$) +{ + my ($Loc, $TypeId, $LVer) = @_; + + my @Fields = split(/\->/, $Loc); + + foreach my $Name (@Fields) + { + my %Info = getBaseType($TypeId, $LVer); + + foreach my $Pos (keys(%{$Info{"Memb"}})) + { + if($Info{"Memb"}{$Pos}{"name"} eq $Name) + { + $TypeId = $Info{"Memb"}{$Pos}{"type"}; + last; + } + } + } + + return $TypeId; +} + +sub writeReport($$) +{ + my ($Level, $Report) = @_; + if($In::Opt{"ReportFormat"} eq "xml") { + $Report = "\n".$Report; + } + if($In::Opt{"StdOut"}) + { # --stdout option + print STDOUT $Report; + } + else + { + my $RPath = getReportPath($Level); + mkpath(getDirname($RPath)); + + open(REPORT, ">", $RPath) || die ("can't open file \'$RPath\': $!\n"); + print REPORT $Report; + close(REPORT); + } +} + +sub getReport($) +{ + my $Level = $_[0]; + if($In::Opt{"ReportFormat"} eq "xml") + { # XML + if($Level eq "Join") + { + my $Report = "\n"; + $Report .= getReport("Binary"); + $Report .= getReport("Source"); + $Report .= "\n"; + return $Report; + } + else + { + my $Report = "\n\n"; + my ($Summary, $MetaData, $AnyChanged) = getSummary($Level); + $Report .= $Summary."\n"; + $Report .= getReportProblems_All($Level); + $Report .= "\n"; + return $Report; + } + } + else + { # HTML + my $CssStyles = readModule("Styles", "Report.css"); + my $JScripts = readModule("Scripts", "Sections.js"); + if($Level eq "Join") + { + $CssStyles .= "\n".readModule("Styles", "Tabs.css"); + $JScripts .= "\n".readModule("Scripts", "Tabs.js"); + my $Title = $In::Opt{"TargetTitle"}.": ".$In::Desc{1}{"Version"}." to ".$In::Desc{2}{"Version"}." compatibility report"; + my $Keywords = $In::Opt{"TargetTitle"}.", compatibility, API, ABI, report"; + 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"; + my ($BSummary, $BMetaData, $BAnyChanged) = getSummary("Binary"); + my ($SSummary, $SMetaData, $SAnyChanged) = getSummary("Source"); + my $Report = "\n\n".composeHTML_Head($Title, $Keywords, $Des, $CssStyles, $JScripts, ($BAnyChanged or $SAnyChanged)).""; + $Report .= getReportTitle("Join")." +
+ "; + $Report .= "
\n$BSummary\n".getReportProblems_All("Binary").getSourceInfo()."


"; + $Report .= "
\n$SSummary\n".getReportProblems_All("Source").getSourceInfo()."


"; + $Report .= getReportFooter(); + $Report .= "\n\n"; + return $Report; + } + else + { + my ($Summary, $MetaData, $AnyChanged) = getSummary($Level); + my $Title = $In::Opt{"TargetTitle"}.": ".$In::Desc{1}{"Version"}." to ".$In::Desc{2}{"Version"}." ".lc($Level)." compatibility report"; + my $Keywords = $In::Opt{"TargetTitle"}.", ".lc($Level)." compatibility, API, report"; + my $Des = "$Level compatibility report for the ".$In::Opt{"TargetTitle"}." ".$In::Opt{"TargetComponent"}." between ".$In::Desc{1}{"Version"}." and ".$In::Desc{2}{"Version"}." versions"; + if($Level eq "Binary") + { + if($In::ABI{1}{"Arch"} eq $In::ABI{2}{"Arch"}) { + $Des .= " on ".showArch($In::ABI{1}{"Arch"}); + } + } + my $Report = "\n".composeHTML_Head($Title, $Keywords, $Des, $CssStyles, $JScripts, $AnyChanged)."\n\n
\n"; + $Report .= getReportTitle($Level)."\n".$Summary."\n"; + $Report .= getReportProblems_All($Level); + $Report .= getSourceInfo(); + $Report .= "
\n


\n"; + $Report .= getReportFooter(); + $Report .= "\n\n"; + return $Report; + } + } +} + +sub getReportProblems_All($) +{ + my $Level = $_[0]; + + my $Report = getReportAdded($Level).getReportRemoved($Level); + $Report .= getReportProblems("High", $Level); + $Report .= getReportProblems("Medium", $Level); + $Report .= getReportProblems("Low", $Level); + $Report .= getReportProblems("Safe", $Level); + + # clean memory + delete($TypeProblemsIndex{$Level}); + delete($TypeChanges{$Level}); + delete($CompatProblems{$Level}); + + return $Report; +} + +sub createReport() +{ + if($In::Opt{"JoinReport"}) { + writeReport("Join", getReport("Join")); + } + elsif($In::Opt{"DoubleReport"}) + { # default + writeReport("Binary", getReport("Binary")); + writeReport("Source", getReport("Source")); + } + elsif($In::Opt{"BinOnly"}) + { # --binary + writeReport("Binary", getReport("Binary")); + } + elsif($In::Opt{"SrcOnly"}) + { # --source + writeReport("Source", getReport("Source")); + } +} + +sub getReportFooter() +{ + my $Footer = ""; + + $Footer .= "
\n"; + $Footer .= "\n"; + $Footer .= "
\n"; + + return $Footer; +} + +sub getReportProblems($$) +{ + my ($Severity, $Level) = @_; + + my $Report = getReportTypeProblems($Severity, $Level); + if(my $SProblems = getReportSymbolProblems($Severity, $Level)) { + $Report .= $SProblems; + } + + if($Severity eq "Low" or $Severity eq "Safe") { + $Report .= getReportChangedConstants($Severity, $Level); + } + + if($In::Opt{"ReportFormat"} eq "html") + { + if($Report) + { # add anchor + if($In::Opt{"JoinReport"}) + { + if($Severity eq "Safe") { + $Report = "".$Report; + } + else { + $Report = "".$Report; + } + } + else + { + if($Severity eq "Safe") { + $Report = "".$Report; + } + else { + $Report = "".$Report; + } + } + } + } + return $Report; +} + +sub composeHTML_Head($$$$$$) +{ + my ($Title, $Keywords, $Des, $Styles, $Scripts, $AnyChanged) = @_; + + my $Head = "\n"; + $Head .= "\n"; + $Head .= "\n"; + $Head .= "\n"; + $Head .= "\n"; + $Head .= "\n"; + $Head .= "\n"; + + if(not $AnyChanged) { + $Head .= "\n"; + } + + $Head .= "$Title\n"; + $Head .= "\n"; + $Head .= "\n"; + $Head .= "\n"; + + return $Head; +} + +sub insertIDs($) +{ + my $Text = $_[0]; + while($Text=~/CONTENT_ID/) + { + if(int($Content_Counter)%2) { + $ContentID -= 1; + } + $Text=~s/CONTENT_ID/c_$ContentID/; + $ContentID += 1; + $Content_Counter += 1; + } + return $Text; +} + +sub uncoverConstant($$) +{ + my ($LVer, $Constant) = @_; + + if(isCyclical(\@RecurConstant, $Constant)) { + return $Constant; + } + + if(defined $Cache{"uncoverConstant"}{$LVer}{$Constant}) { + return $Cache{"uncoverConstant"}{$LVer}{$Constant}; + } + + if(defined $Constants{$LVer}{$Constant}) + { + my $Value = $Constants{$LVer}{$Constant}{"Value"}; + if(defined $Constants{$LVer}{$Value}) + { + push(@RecurConstant, $Constant); + my $Uncovered = uncoverConstant($LVer, $Value); + if($Uncovered ne "") { + $Value = $Uncovered; + } + pop(@RecurConstant); + } + + # FIXME: uncover $Value using all the enum constants + # USE CASE: change of define NC_LONG from NC_INT (enum value) to NC_INT (define) + return ($Cache{"uncoverConstant"}{$LVer}{$Constant} = $Value); + } + return ($Cache{"uncoverConstant"}{$LVer}{$Constant} = ""); +} + +sub simpleConstant($$) +{ + my ($LVer, $Value) = @_; + if($Value=~/\W/) + { + my $Value_Copy = $Value; + while($Value_Copy=~s/([a-z_]\w+)/\@/i) + { + my $Word = $1; + if($Value!~/$Word\s*\(/) + { + my $Val = uncoverConstant($LVer, $Word); + if($Val ne "") + { + $Value=~s/\b$Word\b/$Val/g; + } + } + } + } + return $Value; +} + +sub computeValue($) +{ + my $Value = $_[0]; + + if($Value=~/\A\((-?[\d]+)\)\Z/) { + return $1; + } + + if($Value=~/\A[\d\-\+()]+\Z/) { + return eval($Value); + } + + return $Value; +} + +my $IgnoreConstant = join("|", + "VERSION", + "VERSIONCODE", + "VERNUM", + "VERS_INFO", + "PATCHLEVEL", + "INSTALLPREFIX", + "VBUILD", + "VPATCH", + "VMINOR", + "BUILD_STRING", + "BUILD_TIME", + "PACKAGE_STRING", + "PRODUCTION", + "CONFIGURE_COMMAND", + "INSTALLDIR", + "BINDIR", + "CONFIG_FILE_PATH", + "DATADIR", + "EXTENSION_DIR", + "INCLUDE_PATH", + "LIBDIR", + "LOCALSTATEDIR", + "SBINDIR", + "SYSCONFDIR", + "RELEASE", + "SOURCE_ID", + "SUBMINOR", + "MINOR", + "MINNOR", + "MINORVERSION", + "MAJOR", + "MAJORVERSION", + "MICRO", + "MICROVERSION", + "BINARY_AGE", + "INTERFACE_AGE", + "CORE_ABI", + "PATCH", + "COPYRIGHT", + "TIMESTAMP", + "REVISION", + "PACKAGE_TAG", + "PACKAGEDATE", + "NUMVERSION", + "Release", + "Version" +); + +sub constantFilter($$$) +{ + my ($Name, $Value, $Level) = @_; + + if($Level eq "Binary") + { + if($Name=~/_t\Z/) + { # __malloc_ptr_t + return 1; + } + if($Name=~/(\A|_)($IgnoreConstant)(_|\Z)/) + { # version + return 1; + } + if($Name=~/(\A|[a-z])(Release|Version)([A-Z]|\Z)/) + { # version + return 1; + } + my $LShort = $In::Opt{"TargetLibShort"}; + if($Name=~/(\A|_)(lib|open|)$LShort(_|)(VERSION|VER|DATE|API|PREFIX)(_|\Z)/i) + { # version + return 1; + } + if($Value=~/\A('|"|)[\/\\]\w+([\/\\]|:|('|"|)\Z)/ or $Value=~/[\/\\]\w+[\/\\]\w+/) + { # /lib64:/usr/lib64:/lib:/usr/lib:/usr/X11R6/lib/Xaw3d ... + return 1; + } + + if($Value=~/\A["'].*['"]/i) + { # string + return 0; + } + + if($Value=~/\A[({]*\s*[a-z_]+\w*(\s+|[\|,])/i) + { # static int gcry_pth_init + # extern ABC + # (RE_BACKSLASH_ESCAPE_IN_LISTS | RE... + # { H5FD_MEM_SUPER, H5FD_MEM_SUPER, ... + return 1; + } + if($Value=~/\w+\s*\(/i) + { # foo(p) + return 1; + } + if($Value=~/\A[a-z_]+\w*\Z/i) + { # asn1_node_st + # __SMTH_P + return 1; + } + } + + return 0; +} + +sub mergeConstants($) +{ + my $Level = $_[0]; + foreach my $Constant (sort keys(%{$Constants{1}})) + { + if($In::Desc{1}{"SkipConstants"}{$Constant}) + { # skipped by the user + next; + } + + if(my $Header = $Constants{1}{$Constant}{"Header"}) + { + if(not isTargetHeader($Header, 1) + and not isTargetHeader($Header, 2)) { + next; + } + } + elsif(my $Source = $Constants{1}{$Constant}{"Source"}) + { + if(not isTargetSource($Source, 1) + and not isTargetSource($Source, 2)) { + next; + } + } + else { + next; + } + + my $Old_Value = uncoverConstant(1, $Constant); + + if(constantFilter($Constant, $Old_Value, $Level)) + { # separate binary and source problems + next; + } + + if(not defined $Constants{2}{$Constant}{"Value"}) + { # removed + if(not defined $In::Opt{"SkipRemovedConstants"}) + { + %{$CompatProblems_Constants{$Level}{$Constant}{"Removed_Constant"}} = ( + "Target"=>$Constant, + "Old_Value"=>$Old_Value ); + } + next; + } + + if($Constants{2}{$Constant}{"Value"} eq "") + { # empty value + # TODO: implement a rule + next; + } + + my $New_Value = uncoverConstant(2, $Constant); + + my $Old_Value_Pure = $Old_Value; + my $New_Value_Pure = $New_Value; + + $Old_Value_Pure=~s/(\W)\s+/$1/g; + $Old_Value_Pure=~s/\s+(\W)/$1/g; + $New_Value_Pure=~s/(\W)\s+/$1/g; + $New_Value_Pure=~s/\s+(\W)/$1/g; + + next if($New_Value_Pure eq "" or $Old_Value_Pure eq ""); + + if($New_Value_Pure ne $Old_Value_Pure) + { # different values + if(simpleConstant(1, $Old_Value) eq simpleConstant(2, $New_Value)) + { # complex values + next; + } + if(computeValue($Old_Value) eq computeValue($New_Value)) + { # expressions + next; + } + if(convertInteger($Old_Value) eq convertInteger($New_Value)) + { # 0x0001 and 0x1, 0x1 and 1 equal constants + next; + } + if($Old_Value eq "0" and $New_Value eq "NULL") + { # 0 => NULL + next; + } + if($Old_Value eq "NULL" and $New_Value eq "0") + { # NULL => 0 + next; + } + %{$CompatProblems_Constants{$Level}{$Constant}{"Changed_Constant"}} = ( + "Target"=>$Constant, + "Old_Value"=>$Old_Value, + "New_Value"=>$New_Value ); + } + } + + if(defined $In::Opt{"SkipAddedConstants"}) { + return; + } + + foreach my $Constant (keys(%{$Constants{2}})) + { + if(not defined $Constants{1}{$Constant}{"Value"}) + { + if($In::Desc{2}{"SkipConstants"}{$Constant}) + { # skipped by the user + next; + } + + if(my $Header = $Constants{2}{$Constant}{"Header"}) + { + if(not isTargetHeader($Header, 1) + and not isTargetHeader($Header, 2)) + { # user-defined header + next; + } + } + elsif(my $Source = $Constants{2}{$Constant}{"Source"}) + { + if(not isTargetSource($Source, 1) + and not isTargetSource($Source, 2)) + { # user-defined header + next; + } + } + else { + next; + } + + my $New_Value = uncoverConstant(2, $Constant); + if(not defined $New_Value or $New_Value eq "") { + next; + } + + if(constantFilter($Constant, $New_Value, $Level)) + { # separate binary and source problems + next; + } + + %{$CompatProblems_Constants{$Level}{$Constant}{"Added_Constant"}} = ( + "Target"=>$Constant, + "New_Value"=>$New_Value ); + } + } +} + +sub convertInteger($) +{ + my $Value = $_[0]; + if($Value=~/\A0x[a-f0-9]+\Z/) + { # hexadecimal + return hex($Value); + } + elsif($Value=~/\A0[0-7]+\Z/) + { # octal + return oct($Value); + } + elsif($Value=~/\A0b[0-1]+\Z/) + { # binary + return oct($Value); + } + else { + return $Value; + } +} + +sub getSymbolSize($$) +{ # size from the shared library + my ($Symbol, $LVer) = @_; + + if(defined $In::ABI{$LVer}{"SymLib"}{$Symbol} + and my $LibName = $In::ABI{$LVer}{"SymLib"}{$Symbol}) + { + if(defined $In::ABI{$LVer}{"Symbols"}{$LibName}{$Symbol} + and my $Size = $In::ABI{$LVer}{"Symbols"}{$LibName}{$Symbol}) + { + if($Size<0) { + return -$Size; + } + } + } + return 0; +} + +sub createSymbolsList($$$$$) +{ + my ($DPath, $SaveTo, $LName, $LVersion, $ArchName) = @_; + + $In::ABI{1} = readABIDump(1, $DPath); + initAliases(1); + + prepareSymbols(1); + + my %SymbolHeaderLib = (); + my $Total = 0; + + # Get List + foreach my $Symbol (sort keys(%{$CompSign{1}})) + { + if(not linkSymbol($Symbol, 1, "-Deps")) + { # skip src only and all external functions + next; + } + if(not symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Public", "Binary", 1)) + { # skip other symbols + next; + } + my $HeaderName = $CompSign{1}{$Symbol}{"Header"}; + if(not $HeaderName) + { # skip src only and all external functions + next; + } + my $DyLib = $In::ABI{1}{"SymLib"}{$Symbol}; + if(not $DyLib) + { # skip src only and all external functions + next; + } + $SymbolHeaderLib{$HeaderName}{$DyLib}{$Symbol} = 1; + $Total += 1; + } + # Draw List + my $SYMBOLS_LIST = "

Public symbols in $LName ($LVersion)"; + $SYMBOLS_LIST .= " on ".showArch($ArchName)."
Total: $Total


"; + foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%SymbolHeaderLib)) + { + foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$SymbolHeaderLib{$HeaderName}})) + { + my %NS_Symbol = (); + foreach my $Symbol (keys(%{$SymbolHeaderLib{$HeaderName}{$DyLib}})) { + $NS_Symbol{selectSymbolNs($Symbol, 1)}{$Symbol} = 1; + } + foreach my $NameSpace (sort keys(%NS_Symbol)) + { + $SYMBOLS_LIST .= getTitle($HeaderName, $DyLib, $NameSpace); + my @SortedInterfaces = sort {lc($CompSign{1}{$a}{"Unmangled"}) cmp lc($CompSign{1}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$NS_Symbol{$NameSpace}}); + foreach my $Symbol (@SortedInterfaces) + { + my $SubReport = ""; + my $Signature = highLight_ItalicColor($Symbol, 1); + if($NameSpace) { + $Signature = cutNs($Signature, $NameSpace); + } + if($Symbol=~/\A(_Z|\?)/) { + $SubReport = insertIDs($ContentSpanStart.$Signature.$ContentSpanEnd."
\n".$ContentDivStart."$Symbol

".$ContentDivEnd."\n"); + } + else { + $SubReport = "".$Signature."
\n"; + } + $SYMBOLS_LIST .= $SubReport; + } + } + $SYMBOLS_LIST .= "
\n"; + } + } + # clear info + (%CompSign, %ClassMethods, %AllocableClass, %ClassNames, %In::ABI) = (); + + ($Content_Counter, $ContentID) = (0, 0); + + my $CssStyles = readModule("Styles", "SymbolsList.css"); + my $JScripts = readModule("Scripts", "Sections.js"); + $SYMBOLS_LIST = "".$SYMBOLS_LIST.$TOP_REF."
\n"; + my $Title = "$LName: public symbols"; + my $Keywords = "$LName, API, symbols"; + my $Des = "List of symbols in $LName ($LVersion) on ".showArch($ArchName); + $SYMBOLS_LIST = composeHTML_Head($Title, $Keywords, $Des, $CssStyles, $JScripts, 1)." +
\n$SYMBOLS_LIST
+

\n".getReportFooter()." + "; + writeFile($SaveTo, $SYMBOLS_LIST); +} + +sub dumpSorting($) +{ + my $Hash = $_[0]; + if(not $Hash) { + return []; + } + + my @Keys = keys(%{$Hash}); + if($#Keys<0) { + return []; + } + + if($Keys[0]=~/\A\d+\Z/) + { # numbers + return [sort {$a<=>$b} @Keys]; + } + else + { # strings + return [sort {$a cmp $b} @Keys]; + } +} + +sub exitReport() +{ # the tool has run without any errors + printReport(); + if($In::Opt{"CompileError"}) + { # errors in headers may add false positives/negatives + exit(getErrorCode("Compile_Error")); + } + if($In::Opt{"BinOnly"} and $RESULT{"Binary"}{"Problems"}) + { # --binary + exit(getErrorCode("Incompatible")); + } + elsif($In::Opt{"SrcOnly"} + and $RESULT{"Source"}{"Problems"}) + { # --source + exit(getErrorCode("Incompatible")); + } + elsif($RESULT{"Source"}{"Problems"} + or $RESULT{"Binary"}{"Problems"}) + { # default + exit(getErrorCode("Incompatible")); + } + else { + exit(getErrorCode("Compatible")); + } +} + +sub readRules($) +{ + my $Kind = $_[0]; + if(not -f $RULES_PATH{$Kind}) { + exitStatus("Module_Error", "can't access \'".$RULES_PATH{$Kind}."\'"); + } + my $Content = readFile($RULES_PATH{$Kind}); + while(my $Rule = parseTag(\$Content, "rule")) + { + my $RId = parseTag(\$Rule, "id"); + my @Properties = ("Severity", "Change", "Effect", "Overcome", "Kind"); + foreach my $Prop (@Properties) { + if(my $Value = parseTag(\$Rule, lc($Prop))) + { + $Value=~s/\n[ ]*//; + $CompatRules{$Kind}{$RId}{$Prop} = $Value; + } + } + if($CompatRules{$Kind}{$RId}{"Kind"}=~/\A(Symbols|Parameters)\Z/) { + $CompatRules{$Kind}{$RId}{"Kind"} = "Symbols"; + } + else { + $CompatRules{$Kind}{$RId}{"Kind"} = "Types"; + } + } +} + +sub getReportPath($) +{ + my $Level = $_[0]; + my $Dir = "compat_reports/".$In::Opt{"TargetLib"}."/".$In::Desc{1}{"Version"}."_to_".$In::Desc{2}{"Version"}; + if($Level eq "Binary") + { + if($In::Opt{"BinReportPath"}) + { # --bin-report-path + return $In::Opt{"BinReportPath"}; + } + elsif($In::Opt{"OutputReportPath"}) + { # --report-path + return $In::Opt{"OutputReportPath"}; + } + else + { # default + return $Dir."/abi_compat_report.".$In::Opt{"ReportFormat"}; + } + } + elsif($Level eq "Source") + { + if($In::Opt{"SrcReportPath"}) + { # --src-report-path + return $In::Opt{"SrcReportPath"}; + } + elsif($In::Opt{"OutputReportPath"}) + { # --report-path + return $In::Opt{"OutputReportPath"}; + } + else + { # default + return $Dir."/src_compat_report.".$In::Opt{"ReportFormat"}; + } + } + else + { + if($In::Opt{"OutputReportPath"}) + { # --report-path + return $In::Opt{"OutputReportPath"}; + } + else + { # default + return $Dir."/compat_report.".$In::Opt{"ReportFormat"}; + } + } +} + +sub printStatMsg($) +{ + my $Level = $_[0]; + printMsg("INFO", "Total ".lc($Level)." compatibility problems: ".$RESULT{$Level}{"Problems"}.", warnings: ".$RESULT{$Level}{"Warnings"}); +} + +sub listAffected($) +{ + my $Level = $_[0]; + my $List = ""; + foreach (keys(%{$TotalAffected{$Level}})) + { + if($In::Opt{"StrictCompat"} and $TotalAffected{$Level}{$_} eq "Low") + { # skip "Low"-severity problems + next; + } + $List .= "$_\n"; + } + my $Dir = getDirname(getReportPath($Level)); + if($Level eq "Binary") { + writeFile($Dir."/abi_affected.txt", $List); + } + elsif($Level eq "Source") { + writeFile($Dir."/src_affected.txt", $List); + } +} + +sub printReport() +{ + printMsg("INFO", "Creating compatibility report ..."); + + createReport(); + + if($In::Opt{"JoinReport"} or $In::Opt{"DoubleReport"}) + { + if($RESULT{"Binary"}{"Problems"} + or $RESULT{"Source"}{"Problems"}) + { + printMsg("INFO", "Binary compatibility: ".(100-$RESULT{"Binary"}{"Affected"})."\%"); + printMsg("INFO", "Source compatibility: ".(100-$RESULT{"Source"}{"Affected"})."\%"); + } + else + { + printMsg("INFO", "Binary compatibility: 100\%"); + printMsg("INFO", "Source compatibility: 100\%"); + } + printStatMsg("Binary"); + printStatMsg("Source"); + + if($In::Opt{"ListAffected"}) + { # --list-affected + listAffected("Binary"); + listAffected("Source"); + } + } + elsif($In::Opt{"BinOnly"}) + { + if($RESULT{"Binary"}{"Problems"}) { + printMsg("INFO", "Binary compatibility: ".(100-$RESULT{"Binary"}{"Affected"})."\%"); + } + else { + printMsg("INFO", "Binary compatibility: 100\%"); + } + printStatMsg("Binary"); + + if($In::Opt{"ListAffected"}) + { # --list-affected + listAffected("Binary"); + } + } + elsif($In::Opt{"SrcOnly"}) + { + if($RESULT{"Source"}{"Problems"}) { + printMsg("INFO", "Source compatibility: ".(100-$RESULT{"Source"}{"Affected"})."\%"); + } + else { + printMsg("INFO", "Source compatibility: 100\%"); + } + printStatMsg("Source"); + + if($In::Opt{"ListAffected"}) + { # --list-affected + listAffected("Source"); + } + } + + if($In::Opt{"StdOut"}) + { + if($In::Opt{"JoinReport"} or not $In::Opt{"DoubleReport"}) + { # --binary or --source + printMsg("INFO", "Compatibility report has been generated to stdout"); + } + else + { # default + printMsg("INFO", "Compatibility reports have been generated to stdout"); + } + } + else + { + if($In::Opt{"JoinReport"}) + { # default + printMsg("INFO", "Report: ".pathFmt(getReportPath("Join"))); + } + elsif($In::Opt{"DoubleReport"}) + { + printMsg("INFO", "Report (BC): ".pathFmt(getReportPath("Binary"))); + printMsg("INFO", "Report (SC): ".pathFmt(getReportPath("Source"))); + } + elsif($In::Opt{"BinOnly"}) + { # --binary + printMsg("INFO", "Report: ".pathFmt(getReportPath("Binary"))); + } + elsif($In::Opt{"SrcOnly"}) + { # --source + printMsg("INFO", "Report: ".pathFmt(getReportPath("Source"))); + } + } +} + +sub unpackDump($) +{ + my $Path = $_[0]; + + $Path = getAbsPath($Path); + my ($Dir, $FileName) = sepPath($Path); + + my $TmpDir = $In::Opt{"Tmp"}; + my $UnpackDir = $TmpDir."/unpack"; + rmtree($UnpackDir); + mkpath($UnpackDir); + + if($FileName=~s/\Q.zip\E\Z//g) + { # *.zip + my $UnzipCmd = getCmdPath("unzip"); + if(not $UnzipCmd) { + exitStatus("Not_Found", "can't find \"unzip\" command"); + } + chdir($UnpackDir); + system("$UnzipCmd \"$Path\" >\"$TmpDir/null\""); + if($?) { + exitStatus("Error", "can't extract \'$Path\' ($?): $!"); + } + chdir($In::Opt{"OrigDir"}); + my @Contents = cmdFind($UnpackDir, "f"); + if(not @Contents) { + exitStatus("Error", "can't extract \'$Path\'"); + } + return $Contents[0]; + } + elsif($FileName=~s/\Q.tar.gz\E(\.\w+|)\Z//g) + { # *.tar.gz + # *.tar.gz.amd64 (dh & cdbs) + if($In::Opt{"OS"} eq "windows") + { # -xvzf option is not implemented in tar.exe (2003) + # use "gzip.exe -k -d -f" + "tar.exe -xvf" instead + my $TarCmd = getCmdPath("tar"); + if(not $TarCmd) { + exitStatus("Not_Found", "can't find \"tar\" command"); + } + my $GzipCmd = getCmdPath("gzip"); + if(not $GzipCmd) { + exitStatus("Not_Found", "can't find \"gzip\" command"); + } + chdir($UnpackDir); + system("$GzipCmd -k -d -f \"$Path\""); # keep input files (-k) + if($?) { + exitStatus("Error", "can't extract \'$Path\'"); + } + system("$TarCmd -xvf \"$Dir\\$FileName.tar\" >\"$TmpDir/null\""); + if($?) { + exitStatus("Error", "can't extract \'$Path\' ($?): $!"); + } + chdir($In::Opt{"OrigDir"}); + unlink($Dir."/".$FileName.".tar"); + my @Contents = cmdFind($UnpackDir, "f"); + if(not @Contents) { + exitStatus("Error", "can't extract \'$Path\'"); + } + return $Contents[0]; + } + else + { # Unix, Mac + my $TarCmd = getCmdPath("tar"); + if(not $TarCmd) { + exitStatus("Not_Found", "can't find \"tar\" command"); + } + chdir($UnpackDir); + system("$TarCmd -xvzf \"$Path\" >\"$TmpDir/null\""); + if($?) { + exitStatus("Error", "can't extract \'$Path\' ($?): $!"); + } + chdir($In::Opt{"OrigDir"}); + my @Contents = cmdFind($UnpackDir, "f"); + if(not @Contents) { + exitStatus("Error", "can't extract \'$Path\'"); + } + return $Contents[0]; + } + } +} + +sub createArchive($$) +{ + my ($Path, $To) = @_; + if(not $To) { + $To = "."; + } + + my ($From, $Name) = sepPath($Path); + if($In::Opt{"OS"} eq "windows") + { # *.zip + my $ZipCmd = getCmdPath("zip"); + if(not $ZipCmd) { + exitStatus("Not_Found", "can't find \"zip\""); + } + my $Pkg = $To."/".$Name.".zip"; + unlink($Pkg); + chdir($To); + system("$ZipCmd -j \"$Name.zip\" \"$Path\" >\"".$In::Opt{"Tmp"}."/null\""); + if($?) + { # cannot allocate memory (or other problems with "zip") + chdir($In::Opt{"OrigDir"}); + exitStatus("Error", "can't pack the ABI dump: ".$!); + } + chdir($In::Opt{"OrigDir"}); + unlink($Path); + return $Pkg; + } + else + { # *.tar.gz + my $TarCmd = getCmdPath("tar"); + if(not $TarCmd) { + exitStatus("Not_Found", "can't find \"tar\""); + } + my $GzipCmd = getCmdPath("gzip"); + if(not $GzipCmd) { + exitStatus("Not_Found", "can't find \"gzip\""); + } + my $Pkg = abs_path($To)."/".$Name.".tar.gz"; + if(-e $Pkg) { + unlink($Pkg); + } + system($TarCmd, "-C", $From, "-czf", $Pkg, $Name); + if($?) + { # cannot allocate memory (or other problems with "tar") + exitStatus("Error", "can't pack the ABI dump: ".$!); + } + unlink($Path); + return $To."/".$Name.".tar.gz"; + } +} + +sub defaultDumpPath($$) +{ + my ($N, $V) = @_; + return "abi_dumps/".$N."/".$V."/ABI.dump"; +} + +sub createABIFile($$) +{ + my ($LVer, $DescPath) = @_; + + if(not -e $DescPath) { + exitStatus("Access_Error", "can't access \'$DescPath\'"); + } + + detectDefaultPaths(undef, undef, "bin", undef); + + if(isDump($DescPath)) + { + $In::ABI{$LVer} = readABIDump($LVer, $DescPath); + initAliases($LVer); + + if(my $V = $In::Desc{$LVer}{"TargetVersion"}) { + $In::Desc{$LVer}{"Version"} = $V; + } + else { + $In::Desc{$LVer}{"Version"} = $In::ABI{$LVer}{"LibraryVersion"}; + } + } + else + { + loadModule("ABIDump"); + readDesc(createDesc($DescPath, $LVer), $LVer); + + initLogging($LVer); + + if($In::Opt{"Debug"}) + { + if(not $In::Opt{"ExtraInfo"}) { + $In::Opt{"ExtraInfo"} = getExtraDir($LVer); + } + } + + detectDefaultPaths("inc", "lib", undef, "gcc"); + createABIDump($LVer); + } + + clearSysFilesCache($LVer); + + printMsg("INFO", "Creating library ABI dump ..."); + + $In::ABI{$LVer}{"ABI_DUMP_VERSION"} = $ABI_DUMP_VERSION; + $In::ABI{$LVer}{"ABI_COMPLIANCE_CHECKER_VERSION"} = $TOOL_VERSION; + + if($In::Opt{"UseXML"}) { + $In::ABI{$LVer}{"XML_ABI_DUMP_VERSION"} = $XML_ABI_DUMP_VERSION; + } + + $In::ABI{$LVer}{"TargetHeaders"} = $In::Desc{$LVer}{"TargetHeader"}; + + foreach ("SymLib", "DepSymLib", "TName_Tid", "TypeTypedef", + "TypedefBase", "Class_SubClasses", "ClassVTable") { + delete($In::ABI{$LVer}{$_}); + } + + my $DumpPath = defaultDumpPath($In::Opt{"TargetLib"}, $In::Desc{1}{"Version"}); + if($In::Opt{"OutputDumpPath"}) + { # user defined path + $DumpPath = $In::Opt{"OutputDumpPath"}; + } + + my $ArExt = $In::Opt{"Ar"}; + my $Archive = ($DumpPath=~s/\Q.$ArExt\E\Z//g); + + if($Archive and not $In::Opt{"StdOut"}) + { # check archive utilities + if($In::Opt{"OS"} eq "windows") + { # using zip + my $ZipCmd = getCmdPath("zip"); + if(not $ZipCmd) { + exitStatus("Not_Found", "can't find \"zip\""); + } + } + else + { # using tar and gzip + my $TarCmd = getCmdPath("tar"); + if(not $TarCmd) { + exitStatus("Not_Found", "can't find \"tar\""); + } + my $GzipCmd = getCmdPath("gzip"); + if(not $GzipCmd) { + exitStatus("Not_Found", "can't find \"gzip\""); + } + } + } + + my $ABI_DUMP = ""; + if($In::Opt{"UseXML"}) + { + loadModule("XmlDump"); + $ABI_DUMP = createXmlDump($LVer); + } + else + { # default + $ABI_DUMP = Dumper($In::ABI{$LVer}); + } + if($In::Opt{"StdOut"}) + { + print STDOUT $ABI_DUMP; + printMsg("INFO", "ABI dump has been generated to stdout"); + return; + } + else + { # to file + my ($DDir, $DName) = sepPath($DumpPath); + my $DPath = $In::Opt{"Tmp"}."/".$DName; + if(not $Archive) { + $DPath = $DumpPath; + } + + mkpath($DDir); + + open(DUMP, ">", $DPath) || die ("can't open file \'$DPath\': $!\n"); + print DUMP $ABI_DUMP; + close(DUMP); + + # Ensure contents of archive have deterministic contents + chmod(0644, $DPath); + utime(0, 0, $DPath); + + if(not -s $DPath) { + exitStatus("Error", "can't create ABI dump because something is going wrong with the Data::Dumper module"); + } + if($Archive) { + $DumpPath = createArchive($DPath, $DDir); + } + + printMsg("INFO", "Dump path: ".pathFmt($DumpPath)); + } +} + +sub readABIDump($$) +{ + my ($LVer, $Path) = @_; + + my $FilePath = ""; + if(isDump_U($Path)) + { # input *.abi + $FilePath = $Path; + } + else + { # input *.abi.tar.gz + $FilePath = unpackDump($Path); + if(not isDump_U($FilePath)) { + exitStatus("Invalid_Dump", "specified ABI dump \'$Path\' is not valid, try to recreate it"); + } + } + + my $ABIRef = {}; + + my $Line = readLineNum($FilePath, 0); + if($Line=~/xml/) + { # XML format + loadModule("XmlDump"); + $ABIRef = readXmlDump($FilePath); + } + else + { # Perl Data::Dumper format (default) + open(DUMP, $FilePath); + local $/ = undef; + my $Content = ; + close(DUMP); + + if(getDirname($FilePath) eq $In::Opt{"Tmp"}."/unpack") + { # remove temp file + unlink($FilePath); + } + if($Content!~/};\s*\Z/) { + exitStatus("Invalid_Dump", "specified ABI dump \'$Path\' is not valid, try to recreate it"); + } + $ABIRef = eval($Content); + if(not $ABIRef) { + exitStatus("Error", "internal error - eval() procedure seem to not working correctly, try to remove 'use strict' and try again"); + } + } + + my $ABIVer = $ABIRef->{"ABI_DUMP_VERSION"}; + + if($ABIVer) + { + if(cmpVersions($ABIVer, $ABI_DUMP_VERSION)>0) + { # future formats + exitStatus("Dump_Version", "the versions of the ABI dump is newer than version of the tool"); + } + } + + if(cmpVersions($ABIVer, $ABI_DUMP_VERSION_MIN)<0 and not $In::Opt{"CountSymbols"}) { + exitStatus("Dump_Version", "the version of the ABI dump is too old and unsupported anymore, please regenerate it"); + } + + if(defined $ABIRef->{"ABI_DUMPER_VERSION"}) + { # DWARF ABI Dump + $UseConv_Real{$LVer}{"P"} = 1; + $UseConv_Real{$LVer}{"R"} = 0; # not implemented yet + + $UsedDump{$LVer}{"DWARF"} = 1; + + if($ABIRef->{"LibraryName"}=~/\.ko(\.|\Z)/) { + $In::Opt{"TargetComponent"} = "module"; + } + else { + $In::Opt{"TargetComponent"} = "object"; + } + } + + if(index($ABIRef->{"LibraryName"}, "libstdc++")==0 + or index($ABIRef->{"LibraryName"}, "libc++")==0) { + $In::Opt{"StdcxxTesting"} = 1; + } + + if($ABIRef->{"BinOnly"}) + { # ABI dump created with --binary option + $UsedDump{$LVer}{"BinOnly"} = 1; + } + else + { # default + $UsedDump{$LVer}{"SrcBin"} = 1; + } + + if(defined $ABIRef->{"Mode"} + and $ABIRef->{"Mode"} eq "Extended") + { # --ext option + $In::Opt{"ExtendedCheck"} = 1; + } + + if($ABIRef->{"Extra"}) { + $In::Opt{"ExtraDump"} = 1; + } + + if(not keys(%{$ABIRef->{"SymbolInfo"}})) + { # validation of old-version dumps + if(not $In::Opt{"ExtendedCheck"}) { + exitStatus("Invalid_Dump", "no symbols info in the ABI dump"); + } + } + + if(defined $ABIRef->{"GccConstants"}) + { # support for 3.0 + foreach my $Name (keys(%{$ABIRef->{"GccConstants"}})) { + $ABIRef->{"Constants"}{$Name}{"Value"} = $ABIRef->{"GccConstants"}{$Name}; + } + } + elsif(defined $ABIRef->{"CompilerConstants"}) + { + foreach my $Name (keys(%{$ABIRef->{"CompilerConstants"}})) { + $ABIRef->{"Constants"}{$Name}{"Value"} = $ABIRef->{"CompilerConstants"}{$Name}; + } + } + + if(not $ABIRef->{"SymbolVersion"}) { + $ABIRef->{"SymbolVersion"} = $ABIRef->{"SymVer"}; + } + + if(defined $ABIRef->{"TargetHeaders"}) { + $In::Desc{$LVer}{"TargetHeader"} = $ABIRef->{"TargetHeaders"}; + } + + foreach my $LName (keys(%{$ABIRef->{"Symbols"}})) + { + foreach my $Symbol (keys(%{$ABIRef->{"Symbols"}{$LName}})) { + $ABIRef->{"SymLib"}{$Symbol} = $LName; + } + } + + foreach my $LName (keys(%{$ABIRef->{"DepSymbols"}})) + { + foreach my $Symbol (keys(%{$ABIRef->{"DepSymbols"}{$LName}})) { + $ABIRef->{"DepSymLib"}{$Symbol} = $LName; + } + } + + $In::Opt{"Target"} = $ABIRef->{"Target"}; + $In::Desc{$LVer}{"Dump"} = 1; + + return $ABIRef; +} + +sub prepareCompare($) +{ + my $LVer = $_[0]; + + foreach my $Lib_Name (keys(%{$In::ABI{$LVer}{"Symbols"}})) + { + foreach my $Symbol (keys(%{$In::ABI{$LVer}{"Symbols"}{$Lib_Name}})) + { + if($In::ABI{$LVer}{"Symbols"}{$Lib_Name}{$Symbol}<0) + { # data marked as -size in the dump + $GlobalDataObject{$LVer}{$Symbol} = -$In::ABI{$LVer}{"Symbols"}{$Lib_Name}{$Symbol}; + + if($Symbol=~/\A(.+?)\@.+/) { + $GlobalDataObject{$LVer}{$1} = $GlobalDataObject{$LVer}{$Symbol}; + } + } + } + } + + foreach my $TypeId (sort {$a<=>$b} keys(%{$TypeInfo{$LVer}})) + { # NOTE: order is important + if(not defined $TypeInfo{$LVer}{$TypeId}{"Tid"}) { + $TypeInfo{$LVer}{$TypeId}{"Tid"} = $TypeId; + } + + my $TInfo = $TypeInfo{$LVer}{$TypeId}; + if(defined $TInfo->{"Base"}) + { + foreach my $SubId (keys(%{$TInfo->{"Base"}})) + { + if($SubId eq $TypeId) + { # Fix erroneous ABI dump + delete($TypeInfo{$LVer}{$TypeId}{"Base"}{$SubId}); + next; + } + + $In::ABI{$LVer}{"Class_SubClasses"}{$SubId}{$TypeId} = 1; + } + } + + if($TInfo->{"BaseType"} eq $TypeId) + { # fix ABI dump + delete($TypeInfo{$LVer}{$TypeId}{"BaseType"}); + } + + if($TInfo->{"Type"} eq "Typedef" and not $TInfo->{"Artificial"}) + { + if(my $BTid = $TInfo->{"BaseType"}) + { + my $BName = $TypeInfo{$LVer}{$BTid}{"Name"}; + if(not $BName) + { # broken type + next; + } + if($TInfo->{"Name"} eq $BName) + { # typedef to "class Class" + # should not be registered in TName_Tid + next; + } + if(not $In::ABI{$LVer}{"TypedefBase"}{$TInfo->{"Name"}}) { + $In::ABI{$LVer}{"TypedefBase"}{$TInfo->{"Name"}} = $BName; + } + } + } + if(not $TName_Tid{$LVer}{$TInfo->{"Name"}}) + { # classes: class (id1), typedef (artificial, id2 > id1) + $TName_Tid{$LVer}{$TInfo->{"Name"}} = $TypeId; + } + } +} + +sub compareABIDumps($$) +{ + my ($V1, $V2) = @_; + my $DumpPath1 = defaultDumpPath($In::Opt{"TargetLib"}, $V1); + my $DumpPath2 = defaultDumpPath($In::Opt{"TargetLib"}, $V2); + + unlink($DumpPath1); + unlink($DumpPath2); + + my $Pid = fork(); + if($Pid) + { # dump on two CPU cores + my @PARAMS = ("-dump", $In::Desc{1}{"Path"}, "-l", $In::Opt{"TargetLib"}); + @PARAMS = (@PARAMS, "-vnum", $V1); + + if($In::Desc{1}{"RelativeDirectory"}) { + @PARAMS = (@PARAMS, "-relpath", $In::Desc{1}{"RelativeDirectory"}); + } + if($In::Desc{1}{"OutputLogPath"}) { + @PARAMS = (@PARAMS, "-log-path", $In::Desc{1}{"OutputLogPath"}); + } + if($In::Opt{"CrossGcc"}) { + @PARAMS = (@PARAMS, "-cross-gcc", $In::Opt{"CrossGcc"}); + } + if($In::Opt{"Quiet"}) + { + @PARAMS = (@PARAMS, "-quiet"); + @PARAMS = (@PARAMS, "-logging-mode", "a"); + } + elsif($In::Opt{"LogMode"} and $In::Opt{"LogMode"} ne "w") + { # "w" is default + @PARAMS = (@PARAMS, "-logging-mode", $In::Opt{"LogMode"}); + } + if($In::Opt{"ExtendedCheck"}) { + @PARAMS = (@PARAMS, "-extended"); + } + if($In::Opt{"UserLang"}) { + @PARAMS = (@PARAMS, "-lang", $In::Opt{"UserLang"}); + } + if($In::Opt{"BinOnly"}) { + @PARAMS = (@PARAMS, "-binary"); + } + if($In::Opt{"SrcOnly"}) { + @PARAMS = (@PARAMS, "-source"); + } + if($In::Opt{"SortDump"}) { + @PARAMS = (@PARAMS, "-sort"); + } + if($In::Opt{"DumpFormat"} and $In::Opt{"DumpFormat"} ne "perl") { + @PARAMS = (@PARAMS, "-dump-format", $In::Opt{"DumpFormat"}); + } + if($In::Opt{"CheckHeadersOnly"}) { + @PARAMS = (@PARAMS, "-headers-only"); + } + if($In::Opt{"CxxIncompat"}) { + @PARAMS = (@PARAMS, "-cxx-incompatible"); + } + if($In::Opt{"Debug"}) + { + @PARAMS = (@PARAMS, "-debug"); + printMsg("INFO", "Executing perl $0 @PARAMS"); + } + system("perl", $0, @PARAMS); + if(not -f $DumpPath1) { + exit(1); + } + } + else + { # child + my @PARAMS = ("-dump", $In::Desc{2}{"Path"}, "-l", $In::Opt{"TargetLib"}); + @PARAMS = (@PARAMS, "-vnum", $V2); + + if($In::Desc{2}{"RelativeDirectory"}) { + @PARAMS = (@PARAMS, "-relpath", $In::Desc{2}{"RelativeDirectory"}); + } + if($In::Desc{2}{"OutputLogPath"}) { + @PARAMS = (@PARAMS, "-log-path", $In::Desc{2}{"OutputLogPath"}); + } + if($In::Opt{"CrossGcc"}) { + @PARAMS = (@PARAMS, "-cross-gcc", $In::Opt{"CrossGcc"}); + } + if($In::Opt{"Quiet"}) + { + @PARAMS = (@PARAMS, "-quiet"); + @PARAMS = (@PARAMS, "-logging-mode", "a"); + } + elsif($In::Opt{"LogMode"} and $In::Opt{"LogMode"} ne "w") + { # "w" is default + @PARAMS = (@PARAMS, "-logging-mode", $In::Opt{"LogMode"}); + } + if($In::Opt{"ExtendedCheck"}) { + @PARAMS = (@PARAMS, "-extended"); + } + if($In::Opt{"UserLang"}) { + @PARAMS = (@PARAMS, "-lang", $In::Opt{"UserLang"}); + } + if($In::Opt{"BinOnly"}) { + @PARAMS = (@PARAMS, "-binary"); + } + if($In::Opt{"SrcOnly"}) { + @PARAMS = (@PARAMS, "-source"); + } + if($In::Opt{"SortDump"}) { + @PARAMS = (@PARAMS, "-sort"); + } + if($In::Opt{"DumpFormat"} and $In::Opt{"DumpFormat"} ne "perl") { + @PARAMS = (@PARAMS, "-dump-format", $In::Opt{"DumpFormat"}); + } + if($In::Opt{"CheckHeadersOnly"}) { + @PARAMS = (@PARAMS, "-headers-only"); + } + if($In::Opt{"CxxIncompat"}) { + @PARAMS = (@PARAMS, "-cxx-incompatible"); + } + if($In::Opt{"Debug"}) + { + @PARAMS = (@PARAMS, "-debug"); + printMsg("INFO", "Executing perl $0 @PARAMS"); + } + system("perl", $0, @PARAMS); + if(not -f $DumpPath2) { + exit(1); + } + else { + exit(0); + } + } + waitpid($Pid, 0); + + my @CMP_PARAMS = ("-l", $In::Opt{"TargetLib"}); + @CMP_PARAMS = (@CMP_PARAMS, "-d1", $DumpPath1); + @CMP_PARAMS = (@CMP_PARAMS, "-d2", $DumpPath2); + if($In::Opt{"TargetTitle"} ne $In::Opt{"TargetLib"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-title", $In::Opt{"TargetTitle"}); + } + if($In::Opt{"ShowRetVal"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-show-retval"); + } + if($In::Opt{"CrossGcc"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-cross-gcc", $In::Opt{"CrossGcc"}); + } + @CMP_PARAMS = (@CMP_PARAMS, "-logging-mode", "a"); + if($In::Opt{"Quiet"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-quiet"); + } + if($In::Opt{"ReportFormat"} + and $In::Opt{"ReportFormat"} ne "html") + { # HTML is default format + @CMP_PARAMS = (@CMP_PARAMS, "-report-format", $In::Opt{"ReportFormat"}); + } + if($In::Opt{"OutputReportPath"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-report-path", $In::Opt{"OutputReportPath"}); + } + if($In::Opt{"BinReportPath"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-bin-report-path", $In::Opt{"BinReportPath"}); + } + if($In::Opt{"SrcReportPath"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-src-report-path", $In::Opt{"SrcReportPath"}); + } + if($In::Opt{"LoggingPath"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-log-path", $In::Opt{"LoggingPath"}); + } + if($In::Opt{"CheckHeadersOnly"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-headers-only"); + } + if($In::Opt{"BinOnly"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-binary"); + } + if($In::Opt{"SrcOnly"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-source"); + } + if($In::Opt{"FilterPath"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-filter", $In::Opt{"FilterPath"}); + } + if($In::Opt{"SkipInternalSymbols"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-skip-internal-symbols", $In::Opt{"SkipInternalSymbols"}); + } + if($In::Opt{"SkipInternalTypes"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-skip-internal-types", $In::Opt{"SkipInternalTypes"}); + } + if($In::Opt{"Debug"}) + { + @CMP_PARAMS = (@CMP_PARAMS, "-debug"); + printMsg("INFO", "Executing perl $0 @CMP_PARAMS"); + } + system("perl", $0, @CMP_PARAMS); + exit($?>>8); +} + +sub compareInit() +{ + # read input XML descriptors or ABI dumps + if(not $In::Desc{1}{"Path"}) { + exitStatus("Error", "-old option is not specified"); + } + if(not -e $In::Desc{1}{"Path"}) { + exitStatus("Access_Error", "can't access \'".$In::Desc{1}{"Path"}."\'"); + } + + if(not $In::Desc{2}{"Path"}) { + exitStatus("Error", "-new option is not specified"); + } + if(not -e $In::Desc{2}{"Path"}) { + exitStatus("Access_Error", "can't access \'".$In::Desc{2}{"Path"}."\'"); + } + + detectDefaultPaths(undef, undef, "bin", undef); # to extract dumps + + printMsg("INFO", "Preparing, please wait ..."); + + if($In::Opt{"UseDumps"}) + { # --use-dumps + # parallel processing + if(isDump($In::Desc{1}{"Path"}) + or isDump($In::Desc{2}{"Path"})) { + exitStatus("Error", "please specify input XML descriptors instead of ABI dumps to use with -use-dumps option."); + } + + readDesc(createDesc($In::Desc{1}{"Path"}, 1), 1); + readDesc(createDesc($In::Desc{2}{"Path"}, 2), 2); + + compareABIDumps($In::Desc{1}{"Version"}, $In::Desc{2}{"Version"}); + } + + if(isDump($In::Desc{1}{"Path"})) + { + $In::ABI{1} = readABIDump(1, $In::Desc{1}{"Path"}); + initAliases(1); + + if(my $V = $In::Desc{1}{"TargetVersion"}) { + $In::Desc{1}{"Version"} = $V; + } + else { + $In::Desc{1}{"Version"} = $In::ABI{1}{"LibraryVersion"}; + } + + if(not defined $In::Desc{1}{"Version"}) { + $In::Desc{1}{"Version"} = "X"; + } + } + else + { + loadModule("ABIDump"); + readDesc(createDesc($In::Desc{1}{"Path"}, 1), 1); + + initLogging(1); + detectDefaultPaths("inc", "lib", undef, "gcc"); + createABIDump(1); + } + + if(isDump($In::Desc{2}{"Path"})) + { + $In::ABI{2} = readABIDump(2, $In::Desc{2}{"Path"}); + initAliases(2); + + if(my $V = $In::Desc{2}{"TargetVersion"}) { + $In::Desc{2}{"Version"} = $V; + } + else { + $In::Desc{2}{"Version"} = $In::ABI{2}{"LibraryVersion"}; + } + + if(not defined $In::Desc{2}{"Version"}) { + $In::Desc{2}{"Version"} = "Y"; + } + } + else + { + loadModule("ABIDump"); + readDesc(createDesc($In::Desc{2}{"Path"}, 2), 2); + + initLogging(2); + detectDefaultPaths("inc", "lib", undef, "gcc"); + createABIDump(2); + } + + clearSysFilesCache(1); + clearSysFilesCache(2); + + if(my $FPath = $In::Opt{"FilterPath"}) + { + if(not -f $FPath) { + exitStatus("Access_Error", "can't access \'".$FPath."\'"); + } + + if(my $Filt = readFile($FPath)) + { + readFilter($Filt, 1); + readFilter($Filt, 2); + } + } + + prepareCompare(1); + prepareCompare(2); + + if($In::Opt{"AppPath"} and not keys(%{$In::ABI{1}{"SymLib"}})) { + printMsg("WARNING", "the application ".getFilename($In::Opt{"AppPath"})." has no symbols imported from libraries"); + } + + prepareSymbols(1); + prepareSymbols(2); + + # Virtual Tables + registerVTable(1); + registerVTable(2); + + registerOverriding(1); + registerOverriding(2); + + setVirtFuncPositions(1); + setVirtFuncPositions(2); + + # Other + addParamNames(1); + addParamNames(2); + + detectChangedTypedefs(); +} + +sub compareAPIs($) +{ + my $Level = $_[0]; + + readRules($Level); + loadModule("CallConv"); + + if($Level eq "Binary") { + printMsg("INFO", "Comparing ABIs ..."); + } + else { + printMsg("INFO", "Comparing APIs ..."); + } + + if($In::Opt{"CheckHeadersOnly"} + or $Level eq "Source") + { # added/removed in headers + detectAdded_H($Level); + detectRemoved_H($Level); + } + else + { # added/removed in libs + detectAdded($Level); + detectRemoved($Level); + } + + mergeSymbols($Level); + + if(not defined $In::Opt{"DisableConstantsCheck"}) + { + if(keys(%{$CheckedSymbols{$Level}})) { + mergeConstants($Level); + } + } + + $Cache{"mergeTypes"} = (); # free memory + + if($In::Opt{"CheckHeadersOnly"} + or $Level eq "Source") + { # added/removed in headers + mergeHeaders($Level); + } + else + { # added/removed in libs + mergeLibs($Level); + } +} + +sub initAliases($) +{ + my $LVer = $_[0]; + + initABI($LVer); + + $SymbolInfo{$LVer} = $In::ABI{$LVer}{"SymbolInfo"}; + $TypeInfo{$LVer} = $In::ABI{$LVer}{"TypeInfo"}; + $TName_Tid{$LVer} = $In::ABI{$LVer}{"TName_Tid"}; + $Constants{$LVer} = $In::ABI{$LVer}{"Constants"}; + + initAliases_TypeAttr($LVer); +} + +sub scenario() +{ + setTarget("default"); + + initAliases(1); + initAliases(2); + + $In::Opt{"Locale"} = "C.UTF-8"; + $In::Opt{"OrigDir"} = cwd(); + $In::Opt{"Tmp"} = tempdir(CLEANUP=>1); + $In::Opt{"TargetLibShort"} = libPart($In::Opt{"TargetLib"}, "shortest"); + + $In::Opt{"DoubleReport"} = 0; + $In::Opt{"JoinReport"} = 1; + + $In::Opt{"SysPaths"}{"include"} = []; + $In::Opt{"SysPaths"}{"lib"} = []; + $In::Opt{"SysPaths"}{"bin"} = []; + + $In::Opt{"CompileError"} = 0; + + if($In::Opt{"TargetComponent"}) { + $In::Opt{"TargetComponent"} = lc($In::Opt{"TargetComponent"}); + } + else + { # default: library + $In::Opt{"TargetComponent"} = "library"; + } + + foreach (keys(%{$In::Desc{0}})) + { # common options + $In::Desc{1}{$_} = $In::Desc{0}{$_}; + $In::Desc{2}{$_} = $In::Desc{0}{$_}; + } + + $In::Opt{"AddTemplateInstances"} = 1; + $In::Opt{"GccMissedMangling"} = 0; + + if($In::Opt{"StdOut"}) + { # enable quiet mode + $In::Opt{"Quiet"} = 1; + $In::Opt{"JoinReport"} = 1; + } + if(not $In::Opt{"LogMode"}) + { # default + $In::Opt{"LogMode"} = "w"; + } + + if($In::Opt{"UserLang"}) { + $In::Opt{"UserLang"} = uc($In::Opt{"UserLang"}); + } + + if(my $LoggingPath = $In::Opt{"LoggingPath"}) + { + $In::Desc{1}{"OutputLogPath"} = $LoggingPath; + $In::Desc{2}{"OutputLogPath"} = $LoggingPath; + if($In::Opt{"Quiet"}) { + $In::Opt{"DefaultLog"} = $LoggingPath; + } + } + + if($In::Opt{"Force"}) { + $In::Opt{"GccMissedMangling"} = 1; + } + + if($In::Opt{"Quick"}) { + $In::Opt{"AddTemplateInstances"} = 0; + } + if(my $DP = $In::Opt{"OutputDumpPath"}) + { # validate + if(not isDump($DP)) { + exitStatus("Error", "the dump path should be a path to *.dump or *.dump.".$In::Opt{"Ar"}." file"); + } + } + if($In::Opt{"BinOnly"} + and $In::Opt{"SrcOnly"}) + { # both --binary and --source + # is the default mode + if(not $In::Opt{"CmpSystems"}) + { + $In::Opt{"BinOnly"} = 0; + $In::Opt{"SrcOnly"} = 0; + } + + $In::Opt{"DoubleReport"} = 1; + $In::Opt{"JoinReport"} = 0; + + if($In::Opt{"OutputReportPath"}) + { # --report-path + $In::Opt{"DoubleReport"} = 0; + $In::Opt{"JoinReport"} = 1; + } + } + elsif($In::Opt{"BinOnly"} + or $In::Opt{"SrcOnly"}) + { # --binary or --source + $In::Opt{"DoubleReport"} = 0; + $In::Opt{"JoinReport"} = 0; + } + if($In::Opt{"UseXML"}) + { # --xml option + $In::Opt{"ReportFormat"} = "xml"; + $In::Opt{"DumpFormat"} = "xml"; + } + if($In::Opt{"ReportFormat"}) + { # validate + $In::Opt{"ReportFormat"} = lc($In::Opt{"ReportFormat"}); + if($In::Opt{"ReportFormat"}!~/\A(xml|html|htm)\Z/) { + exitStatus("Error", "unknown report format \'".$In::Opt{"ReportFormat"}."\'"); + } + if($In::Opt{"ReportFormat"} eq "htm") + { # HTM == HTML + $In::Opt{"ReportFormat"} = "html"; + } + elsif($In::Opt{"ReportFormat"} eq "xml") + { # --report-format=XML equal to --xml + $In::Opt{"UseXML"} = 1; + } + } + else + { # default: HTML + $In::Opt{"ReportFormat"} = "html"; + } + if($In::Opt{"DumpFormat"}) + { # validate + $In::Opt{"DumpFormat"} = lc($In::Opt{"DumpFormat"}); + if($In::Opt{"DumpFormat"}!~/\A(xml|perl)\Z/) { + exitStatus("Error", "unknown ABI dump format \'".$In::Opt{"DumpFormat"}."\'"); + } + if($In::Opt{"DumpFormat"} eq "xml") + { # --dump-format=XML equal to --xml + $In::Opt{"UseXML"} = 1; + } + } + else + { # default: Perl Data::Dumper + $In::Opt{"DumpFormat"} = "perl"; + } + if($In::Opt{"Quiet"} and $In::Opt{"LogMode"}!~/a|n/) + { # --quiet log + if(-f $In::Opt{"DefaultLog"}) { + unlink($In::Opt{"DefaultLog"}); + } + } + if($In::Opt{"ExtraInfo"}) { + $In::Opt{"CheckUndefined"} = 1; + } + + if($In::Opt{"TestTool"} and $In::Opt{"UseDumps"}) + { # --test && --use-dumps == --test-dump + $In::Opt{"TestDump"} = 1; + } + if($In::Opt{"Tolerant"}) + { # enable all + $In::Opt{"Tolerance"} = 1234; + } + if($In::Opt{"Help"}) + { + helpMsg(); + exit(0); + } + if($In::Opt{"InfoMsg"}) + { + infoMsg(); + exit(0); + } + if($In::Opt{"ShowVersion"}) + { + printMsg("INFO", "ABI Compliance Checker (ABICC) $TOOL_VERSION\nCopyright (C) 2019 Andrey Ponomarenko's ABI Laboratory\nLicense: GNU LGPL 2.1 \nThis program is free software: you can redistribute it and/or modify it.\n\nWritten by Andrey Ponomarenko."); + exit(0); + } + if($In::Opt{"DumpVersion"}) + { + printMsg("INFO", $TOOL_VERSION); + exit(0); + } + if($In::Opt{"ExtendedCheck"}) { + $In::Opt{"CheckHeadersOnly"} = 1; + } + if($In::Opt{"SystemRoot"}) + { # user defined root + if(not -e $In::Opt{"SystemRoot"}) { + exitStatus("Access_Error", "can't access \'".$In::Opt{"SystemRoot"}."\'"); + } + $In::Opt{"SystemRoot"}=~s/[\/]+\Z//g; + if($In::Opt{"SystemRoot"}) { + $In::Opt{"SystemRoot"} = getAbsPath($In::Opt{"SystemRoot"}); + } + } + $Data::Dumper::Sortkeys = 1; + + if($In::Opt{"SortDump"}) + { + $Data::Dumper::Useperl = 1; + $Data::Dumper::Sortkeys = \&dump_sorting; + } + + if(my $TargetLibsPath = $In::Opt{"TargetLibsPath"}) + { + if(not -f $TargetLibsPath) { + exitStatus("Access_Error", "can't access file \'$TargetLibsPath\'"); + } + foreach my $Lib (split(/\s*\n\s*/, readFile($TargetLibsPath))) + { + if($In::Opt{"OS"} eq "windows") { + $In::Opt{"TargetLibs"}{lc($Lib)} = 1; + } + else { + $In::Opt{"TargetLibs"}{$Lib} = 1; + } + } + } + if(my $TPath = $In::Opt{"TargetHeadersPath"}) + { # --headers-list + if(not -f $TPath) { + exitStatus("Access_Error", "can't access file \'$TPath\'"); + } + + $In::Desc{1}{"TargetHeader"} = {}; + $In::Desc{2}{"TargetHeader"} = {}; + + foreach my $Header (split(/\s*\n\s*/, readFile($TPath))) + { + my $Name = getFilename($Header); + $In::Desc{1}{"TargetHeader"}{$Name} = 1; + $In::Desc{2}{"TargetHeader"}{$Name} = 1; + } + } + if($In::Opt{"TargetHeader"}) + { # --header + $In::Desc{1}{"TargetHeader"} = {}; + $In::Desc{2}{"TargetHeader"} = {}; + + my $Name = getFilename($In::Opt{"TargetHeader"}); + $In::Desc{1}{"TargetHeader"}{$Name} = 1; + $In::Desc{2}{"TargetHeader"}{$Name} = 1; + } + if($In::Opt{"TestABIDumper"}) + { + if($In::Opt{"OS"} ne "linux") { + exitStatus("Error", "-test-abi-dumper option is available on Linux only"); + } + } + if($In::Opt{"TestTool"} + or $In::Opt{"TestDump"} + or $In::Opt{"TestABIDumper"}) + { # --test, --test-dump + detectDefaultPaths(undef, undef, "bin", "gcc"); # to compile libs + loadModule("RegTests"); + testTool(); + exit(0); + } + if($In::Opt{"DumpSystem"}) + { # --dump-system + if(not $In::Opt{"TargetSysInfo"}) + { + if(-d $MODULES_DIR."/Targets/" + and -d $MODULES_DIR."/Targets/".$In::Opt{"Target"}) { + $In::Opt{"TargetSysInfo"} = $MODULES_DIR."/Targets/".$In::Opt{"Target"}; + } + } + + if(not $In::Opt{"TargetSysInfo"}) { + exitStatus("Error", "-sysinfo option should be specified to dump system ABI"); + } + + if(not -d $In::Opt{"TargetSysInfo"}) { + exitStatus("Access_Error", "can't access \'".$In::Opt{"TargetSysInfo"}."\'"); + } + + loadModule("SysCheck"); + if($In::Opt{"DumpSystem"}=~/\.(xml|desc)\Z/) + { # system XML descriptor + if(not -f $In::Opt{"DumpSystem"}) { + exitStatus("Access_Error", "can't access file \'".$In::Opt{"DumpSystem"}."\'"); + } + + my $SDesc = readFile($In::Opt{"DumpSystem"}); + if(my $RelDir = $In::Desc{1}{"RelativeDirectory"}) { + $SDesc=~s/\{RELPATH\}/$RelDir/g; + } + + readSysDesc($SDesc); + } + elsif(defined $In::Opt{"SystemRoot"}) + { # -sysroot "/" option + # default target: /usr/lib, /usr/include + # search libs: /usr/lib and /lib + my $SystemRoot = $In::Opt{"SystemRoot"}; + + if(not -e $SystemRoot."/usr/lib") { + exitStatus("Access_Error", "can't access '".$SystemRoot."/usr/lib'"); + } + if(not -e $SystemRoot."/lib") { + exitStatus("Access_Error", "can't access '".$SystemRoot."/lib'"); + } + if(not -e $SystemRoot."/usr/include") { + exitStatus("Access_Error", "can't access '".$SystemRoot."/usr/include'"); + } + readSysDesc(" + + ".$In::Opt{"DumpSystem"}." + + + $SystemRoot/usr/include + + + $SystemRoot/usr/lib + + + $SystemRoot/lib + "); + } + else { + exitStatus("Error", "-sysroot option should be specified, usually it's \"/\""); + } + detectDefaultPaths(undef, undef, "bin", "gcc"); # to check symbols + if($In::Opt{"Target"} eq "windows") + { # to run dumpbin.exe + # and undname.exe + checkWin32Env(); + } + dumpSystem(); + exit(0); + } + + if($In::Opt{"CmpSystems"}) + { # --cmp-systems + detectDefaultPaths(undef, undef, "bin", undef); # to extract dumps + loadModule("SysCheck"); + + if(not $In::Opt{"BinOnly"} + and not $In::Opt{"SrcOnly"}) + { # default + $In::Opt{"BinOnly"} = 1; + } + + cmpSystems($In::Desc{1}{"Path"}, $In::Desc{2}{"Path"}); + exit(0); + } + + if(not $In::Opt{"CountSymbols"}) + { + if(not $In::Opt{"TargetLib"}) { + exitStatus("Error", "library name is not selected (-l option)"); + } + else + { # validate library name + if($In::Opt{"TargetLib"}=~/[\*\/\\]/) { + exitStatus("Error", "\"\\\", \"\/\" and \"*\" symbols are not allowed in the library name"); + } + } + } + + if(not $In::Opt{"TargetTitle"}) { + $In::Opt{"TargetTitle"} = $In::Opt{"TargetLib"}; + } + + if(my $SymbolsListPath = $In::Opt{"SymbolsListPath"}) + { + if(not -f $SymbolsListPath) { + exitStatus("Access_Error", "can't access file \'$SymbolsListPath\'"); + } + foreach my $S (split(/\s*\n\s*/, readFile($SymbolsListPath))) + { + $In::Desc{1}{"SymbolsList"}{$S} = 1; + $In::Desc{2}{"SymbolsList"}{$S} = 1; + } + } + if(my $TypesListPath = $In::Opt{"TypesListPath"}) + { + if(not -f $TypesListPath) { + exitStatus("Access_Error", "can't access file \'$TypesListPath\'"); + } + foreach my $Type (split(/\s*\n\s*/, readFile($TypesListPath))) + { + $In::Desc{1}{"TypesList"}{$Type} = 1; + $In::Desc{2}{"TypesList"}{$Type} = 1; + } + } + if(my $SymbolsListPath = $In::Opt{"SkipSymbolsListPath"}) + { + if(not -f $SymbolsListPath) { + exitStatus("Access_Error", "can't access file \'$SymbolsListPath\'"); + } + foreach my $Interface (split(/\s*\n\s*/, readFile($SymbolsListPath))) + { + $In::Desc{1}{"SkipSymbols"}{$Interface} = 1; + $In::Desc{2}{"SkipSymbols"}{$Interface} = 1; + } + } + if(my $TypesListPath = $In::Opt{"SkipTypesListPath"}) + { + if(not -f $TypesListPath) { + exitStatus("Access_Error", "can't access file \'$TypesListPath\'"); + } + foreach my $Type (split(/\s*\n\s*/, readFile($TypesListPath))) + { + $In::Desc{1}{"SkipTypes"}{$Type} = 1; + $In::Desc{2}{"SkipTypes"}{$Type} = 1; + } + } + if(my $HeadersList = $In::Opt{"SkipHeadersPath"}) + { + if(not -f $HeadersList) { + exitStatus("Access_Error", "can't access file \'$HeadersList\'"); + } + foreach my $Path (split(/\s*\n\s*/, readFile($HeadersList))) + { + my ($CPath, $Type) = classifyPath($Path); + $In::Desc{1}{"SkipHeaders"}{$Type}{$CPath} = 1; + $In::Desc{2}{"SkipHeaders"}{$Type}{$CPath} = 1; + } + } + if(my $ParamNamesPath = $In::Opt{"ParamNamesPath"}) + { + if(not -f $ParamNamesPath) { + exitStatus("Access_Error", "can't access file \'$ParamNamesPath\'"); + } + foreach my $Line (split(/\n/, readFile($ParamNamesPath))) + { + if($Line=~s/\A(\w+)\;//) + { + my $Interface = $1; + if($Line=~/;(\d+);/) + { + while($Line=~s/(\d+);(\w+)//) { + $AddSymbolParams{$Interface}{$1}=$2; + } + } + else + { + my $Num = 0; + foreach my $Name (split(/;/, $Line)) { + $AddSymbolParams{$Interface}{$Num++}=$Name; + } + } + } + } + } + + if(my $AppPath = $In::Opt{"AppPath"}) + { + if(not -f $AppPath) { + exitStatus("Access_Error", "can't access file \'$AppPath\'"); + } + + detectDefaultPaths(undef, undef, "bin", "gcc"); + foreach my $Symbol (readSymbols_App($AppPath)) { + $In::Opt{"SymbolsList_App"}{$Symbol} = 1; + } + } + + if(my $Path = $In::Opt{"CountSymbols"}) + { + if(not -e $Path) { + exitStatus("Access_Error", "can't access \'$Path\'"); + } + + $In::ABI{1} = readABIDump(1, $Path); + initAliases(1); + + foreach my $Id (keys(%{$SymbolInfo{1}})) + { + my $MnglName = $SymbolInfo{1}{$Id}{"MnglName"}; + if(not $MnglName) { + $MnglName = $SymbolInfo{1}{$Id}{"ShortName"} + } + + if(my $SV = $In::ABI{1}{"SymbolVersion"}{$MnglName}) { + $CompSign{1}{$SV} = $SymbolInfo{1}{$Id}; + } + else { + $CompSign{1}{$MnglName} = $SymbolInfo{1}{$Id}; + } + + if(my $Alias = $CompSign{1}{$MnglName}{"Alias"}) { + $CompSign{1}{$Alias} = $SymbolInfo{1}{$Id}; + } + } + + my $Count = 0; + foreach my $Symbol (sort keys(%{$CompSign{1}})) + { + if($CompSign{1}{$Symbol}{"PureVirt"}) { + next; + } + + if(not $CompSign{1}{$Symbol}{"Header"}) + { + if(index($CompSign{1}{$Symbol}{"Source"}, ".f")==-1) + { # Fortran + next; + } + } + + $Count += symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected + InlineVirt", "Binary", 1); + } + + printMsg("INFO", $Count); + exit(0); + } + + if($In::Opt{"DumpABI"}) + { + createABIFile(1, $In::Opt{"DumpABI"}); + + if($In::Opt{"CompileError"}) { + exit(getErrorCode("Compile_Error")); + } + + exit(0); + } + + # default: compare APIs + compareInit(); + if($In::Opt{"JoinReport"} or $In::Opt{"DoubleReport"}) + { + compareAPIs("Binary"); + compareAPIs("Source"); + } + elsif($In::Opt{"BinOnly"}) { + compareAPIs("Binary"); + } + elsif($In::Opt{"SrcOnly"}) { + compareAPIs("Source"); + } + exitReport(); +} + +scenario(); diff --git a/abi-compliance-checker-2.4/doc/Changelog.html b/abi-compliance-checker-2.4/doc/Changelog.html new file mode 100644 index 0000000..0514d7a --- /dev/null +++ b/abi-compliance-checker-2.4/doc/Changelog.html @@ -0,0 +1,2571 @@ + + + + + + ABI Compliance Checker: History + + + + + + +Fork me on GitHub + +
+ +

ABI Compliance
Checker: History

+

+
+ +Version 2.3 (May 15, 2018)
+Improvements +

    +
  • + Support for GCC 8 +
  • +
  • + Use -fdump-lang-class instead of -fdump-class-hierarchy +
  • +
  • + Enable internal mangling of C++ funcs for all future GCC versions +
  • +
  • + Added -keep-reserved option to report changes in reserved fields +
  • +
  • + Fixed license to LGPL 2.1 +
  • +
+Bug Fixes +
    +
  • + Fix detection of GCC 7 compiled with --with-gcc-major-version-only +
  • +
  • + Redirect stderr of ‘objdump -f’ to null +
  • +
  • + Escape braces in regex for compatibility with future Perl 5 versions +
  • +
  • + Fixed internal mangling +
  • +
+
+ +Version 2.2 (August 30, 2017)
+Improvements +
    +
  • + Improved support for Fortran +
  • +
+Bug Fixes +
    +
  • + Fixed analysis of inline functions +
  • +
  • + Fixed analysis of calling conventions +
  • +
  • + Fixed handling of C++ keywords in C code +
  • +
  • + Fixed -lang option +
  • +
  • + Fixed handling of errors when compressing ABI dumps +
  • +
  • + Fixed style of the report +
  • +
+
+ +Version 2.1 (June 17, 2017)
+Improvements +
    +
  • + Show added v-table symbols for public classes +
  • +
+Bug Fixes +
    +
  • + Fixed analysis of static methods +
  • +
  • + Fixed analysis of typedefs +
  • +
  • + Fixed analysis of zero-size structs +
  • +
  • + Fixed logs +
  • +
  • + Fixed analysis of static libraries on Windows +
  • +
  • + Fixed search for files on Windows +
  • +
+Other +
    +
  • + Documented -skip-internal-types option in -help +
  • +
  • + Add noindex meta tag to report if no changes detected +
  • +
+
+ +Version 2.0 (January 28, 2017)
+Improvements +
    +
  • + Code refactoring +
  • +
  • + Works faster on big libraries +
  • +
  • + Added a module to create ABI dump from AST tree +
  • +
  • + Added a module to create AST dump +
  • +
  • + Added a module to parse GCC AST +
  • +
  • + Added a module to find system files and automatically generate include paths +
  • +
  • + Added a module to mangle C++ symbols +
  • +
  • + Added a module to read ELF binaries +
  • +
  • + Added a module to handle type attributes +
  • +
  • + Added a module to handle XML descriptors +
  • +
  • + Added a module to filter symbols +
  • +
  • + Added a module to handle input data +
  • +
  • + Added a module for logging +
  • +
  • + Extended test suite +
  • +
  • + Partial support for GCC 6 +
  • +
  • + Improved support for Solaris +
  • +
  • + Compare versioned data types +
  • +
+New Options +
    +
  • + Added -filter option: a path to XML descriptor with skip_* rules to filter analyzed symbols in the report +
  • +
  • + Added -keep-cxx option to check _ZS*, _ZNS* and _ZNKS* symbols +
  • +
+Bug Fixes +
    +
  • + Fixed automatic generation of include paths +
  • +
  • + Fixed report for removed virtual symbols +
  • +
  • + Fixed XML-format ABI dumps +
  • +
  • + Fixed source-compatibility reports +
  • +
  • + Fixed counter of checked data types +
  • +
  • + Fixed lists of affected symbols +
  • +
  • + Fixed analysis of standard C++ libraries +
  • +
  • + Fixed analysis of added and removed virtual methods +
  • +
  • + Fixed style of the report +
  • +
  • + Fixed analysis of alias symbols +
  • +
  • + Uncover changed typedefs properly +
  • +
  • + Fixed Parameter_From_Register and Parameter_To_Register rules +
  • +
  • + Fixed analysis of data types derived from template instances +
  • +
  • + Enable -headers-only option automatically if header file is used as input library descriptor +
  • +
  • + Fixed analysis of template instances +
  • +
  • + Fixed analysis of static data +
  • +
  • + Fixed error message if modules are not installed +
  • +
  • + Fixed analysis of versioned symbols +
  • +
  • + Fixed -ext option +
  • +
  • + Fixed -use-dumps option +
  • +
  • + Fixed -debug option +
  • +
  • + Fixed console output +
  • +
+Other +
    +
  • + Removed support for too old ABI dumps +
  • +
+
+ +Version 1.99.25 (October 07, 2016)
+New Options +
    +
  • + -test-abi-dumper: compare ABI dumps created by the ABI Dumper tool +
  • +
+Bug Fixes +
    +
  • + Do not list a symbol as removed in the source-compatibility report if it is presented in the dynsym table +
  • +
+
+ +Version 1.99.24 (October 04, 2016)
+Improvements +
    +
  • + Support for Clang +
  • +
  • + Improved a module to compare operating systems +
  • +
  • + Improved support for C++ keywords in C code +
  • +
  • + Improved support for Windows +
  • +
  • + Improved support for MinGW +
  • +
+New Options +
    +
  • + -skip-typedef-uncover: do not report a problem if type is covered or uncovered by typedef (useful for broken debug info) +
  • +
  • + -mingw-compatible: if input header files are compatible with the MinGW GCC compiler, then you can tell the tool about this and speedup the analysis +
  • +
  • + -skip-unidentified: skip header files in 'headers' and 'include_preamble' sections of the XML descriptor that cannot be found +
  • +
  • + -disable-constants-check: do not check for changes in constants +
  • +
  • + -skip-added-constants: do not detect added constants +
  • +
  • + -skip-removed-constants: do not detect removed constants +
  • +
+Bug Fixes +
    +
  • + Do not show GCC version in the source compatibility report +
  • +
  • + Renamed CPU Type column to Arch in the report +
  • +
  • + Support for ABI dumps v3.3 +
  • +
  • + The -cpp-compatible option is now enabled by default +
  • +
+
+ +Version 1.99.23 (August 15, 2016)
+Improvements +
    +
  • + Improved generation of quick empty reports +
  • +
  • + Improved SysCheck.pm module for analysis of operating systems +
  • +
  • + Improved support for Windows 10 +
  • +
+New Options +
    +
  • + -disable-quick-empty-report: do not generate quick empty report if input ABI dumps are equal +
  • +
+Bug Fixes +
    +
  • + Fixed lists of affected symbols in the XML-format report. +
  • +
+
+ +Version 1.99.22 (July 04, 2016)
+Improvements +
    +
  • + New style of the report +
  • +
+New Options +
    +
  • + -old-style: generate old-style report +
  • +
+Bug Fixes +
    +
  • + Fixed handling of patterns in -skip-* options +
  • +
  • + Fixed rounding of the BC rate in the report +
  • +
+
+ +Version 1.99.21 (May 26, 2016)
+Improvements +
    +
  • + Up to 4 times faster on big libraries +
  • +
  • + Show compatibility rate instead of verdict in the report +
  • +
  • + Highlight the numbers of high/medium/low severity problems in the report +
  • +
  • + Show percentage of affected methods in the problem description +
  • +
+New Options +
    +
  • + -count-symbols: count public symbols in the ABI dump +
  • +
+Bug Fixes +
    +
  • + Fixed quick comparison of equal ABI dumps +
  • +
+
+ +Version 1.99.20 (May 14, 2016)
+Improvements +
    +
  • + Use regular expressions instead of wildcards in -skip-* options +
  • +
+
+ +Version 1.99.19 (April 18, 2016)
+Improvements +
    +
  • + Support for GCC 5.1 +
  • +
+Misc +
    +
  • + Separated LICENSE file +
  • +
  • + Removed obsolete descriptors from the package +
  • +
  • + Updated docs +
  • +
+
+ +Version 1.99.18 (April 03, 2016)
+Bug Fixes +
    +
  • + Fixed comparison of qualifiers in parameter data types +
  • +
  • + Fixed problem descriptions in the report +
  • +
  • + Reduced size of the report +
  • +
  • + Fixed console output +
  • +
+
+ +Version 1.99.17 (March 12, 2016)
+Improvements +
    +
  • + Added a check for changes in parameters of function pointers +
  • +
+Bug Fixes +
    +
  • + Fixed comparison of virtual table entries +
  • +
  • + Do not show time stamp in the report +
  • +
  • + Fixed -skip-symbols option +
  • +
  • + Fixed detection of the GCC version number +
  • +
+
+ +Version 1.99.16 (January 28, 2016)
+Improvements +
    +
  • + Do not check private part of the ABI when comparing ABI dumps created by the ABI Dumper tool with use of the -public-headers option +
  • +
+New Options +
    +
  • + -check-private-abi: enable check of the private ABI +
  • +
+Bug Fixes +
    +
  • + Fixed counting of checked data types +
  • +
+
+ +Version 1.99.15 (December 11, 2015)
+Bug Fixes +
    +
  • + Fixed hang on some template instances +
  • +
  • + Fixed support for old ABI dumps (formatting of data types) +
  • +
  • + Fixed false positives in the report if input ABI dumps contain __unknown__ types +
  • +
+
+ +Version 1.99.14 (November 01, 2015)
+New Options +
    +
  • + -skip-types: set list of types that should not be checked +
  • +
+Bug Fixes +
    +
  • + Fixed formatting of symbol signatures +
  • +
  • + Fixed checks of template types +
  • +
+
+ +Version 1.99.13 (October 18, 2015)
+Improvements +
    +
  • + Check public symbols only if comparing public ABI dumps generated by the ABI dumper (with -public-headers option) +
  • +
  • + Added more metadata to the report +
  • +
+New Options +
    +
  • + -skip-internal-types: do not check data types matched by the pattern +
  • +
+Bug Fixes +
    +
  • + Fixed Type_Became_Opaque, Parameter_Changed_Register, Parameter_From_Register and Parameter_To_Register rules +
  • +
  • + Fixed method signatures in the report (C++) +
  • +
  • + Fixed -headers-list option +
  • +
  • + Option -skip-internal renamed to -skip-internal-symbols +
  • +
  • + Fixed false positives if vtable-dumper generates incomplete data +
  • +
  • + Fixed Makefile +
  • +
+
+ +Version 1.99.12 (September 20, 2015)
+Bug Fixes +
    +
  • + Fixed title of the report +
  • +
  • + Print warning if version number is not set in the ABI dump +
  • +
+
+ +Version 1.99.11 (September 12, 2015)
+Bug Fixes +
    +
  • + Fixed Removed_Field_And_Layout rule +
  • +
  • + Fixed -skip-symbols option +
  • +
+
+ +Version 1.99.10 (September 08, 2015)
+Improvements +
    +
  • + Improved style of the report +
  • +
  • + New documentation +
  • +
  • + Added more test cases +
  • +
+New Options +
    +
  • + -types-list: specify a list of types that should be checked +
  • +
  • + -gcc-options: specify additional compiler options +
  • +
+Bug Fixes +
    +
  • + Limited number of affected symbols shown for each BC problem in the report +
  • +
  • + Fixed check of the DataType_Size_And_Stack rule +
  • +
  • + Fixed checks of base classes +
  • +
  • + Carefully substitute parameters in rules +
  • +
  • + Allow to check for source-compatibility in the -cmp-systems mode by specifying both -bin and -src options +
  • +
  • + Fixed sorting of problems listed in the report +
  • +
  • + Limit number of checked types if -headers-list option is specified +
  • +
  • + Fixed -headers-list and -skip-symbols options +
  • +
  • + Fixed cross-platform issues +
  • +
  • + Set LANG=C.UTF-8 for objdump +
  • +
  • + Fixed list of headers in the report +
  • +
  • + Fixed number of checked headers in the report summary. Removed support for old dumps < 1.18 +
  • +
  • + Renamed -lib-full option to -title +
  • +
  • + Carefully detect architecture of input objects and word size +
  • +
  • + Corrected detection of the GCC target architecture +
  • +
  • + More accurate checking of ABI dump names +
  • +
  • + Fixed regressions with SysCheck.pm module +
  • +
  • + Removed obsolete code and options +
  • +
  • + Fixed the objdump command line when the path contains spaces +
  • +
  • + Simplified Makefile +
  • +
  • + Do not install system descriptors to PREFIX/share +
  • +
  • + Take -relpath option into account in the SysCheck.pm module +
  • +
  • + Fixed CSS styles of the CmpSystems module +
  • +
+
+ +Version 1.99.9 (January 23, 2014)
+New Options +
    +
  • + -skip-internal: do not check internal interfaces matched by the pattern +
  • +
+Bug Fixes +
    +
  • + Fixed duplicated entries in the XML report +
  • +
  • + Reduced size of the XML report +
  • +
  • + Fixed duplicated problems related to changed size of a global data +
  • +
  • + Options -v1 and -v2 can now be used when comparing ABI dumps to change library versions shown in the report +
  • +
  • + Fixed false positives with the size change of a template instance type +
  • +
  • + Fixed false positives with the change of a parameter/field type name +
  • +
+
+ +Version 1.99.8.5 (October 03, 2013)
+Improvements +
    +
  • + Optimized performance and memory usage (up to 90%) on input objects with a huge number of changes and deep data type trees (e.g. Linux kernel) +
  • +
  • + Partial support for GCC 4.8.{0-1}, waiting for a fix for the bug 57850 in the next GCC versions +
  • +
  • + Support for incomplete ABI dumps +
  • +
+New Options +
    +
  • + -affected-limit +
  • +
  • + -cpp-incompatible +
  • +
+Bug Fixes +
    +
  • + Fixed identification of template constructors and destructors +
  • +
  • + Do not show "this" first argument of methods in the report +
  • +
  • + Corrected descriptions of affected symbols in the report +
  • +
  • + Fixed false alarms on changed offset of parameters +
  • +
  • + Do not hang on class A<N>:public A<N-1> +
  • +
  • + Corrected identification of header files in the include_preamble +
  • +
  • + Corrected comparison of function pointer types +
  • +
  • + Corrected rule Global_Data_Size +
  • +
+Other +
    +
  • + Code refactoring +
  • +
+
+ +Version 1.99.7 (July 01, 2013)
+Improvements +
    +
  • + Added missed fields of template instance types to the ABI dump +
  • +
  • + Improved support for old ABI dumps +
  • +
  • + Added Struct_Field_Size_Increased rule +
  • +
  • + Support for vector types (GCC extension) +
  • +
  • + Removed duplicates from the ABI dump +
  • +
  • + Corrected visualization of v-table content in the report +
  • +
  • + Corrected identification of target headers +
  • +
  • + Corrected source-compatibility check +
  • +
  • + Performance optimization (5%) +
  • +
+New Options +
    +
  • + -check - to check completeness of the ABI dump +
  • +
+Bug Fixes +
    +
  • + Fixed default arguments of methods (broken in 1.99.1 due to added "this" parameter) +
  • +
+Other +
    +
  • + Extended test suite +
  • +
+
+ +Version 1.99.1 (June 07, 2013)
+Improvements +
    +
  • + Support for ABI Dumper 0.97 +
  • +
  • + Show added/removed inline virtual functions in the binary compatibility report +
  • +
  • + Added "this" hidden parameter to non-static class methods in the ABI dump +
  • +
+Bug Fixes +
    +
  • + Fixed XmlDump module +
  • +
  • + Fixed CallConv module +
  • +
+Other +
    +
  • + Support for old ABI dumps +
  • +
+
+ +Version 1.99 (May 24, 2013)
+Improvements +
    +
  • + Support for ABI Dumper 0.95 +
  • +
  • + Added 14 binary-compatibility rules +
  • +
  • + Added 10 source-compatibility rules +
  • +
  • + Improved model of type alignment +
  • +
  • + Changed version of ABI dump format to 3.0 +
  • +
  • + Added _vptr member to virtual classes +
  • +
  • + Added constants defined by GCC to ABI dump +
  • +
  • + Improved analysis of constants +
  • +
+New Options +
    +
  • + -skip-symbols +
  • +
+Bug Fixes +
    +
  • + Increased severity of Field_Became_Non_Mutable rule +
  • +
+Other +
    +
  • + Support for old ABI dumps +
  • +
  • + Extended test suite (+11 test cases) +
  • +
  • + Code cleaning +
  • +
+
+ +Version 1.98.8 (February 07, 2013)
+Improvements +
    +
  • + Show added and removed constants (#defines) in the report +
  • +
  • + Show changes in unnamed enumerations +
  • +
  • + Avoid false alarm about renamed field if old name of this field is defined to new (SC) +
  • +
  • + Recursive comparing of structured data types in *_Format rules +
  • +
  • + Added Typedef_BaseType_Format rule to check format changes in the typedef base type +
  • +
+New Options +
    +
  • + -tolerant +
  • +
  • + -tolerance +
  • +
+Bug Fixes +
    +
  • + Increased severity of Parameter_BaseType_And_Size BC rule (Low to Medium) +
  • +
  • + Increased severity of Added_Field SC rule (Safe to Low) +
  • +
  • + Corrected handling of the tool error codes in the test suite +
  • +
  • + Corrected handling of relative paths in the descriptor options +
  • +
  • + Skipping linker-related options in gcc_options option of the descriptor +
  • +
  • + Corrected internal mangler for C++ functions +
  • +
  • + Corrected conditions for enabling of C++ compatibility mode +
  • +
  • + Corrected handling of C++ keywords in C-code +
  • +
  • + Corrected -extended option +
  • +
  • + Corrected Typedef_BaseType rule +
  • +
  • + Corrected parsing of default function arguments +
  • +
  • + Do not check presence of archive utilities if not used +
  • +
  • + Other fixes +
  • +
+Other +
    +
  • + Extended test suite +
  • +
  • + Code cleaning +
  • +
+
+ +Version 1.98.7 (December 14, 2012)
+Improvements +
    +
  • + Extended extra info dumped by -extra-info option +
  • +
  • + Extended additional info dumped by -extra-dump option +
  • +
  • + Added specifiers for structs, unions and enums in the report and ABI dump +
  • +
  • + Improved support for old ABI dumps +
  • +
+Bug Fixes +
    +
  • + Corrected -debug option +
  • +
  • + Corrected creating of archives with ABI dumps +
  • +
  • + Corrected parsing of includes in header files +
  • +
  • + Corrected processing of undefined symbols +
  • +
  • + Corrected -app option +
  • +
  • + Corrected processing of default paths to system libraries +
  • +
  • + Corrected Makefile +
  • +
  • + Other fixes +
  • +
+Other +
    +
  • + Code cleaning and refactoring +
  • +
+
+ +Version 1.98.6 (December 04, 2012)
+Improvements +
    +
  • + Extended -extra-info and -extra-dump options +
  • +
+Bug Fixes +
    +
  • + Corrected processing of input XML descriptor +
  • +
  • + Corrected "Parameter_Default_Value_Removed" rule +
  • +
  • + Other fixes +
  • +
+Other +
    +
  • + Code cleaning +
  • +
+
+ +Version 1.98.5 (November 30, 2012)
+Improvements +
    +
  • + Support for Mac OS X 10.8 +
  • +
  • + Support for OpenBSD +
  • +
  • + Support for old GCC versions <= 4.2 +
  • +
  • + Added "throw" and "weak" attributes of methods to ABI dump +
  • +
+New Options +
    +
  • + -extra-info +
  • +
  • + -extra-dump +
  • +
  • + -force +
  • +
+Bug Fixes +
    +
  • + Corrected order of user-defined include paths +
  • +
  • + Corrected internal C++ mangler +
  • +
  • + Removed false positives of the "Parameter_Type_Format" rule +
  • +
  • + Other fixes +
  • +
+Other +
    +
  • + Extended test suite +
  • +
  • + Code cleaning +
  • +
  • + Docs cleaning +
  • +
+
+ +Version 1.98.4 (October 18, 2012)
+Improvements +
    +
  • + Optimization of memory usage (5%-10%) and performance (5%-10%) +
  • +
  • + Added "Used Reserved Field" rule of binary compatibility analysis +
  • +
  • + Improved design of the operating system compatibility report +
  • +
  • + Added meta descriptors for 334 libraries +
  • +
+Bug Fixes +
    +
  • + Fixed an issue with diagnostics of added virtual functions +
  • +
  • + Corrected the list of functions affected by the compatibility problem +
  • +
  • + Avoid false alarm about removed function if this function became macro (SC) +
  • +
  • + Corrected parser of C header files +
  • +
  • + Other fixes +
  • +
+Other +
    +
  • + Extended test suite +
  • +
  • + Code cleaning +
  • +
+
+ +Version 1.98.3 (July 19, 2012)
+Improvements +
    +
  • + Implemented a model of calling conventions on x86 and x86_64 +
  • +
  • + Improved diagnostics of changes in function parameters and return value (distribution on registers and stack) +
  • +
+Bug Fixes +
    +
  • + Corrected parser of C++ header files to detect non-member functions inside namespaces +
  • +
+Other +
    +
  • + Added requirement for Ctags (5.8 or newer) +
  • +
  • + Code cleaning +
  • +
+
+ +Version 1.98.2 (June 26, 2012)
+Improvements +
    +
  • + Support for reading ABI dumps in XML format +
  • +
  • + Automatic enabling of c++0x support if needed to compile headers +
  • +
+Bug Fixes +
    +
  • + Corrected XML and Perl (default) formats of ABI dumps +
  • +
  • + Improved support for old ABI dumps +
  • +
  • + Improved -dump-system option +
  • +
+Other +
    +
  • + Improved documentation +
  • +
+
+ +Version 1.98.1 (June 18, 2012)
+Bug Fixes +
    +
  • + Removed symbols marked as LOCAL in a shared library from lists of added/removed symbols in source-compatibility report +
  • +
  • + Fixed a false positive with some removed extern "C" symbols in source-compatibility report +
  • +
  • + Fixed a bug with removed debug/ directory when using -use-dumps and -debug options together +
  • +
  • + Added support for "skip_including" section of target system XML descriptors (modules/Targets) used by -dump-system option +
  • +
  • + Extended XML ABI dumps by size attribute of library symbols +
  • +
+
+ +Version 1.98.0 (June 14, 2012)
+New Features +
    +
  • + Implemented XML format of ABI dumps +
  • +
+New Options +
    +
  • + -dump-format +
  • +
+Bug Fixes +
    +
  • + Improved support for old ABI dumps +
  • +
  • + Other fixes +
  • +
+
+ +Version 1.97.8 (June 08, 2012)
+Improvements +
    +
  • + Redesigned format of ABI dump +
  • +
  • + Optimization of memory usage (5%-10%) and performance (5%-10%) +
  • +
  • + Improved support for GCC 4.0-4.5 +
  • +
  • + Added "add_namespaces" section of the XML descriptor +
  • +
+New Options +
    +
  • + -open +
  • +
  • + -sort +
  • +
+Bug Fixes +
    +
  • + Improved support for old ABI dumps +
  • +
  • + Fixed a bug with auto-detection of include paths when comparing two XML descriptors +
  • +
  • + Corrected output of -debug option +
  • +
  • + Corrected -dump-system and -cmp-systems options +
  • +
  • + Other fixes +
  • +
+Other +
    +
  • + Code cleaning +
  • +
+
+ +Version 1.97.5 (May 14, 2012)
+Improvements +
    +
  • + Extended ABI dumps by source-level type declarations +
  • +
  • + Improved support for old ABI dumps +
  • +
  • + Optimization of memory usage (20%-30%) and performance (10%-20%) +
  • +
+Bug Fixes +
    +
  • + Corrected auto-detection of a set of target headers to check/dump in -headers-only mode +
  • +
  • + Other fixes +
  • +
+Other +
    +
  • + Extended regression test suite +
  • +
  • + Code cleaning +
  • +
+
+ +Version 1.97.4 (April 16, 2012)
+New Features +
    +
  • + Added 16 new binary-compatibility rules +
  • +
  • + Added 17 new source-compatibility rules +
  • +
+Bug Fixes +
    +
  • + Added some missed typedef types to function signatures +
  • +
+Other +
    +
  • + Improved support for old ABI dumps +
  • +
  • + Support for GCC 4.7 +
  • +
  • + Extended regression test suite +
  • +
  • + Code cleaning +
  • +
+
+ +Version 1.97.3 (April 03, 2012)
+Completed Tasks +
    +
  • + Implement source-level compatibility checks +
  • +
  • + Implement 2.0 architecture +
  • +
+New Features +
    +
  • + Added -browse=PROG option +
  • +
  • + Added -xml alias option for --report-format=xml +
  • +
  • + Added -binary option to generate binary-compatibility report only +
  • +
  • + Added -source option to generate source-compatibility report only +
  • +
  • + Added -bin-report-path option: path to binary-compatibility report +
  • +
  • + Added -src-report-path option: path to source-compatibility report +
  • +
+Other Features +
    +
  • + Improved debug mode (-debug) +
  • +
  • + Improved mark-up of the HTML report +
  • +
  • + Improved mark-up of the HTML report +
  • +
  • + Improved support for old ABI dumps +
  • +
+Bug Fixes +
    +
  • + Corrected ABI dumps +
  • +
+
+ +Version 1.96.8 (February 17, 2012)
+Completed Tasks +
    +
  • + Implement XML format of compatibility report +
  • +
  • + Testing on MeeGo 1.2 Harmattan Beta2 +
  • +
+New Features +
    +
  • + Added hidden statistics line to compatibility report for operating systems +
  • +
  • + Added -headers-list option +
  • +
  • + Added -lang option +
  • +
  • + Support for symbolic links in /usr/include and /usr/lib +
  • +
  • + Added "skip_include_paths" section of the XML-descriptor +
  • +
  • + Added "skip_including" section of the XML-descriptor +
  • +
  • + Added -list-affected option to create plain list of incompatible symbols +
  • +
  • + Added -quiet option to print all errors and warnings to the log file instead of stderr and stdout +
  • +
  • + Added -stdout option to print results to stdout +
  • +
  • + Added an option to check binary compatibility in the extended sense +
  • +
  • + Improve diagnostic messages for added base classes with virtual functions +
  • +
  • + Added -update option to installer +
  • +
  • + Added a relative default directory to locate modules after installation +
  • +
  • + Compatibility rate = (high+1/2*medium+1/4*low) / number of symbols +
  • +
+Bug Fixes +
    +
  • + Some symbols with extern "C" linkage are missed in -headers-only mode +
  • +
  • + Changes in global data are not detected under Windows +
  • +
  • + False negative: change global data to be "const" +
  • +
  • + Removed middle enumeration value is reported as renamed +
  • +
  • + False positive: change parameter type from "const int" to "int" +
  • +
  • + Support for old ABI dump format of ACC 1.21.6 +
  • +
  • + The tool doesn't search for included headers in /usr/lib/qt4/include/ +
  • +
  • + False Negative: Header is incompatible with itself +
  • +
  • + Check libstdc++ in -headers-only mode +
  • +
  • + Restrict checked header files in the -headers-only mode +
  • +
  • + Problem with mangled C++ functions using old ABI dump formats in -headers-only mode +
  • +
  • + Incorrect size of method pointer in ABI dumps +
  • +
  • + False negative: add/remove "register" modifier of the parameter +
  • +
  • + Incorrect WORD size when using old ABI dump format +
  • +
  • + Conflict of a static method with a function of the same name in the ABI dump +
  • +
  • + Missed right bracket of "func-ptr" type in HTML report +
  • +
  • + Incorrect report for overridden methods +
  • +
  • + False negative: override a virtual that doesn't come from a primary base +
  • +
  • + False negative: change a function parameter to be "restrict" +
  • +
  • + False negative: change a field to be "volatile" +
  • +
  • + False negative: change "const"-ness of a return value +
  • +
  • + False negative: change "volatile" attribute of a method +
  • +
  • + False positive: removed symbols with inline virtual prototype in the leaf class with default constructor +
  • +
+
+ +Version 1.94 (September 09, 2011)
+Completed Tasks +
    +
  • + Separated regression tests into the module +
  • +
+New Features +
    +
  • + Added Makefile.pl installer +
  • +
+
+ +Version 1.93.8 (September 08, 2011)
+Completed Tasks +
    +
  • + Separated rules DB +
  • +
  • + Testing on Symbian SDK +
  • +
  • + Testing on Windows SDK +
  • +
  • + Support for OS3000 +
  • +
+New Features +
    +
  • + SONAME change in the OS comparison table +
  • +
  • + Added a compatibility percentage to the OS comparison table +
  • +
  • + Added -debug option +
  • +
  • + Added listing of symbols in OS comparison table +
  • +
  • + Use zip format of dumps in Windows +
  • +
  • + Added -dump-system descriptor.xml option +
  • +
  • + Analysis of static libraries +
  • +
  • + Added -sysinfo option +
  • +
  • + Added -component option +
  • +
  • + Added -nostdinc option +
  • +
  • + Added "weakly"- and "almost"-compatible verdicts +
  • +
  • + Added "skip_namespaces" section of the library XML-descriptor +
  • +
  • + Search for modules/ directory in the system +
  • +
+Bug Fixes +
    +
  • + Missed typedefs in the ABI dump using GCC 4.4.1 +
  • +
  • + False negative: change enum member value from zero to non-zero +
  • +
  • + False negative: interchange the positions of two fields in a structure +
  • +
  • + False positive: add a field instead of padding fields +
  • +
  • + Problems with "copied" classes +
  • +
  • + Changes in "private" fields +
  • +
  • + Illegal modulus zero at abi-compliance-checker.pl +
  • +
  • + Incorrect order of include paths +
  • +
  • + Change constness of a class method +
  • +
  • + False negative: change "struct Type" to "union Type" +
  • +
  • + Change parameter type from "..." to "int" +
  • +
  • + Remove/add "const"-qualifier of a method +
  • +
  • + False negative: renamed parameters +
  • +
+Other +
    +
  • + Improved debug mode +
  • +
+
+ +Version 1.23.5 (July 01, 2011)
+Bug Fixes +
    +
  • + Corrected exit codes: 0 - compatible, 1 - incompatible, 2 - error, ... +
  • +
  • + Corrected diagnostic messages for C++ functions with changed signature +
  • +
  • + Fixed regression with C++ non-member functions +
  • +
  • + Removed false positive with overridden private methods +
  • +
  • + Corrected functionality for checking binary compatibility of operating systems +
  • +
  • + Removed false positive for removed default version of a symbol +
  • +
  • + Adapted -dump-system option for MeeGo 1.2 Harmattan +
  • +
  • + Fixed hanging execution on "#include "../../file.h" +
  • +
  • + Fixed incorrect automatic include paths +
  • +
+
+ +Version 1.23 (June 07, 2011)
+New Features +
    +
  • + Added 42 compatibility checks (total: 83) +
  • +
  • + Improved diagnostics of compatibility problems +
  • +
  • + Opened an issue tracker +
  • +
  • + Support for cross-compilers +
  • +
  • + Ported to Mac OS X (10.5) and MS Windows (Xp, Vista, 7) +
  • +
  • + Added a viewer of "real" v-table layouts for changed C++ classes +
  • +
  • + Added functionality to check OS backward compatibility +
  • +
  • + Supports for old-version dump formats (>=1.18) +
  • +
  • + Separated versioning of dump formats +
  • +
  • + Improved design of the compatibility report +
  • +
  • + Improved performance +
  • +
  • + Support for old GCC 3.4.4 +
  • +
+New Options +
    +
  • + -cross-gcc: support for cross-compilers +
  • +
  • + -sysroot: specify the alternative system root directory +
  • +
  • + -dump-system, -cmp-systems: checking OS backward compatibility +
  • +
  • + -use-dumps: check for compatibility using the intermediate dumping +
  • +
  • + -show-retval: show symbol's return value type in the report +
  • +
  • + -old-dumps: support for old-version dumps +
  • +
  • + -test-dump: test for dumping functionality +
  • +
  • + -report-path: change the location of output compatibility report +
  • +
  • + -dump-path: change the location of output ABI dump +
  • +
  • + -log1-path, -log2-path: change the location of output logs +
  • +
+Bug Fixes +
    +
  • + Reduced false positives +
  • +
  • + Support for C-headers containing C++ keywords +
  • +
  • + Corrected automatic include paths for headers +
  • +
+
+ +Version 1.21.12 (April 29, 2011)
+Bug Fixes +
    +
  • + Corrected automatic detection of include paths for header files +
  • +
  • + Removed false positives with overridden virtual functions +
  • +
  • + Corrected processing of typedef type names, fixed potential program hangup +
  • +
  • + Added some missed problems relating to the return type changes of a function +
  • +
  • + Corrected processing of a translation unit dump generated by modern GCC versions +
  • +
  • + Corrected identifying of inline functions (using the -fkeep-inline-functions GCC option) +
  • +
  • + Corrected parser for C++: analysis of const global data and functions inside a namespace +
  • +
  • + Corrected names and v-table checks for template types +
  • +
  • + Corrected checks for changes in enumerations +
  • +
  • + Corrected ABI dumping and sorting of dumps +
  • +
  • + Corrected analysis of added/removed virtual functions +
  • +
  • + Corrected help message and documentation +
  • +
  • + Documentation has been moved to "doc/" subdirectory +
  • +
  • + Using File::Temp for storing temporary files +
  • +
  • + Support for latest GCC 4.6.0 and old GCC v3.x series +
  • +
  • + Fixed infinite loop finding the path for "which" command in the system +
  • +
  • + Corrected distinction of descriptor kinds (headers, libraries, directories and XML-descriptors) +
  • +
  • + Corrected processing of "include_paths" section of the XML-descriptor +
  • +
+
+ +Version 1.21 (August 19, 2010)
+New Features +
    +
  • + Added -check-implementation option: compare disassembled binary code to detect changes in the interface implementation +
  • +
  • + Added -objects-only option: compare shared objects without header files +
  • +
  • + Added -v1 and -v2 options: specify version number outside the descriptor +
  • +
  • + Improved help message +
  • +
  • + Improved performance of the tool +
  • +
  • + Removed template instances and stdc++ interfaces from the report (C++) +
  • +
  • + Added README.html and CHANGES.html to the package +
  • +
+Bug Fixes +
    +
  • + Corrected ABI compatibility report +
  • +
  • + Corrected interface names and versions in the report +
  • +
  • + Corrected number of problems in report summary +
  • +
  • + Corrected ABI dump +
  • +
+
+ +Version 1.20 (August 30, 2010)
+New Features +
    +
  • + Added "defines" section of the library descriptor: this section allows one to add defines at the headers compiling stage +
  • +
+Bug Fixes +
    +
  • + Corrected reports about added/withdrawn members in the structure types and added/withdrawn parameters +
  • +
  • + Corrected report about added/withdrawn virtual functions if -headers_only option specified +
  • +
  • + Corrected processing of header paths containing special characters +
  • +
+
+ +Version 1.19 (July 22, 2010)
+New Features +
    +
  • + Added -library_full_name option to display full library name in title of the report +
  • +
  • + Added -relpath option to replace the {RELPATH} in the descriptor for ABI dumping +
  • +
  • + Added "skip_libs" section of the library descriptor: this section contains a list of shared objects and/or directories with shared objects that should not be processed +
  • +
  • + Improved performance on big libraries +
  • +
+Bug Fixes +
    +
  • + Removed duplicated problems from the report +
  • +
  • + Corrected names of the template instances +
  • +
  • + Corrected checking of reference type changes +
  • +
  • + Corrected titles in the report +
  • +
  • + Corrected size of some array types +
  • +
  • + Corrected checking of added/withdrawn members in the structure types with reserved members +
  • +
  • + Corrected checking of added/withdrawn parameters +
  • +
+
+ +Version 1.18 (June 25, 2010)
+New Features +
    +
  • + Added -relpath1 and -relpath2 options to replace the {RELPATH} in the descriptors; old option -relpath was removed +
  • +
  • + Added "add_include_paths" section of the library descriptor: this section contains a list of include paths that should be added to the automatically detected include paths +
  • +
+Bug Fixes +
    +
  • + Added some previously missed compatibility problems in the report +
  • +
  • + Corrected techniques for auto-detection of header file dependencies (include paths) +
  • +
  • + Removed problems relating to the changes in the temporary header files +
  • +
  • + Corrected interface signatures in the report +
  • +
  • + Corrected checking of added/withdrawn parameters +
  • +
  • + Corrected changes in the virtual tables of the libraries with symbol versioning +
  • +
  • + Corrected checking of complex namespaces changes (C++) +
  • +
  • + Added namespaces information to the ABI dump +
  • +
+
+ +Version 1.17.2 (June 16, 2010)
+Bug Fixes +
    +
  • + Fixed -separately option +
  • +
  • + Corrected permissions of LICENSE file +
  • +
  • + Corrected tool description +
  • +
+
+ +Version 1.17.1 (June 09, 2010)
+New Features +
    +
  • + Added -relpath option for adding prefixes to the paths in the library descriptor +
  • +
+Bug Fixes +
    +
  • + Corrected checking of added/withdrawn parameters +
  • +
  • + Corrected processing of mixed C/C++ header sets +
  • +
  • + Corrected checking of parameter type changes +
  • +
+
+ +Version 1.17 (June 08, 2010)
+New Features +
    +
  • + Visualizing of the serious changes (added/withdrawn parameters) in the interface signature +
  • +
  • + Recursive analysis of constant changes +
  • +
  • + Separated stderr and stdout streams of the tool +
  • +
  • + Added "skip_constants" section of the library descriptor to skip checking of some constants +
  • +
  • + Added -params option to add function parameter names to the report +
  • +
+Bug Fixes +
    +
  • + Corrected analysis of virtual table layout changes +
  • +
  • + Corrected analysis of parameter type changes +
  • +
  • + Corrected complex array type names +
  • +
  • + Corrected typedef names +
  • +
  • + Corrected analysis of structure layout changes +
  • +
  • + Fixed tool hanging on some C++ headers (with many namespaces) +
  • +
  • + Corrected analysis of Glibc headers +
  • +
  • + Corrected analysis of library language changes (if added some C++ headers) +
  • +
  • + Corrected descriptions of some compatibility problems +
  • +
  • + Corrected analysis of added/withdrawn parameters in C headers +
  • +
+
+ +Version 1.16 (May 05, 2010)
+New Features +
    +
  • + Added -strict option for treating all compatibility warnings as problems +
  • +
  • + Added -dumpversion option for printing tool version and don't do anything else +
  • +
  • + Ignoring hidden .svn, .git, .bzr, .hg, and CVS directories +
  • +
  • + Improved header files sorting for protecting from compilation errors on the intermediate phase of temporary header file compilation +
  • +
  • + Improved techniques for auto-detection of header file dependencies (include paths) +
  • +
  • + Ignoring problems related to changes of constants (defines) describing library version (*_VERSION_*, *_COPYRIGHT_* and other) +
  • +
  • + New internal test cases +
  • +
+Bug Fixes +
    +
  • + Checking of some previously missed C++ namespaces +
  • +
  • + Removed hidden "void const** __vtt_parm" parameters from signatures of some constructors +
  • +
  • + Corrected dumping of C++ classes ABI +
  • +
  • + Corrected checking of pure virtual destructors +
  • +
  • + Removed unnecessary built-in constants from the ABI dump +
  • +
+
+ +Version 1.15 (March 26, 2010)
+New Features +
    +
  • + The license was changed to dual GNU GPL and LGPL +
  • +
  • + Added "skip_headers" section of the library descriptor +
  • +
+Bug Fixes +
    +
  • + Corrected processing of "include_paths" section of the library descriptor +
  • +
  • + Corrected processing of relative paths in the "headers" and "include_paths" sections of the descriptor +
  • +
  • + Directory with temporary files renamed from "temp" to hidden ".tmp_dir" +
  • +
  • + Corrected processing of shared object dependencies +
  • +
  • + Corrected processing of some previously missed functions and conversion operators in C++ +
  • +
  • + Corrected internal test suite +
  • +
  • + Corrected some error messages +
  • +
+
+ +Version 1.14 (March 03, 2010)
+New Features +
    +
  • + Added techniques to auto-detect dependencies of a header file (include paths); providing of "include_paths" section of the descriptor is not necessary for now +
  • +
  • + Ported to FreeBSD and Haiku +
  • +
  • + Added check for gcc/g++ version (>=3.0.0) +
  • +
  • + Added sorting of interface problems by namespace in the report (C++) +
  • +
  • + Improved internal test suite +
  • +
  • + Added log for describing tool actions and occurred errors +
  • +
  • + Added exit error code (high/medium risk for ABI break) +
  • +
+Bug Fixes +
    +
  • + Corrected ABI dumping +
  • +
  • + Corrected styles of the report +
  • +
+
+ +Version 1.13 (February 16, 2010)
+New Features +
    +
  • + Added -version option +
  • + +
+Bug Fixes +
    +
  • + Corrected processing of tab characters in the descriptor +
  • +
  • + Corrected help message +
  • +
  • + Corrected descriptor template structure +
  • +
  • + Corrected error and warning messages +
  • +
  • + Corrected processing of shared object dependencies +
  • +
+
+ +Version 1.12 (December 04, 2009)
+New Features +
    +
  • + New help message +
  • +
  • + Highlighting of [in-charge], [not-in-charge] constructors and destructors in the report was improved +
  • +
  • + New option -time for enabling time measurements +
  • +
  • + New internal test cases +
  • +
+Bug Fixes +
    +
  • + Corrected classification of compatibility problems in the report +
  • +
  • + Reduced priority of problems related to changes in a method's object +
  • +
  • + Corrected complex template type names +
  • +
+
+ +Version 1.11 (November 10, 2009)
+New Features +
    +
  • + Added -app option to check portability of applications to the new library version +
  • +
  • + Memory usage decreased twice +
  • +
+Bug Fixes +
    +
  • + Corrected checking of added middle structure members +
  • +
  • + Corrected names of template types (with intrinsic, bool and string parameters) +
  • +
  • + Corrected highlighting of function signatures in the report +
  • +
+
+ +Version 1.10 (November 02, 2009)
+New Features +
    +
  • + Checking added/dropped function parameters (C language only) +
  • +
  • + Improved design of the report +
  • +
  • + New internal test cases +
  • +
+Bug Fixes +
    +
  • + Incorrect checking of redefined virtual functions and differences in parameter types +
  • +
  • + More careful checking of withdrawn interfaces using shared library dependencies +
  • +
+
+ +Version 1.9 (October 12, 2009)
+Improvements +
    +
  • + Improved design of ABI compliance report +
  • +
  • + Improved algorithms of checking parameter/field type change +
  • +
  • + New internal test cases +
  • +
+Bug Fixes +
    +
  • + Fixed incorrect names of typedefs and function pointer types +
  • +
  • + Checking of some previously missed C++ functions +
  • +
  • + Removed some false positives from the report (for anon-types) +
  • +
+
+ +Version 1.8 (September 29, 2009)
+New Features +
    +
  • + Added ability to specify a file with a list of interfaces that should be checked +
  • +
+Bug Fixes +
    +
  • + Size of ABI info dumps have been reduced through removing of unnecessary information +
  • +
  • + Incorrect names of template instances and function pointer types +
  • +
  • + Incorrect positions of function parameters in the "Interface Problems" section of the report +
  • +
  • + Removed some false positives from the report +
  • +
  • + Incorrect handling of special symbols in the paths to header files and shared objects +
  • +
+
+ +Version 1.7 (September 11, 2009)
+New Features +
    +
  • + Checking of incorrect symbols versioning +
  • +
  • + Checking the values of defines (constants) +
  • +
  • + Ability to check header files without shared objects; It is easy to run, but may provide a low quality report with a lot of false positives and without detecting of added/withdrawn interfaces +
  • +
  • + Number of checked interfaces and data types in the report +
  • +
  • + Added tests for checking new features +
  • +
+Bug Fixes +
    +
  • + Incorrect processing of duplicated headers in the input set (headers with the same name but different paths) +
  • +
  • + Incorrect header files include order +
  • +
  • + Sorting in the ABI dumps +
  • +
  • + Incorrect processing of redefined virtual methods +
  • +
  • + Incorrect processing of anon types +
  • +
  • + Absence of some necessary information about C++ functions in the ABI dumps +
  • +
+
+ +Version 1.6 (August 31, 2009)
+Bug Fixes +
    +
  • + Corrected processing of relative paths in the library descriptor +
  • +
  • + Display machine hardware name instead of processor type in the report +
  • +
  • + Fixed grammar/spelling errors +
  • +
  • + Renamed "internal_interfaces" section of the library descriptor to "skip_interfaces" +
  • +
  • + Cosmetic changes in the code +
  • +
+
+ +Version 1.5 (August 25, 2009)
+Bug Fixes +
    +
  • + Absent information about opaque types and internal interfaces has been added to the ABI dump +
  • +
  • + Fixed style of the report +
  • +
  • + Fixed grammar/spelling +
  • +
  • + Renamed "internal_functions" section of the library descriptor to "internal_interfaces" +
  • +
  • + Renamed outptu ABI dump to "*.abi.tar.gz" (previously it was "*.info.tar.gz") +
  • +
  • + Corrected interface names in the report (for -separately option) +
  • +
+
+ +Version 1.4 (August 18, 2009)
+Improvements +
    +
  • + Added ability to check ABI compliance of library versions located on different machines +
  • +
  • + Header files checking mode by default has been changed: checking all header files together instead of separate checking +
  • +
+Bug Fixes +
    +
  • + Incorrect description for affected interfaces +
  • +
  • + Incorrect virtual table checking +
  • +
+
+ +Version 1.3 (August 14, 2009)
+Bug Fixes +
    +
  • + Incorrect number of binary compatibility problems in the report summary +
  • +
  • + Incorrect design of problem descriptions in the report +
  • +
+
+ +Version 1.1 (August 06, 2009)
+Improvements +
    +
  • + Design of the report has been greatly improved +
  • +
+
+ +Version 1.0 (July 31, 2009)
+Initial version of the tool. + + + +
+
+ +
+ + diff --git a/abi-compliance-checker-2.4/doc/Xml-Descriptor.html b/abi-compliance-checker-2.4/doc/Xml-Descriptor.html new file mode 100644 index 0000000..ca9c392 --- /dev/null +++ b/abi-compliance-checker-2.4/doc/Xml-Descriptor.html @@ -0,0 +1,602 @@ + + + + + + XML Descriptor + + + + + + +Fork me on GitHub + +
+ +

XML-Descriptor

+

+The library descriptor is a simple XML-file that specifies version number, paths to header files and shared libraries and optionally some other information. +

+ +
+
Table of Contents
+ +
+ + +

Primary Sections

+
+
+<version>
+    /* Version of the library */
+</version>
+
+<headers>
+    /* The list of paths to header files or/and
+       directories with header files, one per line */
+</headers>
+
+<libs>
+    /* The list of paths to shared libraries or/and
+       directories with shared libraries, one per line */
+</libs>
+
+
+

+ + +

Optional Sections

+
+
+<include_paths>
+    /* The list of paths to be searched for header files
+       needed for compiling of library headers, one per
+       line. NOTE: If you define this section then the tool
+       will not automatically detect include paths */
+</include_paths>
+
+<add_include_paths>
+    /* The list of include paths that should be added
+    to the automatically detected include paths, one per
+    line */
+</add_include_paths>
+
+<skip_include_paths>
+    /* The list of include paths that will be removed from
+    the list of automatically generated include paths, one
+    per line */
+</skip_include_paths>
+
+<gcc_options>
+    /* Additional GCC options, one per line */
+</gcc_options>
+
+<include_preamble>
+    /* The list of header files that should be included
+    before other headers, one per line. For example, it
+    is a tree.h for libxml2 and ft2build.h for freetype2
+    library */
+</include_preamble>
+
+<defines>
+    /* Add defines at the headers compiling stage, one per
+    line:
+        #define A B
+        #define C D */
+</defines>
+
+<add_namespaces>
+    /* The list of namespaces that should be added to the
+    alanysis  if the tool cannot find them automatically,
+    one per line */ 
+</add_namespaces>
+
+<skip_types>
+    /* The list of data types, that
+    should not be checked, one per line */
+</skip_types>
+
+<skip_symbols>
+    /* The list of functions (mangled/symbol names in C++),
+    that should not be checked, one per line */
+</skip_symbols>
+
+<skip_namespaces>
+    /* The list of C++ namespaces, that
+    should not be checked, one per line */
+</skip_namespaces>
+
+<skip_constants>
+    /* The list of constants that should not be checked,
+    one name per line */
+</skip_constants>
+
+<skip_headers>
+    /* The list of header files and/or directories
+    with header files that should not be checked, one per
+    line */
+</skip_headers>
+
+<skip_libs>
+    /* The list of shared libraries and/or directories
+    with shared libraries that should not be checked, one
+    per line */
+</skip_libs>
+
+<skip_including>
+    /* The list of header files, that cannot be included
+    directly (or non-self compiled ones), one per line */
+</skip_including>
+
+<search_headers>
+    /* List of directories to be searched
+    for header files to automatically
+    generate include paths, one per line */
+</search_headers>
+
+<search_libs>
+    /* List of directories to be searched
+    for shared librariess to resolve
+    dependencies, one per line */
+</search_libs>
+
+<tools>
+    /* List of directories with tools used
+    for analysis (GCC toolchain), one per line */
+</tools>
+
+<cross_prefix>
+    /* GCC toolchain prefix.
+    Examples:
+        arm-linux-gnueabi
+        arm-none-symbianelf */
+</cross_prefix>
+
+
+

+ + +

Examples

+ +libssh: +
+
+<version>
+    0.3.4
+</version>
+
+<headers>
+    /usr/local/libssh/0.3.4/include/
+</headers>
+
+<libs>
+    /usr/local/libssh/0.3.4/lib/
+</libs>
+
+
+

+ +atk: +

+
+<version>
+    1.28.0
+</version>
+
+<headers>
+    /usr/local/atk-1.28.0/include/atk-1.0/atk/atk.h
+</headers>
+
+<libs>
+    /usr/local/atk-1.28.0/lib/
+</libs>
+
+<include_paths>
+    /usr/include/glib-2.0/
+    /usr/lib/glib-2.0/include/
+</include_paths>
+
+
+

+ +libxml2: +

+
+<version>
+    2.7.6
+</version>
+
+<headers>
+    /usr/local/libxml2-2.7.6/include/
+</headers>
+
+<libs>
+    /usr/local/libxml2-2.7.6/lib/libxml2.so.2.7.6
+</libs>
+
+<include_preamble>
+    tree.h
+</include_preamble>
+
+
+

+ +libX11: +

+
+<version>
+    1.3.2
+</version>
+
+<headers>
+    /usr/local/libX11-1.3.2/include/
+</headers>
+
+<libs>
+    /usr/local/libX11-1.3.2/lib/
+</libs>
+
+<include_preamble>
+    Xlib.h
+</include_preamble>
+
+
+

+ +BlackBerry 10 Native SDK: +

+
+<version>
+    10
+</version>
+
+<headers>
+    /home/RIM/bbndk/target_10_0_9_1673/qnx6/usr/include/bb/
+</headers>
+
+<search_headers>
+    /home/RIM/bbndk/target_10_0_9_1673/qnx6/usr/include/
+</search_headers>
+
+<libs>
+    /home/RIM/bbndk/target_10_0_9_1673/qnx6/x86/usr/lib/
+</libs>
+
+<tools>
+    /home/RIM/bbndk/host_10_0_9_404/linux/x86/usr/bin/
+</tools>
+
+<cross_prefix>
+    i486-pc-nto-qnx8.0.0
+</cross_prefix>
+
+
+

+ +libQt5Core: +

+
+<version>
+    5.5.0
+</version>
+
+<headers>
+    /usr/local/Qt-5.5.0/include/QtCore
+</headers>
+
+<libs>
+    /usr/local/Qt-5.5.0/lib/libQt5Core.so.5.5.0
+</libs>
+
+<include_paths>
+    /usr/local/Qt-5.5.0/include/
+</include_paths>
+
+<skip_headers>
+    /private/
+    qt_windows.h
+    qatomic_*
+    *_impl.h
+</skip_headers>
+ 
+<gcc_options>
+    -fvisibility=hidden
+    -fvisibility-inlines-hidden
+    -fPIC
+    -Wall
+    -W
+    -D_REENTRANT
+    -DQT_NO_CAST_FROM_ASCII
+    -DQT_NO_CAST_TO_ASCII
+    -DQT_NO_STL
+    -DQT_SHARED
+</gcc_options>
+
+
+

+ +libxslt: +

+
+<version>
+    1.1.22
+</version>
+
+<headers>
+    /usr/local/libxslt-1.1.22/include/
+</headers>
+
+<libs>
+    /usr/local/libxslt-1.1.22/lib/libxslt.so
+    /usr/local/libxslt-1.1.22/lib/libexslt.so
+</libs>
+
+<include_paths>
+    /usr/include/libxml2/
+</include_paths>
+
+<include_preamble>
+    xsltInternals.h
+</include_preamble>
+
+
+

+ +libxml++: +

+
+<version>
+    2.26.1
+</version>
+
+<headers>
+    /usr/local/libxml++-2.26.1/include/
+    /usr/local/libxml++-2.26.1/lib/libxml++-2.6/include/
+</headers>
+
+<libs>
+    /usr/local/libxml++-2.26.1/lib/
+</libs>
+
+<include_paths>
+    /usr/include/glib-2.0/
+    /usr/lib/glib-2.0/include/
+    /usr/include/glibmm-2.4/
+    /usr/lib/glibmm-2.4/include/
+</include_paths>
+
+
+

+ +pango: +

+
+<version>
+    1.26.0
+</version>
+
+<headers>
+    /usr/local/pango-1.26.0/include/
+</headers>
+
+<libs>
+    /usr/local/pango-1.26.0/lib/
+</libs>
+
+<include_paths>
+    /usr/include/glib-2.0/
+    /usr/lib/glib-2.0/include/
+    /usr/include/cairo/
+    /usr/include/freetype2/
+    /usr/include/X11/
+</include_paths>
+
+<include_preamble>
+    pango.h
+</include_preamble>
+
+
+

+ +gtk+: +

+
+<version>
+    2.18.4
+</version>
+
+<headers>
+    /usr/local/gtk+-2.18.4/include/gtk-2.0/gdk/gdk.h
+    /usr/local/gtk+-2.18.4/include/gtk-2.0/gtk/gtk.h
+    /usr/local/gtk+-2.18.4/include/gail-1.0/
+    /usr/local/gtk+-2.18.4/include/gtk-unix-print-2.0/
+</headers>
+
+<libs>
+    /usr/local/gtk+-2.18.4/lib/
+</libs>
+
+<include_paths>
+    /usr/include/atk-1.0/
+    /usr/include/glib-2.0/
+    /usr/lib/glib-2.0/include/
+    /usr/include/cairo/
+    /usr/include/pango-1.0/
+</include_paths>
+
+
+

+ +glib: +

+
+<version>
+    2.22.2
+</version>
+
+<headers>
+    /usr/local/glib-2.22.2/include/glib-2.0/glib.h
+    /usr/local/glib-2.22.2/include/glib-2.0/glib-object.h
+    /usr/local/glib-2.22.2/include/glib-2.0/gmodule.h
+</headers>
+
+<libs>
+    /usr/local/glib-2.22.2/lib/
+</libs>
+
+<include_paths>
+    /usr/local/glib-2.22.2/lib/glib-2.0/include/
+</include_paths>
+
+
+

+ +libsoup: +

+
+<version>
+    2.28.0
+</version>
+
+<headers>
+    /usr/local/libsoup-2.28.0/include/
+</headers>
+
+<libs>
+    /usr/local/libsoup-2.28.0/lib/
+</libs>
+
+<include_paths>
+    /usr/include/glib-2.0/
+    /usr/lib/glib-2.0/include/
+</include_paths>
+
+
+

+ +allegro: +

+
+<version>
+    4.9.9.1
+</version>
+
+<headers>
+    /usr/local/include/allegro5/allegro.h
+</headers>
+
+<libs>
+    /usr/local/lib/liballegro-4.9.9.so
+</libs>
+
+
+

+ +mathgl: +

+
+<version>
+    1.9.0.1
+</version>
+
+<headers>
+    /usr/local/mathgl-1.9.0.1/include/
+</headers>
+
+<libs>
+    /usr/local/mathgl-1.9.0.1/lib/
+</libs>
+
+<include_paths>
+    /usr/local/gsl-1.9/include/
+</include_paths>
+
+
+

+ +gsl: +

+
+<version>
+    1.9
+</version>
+
+<headers>
+    /usr/local/gsl-1.9/include/
+</headers>
+
+<libs>
+    /usr/local/gsl-1.9/lib/
+</libs>
+
+<include_preamble>
+    stdlib.h
+</include_preamble>
+
+
+

+ +libjpeg: +

+
+<version>
+    7
+</version>
+
+<headers>
+    /usr/local/libjpeg-7/include/jpeglib.h
+</headers>
+
+<libs>
+    /usr/local/libjpeg-7/lib/
+</libs>
+
+<include_preamble>
+    stdio.h
+</include_preamble>
+
+
+

+ +

+ +
+
+ +
+ + diff --git a/abi-compliance-checker-2.4/doc/index.html b/abi-compliance-checker-2.4/doc/index.html new file mode 100644 index 0000000..ca6ece1 --- /dev/null +++ b/abi-compliance-checker-2.4/doc/index.html @@ -0,0 +1,748 @@ + + + + + + + ABI Compliance Checker + + + + + + + Fork me on GitHub + +
+ +
+ + + + +
+ +

ABI Compliance Checker

+ +
+ A tool for checking backward API/ABI compatibility of a C/C++ library +
+ +

+ ABI Compliance Checker (ABICC) is a tool for checking backward binary and source-level compatibility of a C/C++ library. + +

+ The tool analyzes changes in API/ABI (ABI=API+compiler ABI) that may break binary compatibility and/or source compatibility: changes in calling stack, v-table changes, removed symbols, renamed fields, etc. + +

+ The tool can create and compare ABI dumps for header files and shared objects of a library. The ABI dump for a library can also be created by the ABI Dumper tool if shared objects include debug-info. + +

+ Binary incompatibility may result in crashing or incorrect behavior of applications built with an old version of a library if they run on a new one. Source incompatibility may result in recompilation errors with a new library version. The tool is intended for developers of software libraries and Linux maintainers who are interested in ensuring backward compatibility, i.e. allow old applications to run or to be recompiled with newer library versions. + +

+ The tool is a core of the ABI Tracker and Upstream Tracker projects. + +

+ The tool is developed by Andrey Ponomarenko. + +
+
Table of Contents
+ +
+ + +

Downloads

+

The latest release can be downloaded from this page.

+ +

Read-only access to the latest development version:

+ +git clone https://github.com/lvc/abi-compliance-checker.git + + +

License

+

This program is free software. You may use, redistribute and/or modify it under the terms of the GNU LGPL 2.1

+ + +

Supported Platforms

+GNU/Linux, FreeBSD, Mac OS X, MS Windows. + + +

Dependencies

+
    +
  • + G++ (3.0 or newer) +
  • +
  • + GNU Binutils (readelf, c++filt, objdump) +
  • +
  • + Perl 5 +
  • +
  • + Ctags +
  • +
  • + ABI Dumper +
  • +
+ +WARNING: if you are using ccache program (i.e. gcc points to /usr/lib/ccache/gcc) then it should be newer than 3.1.2 or disabled. +

+On Mac OS X the tool also requires Xcode (g++, c++filt, nm and otool). +

+On MS Windows the tool also requires MinGW, MS Visual C++ (dumpbin, undname, cl), Active Perl 5, adding of tool locations to the PATH and execution of vcvars64.bat script (C:\Microsoft Visual Studio 9.0\VC\bin\). + + +

Installation

+

The tool is ready-to-use after extracting the archive.

+ +

You can also use a Makefile to install the tool into the system:

+sudo make install prefix=PREFIX [/usr, /usr/local] +

This command will install the abi-compliance-checker program into the PREFIX/bin system directory and private modules into the PREFIX/share.

+ +

To verify that the tool is installed correctly and it works on your host run:

+cd tmp/ +

+abi-compliance-checker -test +

+ + +

Usage with ABI Dumper

+This new way is based on the analysis of the debug-info from binary objects. It's more reliable, faster and simple way. +

+The analyzed library should be compiled with "-g -Og" GCC options to contain DWARF debug info. +

+Create ABI dumps for both library versions first using the ABI Dumper tool: +

+abi-dumper OLD.so -o ABI-0.dump -lver 0 +

+abi-dumper NEW.so -o ABI-1.dump -lver 1 +

+And then compare ABI dumps to create report: +

+abi-compliance-checker -l NAME -old ABI-0.dump -new ABI-1.dump +

+The compatibility report will be generated to: +

+compat_reports/NAME/V0_to_V1/compat_report.html +

+You can filter out private symbols from the ABI dumps by specifying of additional -public-headers option of the ABI Dumper tool. + + +

Usage (Original)

+The original usage is based on the analysis of header files and shared objects (without debug-info). +

+You should provide XML descriptors for two library versions (v1.xml and v2.xml files) in order to run the analysis. Library descriptor is a simple XML-file that specifies version number, paths to header files and shared libraries and other optional information. An example of the descriptor is the following (0.3.4.xml): +

+

+
+<version>
+    0.3.4
+</version>
+
+<headers>
+    /usr/local/libssh/0.3.4/include/
+</headers>
+
+<libs>
+    /usr/local/libssh/0.3.4/lib/
+</libs>
+
+
+

+ +Command to compare two versions of a library: +

+abi-compliance-checker -lib NAME -old V1.xml -new V2.xml +

+The compatibility report will be generated to: +

+compat_reports/NAME/V1_to_V2/compat_report.html +

+ + +

Tutorial

+An excellent tutorial "ABI: stability check" is available at Les RPM de Remi Blog. See also ABI compliance checker Notes at glibc wiki. +

+ + +

Examples

+ + + + + + + + + + + + + + + + + +
LibraryVersionsReport
libhttpd2.2.31 vs 2.4.1report
libMagick++6.9.0-0 vs 6.9.0-10report
libssh0.3.4 vs 0.3.91report
+

+See more report examples at http://abi-laboratory.pro/tracker/. + + +

Detectable Binary Compatibility Problems

+
    +
  • + +Problems with Data Types +
      +
    • + Structures and Classes +
        +
      • + added/removed fields (change of a memory layout) +
      • +
      • + change of size +
      • +
      • + changed order of fields +
      • +
      • + change of a field type +
      • +
      • + changes in fields (recursive analysis) +
      • +
      +
    • +
    • + Classes +
        +
      • + added/removed virtual functions (change of a v-table layout) +
      • +
      • + change of virtual function position +
      • +
      • + overridden virtual functions +
      • +
      • + added/removed base classes +
      • +
      • + changes in base classes (recursive analysis) +
      • +
      +
    • +
    • + Unions +
        +
      • + added/removed fields +
      • +
      • + change of size +
      • +
      • + change of a field type +
      • +
      • + changes in fields (recursive analysis) +
      • +
      +
    • +
    • + Enumerations +
        +
      • + change of a member value +
      • +
      • + removed/renamed members + +
        +
        +
      • +
      +
    • +
    + +
  • +
  • + +Problems with Symbols +
      +
    • + removed symbols (functions or global data) +
    • +
    • + added/removed parameters +
    • +
    • + change of a parameter/return value type +
    • +
    • + change of default parameter value +
    • +
    • + renamed parameters +
    • +
    • + incorrect version change +
    • +
    • + changed attributes (const, volatile, static, etc.) + +
      +
      +
    • +
    + +
  • +
  • +Problems with Constants (#defines) +
      +
    • + changed value +
    • +
    + +
  • +
+

+ +See "Binary Compatibility Issues With C++" article from KDE TechBase for more info. + + +

Detectable Source Compatibility Problems

+
    +
  • + +Problems with Data Types +
      +
    • + Structures, Classes and Unions +
        +
      • + removed/renamed fields +
      • +
      • + change of a field type +
      • +
      • + changes in fields (recursive analysis) +
      • +
      +
    • +
    • + Classes +
        +
      • + added/removed base classes +
      • +
      • + change access level of a field or method +
      • +
      • + added pure virtual methods +
      • +
      +
    • +
    • + Enumerations +
        +
      • + removed/renamed members + +
        +
        +
      • +
      +
    • +
    +
  • + +
  • + +Problems with Symbols +
      +
    • + removed symbols (functions or global data) +
    • +
    • + added/removed parameters +
    • +
    • + change of a parameter type +
    • +
    • + removed default value of parameter +
    • +
    • + change of return value type +
    • +
    • + changed attributes (const, static, etc.) +
    • +
    + +
  • + +
+ + +

Test Suite

+The tool is tested properly in the ABI Tracker and Upstream Tracker projects, by the community and by the internal test suite: +

+abi-compliance-checker -test +

+There are about 100 basic tests for C and about 200 basic tests for C++ API/ABI breaks. + + +

Create ABI Dumps

+The library ABI is a representation of the library API at the binary level. The ABI dump is a dump of the model of the ABI used in the tool. +

+The ABI dump consists of: + +

    + +
  • + Types Information +
      +
    • + Attributes (name, size, header, access, base types, etc.) +
    • +
    • + Fields (name, type, size, position, alignment, access, specifiers, etc.) +
    • +
    • + V-table structure (offsets, entries) +
    • +
    • + Etc. +
    • +
    +
  • + +
  • + Symbols Information +
      +
    • + Attributes (name, mangled name, header, access, specifiers, etc.) +
    • +
    • + Parameters (name, type, position, alignment, etc.) +
    • +
    • + Etc. +
    • +
    +
  • + +
  • + Etc. +
  • + +
+ +The ABI dump can be used to create a snapshot of a library ABI in the particular environment and then compare it with any other state of the ABI changed due to changes in the environment (compiler version, external libraries, etc.) or changes in the library API (header files). + +

+The typical case is the comparing of two versions of the same library that require incompatible states of the environment (i.e. these versions cannot be installed simultaneously). In this case one can create a dump for one version of the library and then switch the environment and create ABI dump for other version of the library. Two ABI dumps can be compared by the tool to create the API compatibility report. +

+To create an ABI dump use -dump option: +

+abi-compliance-checker -lib NAME -dump VER.xml +

+The ABI dump will be generated to: +

+abi_dumps/NAME/NAME_VER.abi.tar.gz +

+To compare ABI dumps pass them as the descriptors: +

+abi-compliance-checker -lib NAME -old V1.abi.tar.gz -new V2.abi.tar.gz + + +

Report Format

+The tool supports two formats of a compatibility report: HTML (default) and XML. To generate XML report you should specify -xml additional option. +

+The report consists of: + +

    +
  • + Test Info - The library name and compared version numbers. Environment info: GCC version and CPU type. +
  • +
  • + Test Results - Verdict on compatibility. Number of header files, shared libraries, symbols and data types checked by the tool. +
  • +
  • + Problem Summary - Classification of compatibility problems. +
  • +
  • + Added Symbols - The list of added symbols. +
  • +
  • + Removed Symbols - The list of removed symbols. +
  • +
  • + Problems with Data Types - The list of compatibility problems caused by changes in data types (divided by the severity level: High, Medium and Low). List of affected symbols. +
  • +
  • + Problems with Symbols - The list of compatibility problems caused by changes in symbol parameters or attributes (divided by the severity level). +
  • +
  • + Problems with Constants - The list of changed constants (#defines). +
  • +
  • + Other Changes in Data Types - The list of compatible changes in data types. +
  • +
  • + Other Changes in Symbols - The list of compatible changes in symbols. +
  • +
+ + +

Verdict on Compatibility

+If the tool detects problems with high or medium level of severity or at least one removed symbol then the compatibility verdict is incompatible (otherwise compatible). Low-severity problems can be considered as warnings and don't affect the compatibility verdict unless the -strict option is specified. + + +

Error Codes

+ + + + + + + + + + + + + + +
CodeMeaning
0Compatible. The tool has run without any errors.
1Incompatible. The tool has run without any errors.
2Common error code (undifferentiated).
3A system command is not found.
4Cannot access input files.
5Cannot compile header files.
6Headers have been compiled with minor errors.
7Invalid input ABI dump.
8Unsupported version of input ABI dump.
9Cannot find a module.
10Empty intersection between headers and shared objects.
11Empty set of symbols in headers.
+ + +

FAQ

+
    +
  • + What is an ABI and how does it differ from an API? +

    + An Application Binary Interface (ABI) is the set of supported run-time interfaces provided by a software component or set of components for applications to use, whereas an Application Programming Interface (API) is the set of build-time interfaces. The ABI may be defined by the formula: ABI = API + compiler ABI. +

  • +
  • + Why does this tool need both shared libraries and header files to check ABI compliance? +

    + Without header files it is impossible to determine public symbols in ABI and data type definitions. Without shared libraries it is impossible to determine exported symbols in the ABI of the target library and also impossible to detect added/removed symbols. +

  • +
+ + +

Similar Tools

+
    +
  • + icheck - C interface ABI/API checker. +
  • +
  • + BCS - The Symbian binary compatibility suite. +
  • +
  • + shlib-compat - ABI compatibility checker that uses DWARF debug info. +
  • +
  • + qbic - A tool to check for binary incompatibilities in Qt4 Toolkit. +
  • +
  • + libabigail - A C++ library for ABI analysis. +
  • +
  • + chkshlib, cmpdylib, cmpshlib - Tools to compare binary symbols. +
  • +
+ + +

Bugs

+Please post bug reports, feature requests and questions to the issue tracker. +

+ + +

Maintainers

+The tool is developed by Andrey Ponomarenko. + + +

Changes

+You can find changelog here. + + +

Articles

+ + + + + +
+
+ +
+ + diff --git a/abi-compliance-checker-2.4/modules/Internals/ABIDump.pm b/abi-compliance-checker-2.4/modules/Internals/ABIDump.pm new file mode 100644 index 0000000..c0687b7 --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/ABIDump.pm @@ -0,0 +1,1427 @@ +########################################################################### +# A module to create ABI dump from AST tree +# +# Copyright (C) 2015-2018 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; + +loadModule("ElfTools"); +loadModule("TUDump"); +loadModule("GccAst"); + +my %Cache; + +my %RegisteredObj; +my %RegisteredObj_Short; +my %RegisteredSoname; +my %RegisteredObj_Dir; +my %CheckedDyLib; +my %KnownLibs; +my %CheckedArch; +my @RecurLib; + +sub createABIDump($) +{ + my $LVer = $_[0]; + + if($In::Opt{"CheckHeadersOnly"}) { + $In::ABI{$LVer}{"Language"} = "C++"; + } + else + { + readLibs($LVer); + + if(not keys(%{$In::ABI{$LVer}{"SymLib"}})) { + exitStatus("Error", "the set of public symbols in library(ies) is empty"); + } + } + + if($In::Opt{"TargetArch"}) { + $In::ABI{$LVer}{"Arch"} = $In::Opt{"TargetArch"}; + } + else { + $In::ABI{$LVer}{"Arch"} = getArch_GCC($LVer); + } + + $In::ABI{$LVer}{"WordSize"} = detectWordSize($LVer); + + $In::ABI{$LVer}{"LibraryVersion"} = $In::Desc{$LVer}{"Version"}; + $In::ABI{$LVer}{"LibraryName"} = $In::Opt{"TargetLib"}; + + if(not $In::ABI{$LVer}{"Language"}) { + $In::ABI{$LVer}{"Language"} = "C"; + } + + if($In::Opt{"UserLang"}) { + $In::ABI{$LVer}{"Language"} = $In::Opt{"UserLang"}; + } + + $In::ABI{$LVer}{"GccVersion"} = $In::Opt{"GccVer"}; + + printMsg("INFO", "Checking header(s) ".$In::Desc{$LVer}{"Version"}." ..."); + my $TUDump = createTUDump($LVer); + + if($In::Opt{"Debug"}) + { # debug mode + copy($TUDump, getDebugDir($LVer)."/translation-unit-dump.txt"); + } + + readGccAst($LVer, $TUDump); + + if($In::Opt{"DebugMangling"}) + { + if($In::ABI{$LVer}{"Language"} eq "C++") + { + debugMangling($LVer); + } + } + + delete($In::ABI{$LVer}{"EnumConstants"}); + delete($In::ABI{$LVer}{"ClassVTable_Content"}); + delete($In::ABI{$LVer}{"WeakSymbols"}); + + cleanDump($LVer); + + if(not keys(%{$In::ABI{$LVer}{"SymbolInfo"}})) + { # check if created dump is valid + if(not $In::Opt{"ExtendedCheck"}) + { + if($In::Opt{"CheckHeadersOnly"}) { + exitStatus("Empty_Set", "the set of public symbols is empty"); + } + else { + exitStatus("Empty_Intersection", "the sets of public symbols in headers and libraries have empty intersection"); + } + } + } + + foreach my $HPath (keys(%{$In::Desc{$LVer}{"RegHeader"}})) { + $In::ABI{$LVer}{"Headers"}{getFilename($HPath)} = 1; + } + + foreach my $InfoId (keys(%{$In::ABI{$LVer}{"SymbolInfo"}})) + { + if(my $MnglName = $In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"MnglName"}) + { + if(my $Unmangled = getUnmangled($MnglName, $LVer)) { + $In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"Unmangled"} = $Unmangled; + } + } + } + + my %CompilerConstants = (); # built-in GCC constants + my $CRef = $In::ABI{$LVer}{"Constants"}; + foreach my $Name (keys(%{$CRef})) + { + if(not defined $CRef->{$Name}{"Header"}) + { + $CompilerConstants{$Name} = $CRef->{$Name}{"Value"}; + delete($CRef->{$Name}); + } + } + $In::ABI{$LVer}{"CompilerConstants"} = \%CompilerConstants; + + if($In::Opt{"ExtendedCheck"}) + { # --ext option + $In::ABI{$LVer}{"Mode"} = "Extended"; + } + if($In::Opt{"BinOnly"}) + { # --binary + $In::ABI{$LVer}{"BinOnly"} = 1; + } + if($In::Opt{"ExtraDump"}) + { # --extra-dump + $In::ABI{$LVer}{"Extra"} = 1; + } + + $In::ABI{$LVer}{"Target"} = $In::Opt{"Target"}; +} + +sub readSymbols($) +{ + my $LVer = $_[0]; + + my @LibPaths = getSOPaths($LVer); + if($#LibPaths==-1) { + exitStatus("Error", "library objects are not found"); + } + + foreach my $LibPath (@LibPaths) { + readSymbols_Lib($LVer, $LibPath, 0, "+Weak", 1, 1); + } + + if($In::Opt{"CheckUndefined"}) + { + my %UndefinedLibs = (); + + my @Libs = (keys(%{$In::ABI{$LVer}{"Symbols"}}), keys(%{$In::ABI{$LVer}{"DepSymbols"}})); + + foreach my $LibName (sort @Libs) + { + if(defined $In::ABI{$LVer}{"UndefinedSymbols"}{$LibName}) + { + foreach my $Symbol (keys(%{$In::ABI{$LVer}{"UndefinedSymbols"}{$LibName}})) + { + if($In::ABI{$LVer}{"SymLib"}{$Symbol} + or $In::ABI{$LVer}{"DepSymLib"}{$Symbol}) + { # exported by target library + next; + } + if(index($Symbol, '@')!=-1) + { # exported default symbol version (@@) + $Symbol=~s/\@/\@\@/; + if($In::ABI{$LVer}{"SymLib"}{$Symbol} + or $In::ABI{$LVer}{"DepSymLib"}{$Symbol}) { + next; + } + } + foreach my $Path (find_SymbolLibs($LVer, $Symbol)) { + $UndefinedLibs{$Path} = 1; + } + } + } + } + + if(my @Paths = sort keys(%UndefinedLibs)) + { + my $LibString = ""; + my %Dirs = (); + foreach (@Paths) + { + $KnownLibs{$_} = 1; + my ($Dir, $Name) = sepPath($_); + + if(not grep {$Dir eq $_} (@{$In::Opt{"SysPaths"}{"lib"}})) { + $Dirs{escapeArg($Dir)} = 1; + } + + $Name = libPart($Name, "name"); + $Name=~s/\Alib//; + + $LibString .= " -l$Name"; + } + + foreach my $Dir (sort {$b cmp $a} keys(%Dirs)) + { + $LibString = " -L".escapeArg($Dir).$LibString; + } + + if($In::Opt{"ExtraInfo"}) { + writeFile($In::Opt{"ExtraInfo"}."/libs-string", $LibString); + } + } + } + + if($In::Opt{"ExtraInfo"}) { + writeFile($In::Opt{"ExtraInfo"}."/lib-paths", join("\n", sort keys(%KnownLibs))); + } +} + +sub readSymbols_Lib($$$$$$) +{ + my ($LVer, $Lib_Path, $IsNeededLib, $Weak, $Deps, $Vers) = @_; + + my $Real_Path = realpath_F($Lib_Path); + + if(not $Real_Path) + { # broken link + return (); + } + + my $Lib_Name = getFilename($Real_Path); + my $LExt = $In::Opt{"Ext"}; + + if($In::Opt{"ExtraInfo"}) + { + $KnownLibs{$Real_Path} = 1; + $KnownLibs{$Lib_Path} = 1; # links + } + + if($IsNeededLib) + { + if($CheckedDyLib{$LVer}{$Lib_Name}) { + return (); + } + } + if($#RecurLib>=1 or isCyclical(\@RecurLib, $Lib_Name)) { + return (); + } + $CheckedDyLib{$LVer}{$Lib_Name} = 1; + + my $TmpDir = $In::Opt{"Tmp"}; + + push(@RecurLib, $Lib_Name); + my (%Value_Interface, %Interface_Value, %NeededLib) = (); + my $Lib_ShortName = libPart($Lib_Name, "name+ext"); + + if(not $IsNeededLib) + { # special cases: libstdc++ and libc + if(my $ShortName = libPart($Lib_Name, "short")) + { + if($ShortName eq "libstdc++" + or $ShortName eq "libc++") + { # libstdc++.so.6 + $In::Opt{"StdcxxTesting"} = 1; + } + elsif($ShortName eq "libc") + { # libc-2.11.3.so + $In::Opt{"GlibcTesting"} = 1; + } + } + } + my $DebugPath = ""; + if($In::Opt{"Debug"} and not $In::Opt{"DumpSystem"}) + { # debug mode + $DebugPath = getDebugDir($LVer)."/libs/".getFilename($Lib_Path).".txt"; + mkpath(getDirname($DebugPath)); + } + if($In::Opt{"Target"} eq "macos") + { # Mac OS X: *.dylib, *.a + my $NM = getCmdPath("nm"); + if(not $NM) { + exitStatus("Not_Found", "can't find \"nm\""); + } + $NM .= " -g \"$Lib_Path\" 2>\"$TmpDir/null\""; + if($DebugPath) + { # debug mode + # write to file + system($NM." >\"$DebugPath\""); + open(LIB, $DebugPath); + } + else + { # write to pipe + open(LIB, $NM." |"); + } + while() + { + if($In::Opt{"CheckUndefined"}) + { + if(not $IsNeededLib) + { + if(/ U _([\w\$]+)\s*\Z/) + { + $In::ABI{$LVer}{"UndefinedSymbols"}{$Lib_Name}{$1} = 0; + next; + } + } + } + + if(/ [STD] _([\w\$]+)\s*\Z/) + { + my $Symbol = $1; + if($IsNeededLib) + { + if(not defined $RegisteredObj_Short{$LVer}{$Lib_ShortName}) + { + $In::ABI{$LVer}{"DepSymLib"}{$Symbol} = $Lib_Name; + $In::ABI{$LVer}{"DepSymbols"}{$Lib_Name}{$Symbol} = 1; + } + } + else + { + $In::ABI{$LVer}{"SymLib"}{$Symbol} = $Lib_Name; + $In::ABI{$LVer}{"Symbols"}{$Lib_Name}{$Symbol} = 1; + if($In::ABI{$LVer}{"Language"} ne "C++") + { + if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) { + $In::ABI{$LVer}{"Language"} = "C++"; + } + } + } + } + } + close(LIB); + + if($Deps) + { + if(not $In::Opt{"UseStaticLibs"}) + { # dependencies + my $OtoolCmd = getCmdPath("otool"); + if(not $OtoolCmd) { + exitStatus("Not_Found", "can't find \"otool\""); + } + + open(LIB, "$OtoolCmd -L \"$Lib_Path\" 2>\"$TmpDir/null\" |"); + while() + { + if(/\s*([\/\\].+\.$LExt)\s*/ + and $1 ne $Lib_Path) { + $NeededLib{$1} = 1; + } + } + close(LIB); + } + } + } + elsif($In::Opt{"Target"} eq "windows") + { # Windows *.dll, *.lib + my $DumpBinCmd = getCmdPath("dumpbin"); + if(not $DumpBinCmd) { + exitStatus("Not_Found", "can't find \"dumpbin\""); + } + $DumpBinCmd .= " /EXPORTS \"".$Lib_Path."\" 2>$TmpDir/null"; + if($DebugPath) + { # debug mode + # write to file + system($DumpBinCmd." >\"$DebugPath\""); + open(LIB, $DebugPath); + } + else + { # write to pipe + open(LIB, $DumpBinCmd." |"); + } + while() + { + my $Symbol = undef; + if($In::Opt{"UseStaticLibs"}) + { + if(/\A\s{10,}(\d+\s+|)([_\w\?\@]+)(\s*\Z|\s+)/i) + { + # 16 IID_ISecurityInformation + # ??_7TestBaseClass@api@@6B@ (const api::TestBaseClass::`vftable') + $Symbol = $2; + } + } + else + { # Dll + # 1197 4AC 0000A620 SetThreadStackGuarantee + # 1198 4AD SetThreadToken (forwarded to ...) + # 3368 _o2i_ECPublicKey + # 1 0 00005B30 ??0?N = ... (with pdb) + if(/\A\s*\d+\s+[a-f\d]+\s+[a-f\d]+\s+([\w\?\@]+)\s*(?:=.+)?\Z/i + or /\A\s*\d+\s+[a-f\d]+\s+([\w\?\@]+)\s*\(\s*forwarded\s+/ + or /\A\s*\d+\s+_([\w\?\@]+)\s*(?:=.+)?\Z/) + { # dynamic, static and forwarded symbols + $Symbol = $1; + } + } + + if($Symbol) + { + if($IsNeededLib) + { + if(not defined $RegisteredObj_Short{$LVer}{$Lib_ShortName}) + { + $In::ABI{$LVer}{"DepSymLib"}{$Symbol} = $Lib_Name; + $In::ABI{$LVer}{"DepSymbols"}{$Lib_Name}{$Symbol} = 1; + } + } + else + { + $In::ABI{$LVer}{"SymLib"}{$Symbol} = $Lib_Name; + $In::ABI{$LVer}{"Symbols"}{$Lib_Name}{$Symbol} = 1; + if($In::ABI{$LVer}{"Language"} ne "C++") + { + if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) { + $In::ABI{$LVer}{"Language"} = "C++"; + } + } + } + } + } + close(LIB); + + if($Deps) + { + if(not $In::Opt{"UseStaticLibs"}) + { # dependencies + open(LIB, "$DumpBinCmd /DEPENDENTS \"$Lib_Path\" 2>\"$TmpDir/null\" |"); + while() + { + if(/\s*([^\s]+?\.$LExt)\s*/i + and $1 ne $Lib_Path) { + $NeededLib{pathFmt($1)} = 1; + } + } + close(LIB); + } + } + } + else + { # Unix; *.so, *.a + # Symbian: *.dso, *.lib + my $ReadelfCmd = getCmdPath("readelf"); + if(not $ReadelfCmd) { + exitStatus("Not_Found", "can't find \"readelf\""); + } + my $Cmd = $ReadelfCmd." -Ws \"$Lib_Path\" 2>\"$TmpDir/null\""; + if($DebugPath) + { # debug mode + # write to file + system($Cmd." >\"$DebugPath\""); + open(LIB, $DebugPath); + } + else + { # write to pipe + open(LIB, $Cmd." |"); + } + my $symtab = undef; # indicates that we are processing 'symtab' section of 'readelf' output + while() + { + if(not $In::Opt{"UseStaticLibs"}) + { # dynamic library specifics + if(defined $symtab) + { + if(index($_, "'.dynsym'")!=-1) + { # dynamic table + $symtab = undef; + } + # do nothing with symtab + next; + } + elsif(index($_, "'.symtab'")!=-1) + { # symbol table + $symtab = 1; + next; + } + } + if(my ($Value, $Size, $Type, $Bind, $Vis, $Ndx, $Symbol) = readline_ELF($_)) + { # read ELF entry + if($Ndx eq "UND") + { # ignore interfaces that are imported from somewhere else + if($In::Opt{"CheckUndefined"}) + { + if(not $IsNeededLib) { + $In::ABI{$LVer}{"UndefinedSymbols"}{$Lib_Name}{$Symbol} = 0; + } + } + next; + } + if($Bind eq "WEAK") + { + $In::ABI{$LVer}{"WeakSymbols"}{$Symbol} = 1; + if($Weak eq "-Weak") + { # skip WEAK symbols + next; + } + } + if($IsNeededLib) + { + if(not defined $RegisteredObj_Short{$LVer}{$Lib_ShortName}) + { + $In::ABI{$LVer}{"DepSymLib"}{$Symbol} = $Lib_Name; + $In::ABI{$LVer}{"DepSymbols"}{$Lib_Name}{$Symbol} = ($Type eq "OBJECT")?-$Size:1; + } + } + else + { + $In::ABI{$LVer}{"SymLib"}{$Symbol} = $Lib_Name; + $In::ABI{$LVer}{"Symbols"}{$Lib_Name}{$Symbol} = ($Type eq "OBJECT")?-$Size:1; + if($Vers) + { + if($LExt eq "so") + { # value + $Interface_Value{$LVer}{$Symbol} = $Value; + $Value_Interface{$LVer}{$Value}{$Symbol} = 1; + } + } + if($In::ABI{$LVer}{"Language"} ne "C++") + { + if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) { + $In::ABI{$LVer}{"Language"} = "C++"; + } + } + } + } + } + close(LIB); + + if($Deps and not $In::Opt{"UseStaticLibs"}) + { # dynamic library specifics + $Cmd = $ReadelfCmd." -Wd \"$Lib_Path\" 2>\"$TmpDir/null\""; + open(LIB, $Cmd." |"); + + while() + { + if(/NEEDED.+\[([^\[\]]+)\]/) + { # dependencies: + # 0x00000001 (NEEDED) Shared library: [libc.so.6] + $NeededLib{$1} = 1; + } + } + + close(LIB); + } + } + if($Vers) + { + if(not $IsNeededLib and $LExt eq "so") + { # get symbol versions + my %Found = (); + + # by value + foreach my $Symbol (sort keys(%{$In::ABI{$LVer}{"Symbols"}{$Lib_Name}})) + { + next if(index($Symbol, '@')==-1); + if(my $Value = $Interface_Value{$LVer}{$Symbol}) + { + foreach my $Symbol_SameValue (sort keys(%{$Value_Interface{$LVer}{$Value}})) + { + if($Symbol_SameValue ne $Symbol + and index($Symbol_SameValue, '@')==-1) + { + $In::ABI{$LVer}{"SymbolVersion"}{$Symbol_SameValue} = $Symbol; + $Found{$Symbol} = 1; + + if(index($Symbol, '@@')==-1) { + last; + } + } + } + } + } + + # default + foreach my $Symbol (keys(%{$In::ABI{$LVer}{"Symbols"}{$Lib_Name}})) + { + next if(defined $Found{$Symbol}); + next if(index($Symbol, '@@')==-1); + + if($Symbol=~/\A([^\@]*)\@\@/ + and not $In::ABI{$LVer}{"SymbolVersion"}{$1}) + { + $In::ABI{$LVer}{"SymbolVersion"}{$1} = $Symbol; + $Found{$Symbol} = 1; + } + } + + # non-default + foreach my $Symbol (keys(%{$In::ABI{$LVer}{"Symbols"}{$Lib_Name}})) + { + next if(defined $Found{$Symbol}); + next if(index($Symbol, '@')==-1); + + if($Symbol=~/\A([^\@]*)\@([^\@]*)/ + and not $In::ABI{$LVer}{"SymbolVersion"}{$1}) + { + $In::ABI{$LVer}{"SymbolVersion"}{$1} = $Symbol; + $Found{$Symbol} = 1; + } + } + } + } + if($Deps) + { + foreach my $DyLib (sort keys(%NeededLib)) + { + if($In::Opt{"ExtraDump"}) { + $In::ABI{$LVer}{"Needed"}{$Lib_Name}{getFilename($DyLib)} = 1; + } + + if(my $DepPath = getLibPath($LVer, $DyLib)) + { + if(not $CheckedDyLib{$LVer}{getFilename($DepPath)}) { + readSymbols_Lib($LVer, $DepPath, 1, "+Weak", $Deps, $Vers); + } + } + } + } + pop(@RecurLib); + return $In::ABI{$LVer}{"Symbols"}; +} + +sub readSymbols_App($) +{ + my $Path = $_[0]; + + my $TmpDir = $In::Opt{"Tmp"}; + + my @Imported = (); + if($In::Opt{"Target"} eq "macos") + { + my $NM = getCmdPath("nm"); + if(not $NM) { + exitStatus("Not_Found", "can't find \"nm\""); + } + open(APP, "$NM -g \"$Path\" 2>\"$TmpDir/null\" |"); + while() + { + if(/ U _([\w\$]+)\s*\Z/) { + push(@Imported, $1); + } + } + close(APP); + } + elsif($In::Opt{"Target"} eq "windows") + { + my $DumpBinCmd = getCmdPath("dumpbin"); + if(not $DumpBinCmd) { + exitStatus("Not_Found", "can't find \"dumpbin.exe\""); + } + open(APP, "$DumpBinCmd /IMPORTS \"$Path\" 2>\"$TmpDir/null\" |"); + while() + { + if(/\s*\w+\s+\w+\s+\w+\s+([\w\?\@]+)\s*/) { + push(@Imported, $1); + } + } + close(APP); + } + else + { + my $ReadelfCmd = getCmdPath("readelf"); + if(not $ReadelfCmd) { + exitStatus("Not_Found", "can't find \"readelf\""); + } + open(APP, "$ReadelfCmd -Ws \"$Path\" 2>\"$TmpDir/null\" |"); + my $symtab = undef; # indicates that we are processing 'symtab' section of 'readelf' output + while() + { + if(defined $symtab) + { # do nothing with symtab + if(index($_, "'.dynsym'")!=-1) + { # dynamic table + $symtab = undef; + } + } + elsif(index($_, "'.symtab'")!=-1) + { # symbol table + $symtab = 1; + } + elsif(my @Info = readline_ELF($_)) + { + my ($Ndx, $Symbol) = ($Info[5], $Info[6]); + if($Ndx eq "UND") + { # only imported symbols + push(@Imported, $Symbol); + } + } + } + close(APP); + } + return @Imported; +} + +sub cleanDump($) +{ # clean data + my $LVer = $_[0]; + foreach my $InfoId (keys(%{$In::ABI{$LVer}{"SymbolInfo"}})) + { + if(not keys(%{$In::ABI{$LVer}{"SymbolInfo"}{$InfoId}})) + { + delete($In::ABI{$LVer}{"SymbolInfo"}{$InfoId}); + next; + } + my $MnglName = $In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"MnglName"}; + if(not $MnglName) + { + delete($In::ABI{$LVer}{"SymbolInfo"}{$InfoId}); + next; + } + my $ShortName = $In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"ShortName"}; + if(not $ShortName) + { + delete($In::ABI{$LVer}{"SymbolInfo"}{$InfoId}); + next; + } + if($MnglName eq $ShortName) + { # remove duplicate data + delete($In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"MnglName"}); + } + if(not keys(%{$In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"Param"}})) { + delete($In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"Param"}); + } + if(not keys(%{$In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"TParam"}})) { + delete($In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"TParam"}); + } + delete($In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"Type"}); + } + foreach my $Tid (keys(%{$In::ABI{$LVer}{"TypeInfo"}})) + { + if(not keys(%{$In::ABI{$LVer}{"TypeInfo"}{$Tid}})) + { + delete($In::ABI{$LVer}{"TypeInfo"}{$Tid}); + next; + } + delete($In::ABI{$LVer}{"TypeInfo"}{$Tid}{"Tid"}); + foreach my $Attr ("Header", "Line", "Size", "NameSpace") + { + if(not $In::ABI{$LVer}{"TypeInfo"}{$Tid}{$Attr}) { + delete($In::ABI{$LVer}{"TypeInfo"}{$Tid}{$Attr}); + } + } + if(not keys(%{$In::ABI{$LVer}{"TypeInfo"}{$Tid}{"TParam"}})) { + delete($In::ABI{$LVer}{"TypeInfo"}{$Tid}{"TParam"}); + } + } +} + +sub readLibs($) +{ + my $LVer = $_[0]; + + if($In::Opt{"Target"} eq "windows") + { # dumpbin.exe will crash + # without VS Environment + checkWin32Env(); + } + + readSymbols($LVer); + + translateSymbols(keys(%{$In::ABI{$LVer}{"SymLib"}}), $LVer); + translateSymbols(keys(%{$In::ABI{$LVer}{"DepSymLib"}}), $LVer); +} + +sub getSOPaths($) +{ + my $LVer = $_[0]; + my @Paths = (); + foreach my $P (keys(%{$In::Desc{$LVer}{"Libs"}})) + { + my @Found = getSOPaths_Dir(getAbsPath($P), $LVer); + foreach (@Found) { + push(@Paths, $_); + } + } + return sort @Paths; +} + +sub getSOPaths_Dir($$) +{ + my ($Path, $LVer) = @_; + if(skipLib($Path, $LVer)) { + return (); + } + + my $LExt = $In::Opt{"Ext"}; + + if(-f $Path) + { + if(not libPart($Path, "name")) { + exitStatus("Error", "incorrect format of library (should be *.$LExt): \'$Path\'"); + } + registerObject($Path, $LVer); + registerObject_Dir(getDirname($Path), $LVer); + return ($Path); + } + elsif(-d $Path) + { + $Path=~s/[\/\\]+\Z//g; + my %Libs = (); + if(my $TN = $In::Opt{"TargetLib"} + and grep { $Path eq $_ } @{$In::Opt{"SysPaths"}{"lib"}}) + { # you have specified /usr/lib as the search directory () in the XML descriptor + # and the real name of the library by -l option (bz2, stdc++, Xaw, ...) + foreach my $P (cmdFind($Path,"","*".escapeArg($TN)."*.$LExt*",2)) + { # all files and symlinks that match the name of a library + if(getFilename($P)=~/\A(|lib)\Q$TN\E[\d\-]*\.$LExt[\d\.]*\Z/i) + { + registerObject($P, $LVer); + $Libs{realpath_F($P)} = 1; + } + } + } + else + { # search for all files and symlinks + foreach my $P (findLibs($Path,"","")) + { + next if(ignorePath($P)); + next if(skipLib($P, $LVer)); + registerObject($P, $LVer); + $Libs{realpath_F($P)} = 1; + } + if($In::Opt{"OS"} eq "macos") + { # shared libraries on MacOS X may have no extension + foreach my $P (cmdFind($Path,"f")) + { + next if(ignorePath($P)); + next if(skipLib($P, $LVer)); + if(getFilename($P)!~/\./ and -B $P + and cmdFile($P)=~/(shared|dynamic)\s+library/i) + { + registerObject($P, $LVer); + $Libs{realpath_F($P)} = 1; + } + } + } + } + return keys(%Libs); + } + + return (); +} + +sub registerObject_Dir($$) +{ + my ($Dir, $LVer) = @_; + if(grep {$_ eq $Dir} @{$In::Opt{"SysPaths"}{"lib"}}) + { # system directory + return; + } + if($RegisteredObj_Dir{$LVer}{$Dir}) + { # already registered + return; + } + foreach my $Path (findLibs($Dir,"",1)) + { + if(ignorePath($Path)) { + next; + } + if(skipLib($Path, $LVer)) { + next; + } + registerObject($Path, $LVer); + } + $RegisteredObj_Dir{$LVer}{$Dir} = 1; +} + +sub registerObject($$) +{ + my ($Path, $LVer) = @_; + + my $Name = getFilename($Path); + $RegisteredObj{$LVer}{$Name} = $Path; + if($In::Opt{"Target"}=~/linux|bsd|gnu|solaris/i) + { + if(my $SONAME = getSONAME($Path)) { + $RegisteredSoname{$LVer}{$SONAME} = $Path; + } + } + if(my $Short = libPart($Name, "name+ext")) { + $RegisteredObj_Short{$LVer}{$Short} = $Path; + } + + if(not $CheckedArch{$LVer} and -f $Path) + { + if(my $ObjArch = getArch_Object($Path)) + { + if($ObjArch ne getArch_GCC($LVer)) + { # translation unit dump generated by the GCC compiler should correspond to input objects + $CheckedArch{$LVer} = 1; + printMsg("WARNING", "the architectures of input objects and the used GCC compiler are not equal, please change the compiler by --gcc-path=PATH option."); + } + } + } +} + +sub remove_Unused($$) +{ # remove unused data types from the ABI dump + my ($LVer, $Kind) = @_; + + my %UsedType = (); + + foreach my $InfoId (sort {$a<=>$b} keys(%{$In::ABI{$LVer}{"SymbolInfo"}})) + { + registerSymbolUsage($InfoId, \%UsedType, $LVer); + } + foreach my $Tid (sort {$a<=>$b} keys(%{$In::ABI{$LVer}{"TypeInfo"}})) + { + if($UsedType{$Tid}) + { # All & Extended + next; + } + + if($Kind eq "Extended") + { + if(pickType($Tid, $LVer)) + { + my %Tree = (); + registerTypeUsage($Tid, \%Tree, $LVer); + + my $Tmpl = 0; + foreach (sort {$a<=>$b} keys(%Tree)) + { + if(defined $In::ABI{$LVer}{"TypeInfo"}{$_}{"Template"} + or $In::ABI{$LVer}{"TypeInfo"}{$_}{"Type"} eq "TemplateParam") + { + $Tmpl = 1; + last; + } + } + if(not $Tmpl) + { + foreach (keys(%Tree)) { + $UsedType{$_} = 1; + } + } + } + } + } + + my %Delete = (); + + foreach my $Tid (sort {$a<=>$b} keys(%{$In::ABI{$LVer}{"TypeInfo"}})) + { # remove unused types + if($UsedType{$Tid}) + { # All & Extended + next; + } + + if($Kind eq "Extra") + { + my %Tree = (); + registerTypeUsage($Tid, \%Tree, $LVer); + + foreach (sort {$a<=>$b} keys(%Tree)) + { + if(defined $In::ABI{$LVer}{"TypeInfo"}{$_}{"Template"} + or $In::ABI{$LVer}{"TypeInfo"}{$_}{"Type"} eq "TemplateParam") + { + $Delete{$Tid} = 1; + last; + } + } + } + else + { + # remove type + delete($In::ABI{$LVer}{"TypeInfo"}{$Tid}); + } + } + + if($Kind eq "Extra") + { # remove duplicates + foreach my $Tid (sort {$a<=>$b} keys(%{$In::ABI{$LVer}{"TypeInfo"}})) + { + if($UsedType{$Tid}) + { # All & Extended + next; + } + + my $Name = $In::ABI{$LVer}{"TypeInfo"}{$Tid}{"Name"}; + + if($In::ABI{$LVer}{"TName_Tid"}{$Name} ne $Tid) { + delete($In::ABI{$LVer}{"TypeInfo"}{$Tid}); + } + } + } + + foreach my $Tid (keys(%Delete)) + { + delete($In::ABI{$LVer}{"TypeInfo"}{$Tid}); + } +} + +sub getFirst($$) +{ + my ($Tid, $LVer) = @_; + if(not $Tid) { + return $Tid; + } + + if(my $Name = $In::ABI{$LVer}{"TypeInfo"}{$Tid}{"Name"}) + { + if($In::ABI{$LVer}{"TName_Tid"}{$Name}) { + return $In::ABI{$LVer}{"TName_Tid"}{$Name}; + } + } + + return $Tid; +} + +sub registerSymbolUsage($$$) +{ + my ($InfoId, $UsedType, $LVer) = @_; + + my %FuncInfo = %{$In::ABI{$LVer}{"SymbolInfo"}{$InfoId}}; + if(my $RTid = getFirst($FuncInfo{"Return"}, $LVer)) + { + registerTypeUsage($RTid, $UsedType, $LVer); + $In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"Return"} = $RTid; + } + if(my $FCid = getFirst($FuncInfo{"Class"}, $LVer)) + { + registerTypeUsage($FCid, $UsedType, $LVer); + $In::ABI{$LVer}{"SymbolInfo"}{$InfoId}{"Class"} = $FCid; + + if(my $ThisId = getTypeIdByName($In::ABI{$LVer}{"TypeInfo"}{$FCid}{"Name"}."*const", $LVer)) + { # register "this" pointer + registerTypeUsage($ThisId, $UsedType, $LVer); + } + if(my $ThisId_C = getTypeIdByName($In::ABI{$LVer}{"TypeInfo"}{$FCid}{"Name"}." const*const", $LVer)) + { # register "this" pointer (const method) + registerTypeUsage($ThisId_C, $UsedType, $LVer); + } + } + foreach my $PPos (sort {$a<=>$b} keys(%{$FuncInfo{"Param"}})) + { + if(my $PTid = getFirst($FuncInfo{"Param"}{$PPos}{"type"}, $LVer)) + { + registerTypeUsage($PTid, $UsedType, $LVer); + $FuncInfo{"Param"}{$PPos}{"type"} = $PTid; + } + } + foreach my $TPos (sort {$a<=>$b} keys(%{$FuncInfo{"TParam"}})) + { + my $TPName = $FuncInfo{"TParam"}{$TPos}{"name"}; + if(my $TTid = $In::ABI{$LVer}{"TName_Tid"}{$TPName}) { + registerTypeUsage($TTid, $UsedType, $LVer); + } + } +} + +sub registerTypeUsage($$$) +{ + my ($TypeId, $UsedType, $LVer) = @_; + if(not $TypeId) { + return; + } + if($UsedType->{$TypeId}) + { # already registered + return; + } + + my %TInfo = getType($TypeId, $LVer); + if($TInfo{"Type"}) + { + if(my $NS = $TInfo{"NameSpace"}) + { + if(my $NSTid = $In::ABI{$LVer}{"TName_Tid"}{$NS}) { + registerTypeUsage($NSTid, $UsedType, $LVer); + } + } + + if($TInfo{"Type"}=~/\A(Struct|Union|Class|FuncPtr|Func|MethodPtr|FieldPtr|Enum)\Z/) + { + $UsedType->{$TypeId} = 1; + if($TInfo{"Type"}=~/\A(Struct|Class)\Z/) + { + foreach my $BaseId (sort {$a<=>$b} keys(%{$TInfo{"Base"}})) { + registerTypeUsage($BaseId, $UsedType, $LVer); + } + foreach my $TPos (sort {$a<=>$b} keys(%{$TInfo{"TParam"}})) + { + my $TPName = $TInfo{"TParam"}{$TPos}{"name"}; + if(my $TTid = $In::ABI{$LVer}{"TName_Tid"}{$TPName}) { + registerTypeUsage($TTid, $UsedType, $LVer); + } + } + } + foreach my $Memb_Pos (sort {$a<=>$b} keys(%{$TInfo{"Memb"}})) + { + if(my $MTid = getFirst($TInfo{"Memb"}{$Memb_Pos}{"type"}, $LVer)) + { + registerTypeUsage($MTid, $UsedType, $LVer); + $TInfo{"Memb"}{$Memb_Pos}{"type"} = $MTid; + } + } + if($TInfo{"Type"} eq "FuncPtr" + or $TInfo{"Type"} eq "MethodPtr" + or $TInfo{"Type"} eq "Func") + { + if(my $RTid = $TInfo{"Return"}) { + registerTypeUsage($RTid, $UsedType, $LVer); + } + foreach my $PPos (sort {$a<=>$b} keys(%{$TInfo{"Param"}})) + { + if(my $PTid = $TInfo{"Param"}{$PPos}{"type"}) { + registerTypeUsage($PTid, $UsedType, $LVer); + } + } + } + if($TInfo{"Type"} eq "FieldPtr") + { + if(my $RTid = $TInfo{"Return"}) { + registerTypeUsage($RTid, $UsedType, $LVer); + } + if(my $CTid = $TInfo{"Class"}) { + registerTypeUsage($CTid, $UsedType, $LVer); + } + } + if($TInfo{"Type"} eq "MethodPtr") + { + if(my $CTid = $TInfo{"Class"}) { + registerTypeUsage($CTid, $UsedType, $LVer); + } + } + } + elsif($TInfo{"Type"}=~/\A(Const|ConstVolatile|Volatile|Pointer|Ref|Restrict|Array|Typedef)\Z/) + { + $UsedType->{$TypeId} = 1; + if(my $BTid = getFirst($TInfo{"BaseType"}, $LVer)) + { + registerTypeUsage($BTid, $UsedType, $LVer); + $In::ABI{$LVer}{"TypeInfo"}{$TypeId}{"BaseType"} = $BTid; + } + } + else + { # Intrinsic, TemplateParam, TypeName, SizeOf, etc. + $UsedType->{$TypeId} = 1; + } + } +} + +sub detectWordSize($) +{ + my $LVer = $_[0]; + + my $Size = undef; + + # speed up detection + if(my $Arch = $In::ABI{$LVer}{"Arch"}) + { + if($Arch=~/\A(x86_64|s390x|ppc64|ia64|alpha)\Z/) { + $Size = "8"; + } + elsif($Arch=~/\A(x86|s390|ppc32)\Z/) { + $Size = "4"; + } + } + + if(my $GccPath = $In::Opt{"GccPath"}) + { + my $TmpDir = $In::Opt{"Tmp"}; + writeFile("$TmpDir/empty.h", ""); + + my $Cmd = $GccPath." -E -dD empty.h"; + if(my $Opts = getGccOptions($LVer)) + { # user-defined options + $Cmd .= " ".$Opts; + } + + chdir($TmpDir); + my $Defines = `$Cmd`; + chdir($In::Opt{"OrigDir"}); + + unlink("$TmpDir/empty.h"); + + if($Defines=~/ __SIZEOF_POINTER__\s+(\d+)/) + { # GCC 4 + $Size = $1; + } + elsif($Defines=~/ __PTRDIFF_TYPE__\s+(\w+)/) + { # GCC 3 + my $PTRDIFF = $1; + if($PTRDIFF=~/long/) { + $Size = "8"; + } + else { + $Size = "4"; + } + } + } + + if(not $Size) { + exitStatus("Error", "can't check WORD size"); + } + + return $Size; +} + +sub getLibPath($$) +{ + my ($LVer, $Name) = @_; + if(defined $Cache{"getLibPath"}{$LVer}{$Name}) { + return $Cache{"getLibPath"}{$LVer}{$Name}; + } + return ($Cache{"getLibPath"}{$LVer}{$Name} = getLibPath_I($LVer, $Name)); +} + +sub getLibPath_I($$) +{ + my ($LVer, $Name) = @_; + if(isAbsPath($Name)) + { + if(-f $Name) + { # absolute path + return $Name; + } + else + { # broken + return ""; + } + } + if(defined $RegisteredObj{$LVer}{$Name}) + { # registered paths + return $RegisteredObj{$LVer}{$Name}; + } + if(defined $RegisteredSoname{$LVer}{$Name}) + { # registered paths + return $RegisteredSoname{$LVer}{$Name}; + } + if(my $DefaultPath = $In::Opt{"LibDefaultPath"}{$Name}) + { # ldconfig default paths + return $DefaultPath; + } + foreach my $Dir (@{$In::Opt{"DefaultLibPaths"}}, @{$In::Opt{"SysPaths"}{"lib"}}) + { # search in default linker directories + # and then in all system paths + if(-f $Dir."/".$Name) { + return join_P($Dir,$Name); + } + } + + checkSystemFiles(); + + if(my @AllObjects = keys(%{$In::Opt{"SystemObjects"}{$Name}})) { + return $AllObjects[0]; + } + if(my $ShortName = libPart($Name, "name+ext")) + { + if($ShortName ne $Name) + { # FIXME: check this case + if(my $Path = getLibPath($LVer, $ShortName)) { + return $Path; + } + } + } + # can't find + return ""; +} + +my %Prefix_Lib_Map=( + # symbols for autodetecting library dependencies (by prefix) + "pthread_" => ["libpthread"], + "g_" => ["libglib-2.0", "libgobject-2.0", "libgio-2.0"], + "cairo_" => ["libcairo"], + "gtk_" => ["libgtk-x11-2.0"], + "atk_" => ["libatk-1.0"], + "gdk_" => ["libgdk-x11-2.0"], + "gl" => ["libGL"], + "glu" => ["libGLU"], + "popt" => ["libpopt"], + "Py" => ["libpython"], + "jpeg_" => ["libjpeg"], + "BZ2_" => ["libbz2"], + "Fc" => ["libfontconfig"], + "Xft" => ["libXft"], + "SSL_" => ["libssl"], + "sem_" => ["libpthread"], + "snd_" => ["libasound"], + "art_" => ["libart_lgpl_2"], + "dbus_g" => ["libdbus-glib-1"], + "GOMP_" => ["libgomp"], + "omp_" => ["libgomp"], + "cms" => ["liblcms"] +); + +my %Pattern_Lib_Map=( + "SL[a-z]" => ["libslang"] +); + +my %Symbol_Lib_Map=( + # symbols for autodetecting library dependencies (by name) + "pow" => "libm", + "fmod" => "libm", + "sin" => "libm", + "floor" => "libm", + "cos" => "libm", + "dlopen" => "libdl", + "deflate" => "libz", + "inflate" => "libz", + "move_panel" => "libpanel", + "XOpenDisplay" => "libX11", + "resize_term" => "libncurses", + "clock_gettime" => "librt", + "crypt" => "libcrypt" +); + +sub find_SymbolLibs($$) +{ + my ($LVer, $Symbol) = @_; + + if(index($Symbol, "g_")==0 and $Symbol=~/[A-Z]/) + { # debug symbols + return (); + } + + my $LibExt = $In::Opt{"Ext"}; + + my %Paths = (); + + if(my $LibName = $Symbol_Lib_Map{$Symbol}) + { + if(my $Path = getLibPath($LVer, $LibName.".".$LibExt)) { + $Paths{$Path} = 1; + } + } + + if(my $SymbolPrefix = getPrefix($Symbol)) + { + if(defined $Cache{"find_SymbolLibs"}{$SymbolPrefix}) { + return @{$Cache{"find_SymbolLibs"}{$SymbolPrefix}}; + } + + if(not keys(%Paths)) + { + if(defined $Prefix_Lib_Map{$SymbolPrefix}) + { + foreach my $LibName (@{$Prefix_Lib_Map{$SymbolPrefix}}) + { + if(my $Path = getLibPath($LVer, $LibName.".".$LibExt)) { + $Paths{$Path} = 1; + } + } + } + } + + if(not keys(%Paths)) + { + foreach my $Prefix (sort keys(%Pattern_Lib_Map)) + { + if($Symbol=~/\A$Prefix/) + { + foreach my $LibName (@{$Pattern_Lib_Map{$Prefix}}) + { + if(my $Path = getLibPath($LVer, $LibName.".".$LibExt)) { + $Paths{$Path} = 1; + } + } + } + } + } + + if(not keys(%Paths)) + { + if($SymbolPrefix) + { # try to find a library by symbol prefix + if($SymbolPrefix eq "inotify" and + index($Symbol, "\@GLIBC")!=-1) + { + if(my $Path = getLibPath($LVer, "libc.$LibExt")) { + $Paths{$Path} = 1; + } + } + else + { + if(my $Path = getLibPathPrefix($LVer, $SymbolPrefix)) { + $Paths{$Path} = 1; + } + } + } + } + + if(my @Paths = keys(%Paths)) { + $Cache{"find_SymbolLibs"}{$SymbolPrefix} = \@Paths; + } + } + return keys(%Paths); +} + +sub getLibPathPrefix($$) +{ + my ($LVer, $Prefix) = @_; + my $LibExt = $In::Opt{"Ext"}; + + $Prefix = lc($Prefix); + $Prefix=~s/[_]+\Z//g; + + foreach ("-2", "2", "-1", "1", "") + { # libgnome-2.so + # libxml2.so + # libdbus-1.so + if(my $Path = getLibPath($LVer, "lib".$Prefix.$_.".".$LibExt)) { + return $Path; + } + } + return ""; +} + +return 1; diff --git a/abi-compliance-checker-2.4/modules/Internals/Basic.pm b/abi-compliance-checker-2.4/modules/Internals/Basic.pm new file mode 100644 index 0000000..2839397 --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/Basic.pm @@ -0,0 +1,746 @@ +########################################################################### +# A module with simple functions +# +# Copyright (C) 2015-2018 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; +use Config; +use Fcntl; + +my %Cache; + +my %OS_LibExt = ( + "dynamic" => { + "linux"=>"so", + "macos"=>"dylib", + "windows"=>"dll", + "symbian"=>"dso", + "default"=>"so" + }, + "static" => { + "linux"=>"a", + "windows"=>"lib", + "symbian"=>"lib", + "default"=>"a" + } +); + +sub appendFile($$) +{ + my ($Path, $Content) = @_; + + if(my $Dir = getDirname($Path)) { + mkpath($Dir); + } + + open(FILE, ">>", $Path) || die ("can't open file \'$Path\': $!\n"); + print FILE $Content; + close(FILE); +} + +sub writeFile($$) +{ + my ($Path, $Content) = @_; + + if(my $Dir = getDirname($Path)) { + mkpath($Dir); + } + + open(FILE, ">", $Path) || die ("can't open file \'$Path\': $!\n"); + print FILE $Content; + close(FILE); +} + +sub readFile($) +{ + my $Path = $_[0]; + + open(FILE, $Path); + local $/ = undef; + my $Content = ; + close(FILE); + + if($Path!~/\.(tu|class|abi)\Z/) { + $Content=~s/\r/\n/g; + } + + return $Content; +} + +sub getFilename($) +{ # much faster than basename() from File::Basename module + if(defined $Cache{"getFilename"}{$_[0]}) { + return $Cache{"getFilename"}{$_[0]}; + } + if($_[0] and $_[0]=~/([^\/\\]+)[\/\\]*\Z/) { + return ($Cache{"getFilename"}{$_[0]}=$1); + } + return ($Cache{"getFilename"}{$_[0]}=""); +} + +sub getDirname($) +{ # much faster than dirname() from File::Basename module + if(defined $Cache{"getDirname"}{$_[0]}) { + return $Cache{"getDirname"}{$_[0]}; + } + if($_[0] and $_[0]=~/\A(.*?)[\/\\]+[^\/\\]*[\/\\]*\Z/) { + return ($Cache{"getDirname"}{$_[0]}=$1); + } + return ($Cache{"getDirname"}{$_[0]}=""); +} + +sub sepPath($) { + return (getDirname($_[0]), getFilename($_[0])); +} + +sub escapeArg($) +{ + my $Str = $_[0]; + $Str=~s/([()\[\]{}$ &'"`;,<>\+])/\\$1/g; + return $Str; +} + +sub readLineNum($$) +{ + my ($Path, $Num) = @_; + + open(FILE, $Path); + foreach (1 ... $Num) { + ; + } + my $Line = ; + close(FILE); + return $Line; +} + +sub readAttributes($$) +{ + my ($Path, $Num) = @_; + + my %Attributes = (); + if(readLineNum($Path, $Num)=~//) + { + foreach my $AttrVal (split(/;/, $1)) + { + if($AttrVal=~/(.+):(.+)/) + { + my ($Name, $Value) = ($1, $2); + $Attributes{$Name} = $Value; + } + } + } + return \%Attributes; +} + +sub isAbsPath($) { + return ($_[0]=~/\A(\/|\w+:[\/\\])/); +} + +sub specChars($) +{ + my $Str = $_[0]; + if(not $Str) { + return $Str; + } + $Str=~s/\&([^#]|\Z)/&$1/g; + $Str=~s//->/g; # − + $Str=~s/>/>/g; + $Str=~s/([^ ]) ([^ ])/$1\@SP\@$2/g; + $Str=~s/([^ ]) ([^ ])/$1\@SP\@$2/g; + $Str=~s/ / /g; #   + $Str=~s/\@SP\@/ /g; + $Str=~s/\"/"/g; + $Str=~s/\'/'/g; + return $Str; +} + +sub parseTag(@) +{ + my $CodeRef = shift(@_); + my $Tag = shift(@_); + if(not $Tag or not $CodeRef) { + return undef; + } + my $Sp = 0; + if(@_) { + $Sp = shift(@_); + } + my $Start = index(${$CodeRef}, "<$Tag>"); + if($Start!=-1) + { + my $End = index(${$CodeRef}, ""); + if($End!=-1) + { + my $TS = length($Tag)+3; + my $Content = substr(${$CodeRef}, $Start, $End-$Start+$TS, ""); + substr($Content, 0, $TS-1, ""); # cut start tag + substr($Content, -$TS, $TS, ""); # cut end tag + if(not $Sp) + { + $Content=~s/\A\s+//g; + $Content=~s/\s+\Z//g; + } + if(substr($Content, 0, 1) ne "<") { + $Content = xmlSpecChars_R($Content); + } + return $Content; + } + } + return undef; +} + +sub xmlSpecChars($) +{ + my $Str = $_[0]; + if(not $Str) { + return $Str; + } + + $Str=~s/\&([^#]|\Z)/&$1/g; + $Str=~s//>/g; + + $Str=~s/\"/"/g; + $Str=~s/\'/'/g; + + return $Str; +} + +sub xmlSpecChars_R($) +{ + my $Str = $_[0]; + if(not $Str) { + return $Str; + } + + $Str=~s/&/&/g; + $Str=~s/<//g; + + $Str=~s/"/"/g; + $Str=~s/'/'/g; + + return $Str; +} + +sub push_U($@) +{ # push unique + if(my $Array = shift @_) + { + if(@_) + { + my %Exist = map {$_=>1} @{$Array}; + foreach my $Elem (@_) + { + if(not defined $Exist{$Elem}) + { + push(@{$Array}, $Elem); + $Exist{$Elem} = 1; + } + } + } + } +} + +sub getDepth($) +{ + if(defined $Cache{"getDepth"}{$_[0]}) { + return $Cache{"getDepth"}{$_[0]}; + } + return ($Cache{"getDepth"}{$_[0]} = ($_[0]=~tr![\/\\]|\:\:!!)); +} + +sub cmpVersions($$) +{ # compare two versions in dotted-numeric format + my ($V1, $V2) = @_; + return 0 if($V1 eq $V2); + my @V1Parts = split(/\./, $V1); + my @V2Parts = split(/\./, $V2); + for (my $i = 0; $i <= $#V1Parts && $i <= $#V2Parts; $i++) + { + return -1 if(int($V1Parts[$i]) < int($V2Parts[$i])); + return 1 if(int($V1Parts[$i]) > int($V2Parts[$i])); + } + return -1 if($#V1Parts < $#V2Parts); + return 1 if($#V1Parts > $#V2Parts); + return 0; +} + +sub isDump($) +{ + if(getFilename($_[0])=~/\A(.+)\.(abi|abidump|dump)((\.tar\.gz|\.tgz)(\.\w+|)|\.zip|\.xml|)\Z/) + { # NOTE: name.abi.tar.gz.amd64 (dh & cdbs) + return $1; + } + return 0; +} + +sub isDump_U($) +{ + if(getFilename($_[0])=~/\A(.+)\.(abi|abidump|dump)(\.xml|)\Z/) { + return $1; + } + return 0; +} + +sub cutPrefix($$) +{ + my ($Path, $Prefix) = @_; + if(not $Prefix) { + return $Path; + } + $Prefix=~s/[\/\\]+\Z//; + $Path=~s/\A\Q$Prefix\E([\/\\]+|\Z)//; + return $Path; +} + +sub sortByWord($$) +{ + my ($ArrRef, $W) = @_; + if(length($W)<2) { + return; + } + @{$ArrRef} = sort {getFilename($b)=~/\Q$W\E/i<=>getFilename($a)=~/\Q$W\E/i} @{$ArrRef}; +} + +sub showPos($) +{ + my $N = $_[0]; + if(not $N) { + $N = 1; + } + else { + $N = int($N)+1; + } + if($N>3) { + return $N."th"; + } + elsif($N==1) { + return "1st"; + } + elsif($N==2) { + return "2nd"; + } + elsif($N==3) { + return "3rd"; + } + + return $N; +} + +sub isCyclical($$) +{ + my ($Stack, $Value) = @_; + return (grep {$_ eq $Value} @{$Stack}); +} + +sub formatName($$) +{ # type name correction + if(defined $Cache{"formatName"}{$_[1]}{$_[0]}) { + return $Cache{"formatName"}{$_[1]}{$_[0]}; + } + + my $N = $_[0]; + + if($_[1] ne "S") + { + $N=~s/\A[ ]+//g; + $N=~s/[ ]+\Z//g; + $N=~s/[ ]{2,}/ /g; + } + + $N=~s/[ ]*(\W)[ ]*/$1/g; # std::basic_string const + + $N=~s/\b(const|volatile) ([\w\:]+)([\*&,>]|\Z)/$2 $1$3/g; # "const void" to "void const" + + $N=~s/\bvolatile const\b/const volatile/g; + + $N=~s/\b(long long|short|long) unsigned\b/unsigned $1/g; + $N=~s/\b(short|long) int\b/$1/g; + + $N=~s/([\)\]])(const|volatile)\b/$1 $2/g; + + while($N=~s/>>/> >/g) {}; + + if($_[1] eq "S") + { + if(index($N, "operator")!=-1) { + $N=~s/\b(operator[ ]*)> >/$1>>/; + } + } + + $N=~s/,([^ ])/, $1/g; + + return ($Cache{"formatName"}{$_[1]}{$_[0]} = $N); +} + +sub isRecurType($$$) +{ + foreach (@{$_[2]}) + { + if( $_->{"T1"} eq $_[0] + and $_->{"T2"} eq $_[1] ) + { + return 1; + } + } + return 0; +} + +sub pushType($$$) +{ + my %IDs = ( + "T1" => $_[0], + "T2" => $_[1] + ); + push(@{$_[2]}, \%IDs); +} + +sub formatVersion($$) +{ # cut off version digits + my ($V, $Digits) = @_; + my @Elems = split(/\./, $V); + return join(".", splice(@Elems, 0, $Digits)); +} + +sub showNum($) +{ + if($_[0]) + { + my $Num = cutNum($_[0], 2, 0); + if($Num eq "0") + { + foreach my $P (3 .. 7) + { + $Num = cutNum($_[0], $P, 1); + if($Num ne "0") { + last; + } + } + } + if($Num eq "0") { + $Num = $_[0]; + } + return $Num; + } + return $_[0]; +} + +sub cutNum($$$) +{ + my ($num, $digs_to_cut, $z) = @_; + if($num!~/\./) + { + $num .= "."; + foreach (1 .. $digs_to_cut-1) { + $num .= "0"; + } + } + elsif($num=~/\.(.+)\Z/ and length($1)<$digs_to_cut-1) + { + foreach (1 .. $digs_to_cut - 1 - length($1)) { + $num .= "0"; + } + } + elsif($num=~/\d+\.(\d){$digs_to_cut,}/) { + $num=sprintf("%.".($digs_to_cut-1)."f", $num); + } + $num=~s/\.[0]+\Z//g; + if($z) { + $num=~s/(\.[1-9]+)[0]+\Z/$1/g; + } + return $num; +} + +sub getPrefix($) +{ + my $Str = $_[0]; + if($Str=~/\A([_]*[A-Z][a-z]{1,5})[A-Z]/) + { # XmuValidArea: Xmu + return $1; + } + elsif($Str=~/\A([_]*[a-z]+)[A-Z]/) + { # snfReadFont: snf + return $1; + } + elsif($Str=~/\A([_]*[A-Z]{2,})[A-Z][a-z]+([A-Z][a-z]+|\Z)/) + { # XRRTimes: XRR + return $1; + } + elsif($Str=~/\A([_]*[a-z]{1,2}\d+)[a-z\d]*_[a-z]+/i) + { # H5HF_delete: H5 + return $1; + } + elsif($Str=~/\A([_]*[a-z0-9]{2,}_)[a-z]+/i) + { # alarm_event_add: alarm_ + return $1; + } + elsif($Str=~/\A(([a-z])\2{1,})/i) + { # ffopen + return $1; + } + return ""; +} + +sub isBuiltIn($) { + return ($_[0] and $_[0]=~/\|\|\A\./); +} + +sub checkWin32Env() +{ + if(not $ENV{"VCINSTALLDIR"} + or not $ENV{"INCLUDE"}) { + exitStatus("Error", "can't start without VC environment (vcvars64.bat)"); + } +} + +sub symbolParts($) +{ + my $S = $_[0]; + + if(index($S, '@')==-1 + and index($S, '$')==-1) { + return ($S, "", ""); + } + + if($S=~/\A([^\@\$\?]+)([\@\$]+)([^\@\$]+)\Z/) { + return ($1, $2, $3); + } + + return ($S, "", ""); +} + +sub getOSgroup() +{ + my $N = $Config{"osname"}; + my $G = undef; + + if($N=~/macos|darwin|rhapsody/i) { + $G = "macos"; + } + elsif($N=~/freebsd|openbsd|netbsd/i) { + $G = "bsd"; + } + elsif($N=~/haiku|beos/i) { + $G = "beos"; + } + elsif($N=~/symbian|epoc/i) { + $G = "symbian"; + } + elsif($N=~/win/i) { + $G = "windows"; + } + elsif($N=~/solaris/i) { + $G = "solaris"; + } + else + { # linux, unix-like + $G = "linux"; + } + + return $G; +} + +sub getLibExt($$) +{ + my ($Target, $Static) = @_; + + my $LType = "dynamic"; + + if($Static) { + $LType = "static"; + } + + if(my $Ex = $OS_LibExt{$LType}{$Target}) { + return $Ex; + } + return $OS_LibExt{$LType}{"default"}; +} + +sub isAnon($) +{ # "._N" or "$_N" in older GCC versions + return ($_[0] and $_[0]=~/(\.|\$)\_\d+|anon\-/); +} + +sub checkCmd($) +{ + my $Cmd = $_[0]; + + foreach my $Path (sort {length($a)<=>length($b)} split(/:/, $ENV{"PATH"})) + { + if(-x $Path."/".$Cmd) { + return 1; + } + } + + return 0; +} + +sub checkList($$) +{ + my ($Item, $Skip) = @_; + if(not $Skip) { + return 0; + } + foreach my $P (@{$Skip}) + { + my $Pattern = $P; + if(index($Pattern, "*")!=-1) + { # wildcards + $Pattern=~s/\*/.*/g; # to perl format + if($Item=~/$Pattern/) { + return 1; + } + } + elsif(index($Pattern, "/")!=-1 + or index($Pattern, "\\")!=-1) + { # directory + if(index($Item, $Pattern)!=-1) { + return 1; + } + } + elsif($Item eq $Pattern + or getFilename($Item) eq $Pattern) + { # by name + return 1; + } + } + return 0; +} + +sub getArExt($) +{ + my $Target = $_[0]; + if($Target eq "windows") { + return "zip"; + } + return "tar.gz"; +} + +sub cutAttrs($) +{ + if($_[0]=~s/(\))((| (const volatile|const|volatile))(| \[static\]))\Z/$1/) { + return $2; + } + return ""; +} + +sub splitSignature($) +{ + my $Signature = $_[0]; + if(my $ShortName = substr($Signature, 0, findCenter($Signature, "("))) + { + $Signature=~s/\A\Q$ShortName\E\(//g; + cutAttrs($Signature); + $Signature=~s/\)\Z//; + return ($ShortName, $Signature); + } + + # error + return ($Signature, ""); +} + +sub sepParams($$$) +{ + my ($Params, $Comma, $Sp) = @_; + my @Parts = (); + my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 ); + my $Part = 0; + foreach my $Pos (0 .. length($Params) - 1) + { + my $S = substr($Params, $Pos, 1); + if(defined $B{$S}) { + $B{$S} += 1; + } + if($S eq "," and + $B{"("}==$B{")"} and $B{"<"}==$B{">"}) + { + if($Comma) + { # include comma + $Parts[$Part] .= $S; + } + $Part += 1; + } + else { + $Parts[$Part] .= $S; + } + } + if(not $Sp) + { # remove spaces + foreach (@Parts) + { + s/\A //g; + s/ \Z//g; + } + } + return @Parts; +} + +sub findCenter($$) +{ + my ($Sign, $Target) = @_; + my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 ); + my $Center = 0; + if($Sign=~s/(operator([^\w\s\(\)]+|\(\)))//g) + { # operators + $Center+=length($1); + } + foreach my $Pos (0 .. length($Sign)-1) + { + my $S = substr($Sign, $Pos, 1); + if($S eq $Target) + { + if($B{"("}==$B{")"} + and $B{"<"}==$B{">"}) { + return $Center; + } + } + if(defined $B{$S}) { + $B{$S}+=1; + } + $Center+=1; + } + return 0; +} + +sub deleteKeywords($) +{ + my $TypeName = $_[0]; + $TypeName=~s/\b(enum|struct|union|class) //g; + return $TypeName; +} + +sub readBytes($) +{ + sysopen(FILE, $_[0], O_RDONLY); + sysread(FILE, my $Header, 4); + close(FILE); + my @Bytes = map { sprintf('%02x', ord($_)) } split (//, $Header); + return join("", @Bytes); +} + +sub isElf($) +{ + my $Path = $_[0]; + return (readBytes($Path) eq "7f454c46"); +} + +return 1; diff --git a/abi-compliance-checker-2.4/modules/Internals/CallConv.pm b/abi-compliance-checker-2.4/modules/Internals/CallConv.pm new file mode 100644 index 0000000..6ce2923 --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/CallConv.pm @@ -0,0 +1,1354 @@ +########################################################################### +# A module to create a model of calling conventions +# +# Copyright (C) 2009-2011 Institute for System Programming, RAS +# Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies) +# Copyright (C) 2012-2018 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# PLATFORMS +# ========= +# Linux, FreeBSD, Solaris and Mac OS X +# x86 - System V ABI Intel386 Architecture Processor Supplement +# x86_64 - System V ABI AMD64 Architecture Processor Supplement +# +# MS Windows +# x86 - MSDN Argument Passing and Naming Conventions +# x86_64 - MSDN x64 Software Conventions +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; + +my $BYTE = 8; + +my %UsedReg = (); +my %UsedStack = (); + +my %IntAlgn = ( + "x86"=>{ + "double"=>4, + "long double"=>4 + } +); + +sub classifyType($$) +{ + my ($Tid, $LVer) = @_; + + my %Type = getPureType($Tid, $LVer); + my $Arch = $In::ABI{$LVer}{"Arch"}; + + my %Classes = (); + if($Type{"Name"} eq "void") + { + $Classes{0}{"Class"} = "VOID"; + return %Classes; + } + if($In::Opt{"Target"}=~/\A(unix|linux|macos|freebsd|solaris)\Z/) + { # GCC + if($Arch eq "x86") + { + if(isFloat($Type{"Name"})) { + $Classes{0}{"Class"} = "FLOAT"; + } + elsif($Type{"Type"}=~/Intrinsic|Enum|Pointer|Ptr/) { + $Classes{0}{"Class"} = "INTEGRAL"; + } + else { # Struct, Class, Union + $Classes{0}{"Class"} = "MEMORY"; + } + } + elsif($Arch eq "x86_64") + { + if($Type{"Type"}=~/Enum|Pointer|Ptr/ + or isScalar($Type{"Name"}) + or $Type{"Name"}=~/\A(_Bool|bool)\Z/) { + $Classes{0}{"Class"} = "INTEGER"; + } + elsif($Type{"Name"} eq "__int128" + or $Type{"Name"} eq "unsigned __int128") + { + $Classes{0}{"Class"} = "INTEGER"; + $Classes{1}{"Class"} = "INTEGER"; + } + elsif($Type{"Name"}=~/\A(float|double|_Decimal32|_Decimal64|__m64)\Z/) { + $Classes{0}{"Class"} = "SSE"; + } + elsif($Type{"Name"}=~/\A(__float128|_Decimal128|__m128)\Z/) + { + $Classes{0}{"Class"} = "SSE"; + $Classes{8}{"Class"} = "SSEUP"; + } + elsif($Type{"Name"} eq "__m256") + { + $Classes{0}{"Class"} = "SSE"; + $Classes{24}{"Class"} = "SSEUP"; + } + elsif($Type{"Name"} eq "long double") + { + $Classes{0}{"Class"} = "X87"; + $Classes{8}{"Class"} = "X87UP"; + } + elsif($Type{"Name"}=~/\Acomplex (float|double)\Z/) { + $Classes{0}{"Class"} = "MEMORY"; + } + elsif($Type{"Name"} eq "complex long double") { + $Classes{0}{"Class"} = "COMPLEX_X87"; + } + elsif($Type{"Type"}=~/Struct|Class|Union|Array/) + { + if($Type{"Size"}>4*8) { + $Classes{0}{"Class"} = "MEMORY"; + } + else { + %Classes = classifyAggregate($Tid, $LVer); + } + } + else { + $Classes{0}{"Class"} = "MEMORY"; + } + } + elsif($Arch eq "arm") + { + } + } + elsif($In::Opt{"Target"} eq "windows") + { # MS C++ Compiler + if($Arch eq "x86") + { + if(isFloat($Type{"Name"})) { + $Classes{0}{"Class"} = "FLOAT"; + } + elsif($Type{"Type"}=~/Intrinsic|Enum|Pointer|Ptr/) { + $Classes{0}{"Class"} = "INTEGRAL"; + } + elsif($Type{"Type"}=~/\A(Struct|Union)\Z/ and $Type{"Size"}<=8) { + $Classes{0}{"Class"} = "POD"; + } + else { # Struct, Class, Union + $Classes{0}{"Class"} = "MEMORY"; + } + } + elsif($Arch eq "x86_64") + { + if($Type{"Name"}=~/\A(float|double|long double)\Z/) { + $Classes{0}{"Class"} = "FLOAT"; + } + elsif($Type{"Name"}=~/\A__m128(|i|d)\Z/) { + $Classes{0}{"Class"} = "M128"; + } + elsif(isScalar($Type{"Name"}) + or $Type{"Type"}=~/Enum|Pointer|Ptr/ + or $Type{"Name"}=~/\A(_Bool|bool)\Z/ + or ($Type{"Type"}=~/\A(Struct|Union)\Z/ and $Type{"Size"}<=8) + or $Type{"Name"} eq "__m64") { + $Classes{0}{"Class"} = "INTEGRAL"; + } + else { + $Classes{0}{"Class"} = "MEMORY"; + } + } + } + return %Classes; +} + +sub classifyAggregate($$) +{ + my ($Tid, $LVer) = @_; + + my %Type = getPureType($Tid, $LVer); + my $Word = $In::ABI{$LVer}{"WordSize"}; + my $Arch = $In::ABI{$LVer}{"Arch"}; + + my %Group = (); + my $GroupID = 0; + my %Classes = (); + my %Offsets = (); + if($Type{"Type"} eq "Array") + { + my %Base = getOneStepBaseType($Tid, $LVer); + my %BaseType = getPureType($Base{"Tid"}, $LVer); + my $Pos = 0; + my $Max = 0; + if(my $BSize = $BaseType{"Size"}) { + $Max = ($Type{"Size"}/$BSize) - 1; + } + foreach my $Pos (0 .. $Max) + { + # if($TInfo->{1}{"Name"} eq "void") + # { # DWARF ABI Dump + # $Type{"Memb"}{$Pos}{"offset"} = $Type{"Size"}/($Max+1); + # } + $Type{"Memb"}{$Pos}{"algn"} = getAlignment_Model($BaseType{"Tid"}, $LVer); + $Type{"Memb"}{$Pos}{"type"} = $BaseType{"Tid"}; + $Type{"Memb"}{$Pos}{"name"} = "[$Pos]"; + } + } + if($Type{"Type"} eq "Union") + { + foreach my $Pos (keys(%{$Type{"Memb"}})) + { + $Offsets{$Pos} = $Pos; + $Group{0}{$Pos} = 1; + } + } + else + { # Struct, Class + foreach my $Pos (keys(%{$Type{"Memb"}})) + { + my $Offset = getOffset($Pos, \%Type, $LVer)/$BYTE; + $Offsets{$Pos} = $Offset; + my $GroupOffset = int($Offset/$Word)*$Word; + $Group{$GroupOffset}{$Pos} = 1; + } + } + foreach my $GroupOffset (sort {$a<=>$b} (keys(%Group))) + { + my %GroupClasses = (); + foreach my $Pos (sort {$a<=>$b} (keys(%{$Group{$GroupOffset}}))) + { # split the field into the classes + my $MTid = $Type{"Memb"}{$Pos}{"type"}; + my $MName = $Type{"Memb"}{$Pos}{"name"}; + my %SubClasses = classifyType($MTid, $LVer); + foreach my $Offset (sort {$a<=>$b} keys(%SubClasses)) + { + if(defined $SubClasses{$Offset}{"Elems"}) + { + foreach (keys(%{$SubClasses{$Offset}{"Elems"}})) { + $SubClasses{$Offset}{"Elems"}{$_} = joinFields($MName, $SubClasses{$Offset}{"Elems"}{$_}); + } + } + else { + $SubClasses{$Offset}{"Elems"}{0} = $MName; + } + } + + # add to the group + foreach my $Offset (sort {$a<=>$b} keys(%SubClasses)) { + $GroupClasses{$Offsets{$Pos}+$Offset} = $SubClasses{$Offset}; + } + } + + # merge classes in the group + my %MergeGroup = (); + + foreach my $Offset (sort {$a<=>$b} keys(%GroupClasses)) { + $MergeGroup{int($Offset/$Word)}{$Offset} = $GroupClasses{$Offset}; + } + + foreach my $Offset (sort {$a<=>$b} keys(%MergeGroup)) { + while(postMerger($Arch, $MergeGroup{$Offset})) { }; + } + + %GroupClasses = (); + foreach my $M_Offset (sort {$a<=>$b} keys(%MergeGroup)) + { + foreach my $Offset (sort {$a<=>$b} keys(%{$MergeGroup{$M_Offset}})) + { + $GroupClasses{$Offset} = $MergeGroup{$M_Offset}{$Offset}; + } + } + + # add to the result list of classes + foreach my $Offset (sort {$a<=>$b} keys(%GroupClasses)) + { + if($Type{"Type"} eq "Union") + { + foreach my $P (keys(%{$GroupClasses{$Offset}{"Elems"}})) + { + if($P!=0) { + delete($GroupClasses{$Offset}{"Elems"}{$P}); + } + } + } + $Classes{$Offset} = $GroupClasses{$Offset}; + } + } + + return %Classes; +} + +sub postMerger($$) +{ + my ($Arch, $PreClasses) = @_; + my @Offsets = sort {$a<=>$b} keys(%{$PreClasses}); + if($#Offsets==0) { + return 0; + } + my %PostClasses = (); + my $Num = 0; + my $Merged = 0; + while($Num<=$#Offsets-1) + { + my $Offset1 = $Offsets[$Num]; + my $Offset2 = $Offsets[$Num+1]; + my $Class1 = $PreClasses->{$Offset1}{"Class"}; + my $Class2 = $PreClasses->{$Offset2}{"Class"}; + my $ResClass = ""; + + if($In::Opt{"Target"}=~/\A(unix|linux|macos|freebsd|solaris)\Z/) + { # GCC + if($Arch eq "x86_64") + { + if($Class1 eq $Class2) { + $ResClass = $Class1; + } + elsif($Class1 eq "MEMORY" + or $Class2 eq "MEMORY") { + $ResClass = "MEMORY"; + } + elsif($Class1 eq "INTEGER" + or $Class2 eq "INTEGER") { + $ResClass = "INTEGER"; + } + elsif($Class1=~/X87/ + or $Class2=~/X87/) { + $ResClass = "MEMORY"; + } + else { + $ResClass = "SSE"; + } + } + } + + if($ResClass) + { # combine + $PostClasses{$Offset1}{"Class"} = $ResClass; + foreach (keys(%{$PreClasses->{$Offset1}{"Elems"}})) { + $PostClasses{$Offset1}{"Elems"}{$Offset1+$_} = $PreClasses->{$Offset1}{"Elems"}{$_}; + } + foreach (keys(%{$PreClasses->{$Offset2}{"Elems"}})) { + $PostClasses{$Offset1}{"Elems"}{$Offset2+$_} = $PreClasses->{$Offset2}{"Elems"}{$_}; + } + $Merged = 1; + } + else + { # save unchanged + $PostClasses{$Offset1} = $PreClasses->{$Offset1}; + $PostClasses{$Offset2} = $PreClasses->{$Offset2}; + } + $Num += 2; + } + if($Num==$#Offsets) { + $PostClasses{$Offsets[$Num]} = $PreClasses->{$Offsets[$Num]}; + } + %{$PreClasses} = %PostClasses; + return $Merged; +} + +sub joinFields($$) +{ + my ($F1, $F2) = @_; + if(substr($F2, 0, 1) eq "[") + { # array elements + return $F1.$F2; + } + else { # fields + return $F1.".".$F2; + } +} + +sub callingConvention_R_Model($$$) { + return callingConvention_R_I_Model(@_, 1); +} + +sub callingConvention_R_I_Model($$$) +{ + my ($SInfo, $LVer, $Target) = @_; + my %Conv = (); + my $RTid = $SInfo->{"Return"}; + my %Type = getPureType($RTid, $LVer); + my $Word = $In::ABI{$LVer}{"WordSize"}; + my $Arch = $In::ABI{$LVer}{"Arch"}; + + if($Target) { + %UsedReg = (); + } + + my %UsedReg_Copy = %UsedReg; + + my %Classes = classifyType($RTid, $LVer); + + foreach my $Offset (sort {$a<=>$b} keys(%Classes)) + { + my $Elems = undef; + if(defined $Classes{$Offset}{"Elems"}) + { + foreach (keys(%{$Classes{$Offset}{"Elems"}})) { + $Classes{$Offset}{"Elems"}{$_} = joinFields(".result", $Classes{$Offset}{"Elems"}{$_}); + } + $Elems = $Classes{$Offset}{"Elems"}; + } + else { + $Elems = { 0 => ".result" }; + } + + my $CName = $Classes{$Offset}{"Class"}; + + if($CName eq "VOID") { + next; + } + + if($In::Opt{"Target"}=~/\A(unix|linux|macos|freebsd|solaris)\Z/) + { # GCC + if($Arch eq "x86") + { + if($CName eq "FLOAT") + { # x87 register + useRegister("st0", "f", $Elems, $SInfo); + } + elsif($CName eq "INTEGRAL") + { + useRegister("eax", "f", $Elems, $SInfo); + } + elsif($CName eq "MEMORY") { + pushStack_R($SInfo, $Word); + } + } + elsif($Arch eq "x86_64") + { + my @INT = ("rax", "rdx"); + my @SSE = ("xmm0", "xmm1"); + if($CName eq "INTEGER") + { + if(my $R = getLastAvailable($SInfo, "f", @INT)) + { + useRegister($R, "f", $Elems, $SInfo); + } + else + { # revert registers + # pass as MEMORY + %UsedReg = %UsedReg_Copy; + useHidden($SInfo, $Arch, $Word); + $Conv{"Hidden"} = 1; + last; + } + } + elsif($CName eq "SSE") + { + if(my $R = getLastAvailable($SInfo, "8l", @SSE)) + { + useRegister($R, "8l", $Elems, $SInfo); + } + else + { + %UsedReg = %UsedReg_Copy; + useHidden($SInfo, $Arch, $Word); + $Conv{"Hidden"} = 1; + last; + } + } + elsif($CName eq "SSEUP") + { + if(my $R = getLastUsed($SInfo, "xmm0", "xmm1")) + { + useRegister($R, "8h", $Elems, $SInfo); + } + else + { + %UsedReg = %UsedReg_Copy; + useHidden($SInfo, $Arch, $Word); + $Conv{"Hidden"} = 1; + last; + } + } + elsif($CName eq "X87") + { + useRegister("st0", "8l", $Elems, $SInfo); + } + elsif($CName eq "X87UP") + { + useRegister("st0", "8h", $Elems, $SInfo); + } + elsif($CName eq "COMPLEX_X87") + { + useRegister("st0", "f", $Elems, $SInfo); + useRegister("st1", "f", $Elems, $SInfo); + } + elsif($CName eq "MEMORY") + { + useHidden($SInfo, $Arch, $Word); + $Conv{"Hidden"} = 1; + last; + } + } + elsif($Arch eq "arm") + { # TODO + } + } + elsif($In::Opt{"Target"} eq "windows") + { # MS C++ Compiler + if($Arch eq "x86") + { + if($CName eq "FLOAT") + { + useRegister("fp0", "f", $Elems, $SInfo); + } + elsif($CName eq "INTEGRAL") + { + useRegister("eax", "f", $Elems, $SInfo); + } + elsif($CName eq "POD") + { + useRegister("eax", "f", $Elems, $SInfo); + useRegister("edx", "f", $Elems, $SInfo); + } + elsif($CName eq "MEMORY" or $CName eq "M128") + { + useHidden($SInfo, $Arch, $Word); + $Conv{"Hidden"} = 1; + } + } + elsif($Arch eq "x86_64") + { + if($CName eq "FLOAT" or $CName eq "M128") + { + useRegister("xmm0", "f", $Elems, $SInfo); + } + elsif($CName eq "INTEGRAL") + { + useRegister("eax", "f", $Elems, $SInfo); + } + elsif($CName eq "MEMORY") + { + useHidden($SInfo, $Arch, $Word); + $Conv{"Hidden"} = 1; + } + } + } + } + + + if(my %Regs = usedBy(".result", $SInfo)) + { + $Conv{"Method"} = "reg"; + $Conv{"Registers"} = join(", ", sort(keys(%Regs))); + } + elsif(my %Regs = usedBy(".result_ptr", $SInfo)) + { + $Conv{"Method"} = "reg"; + $Conv{"Registers"} = join(", ", sort(keys(%Regs))); + } + + if(not $Conv{"Method"}) + { # unknown + if($Type{"Name"} ne "void") + { + $Conv{"Method"} = "stack"; + $Conv{"Hidden"} = 1; + } + } + + return %Conv; +} + +sub usedBy($$) +{ + my ($Name, $SInfo) = @_; + my %Regs = (); + foreach my $Reg (sort keys(%{$UsedReg{$SInfo}})) + { + foreach my $Size (sort keys(%{$UsedReg{$SInfo}{$Reg}})) + { + foreach my $Offset (sort keys(%{$UsedReg{$SInfo}{$Reg}{$Size}})) + { + if($UsedReg{$SInfo}{$Reg}{$Size}{$Offset}=~/\A\Q$Name\E(\.|\Z)/) { + $Regs{$Reg} = 1; + } + } + } + } + return %Regs; +} + +sub useHidden($$$) +{ + my ($SInfo, $Arch, $Word) = @_; + if($In::Opt{"Target"}=~/\A(unix|linux|macos|freebsd|solaris)\Z/) + { # GCC + if($Arch eq "x86") { + pushStack_R($SInfo, $Word); + } + elsif($Arch eq "x86_64") + { + my $Elems = { 0 => ".result_ptr" }; + useRegister("rdi", "f", $Elems, $SInfo); + } + } + elsif($In::Opt{"Target"} eq "windows") + { # MS C++ Compiler + if($Arch eq "x86") { + pushStack_R($SInfo, $Word); + } + elsif($Arch eq "x86_64") + { + my $Elems = { 0 => ".result_ptr" }; + useRegister("rcx", "f", $Elems, $SInfo); + } + } +} + +sub pushStack_P($$$$) +{ + my ($SInfo, $Pos, $TInfo, $StackAlgn) = @_; + my $PTid = $SInfo->{"Param"}{$Pos}{"type"}; + my $PName = $SInfo->{"Param"}{$Pos}{"name"}; + + if(my $Offset = $SInfo->{"Param"}{$Pos}{"offset"}) + { # DWARF ABI Dump + return pushStack_Offset($SInfo, $Offset, $TInfo->{$PTid}{"Size"}, { 0 => $PName }); + } + else + { + my $Alignment = $SInfo->{"Param"}{$Pos}{"algn"}; + if($Alignment<$StackAlgn) { + $Alignment = $StackAlgn; + } + return pushStack($SInfo, $Alignment, $TInfo->{$PTid}{"Size"}, { 0 => $PName }); + } +} + +sub pushStack_R($$) +{ + my ($SInfo, $Word) = @_; + return pushStack($SInfo, $Word, $Word, { 0 => ".result_ptr" }); +} + +sub pushStack_C($$$) +{ + my ($SInfo, $Class, $TInfo) = @_; + return pushStack($SInfo, $Class->{"Algn"}, $Class->{"Size"}, $Class->{"Elems"}); +} + +sub pushStack($$$$) +{ + my ($SInfo, $Algn, $Size, $Elem) = @_; + my $Offset = 0; + if(my @Offsets = sort {$a<=>$b} keys(%{$UsedStack{$SInfo}})) + { + $Offset = $Offsets[$#Offsets]; + $Offset += $UsedStack{$SInfo}{$Offset}{"Size"}; + $Offset += getPadding($Offset, $Algn); + } + return pushStack_Offset($SInfo, $Offset, $Size, $Elem); +} + +sub pushStack_Offset($$$$) +{ + my ($SInfo, $Offset, $Size, $Elem) = @_; + my %Info = ( + "Size" => $Size, + "Elem" => $Elem + ); + $UsedStack{$SInfo}{$Offset} = \%Info; + return $Offset; +} + +sub useRegister($$$$) +{ + my ($R, $Offset, $Elems, $SInfo) = @_; + if(defined $UsedReg{$SInfo}{$R}) + { + if(defined $UsedReg{$SInfo}{$R}{$Offset}) + { # busy + return 0; + } + } + $UsedReg{$SInfo}{$R}{$Offset}=$Elems; + return $R; +} + +sub getLastAvailable(@) +{ + my $SInfo = shift(@_); + my $Offset = shift(@_); + my $Pos = 0; + foreach (@_) + { + if(not defined $UsedReg{$SInfo}{$_}) { + return $_; + } + elsif(not defined $UsedReg{$SInfo}{$_}{$Offset}) { + return $_; + } + } + return undef; +} + +sub getLastUsed(@) +{ + my $SInfo = shift(@_); + my $Pos = 0; + foreach (@_) + { + if(not defined $UsedReg{$SInfo}{$_}) + { + if($Pos>0) { + return @_[$Pos-1]; + } + else { + return @_[0]; + } + } + $Pos+=1; + } + return undef; +} + +sub callingConvention_P_Model($$$) { + return callingConvention_P_I_Model(@_, 1); +} + +sub callingConvention_P_I_Model($$$$) +{ # calling conventions for different compilers and operating systems + my ($SInfo, $Pos, $LVer, $Target) = @_; + + my $TInfo = $In::ABI{$LVer}{"TypeInfo"}; + my $PTid = $SInfo->{"Param"}{$Pos}{"type"}; + my $PName = $SInfo->{"Param"}{$Pos}{"name"}; + my %Type = getPureType($PTid, $LVer); + my $Word = $In::ABI{$LVer}{"WordSize"}; + my $Arch = $In::ABI{$LVer}{"Arch"}; + + if($Target) + { + %UsedReg = (); + + # distribute return value + if(my $RTid = $SInfo->{"Return"}) { + callingConvention_R_I_Model($SInfo, $LVer, 0); + } + # distribute other parameters + if($Pos>0) + { + my %PConv = (); + my $PPos = 0; + while($PConv{"Next"} ne $Pos) + { + %PConv = callingConvention_P_I_Model($SInfo, $PPos++, $LVer, 0); + if(not $PConv{"Next"}) { + last; + } + } + } + } + + my %UsedReg_Copy = %UsedReg; + + my %Classes = classifyType($PTid, $LVer); + + my $Error = 0; + foreach my $Offset (sort {$a<=>$b} keys(%Classes)) + { + my $Elems = undef; + if(defined $Classes{$Offset}{"Elems"}) + { + foreach (keys(%{$Classes{$Offset}{"Elems"}})) { + $Classes{$Offset}{"Elems"}{$_} = joinFields($PName, $Classes{$Offset}{"Elems"}{$_}); + } + $Elems = $Classes{$Offset}{"Elems"}; + } + else { + $Elems = { 0 => $PName }; + } + + my $CName = $Classes{$Offset}{"Class"}; + + if($CName eq "VOID") { + next; + } + + if($In::Opt{"Target"}=~/\A(unix|linux|macos|freebsd|solaris)\Z/) + { # GCC + if($Arch eq "x86") + { + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + elsif($Arch eq "x86_64") + { + my @INT = ("rdi", "rsi", "rdx", "rcx", "r8", "r9"); + my @SSE = ("xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"); + + if($CName eq "INTEGER") + { + if(my $R = getLastAvailable($SInfo, "f", @INT)) { + useRegister($R, "f", $Elems, $SInfo); + } + else + { # revert registers and + # push the argument on the stack + %UsedReg = %UsedReg_Copy; + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + elsif($CName eq "SSE") + { + if(my $R = getLastAvailable($SInfo, "8l", @SSE)) { + useRegister($R, "8l", $Elems, $SInfo); + } + else + { + %UsedReg = %UsedReg_Copy; + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + elsif($CName eq "SSEUP") + { + if(my $R = getLastUsed($SInfo, @SSE)) { + useRegister($R, "8h", $Elems, $SInfo); + } + else + { + %UsedReg = %UsedReg_Copy; + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + elsif($CName=~/X87|MEMORY/) + { # MEMORY, X87, X87UP, COMPLEX_X87 + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + else + { + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + elsif($Arch eq "arm") + { # Procedure Call Standard for the ARM Architecture + # TODO + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + else + { # TODO + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + elsif($In::Opt{"Target"} eq "windows") + { # MS C++ Compiler + if($Arch eq "x86") + { + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + elsif($Arch eq "x86_64") + { + if($Pos<=3) + { + if($CName eq "FLOAT") + { + useRegister("xmm".$Pos, "8l", $Elems, $SInfo); + } + elsif($CName eq "INTEGRAL") + { + if($Pos==0) { + useRegister("rcx", "f", $Elems, $SInfo); + } + elsif($Pos==1) { + useRegister("rdx", "f", $Elems, $SInfo); + } + elsif($Pos==2) { + useRegister("r8", "f", $Elems, $SInfo); + } + elsif($Pos==3) { + useRegister("r9", "f", $Elems, $SInfo); + } + else + { + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + else + { + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + else + { + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + } + else + { # TODO + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + + my %Conv = (); + + if(my %Regs = usedBy($PName, $SInfo)) + { + $Conv{"Method"} = "reg"; + $Conv{"Registers"} = join(", ", sort(keys(%Regs))); + } + else + { + if($Type{"Name"} ne "void") { + $Conv{"Method"} = "stack"; + } + } + + if(defined $SInfo->{"Param"}{$Pos+1}) + { # TODO + $Conv{"Next"} = $Pos+1; + } + + return %Conv; +} + +sub getAlignment_Model($$) +{ + my ($Tid, $LVer) = @_; + + if(not $Tid) + { # incomplete ABI dump + return 0; + } + + my $TInfo = $In::ABI{$LVer}{"TypeInfo"}; + + if(defined $TInfo->{$Tid}{"Algn"}) { + return $TInfo->{$Tid}{"Algn"}; + } + else + { + if($TInfo->{$Tid}{"Type"}=~/Struct|Class|Union|MethodPtr/) + { + if(defined $TInfo->{$Tid}{"Memb"}) + { + my $Max = 0; + foreach my $Pos (keys(%{$TInfo->{$Tid}{"Memb"}})) + { + my $Algn = $TInfo->{$Tid}{"Memb"}{$Pos}{"algn"}; + if(not $Algn) { + $Algn = getAlignment_Model($TInfo->{$Tid}{"Memb"}{$Pos}{"type"}, $LVer); + } + if($Algn>$Max) { + $Max = $Algn; + } + } + return $Max; + } + return 0; + } + elsif($TInfo->{$Tid}{"Type"} eq "Array") + { + my %Base = getOneStepBaseType($Tid, $LVer); + + if($Base{"Tid"} eq $Tid) + { # emergency exit + return 0; + } + + return getAlignment_Model($Base{"Tid"}, $LVer); + } + elsif($TInfo->{$Tid}{"Type"}=~/Intrinsic|Enum|Pointer|FuncPtr/) + { # model + return getIntAlgn($Tid, $LVer); + } + else + { + my %PureType = getPureType($Tid, $LVer); + + if($PureType{"Tid"} eq $Tid) + { # emergency exit + return 0; + } + + return getAlignment_Model($PureType{"Tid"}, $LVer); + } + } +} + +sub getIntAlgn($$) +{ + my ($Tid, $LVer) = @_; + + my $Name = $In::ABI{$LVer}{"TypeInfo"}{$Tid}{"Name"}; + my $Arch = $In::ABI{$LVer}{"Arch"}; + + if(my $Algn = $IntAlgn{$Arch}{$Name}) { + return $Algn; + } + else + { + my $Size = $In::ABI{$LVer}{"TypeInfo"}{$Tid}{"Size"}; + if($Arch eq "x86_64") + { # x86_64: sizeof==alignment + return $Size; + } + elsif($Arch eq "arm") + { + if($Size>8) + { # 128-bit vector (16) + return 8; + } + return $Size; + } + elsif($Arch eq "x86") + { + if($Size>4) + { # "double" (8) and "long double" (12) + return 4; + } + return $Size; + } + return $Size; + } +} + +sub getAlignment($$$) +{ + my ($Pos, $TypePtr, $LVer) = @_; + my $Tid = $TypePtr->{"Memb"}{$Pos}{"type"}; + + my $TSize = $In::ABI{$LVer}{"TypeInfo"}{$Tid}{"Size"}; + + my $Computed = $TypePtr->{"Memb"}{$Pos}{"algn"}; + my $Alignment = 0; + + if(my $BSize = $TypePtr->{"Memb"}{$Pos}{"bitfield"}) + { # bitfields + if($Computed) + { # real in bits + $Alignment = $Computed; + } + else + { # model + if($BSize eq $TSize*$BYTE) { + $Alignment = $BSize; + } + else { + $Alignment = 1; + } + } + return ($Alignment, $BSize); + } + else + { # other fields + if($Computed) + { # real in bytes + $Alignment = $Computed*$BYTE; + } + else + { # model + $Alignment = getAlignment_Model($Tid, $LVer)*$BYTE; + } + return ($Alignment, $TSize*$BYTE); + } +} + +sub getOffset($$$) +{ # offset of the field including padding + my ($FieldPos, $TypePtr, $LVer) = @_; + + if($TypePtr->{"Type"} eq "Union") { + return 0; + } + + # if((my $Off = $TypePtr->{"Memb"}{$FieldPos}{"offset"}) ne "") + # { # DWARF ABI Dump (generated by the ABI Dumper tool) + # return $Off*$BYTE; + # } + + my $Offset = 0; + my $Buffer = 0; + my $Word = $In::ABI{$LVer}{"WordSize"}; + + foreach my $Pos (0 .. keys(%{$TypePtr->{"Memb"}})-1) + { + my ($Alignment, $MSize) = getAlignment($Pos, $TypePtr, $LVer); + + if(not $Alignment) + { # support for old ABI dumps + if($MSize=~/\A(8|16|32|64)\Z/) + { + if($Buffer+$MSize<$Word*$BYTE) + { + $Alignment = 1; + $Buffer += $MSize; + } + else + { + $Alignment = $MSize; + $Buffer = 0; + } + } + else + { + $Alignment = 1; + $Buffer += $MSize; + } + } + + # padding + $Offset += getPadding($Offset, $Alignment); + if($Pos==$FieldPos) + { # after the padding + # before the field + return $Offset; + } + $Offset += $MSize; + } + return $FieldPos; # if something is going wrong +} + +sub getPadding($$) +{ + my ($Offset, $Alignment) = @_; + my $Padding = 0; + if($Offset % $Alignment!=0) + { # not aligned, add padding + $Padding = $Alignment - $Offset % $Alignment; + } + return $Padding; +} + +sub isMemPadded($$$$$) +{ # check if the target field can be added/removed/changed + # without shifting other fields because of padding bits + my ($FieldPos, $Size, $TypePtr, $Skip, $LVer) = @_; + if($FieldPos==0) { + return 0; + } + + delete($TypePtr->{"Memb"}{""}); + my $Offset = 0; + my (%Alignment, %MSize) = (); + my $MaxAlgn = 0; + my $End = keys(%{$TypePtr->{"Memb"}})-1; + my $NextField = $FieldPos+1; + foreach my $Pos (0 .. $End) + { + if($Skip and $Skip->{$Pos}) + { # skip removed/added fields + if($Pos > $FieldPos) + { # after the target + $NextField += 1; + next; + } + } + ($Alignment{$Pos}, $MSize{$Pos}) = getAlignment($Pos, $TypePtr, $LVer); + + if(not $Alignment{$Pos}) + { # emergency exit + return 0; + } + + if($Alignment{$Pos}>$MaxAlgn) { + $MaxAlgn = $Alignment{$Pos}; + } + if($Pos==$FieldPos) + { + if($Size==-1) + { # added/removed fields + if($Pos!=$End) + { # skip target field and see + # if enough padding will be + # created on the next step + # to include this field + next; + } + } + } + # padding + my $Padding = 0; + if($Offset % $Alignment{$Pos}!=0) + { # not aligned, add padding + $Padding = $Alignment{$Pos} - $Offset % $Alignment{$Pos}; + } + if($Pos==$NextField) + { # try to place target field in the padding + if($Size==-1) + { # added/removed fields + my $TPadding = 0; + if($Offset % $Alignment{$FieldPos}!=0) + {# padding of the target field + $TPadding = $Alignment{$FieldPos} - $Offset % $Alignment{$FieldPos}; + } + if($TPadding+$MSize{$FieldPos}<=$Padding) + { # enough padding to place target field + return 1; + } + else { + return 0; + } + } + else + { # changed fields + my $Delta = $Size-$MSize{$FieldPos}; + if($Delta>=0) + { # increased + if($Size-$MSize{$FieldPos}<=$Padding) + { # enough padding to change target field + return 1; + } + else { + return 0; + } + } + else + { # decreased + $Delta = abs($Delta); + if($Delta+$Padding>=$MSize{$Pos}) + { # try to place the next field + if(($Offset-$Delta) % $Alignment{$Pos} != 0) + { # padding of the next field in new place + my $NPadding = $Alignment{$Pos} - ($Offset-$Delta) % $Alignment{$Pos}; + if($NPadding+$MSize{$Pos}<=$Delta+$Padding) + { # enough delta+padding to store next field + return 0; + } + } + else + { + return 0; + } + } + return 1; + } + } + } + elsif($Pos==$End) + { # target field is the last field + if($Size==-1) + { # added/removed fields + if($Offset % $MaxAlgn!=0) + { # tail padding + my $TailPadding = $MaxAlgn - $Offset % $MaxAlgn; + if($Padding+$MSize{$Pos}<=$TailPadding) + { # enough tail padding to place the last field + return 1; + } + } + return 0; + } + else + { # changed fields + # scenario #1 + my $Offset1 = $Offset+$Padding+$MSize{$Pos}; + if($Offset1 % $MaxAlgn != 0) + { # tail padding + $Offset1 += $MaxAlgn - $Offset1 % $MaxAlgn; + } + # scenario #2 + my $Offset2 = $Offset+$Padding+$Size; + if($Offset2 % $MaxAlgn != 0) + { # tail padding + $Offset2 += $MaxAlgn - $Offset2 % $MaxAlgn; + } + if($Offset1!=$Offset2) + { # different sizes of structure + return 0; + } + return 1; + } + } + $Offset += $Padding+$MSize{$Pos}; + } + return 0; +} + +sub isScalar($) { + return ($_[0]=~/\A(unsigned |)(char|short|int|long|long long)\Z/); +} + +sub isFloat($) { + return ($_[0]=~/\A(float|double|long double)\Z/); +} + +sub callingConvention_R_Real($) +{ + my $SInfo = $_[0]; + my %Conv = (); + my %Regs = (); + my $Hidden = 0; + foreach my $Elem (keys(%{$SInfo->{"Reg"}})) + { + my $Reg = $SInfo->{"Reg"}{$Elem}; + if($Elem eq ".result_ptr") + { + $Hidden = 1; + $Regs{$Reg} = 1; + } + elsif(index($Elem, ".result")==0) { + $Regs{$Reg} = 1; + } + } + if(my @R = sort keys(%Regs)) + { + $Conv{"Method"} = "reg"; + $Conv{"Registers"} = join(", ", @R); + if($Hidden) { + $Conv{"Hidden"} = 1; + } + } + else + { + $Conv{"Method"} = "stack"; + $Conv{"Hidden"} = 1; + } + return %Conv; +} + +sub callingConvention_P_Real($$) +{ + my ($SInfo, $Pos) = @_; + my %Conv = (); + my %Regs = (); + foreach my $Elem (keys(%{$SInfo->{"Reg"}})) + { + my $Reg = $SInfo->{"Reg"}{$Elem}; + if($Elem=~/\A$Pos([\.\+]|\Z)/) { + $Regs{$Reg} = 1; + } + } + if(my @R = sort keys(%Regs)) + { + $Conv{"Method"} = "reg"; + $Conv{"Registers"} = join(", ", @R); + } + else + { + $Conv{"Method"} = "stack"; + + if(defined $SInfo->{"Param"} + and defined $SInfo->{"Param"}{$Pos}) + { + if(not defined $SInfo->{"Param"}{$Pos}{"offset"}) + { + $Conv{"Method"} = "unknown"; + } + } + } + + return %Conv; +} + +return 1; diff --git a/abi-compliance-checker-2.4/modules/Internals/Descriptor.pm b/abi-compliance-checker-2.4/modules/Internals/Descriptor.pm new file mode 100644 index 0000000..fdeee09 --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/Descriptor.pm @@ -0,0 +1,291 @@ +########################################################################### +# A module to handle XML descriptors +# +# Copyright (C) 2015-2018 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; + +sub createDesc($$) +{ + my ($Path, $LVer) = @_; + + if(not -e $Path) { + return undef; + } + + if(-d $Path) + { # directory with headers files and shared objects + return " + + ".$In::Desc{$LVer}{"TargetVersion"}." + + + + $Path + + + + $Path + "; + } + else + { # files + if($Path=~/\.(xml|desc)\Z/i) + { # standard XML-descriptor + return readFile($Path); + } + elsif(isHeaderFile($Path)) + { # header file + $In::Opt{"CheckHeadersOnly"} = 1; + return " + + ".$In::Desc{$LVer}{"TargetVersion"}." + + + + $Path + + + + "; + } + else + { # standard XML-descriptor + return readFile($Path); + } + } +} + +sub readDesc($$) +{ + my ($Content, $LVer) = @_; + + if(not $Content) { + exitStatus("Error", "XML descriptor is empty"); + } + if($Content!~/\//g; + + my $DescRef = $In::Desc{$LVer}; + + $DescRef->{"Version"} = parseTag(\$Content, "version"); + if(my $TV = $DescRef->{"TargetVersion"}) { + $DescRef->{"Version"} = $TV; + } + elsif($DescRef->{"Version"}=="") + { + if($LVer==1) + { + $DescRef->{"Version"} = "X"; + print STDERR "WARNING: version number #1 is not set (use --v1=NUM option)\n"; + } + else + { + $DescRef->{"Version"} = "Y"; + print STDERR "WARNING: version number #2 is not set (use --v2=NUM option)\n"; + } + } + + if(not $DescRef->{"Version"}) { + exitStatus("Error", "version in the XML descriptor is not specified (section \"version\")"); + } + if($Content=~/\{RELPATH\}/) + { + if(my $RelDir = $DescRef->{"RelativeDirectory"}) { + $Content =~ s/\{RELPATH\}/$RelDir/g; + } + else { + exitStatus("Error", "you have not specified -relpath* option, but the XML descriptor contains {RELPATH} macro"); + } + } + + foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "headers"))) + { + if(not -e $Path) { + exitStatus("Access_Error", "can't access \'$Path\'"); + } + + $DescRef->{"Headers"}{$Path} = keys(%{$DescRef->{"Headers"}}); + } + if(not defined $DescRef->{"Headers"}) { + exitStatus("Error", "can't find header files info in the XML descriptor"); + } + + if(not $In::Opt{"CheckHeadersOnly"}) + { + foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "libs"))) + { + if(not -e $Path) { + exitStatus("Access_Error", "can't access \'$Path\'"); + } + + $DescRef->{"Libs"}{$Path} = 1; + } + + if(not defined $DescRef->{"Libs"}) { + exitStatus("Error", "can't find libraries info in the XML descriptor"); + } + } + foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_headers"))) + { + if(not -d $Path) { + exitStatus("Access_Error", "can't access directory \'$Path\'"); + } + $Path = getAbsPath($Path); + push_U($In::Opt{"SysPaths"}{"include"}, $Path); + } + foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_libs"))) + { + if(not -d $Path) { + exitStatus("Access_Error", "can't access directory \'$Path\'"); + } + $Path = getAbsPath($Path); + push_U($In::Opt{"SysPaths"}{"lib"}, $Path); + } + foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "tools"))) + { + if(not -d $Path) { + exitStatus("Access_Error", "can't access directory \'$Path\'"); + } + $Path = getAbsPath($Path); + push_U($In::Opt{"SysPaths"}{"bin"}, $Path); + $In::Opt{"TargetTools"}{$Path} = 1; + } + if(my $Prefix = parseTag(\$Content, "cross_prefix")) { + $In::Opt{"CrossPrefix"} = $Prefix; + } + + $DescRef->{"IncludePaths"} = []; + foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "include_paths"))) + { + if(not -d $Path) { + exitStatus("Access_Error", "can't access directory \'$Path\'"); + } + $Path = getAbsPath($Path); + push(@{$DescRef->{"IncludePaths"}}, $Path); + } + + if(not @{$DescRef->{"IncludePaths"}}) { + $DescRef->{"AutoIncludePaths"} = 1; + } + + $DescRef->{"AddIncludePaths"} = []; + foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "add_include_paths"))) + { + if(not -d $Path) { + exitStatus("Access_Error", "can't access directory \'$Path\'"); + } + $Path = getAbsPath($Path); + push(@{$DescRef->{"AddIncludePaths"}}, $Path); + } + + foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_include_paths"))) + { # skip some auto-generated include paths + $Path = getAbsPath($Path); + $DescRef->{"SkipIncludePaths"}{$Path} = 1; + } + foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_including"))) + { # skip direct including of some headers + if(my ($CPath, $Type) = classifyPath($Path)) { + $DescRef->{"SkipHeaders"}{$Type}{$CPath} = 2; + } + } + foreach my $Option (split(/\s*\n\s*/, parseTag(\$Content, "gcc_options"))) + { + if($Option!~/\A\-(Wl|l|L)/) + { # skip linker options + $DescRef->{"CompilerOptions"} .= " ".$Option; + } + } + foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_headers"))) + { + if(my ($CPath, $Type) = classifyPath($Path)) { + $DescRef->{"SkipHeaders"}{$Type}{$CPath} = 1; + } + } + foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_libs"))) + { + if(my ($CPath, $Type) = classifyPath($Path)) { + $DescRef->{"SkipLibs"}{$Type}{$CPath} = 1; + } + } + if(my $DDefines = parseTag(\$Content, "defines")) + { + if($DescRef->{"Defines"}) + { # multiple descriptors + $DescRef->{"Defines"} .= "\n".$DDefines; + } + else { + $DescRef->{"Defines"} = $DDefines; + } + } + foreach my $Order (split(/\s*\n\s*/, parseTag(\$Content, "include_order"))) + { + if($Order=~/\A(.+):(.+)\Z/) { + $DescRef->{"IncludeOrder"}{$1} = $2; + } + } + foreach my $NameSpace (split(/\s*\n\s*/, parseTag(\$Content, "add_namespaces"))) { + $DescRef->{"AddNameSpaces"}{$NameSpace} = 1; + } + if(my $DIncPreamble = parseTag(\$Content, "include_preamble")) + { + if($DescRef->{"IncludePreamble"}) + { # multiple descriptors + $DescRef->{"IncludePreamble"} .= "\n".$DIncPreamble; + } + else { + $DescRef->{"IncludePreamble"} = $DIncPreamble; + } + } + + readFilter($Content, $LVer); +} + +sub readFilter($$) +{ + my ($Content, $LVer) = @_; + + $Content=~s/\/\*(.|\n)+?\*\///g; + $Content=~s/<\!--(.|\n)+?-->//g; + + my $DescRef = $In::Desc{$LVer}; + + foreach my $TName (split(/\s*\n\s*/, parseTag(\$Content, "opaque_types")), + split(/\s*\n\s*/, parseTag(\$Content, "skip_types"))) { + $DescRef->{"SkipTypes"}{$TName} = 1; + } + foreach my $Symbol (split(/\s*\n\s*/, parseTag(\$Content, "skip_interfaces")), + split(/\s*\n\s*/, parseTag(\$Content, "skip_symbols"))) { + $DescRef->{"SkipSymbols"}{$Symbol} = 1; + } + foreach my $NameSpace (split(/\s*\n\s*/, parseTag(\$Content, "skip_namespaces"))) { + $DescRef->{"SkipNameSpaces"}{$NameSpace} = 1; + } + foreach my $Constant (split(/\s*\n\s*/, parseTag(\$Content, "skip_constants"))) { + $DescRef->{"SkipConstants"}{$Constant} = 1; + } +} + +return 1; diff --git a/abi-compliance-checker-2.4/modules/Internals/ElfTools.pm b/abi-compliance-checker-2.4/modules/Internals/ElfTools.pm new file mode 100644 index 0000000..a1bb7ce --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/ElfTools.pm @@ -0,0 +1,286 @@ +########################################################################### +# A module to read ELF binaries +# +# Copyright (C) 2015-2018 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; + +my %Cache; + +my %ELF_BIND = map {$_=>1} ( + "WEAK", + "GLOBAL" +); + +my %ELF_TYPE = map {$_=>1} ( + "FUNC", + "IFUNC", + "OBJECT", + "COMMON" +); + +my %ELF_VIS = map {$_=>1} ( + "DEFAULT", + "PROTECTED" +); + +sub readline_ELF($) +{ # read the line of 'readelf' output corresponding to the symbol + my @Info = split(/\s+/, $_[0]); + # Num: Value Size Type Bind Vis Ndx Name + # 3629: 000b09c0 32 FUNC GLOBAL DEFAULT 13 _ZNSt12__basic_fileIcED1Ev@@GLIBCXX_3.4 + # 135: 00000000 0 FUNC GLOBAL DEFAULT UND av_image_fill_pointers@LIBAVUTIL_52 (3) + shift(@Info); # spaces + shift(@Info); # num + + if($#Info==7) + { # UND SYMBOL (N) + if($Info[7]=~/\(\d+\)/) { + pop(@Info); + } + } + + if($#Info!=6) + { # other lines + return (); + } + return () if(not defined $ELF_TYPE{$Info[2]} and $Info[5] ne "UND"); + return () if(not defined $ELF_BIND{$Info[3]}); + return () if(not defined $ELF_VIS{$Info[4]}); + if($Info[5] eq "ABS" and $Info[0]=~/\A0+\Z/) + { # 1272: 00000000 0 OBJECT GLOBAL DEFAULT ABS CXXABI_1.3 + return (); + } + if($In::Opt{"Target"} eq "symbian") + { # _ZN12CCTTokenType4NewLE4TUid3RFs@@ctfinder{000a0000}[102020e5].dll + if(index($Info[6], "_._.absent_export_")!=-1) + { # "_._.absent_export_111"@@libstdcpp{00010001}[10282872].dll + return (); + } + $Info[6]=~s/\@.+//g; # remove version + } + if(index($Info[2], "0x") == 0) + { # size == 0x3d158 + $Info[2] = hex($Info[2]); + } + return @Info; +} + +sub getSONAME($) +{ + my $Path = $_[0]; + + if(defined $Cache{"getSONAME"}{$Path}) { + return $Cache{"getSONAME"}{$Path}; + } + my $Objdump = getCmdPath("objdump"); + if(not $Objdump) { + exitStatus("Not_Found", "can't find \"objdump\""); + } + my $TmpDir = $In::Opt{"Tmp"}; + my $SonameCmd = "$Objdump -x \"$Path\" 2>$TmpDir/null"; + if($In::Opt{"OS"} eq "windows") { + $SonameCmd .= " | find \"SONAME\""; + } + else { + $SonameCmd .= " | grep SONAME"; + } + if(my $Info = `$SonameCmd`) + { + if($Info=~/SONAME\s+([^\s]+)/) { + return ($Cache{"getSONAME"}{$Path} = $1); + } + } + return ($Cache{"getSONAME"}{$Path}=""); +} + +sub getArch_Object($) +{ + my $Path = $_[0]; + + my %MachineType = ( + "14C" => "x86", + "8664" => "x86_64", + "1C0" => "arm", + "200" => "ia64" + ); + + my %ArchName = ( + "s390:31-bit" => "s390", + "s390:64-bit" => "s390x", + "powerpc:common" => "ppc32", + "powerpc:common64" => "ppc64", + "i386:x86-64" => "x86_64", + "mips:3000" => "mips", + "sparc:v8plus" => "sparcv9" + ); + + if($In::Opt{"OS"} eq "windows") + { + my $DumpbinCmd = getCmdPath("dumpbin"); + if(not $DumpbinCmd) { + exitStatus("Not_Found", "can't find \"dumpbin\""); + } + + my $Cmd = $DumpbinCmd." /headers \"$Path\""; + my $Out = `$Cmd`; + + if($Out=~/(\w+)\smachine/) + { + if(my $Type = $MachineType{uc($1)}) + { + return $Type; + } + } + } + elsif($In::Opt{"OS"} eq "macos") + { + my $OtoolCmd = getCmdPath("otool"); + if(not $OtoolCmd) { + exitStatus("Not_Found", "can't find \"otool\""); + } + + my $Cmd = $OtoolCmd." -hv -arch all \"$Path\""; + my $Out = qx/$Cmd/; + + if($Out=~/X86_64/i) { + return "x86_64"; + } + elsif($Out=~/X86/i) { + return "x86"; + } + } + else + { # linux, bsd, gnu, solaris, ... + my $ObjdumpCmd = getCmdPath("objdump"); + if(not $ObjdumpCmd) { + exitStatus("Not_Found", "can't find \"objdump\""); + } + + my $TmpDir = $In::Opt{"Tmp"}; + my $Cmd = $ObjdumpCmd." -f \"$Path\" 2>$TmpDir/null"; + + my $Locale = $In::Opt{"Locale"}; + if($In::Opt{"OS"} eq "windows") { + $Cmd = "set LANG=$Locale & ".$Cmd; + } + else { + $Cmd = "LANG=$Locale ".$Cmd; + } + my $Out = `$Cmd`; + + if($Out=~/architecture:\s+([\w\-\:]+)/) + { + my $Arch = $1; + if($Arch=~s/\:(.+)//) + { + my $Suffix = $1; + + if(my $Name = $ArchName{$Arch.":".$Suffix}) + { + $Arch = $Name; + } + } + + if($Arch=~/i[3-6]86/) { + $Arch = "x86"; + } + + if($Arch eq "x86-64") { + $Arch = "x86_64"; + } + + if($Arch eq "ia64-elf64") { + $Arch = "ia64"; + } + + return $Arch; + } + } + + return undef; +} + +sub getArch_GCC($) +{ + my $LVer = $_[0]; + + if(defined $Cache{"getArch_GCC"}{$LVer}) { + return $Cache{"getArch_GCC"}{$LVer}; + } + + my $GccPath = $In::Opt{"GccPath"}; + + if(not $GccPath) { + return undef; + } + + my $Arch = undef; + + if(my $Target = $In::Opt{"GccTarget"}) + { + if($Target=~/x86_64/) { + $Arch = "x86_64"; + } + elsif($Target=~/i[3-6]86/) { + $Arch = "x86"; + } + elsif($Target=~/\Aarm/i) { + $Arch = "arm"; + } + } + + if(not $Arch) + { + my $TmpDir = $In::Opt{"Tmp"}; + my $OrigDir = $In::Opt{"OrigDir"}; + + writeFile($TmpDir."/test.c", "int main(){return 0;}\n"); + + my $Cmd = $GccPath." test.c -o test"; + if(my $Opts = getGccOptions($LVer)) + { # user-defined options + $Cmd .= " ".$Opts; + } + + chdir($TmpDir); + system($Cmd); + chdir($OrigDir); + + my $EX = join_P($TmpDir, "test"); + + if($In::Opt{"OS"} eq "windows") { + $EX = join_P($TmpDir, "test.exe"); + } + + $Arch = getArch_Object($EX); + + unlink("$TmpDir/test.c"); + unlink($EX); + } + + if(not $Arch) { + exitStatus("Error", "can't check ARCH type"); + } + + return ($Cache{"getArch_GCC"}{$LVer} = $Arch); +} + +return 1; diff --git a/abi-compliance-checker-2.4/modules/Internals/Filter.pm b/abi-compliance-checker-2.4/modules/Internals/Filter.pm new file mode 100644 index 0000000..8bf6087 --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/Filter.pm @@ -0,0 +1,849 @@ +########################################################################### +# A module to filter symbols +# +# Copyright (C) 2015-2018 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; + +my %Cache; + +sub symbolFilter($$$$) +{ # some special cases when the symbol cannot be imported + my ($Symbol, $SInfo, $Type, $Level, $LVer) = @_; + + if($SInfo->{"Private"}) + { # skip private methods + return 0; + } + + if(isPrivateData($Symbol)) + { # non-public global data + return 0; + } + + if(defined $In::Opt{"SkipInternalSymbols"} + and my $Pattern = $In::Opt{"SkipInternalSymbols"}) + { + if($Symbol=~/($Pattern)/) { + return 0; + } + } + + if($Symbol=~/\A_Z/) + { + if($Symbol=~/[CD][3-4]E/) { + return 0; + } + } + + if($Type=~/Affected/) + { + my $Header = $SInfo->{"Header"}; + + if($In::Desc{$LVer}{"SkipSymbols"}{$Symbol}) + { # user defined symbols to ignore + return 0; + } + + if($In::Opt{"SymbolsListPath"} and not $In::Desc{$LVer}{"SymbolsList"}{$Symbol}) + { # user defined symbols + if(not $In::Opt{"TargetHeadersPath"} or not $Header + or not isTargetHeader($Header, $LVer)) + { # -symbols-list | -headers-list + return 0; + } + } + + if($In::Opt{"AppPath"} and not $In::Opt{"SymbolsList_App"}{$Symbol}) + { # user defined symbols (in application) + return 0; + } + + my $ClassId = $SInfo->{"Class"}; + + if($ClassId) + { + if(not isTargetType($ClassId, $LVer)) { + return 0; + } + } + + my $NameSpace = $SInfo->{"NameSpace"}; + if(not $NameSpace and $ClassId) + { # class methods have no "NameSpace" attribute + $NameSpace = $In::ABI{$LVer}{"TypeInfo"}{$ClassId}{"NameSpace"}; + } + if($NameSpace) + { # user defined namespaces to ignore + if($In::Desc{$LVer}{"SkipNameSpaces"}{$NameSpace}) { + return 0; + } + foreach my $NS (keys(%{$In::Desc{$LVer}{"SkipNameSpaces"}})) + { # nested namespaces + if($NameSpace=~/\A\Q$NS\E(\:\:|\Z)/) { + return 0; + } + } + } + if($Header) + { + if(my $Skip = skipHeader($Header, $LVer)) + { # --skip-headers or (not ) + if($Skip==1) { + return 0; + } + } + } + if($In::Opt{"TypesListPath"} and $ClassId) + { # user defined types + my $CName = $In::ABI{$LVer}{"TypeInfo"}{$ClassId}{"Name"}; + + if(not $In::Desc{$LVer}{"TypesList"}{$CName}) + { + if(my $NS = $In::ABI{$LVer}{"TypeInfo"}{$ClassId}{"NameSpace"}) + { + $CName=~s/\A\Q$NS\E\:\://g; + } + + if(not $In::Desc{$LVer}{"TypesList"}{$CName}) + { + my $Found = 0; + + while($CName=~s/\:\:.+?\Z//) + { + if($In::Desc{$LVer}{"TypesList"}{$CName}) + { + $Found = 1; + last; + } + } + + if(not $Found) { + return 0; + } + } + } + } + + if(not selectSymbol($Symbol, $SInfo, $Level, $LVer)) + { # non-target symbols + return 0; + } + + if($Level eq "Binary") + { + if($SInfo->{"InLine"} + or (not $SInfo->{"Static"} and isInLineInst($SInfo, $LVer))) + { # example: _ZN6Givaro6ZpzDomINS_7IntegerEE3EndEv is not exported (inlined) + if($ClassId and $SInfo->{"Virt"}) + { # inline virtual methods + if($Type=~/InlineVirt/) { + return 1; + } + my $Allocable = (not isCopyingClass($ClassId, $LVer)); + if(not $Allocable) + { # check bases + foreach my $DCId (getSubClasses($ClassId, $LVer, 1)) + { + if(not isCopyingClass($DCId, $LVer)) + { # exists a derived class without default c-tor + $Allocable=1; + last; + } + } + } + if(not $Allocable) { + return 0; + } + } + else + { # inline non-virtual methods + return 0; + } + } + } + } + return 1; +} + +sub selectSymbol($$$$) +{ # select symbol to check or to dump + my ($Symbol, $SInfo, $Level, $LVer) = @_; + + if($SInfo->{"Constructor"}==1) + { + if(index($Symbol, "C4E")!=-1) { + return 0; + } + } + elsif($SInfo->{"Destructor"}==1) + { + if(index($Symbol, "D4E")!=-1) { + return 0; + } + } + + if($Level eq "Dump") + { + if($SInfo->{"Virt"} or $SInfo->{"PureVirt"}) + { # TODO: check if this symbol is from + # base classes of other target symbols + return 1; + } + } + + if(not $In::Opt{"StdcxxTesting"} and not $In::Opt{"KeepCxx"} + and $Symbol=~/\A(_ZS|_ZNS|_ZNKS)/) + { # stdc++ interfaces + return 0; + } + + my $Target = 0; + if(my $Header = $SInfo->{"Header"}) { + $Target = isTargetHeader($Header, $LVer); + } + + if(not $Target) + { + if(my $Source = $SInfo->{"Source"}) { + $Target = isTargetSource($Source, $LVer); + } + } + + if($In::Opt{"ExtendedCheck"}) + { + if(index($Symbol, "external_func_")==0) { + $Target = 1; + } + } + if($In::Opt{"CheckHeadersOnly"} + or $Level eq "Source") + { + if($Target) + { + if($Level eq "Dump") + { # dumped + if($In::Opt{"BinOnly"}) + { + if(not $SInfo->{"InLine"} or $SInfo->{"Data"}) { + return 1; + } + } + else { + return 1; + } + } + elsif($Level eq "Source") + { # checked + return 1; + } + elsif($Level eq "Binary") + { # checked + if(not $SInfo->{"InLine"} or $SInfo->{"Data"} + or $SInfo->{"Virt"} or $SInfo->{"PureVirt"}) { + return 1; + } + } + } + } + else + { # library is available + if(linkSymbol($Symbol, $LVer, "-Deps")) + { # exported symbols + return 1; + } + if($Level eq "Dump") + { # dumped + if($In::Opt{"BinOnly"}) + { + if($SInfo->{"Data"}) + { + if($Target) { + return 1; + } + } + } + else + { # SrcBin + if($Target) { + return 1; + } + } + } + elsif($Level eq "Source") + { # checked + if($SInfo->{"PureVirt"} or $SInfo->{"Data"} or $SInfo->{"InLine"} + or isInLineInst($SInfo, $LVer)) + { # skip LOCAL symbols + if($Target) { + return 1; + } + } + } + elsif($Level eq "Binary") + { # checked + if($SInfo->{"PureVirt"} or $SInfo->{"Data"}) + { + if($Target) { + return 1; + } + } + } + } + return 0; +} + +sub linkSymbol($$$) +{ + my ($Symbol, $RunWith, $Deps) = @_; + if(linkSymbol_I($Symbol, $RunWith, "SymLib")) { + return 1; + } + if($Deps eq "+Deps") + { # check the dependencies + if(linkSymbol_I($Symbol, $RunWith, "DepSymLib")) { + return 1; + } + } + return 0; +} + +sub linkSymbol_I($$$) +{ + my ($Symbol, $RunWith, $Where) = @_; + if(not $Where or not $Symbol) { + return 0; + } + + my $SRef = $In::ABI{$RunWith}{$Where}; + + if($SRef->{$Symbol}) + { # the exact match by symbol name + return 1; + } + if(my $VSym = $In::ABI{$RunWith}{"SymbolVersion"}{$Symbol}) + { # indirect symbol version, i.e. + # foo_old and its symlink foo@v (or foo@@v) + # foo_old may be in symtab table + if($SRef->{$VSym}) { + return 1; + } + } + + if($Symbol=~/[\@\$]/) + { + my ($Sym, $Spec, $Ver) = symbolParts($Symbol); + if($Sym and $Ver) + { # search for the symbol with the same version + # or without version + if($SRef->{$Sym}) + { # old: foo@v|foo@@v + # new: foo + return 1; + } + if($SRef->{$Sym."\@".$Ver}) + { # old: foo|foo@@v + # new: foo@v + return 1; + } + if($SRef->{$Sym."\@\@".$Ver}) + { # old: foo|foo@v + # new: foo@@v + return 1; + } + } + } + + return 0; +} + +sub isPrivateData($) +{ # non-public global data + my $Symbol = $_[0]; + return ($Symbol=~/\A(_ZGV|_ZTI|_ZTS|_ZTT|_ZTV|_ZTC|_ZThn|_ZTv0_n)/); +} + +sub isInLineInst($$) { + return (isTemplateInstance(@_) and not isTemplateSpec(@_)); +} + +sub isTemplateInstance($$) +{ + my ($SInfo, $LVer) = @_; + + if(my $ClassId = $SInfo->{"Class"}) + { + if(my $ClassName = $In::ABI{$LVer}{"TypeInfo"}{$ClassId}{"Name"}) + { + if(index($ClassName,"<")!=-1) { + return 1; + } + } + } + if(my $ShortName = $SInfo->{"ShortName"}) + { + if(index($ShortName,"<")!=-1 + and index($ShortName,">")!=-1) { + return 1; + } + } + + return 0; +} + +sub isTemplateSpec($$) +{ + my ($SInfo, $LVer) = @_; + if(my $ClassId = $SInfo->{"Class"}) + { + if($In::ABI{$LVer}{"TypeInfo"}{$ClassId}{"Spec"}) + { # class specialization + return 1; + } + elsif($SInfo->{"Spec"}) + { # method specialization + return 1; + } + } + return 0; +} + +sub skipHeader($$) +{ + my ($Path, $LVer) = @_; + + if(defined $Cache{"skipHeader"}{$LVer}{$Path}) { + return $Cache{"skipHeader"}{$LVer}{$Path}; + } + + if(defined $In::Opt{"Tolerance"} + and $In::Opt{"Tolerance"}=~/1|2/) + { # --tolerant + if(skipAlienHeader($Path)) { + return ($Cache{"skipHeader"}{$LVer}{$Path} = 1); + } + } + if(not keys(%{$In::Desc{$LVer}{"SkipHeaders"}})) { + return 0; + } + return ($Cache{"skipHeader"}{$LVer}{$Path} = skipHeader_I(@_)); +} + +sub skipHeader_I($$) +{ # returns: + # 1 - if header should NOT be included and checked + # 2 - if header should NOT be included, but should be checked + my ($Path, $LVer) = @_; + my $Name = getFilename($Path); + if(my $Kind = $In::Desc{$LVer}{"SkipHeaders"}{"Name"}{$Name}) { + return $Kind; + } + foreach my $D (sort {$In::Desc{$LVer}{"SkipHeaders"}{"Path"}{$a} cmp $In::Desc{$LVer}{"SkipHeaders"}{"Path"}{$b}} + keys(%{$In::Desc{$LVer}{"SkipHeaders"}{"Path"}})) + { + if(index($Path, $D)!=-1) + { + if($Path=~/\Q$D\E([\/\\]|\Z)/) { + return $In::Desc{$LVer}{"SkipHeaders"}{"Path"}{$D}; + } + } + } + foreach my $P (sort {$In::Desc{$LVer}{"SkipHeaders"}{"Pattern"}{$a} cmp $In::Desc{$LVer}{"SkipHeaders"}{"Pattern"}{$b}} + keys(%{$In::Desc{$LVer}{"SkipHeaders"}{"Pattern"}})) + { + if(my $Kind = $In::Desc{$LVer}{"SkipHeaders"}{"Pattern"}{$P}) + { + if($Name=~/$P/) { + return $Kind; + } + if($P=~/[\/\\]/ and $Path=~/$P/) { + return $Kind; + } + } + } + + return 0; +} + +sub skipLib($$) +{ + my ($Path, $LVer) = @_; + + my $Name = getFilename($Path); + if($In::Desc{$LVer}{"SkipLibs"}{"Name"}{$Name}) { + return 1; + } + my $ShortName = libPart($Name, "name+ext"); + if($In::Desc{$LVer}{"SkipLibs"}{"Name"}{$ShortName}) { + return 1; + } + foreach my $Dir (keys(%{$In::Desc{$LVer}{"SkipLibs"}{"Path"}})) + { + if($Path=~/\Q$Dir\E([\/\\]|\Z)/) { + return 1; + } + } + foreach my $P (keys(%{$In::Desc{$LVer}{"SkipLibs"}{"Pattern"}})) + { + if($Name=~/$P/) { + return 1; + } + if($P=~/[\/\\]/ and $Path=~/$P/) { + return 1; + } + } + return 0; +} + +sub addTargetLibs($) +{ + my $LibsRef = $_[0]; + foreach (@{$LibsRef}) { + $In::Opt{"TargetLibs"}{$_} = 1; + } +} + +sub isTargetLib($) +{ + my $LName = $_[0]; + + if($In::Opt{"OS"} eq "windows") { + $LName = lc($LName); + } + if(my $TN = $In::Opt{"TargetLib"}) + { + if($LName!~/\Q$TN\E/) { + return 0; + } + } + if($In::Opt{"TargetLibs"} + and not $In::Opt{"TargetLibs"}{$LName} + and not $In::Opt{"TargetLibs"}{libPart($LName, "name+ext")}) { + return 0; + } + return 1; +} + +sub pickType($$) +{ + my ($Tid, $LVer) = @_; + + if(my $Dupl = $In::ABI{$LVer}{"TypeTypedef"}{$Tid}) + { + if(defined $In::ABI{$LVer}{"TypeInfo"}{$Dupl}) + { + if($In::ABI{$LVer}{"TypeInfo"}{$Dupl}{"Name"} eq $In::ABI{$LVer}{"TypeInfo"}{$Tid}{"Name"}) + { # duplicate + return 0; + } + } + } + + my $THeader = $In::ABI{$LVer}{"TypeInfo"}{$Tid}{"Header"}; + + if(isBuiltIn($THeader)) { + return 0; + } + + if($In::ABI{$LVer}{"TypeInfo"}{$Tid}{"Type"}!~/Class|Struct|Union|Enum|Typedef/) { + return 0; + } + + if(isAnon($In::ABI{$LVer}{"TypeInfo"}{$Tid}{"Name"})) { + return 0; + } + + if(selfTypedef($Tid, $LVer)) { + return 0; + } + + if(not isTargetType($Tid, $LVer)) { + return 0; + } + + return 1; +} + +sub isTargetType($$) +{ + my ($Tid, $LVer) = @_; + + if(my $THeader = $In::ABI{$LVer}{"TypeInfo"}{$Tid}{"Header"}) + { # NOTE: header is defined to source if undefined (DWARF dumps) + if(not isTargetHeader($THeader, $LVer)) + { # from target headers + return 0; + } + } + elsif(my $TSource = $In::ABI{$LVer}{"TypeInfo"}{$Tid}{"Source"}) + { + if(not isTargetSource($TSource, $LVer)) + { # from target sources + return 0; + } + } + else + { + return 0; + } + + if(my $Name = $In::ABI{$LVer}{"TypeInfo"}{$Tid}{"Name"}) + { + if(my $Pattern = $In::Opt{"SkipInternalTypes"}) + { + if($Name=~/($Pattern)/) { + return 0; + } + } + + if($In::Desc{$LVer}{"SkipTypes"}{$Name}) { + return 0; + } + } + + if($In::ABI{$LVer}{"PublicABI"}) + { + if(isPrivateABI($Tid, $LVer)) { + return 0; + } + } + + return 1; +} + +sub selfTypedef($$) +{ + my ($TypeId, $LVer) = @_; + my %Type = getType($TypeId, $LVer); + if($Type{"Type"} eq "Typedef") + { + my %Base = getOneStepBaseType($TypeId, $LVer); + if($Base{"Type"}=~/Class|Struct/) + { + if($Type{"Name"} eq $Base{"Name"}) { + return 1; + } + elsif($Type{"Name"}=~/::(\w+)\Z/) + { + if($Type{"Name"} eq $Base{"Name"}."::".$1) + { # QPointer::QPointer + return 1; + } + } + } + } + return 0; +} + +sub isOpaque($) +{ + my $T = $_[0]; + if(not defined $T->{"Memb"} + and not defined $T->{"Size"}) + { + return 1; + } + return 0; +} + +sub isPrivateABI($$) +{ + my ($TypeId, $LVer) = @_; + + if($In::Opt{"CheckPrivateABI"}) { + return 0; + } + + if(defined $In::ABI{$LVer}{"TypeInfo"}{$TypeId}{"PrivateABI"}) { + return 1; + } + + return 0; +} + +sub isReserved($) +{ # reserved fields == private + my $MName = $_[0]; + + if($In::Opt{"KeepReserved"}) { + return 0; + } + + if($MName=~/reserved|padding|f_spare/i) { + return 1; + } + if($MName=~/\A[_]*(spare|pad|unused|dummy)[_\d]*\Z/i) { + return 1; + } + if($MName=~/(pad\d+)/i) { + return 1; + } + return 0; +} + +sub specificHeader($$) +{ + my ($Header, $Spec) = @_; + my $Name = getFilename($Header); + + if($Spec eq "windows") + {# MS Windows + return 1 if($Name=~/(\A|[._-])(win|wince|wnt)(\d\d|[._-]|\Z)/i); + return 1 if($Name=~/([._-]w|win)(32|64)/i); + return 1 if($Name=~/\A(Win|Windows)[A-Z]/); + return 1 if($Name=~/\A(w|win|windows)(32|64|\.)/i); + my @Dirs = ( + "win32", + "win64", + "win", + "windows", + "msvcrt" + ); # /gsf-win32/ + if(my $DIRs = join("|", @Dirs)) { + return 1 if($Header=~/[\/\\](|[^\/\\]+[._-])($DIRs)(|[._-][^\/\\]+)([\/\\]|\Z)/i); + } + } + elsif($Spec eq "macos") + { # Mac OS + return 1 if($Name=~/(\A|[_-])mac[._-]/i); + } + + return 0; +} + +sub skipAlienHeader($) +{ + my $Path = $_[0]; + my $Name = getFilename($Path); + my $Dir = getDirname($Path); + + if($In::Opt{"Tolerance"}=~/2/) + { # 2 - skip internal headers + my @Terms = ( + "p", + "priv", + "int", + "impl", + "implementation", + "internal", + "private", + "old", + "compat", + "debug", + "test", + "gen" + ); + + my @Dirs = ( + "private", + "priv", + "port", + "impl", + "internal", + "detail", + "details", + "old", + "compat", + "debug", + "config", + "compiler", + "platform", + "test" + ); + + if(my $TERMs = join("|", @Terms)) { + return 1 if($Name=~/(\A|[._-])($TERMs)([._-]|\Z)/i); + } + if(my $DIRs = join("|", @Dirs)) { + return 1 if($Dir=~/(\A|[\/\\])(|[^\/\\]+[._-])($DIRs)(|[._-][^\/\\]+)([\/\\]|\Z)/i); + } + + return 1 if($Name=~/[a-z](Imp|Impl|I|P)(\.|\Z)/); + } + + if($In::Opt{"Tolerance"}=~/1/) + { # 1 - skip non-Linux headers + if($In::Opt{"OS"} ne "windows") + { + if(specificHeader($Path, "windows")) { + return 1; + } + } + if($In::Opt{"OS"} ne "macos") + { + if(specificHeader($Path, "macos")) { + return 1; + } + } + } + + # valid + return 0; +} + +sub isTargetHeader($$) +{ # --header, --headers-list + my ($H, $V) = @_; + + if(defined $In::Desc{$V}{"TargetHeader"}) + { + if(defined $In::Desc{$V}{"TargetHeader"}{$H}) { + return 1; + } + } + elsif($In::ABI{$V}{"Headers"}) + { + if(defined $In::ABI{$V}{"Headers"}{$H}) { + return 1; + } + } + + return 0; +} + +sub isTargetSource($$) +{ + my ($S, $V) = @_; + + if($In::ABI{$V}{"Sources"}) + { + if(defined $In::ABI{$V}{"Sources"}{$S}) { + return 1; + } + } + + return 0; +} + +sub ignorePath($) +{ + my $Path = $_[0]; + if($Path=~/\~\Z/) + {# skipping system backup files + return 1; + } + if($Path=~/(\A|[\/\\]+)(\.(svn|git|bzr|hg)|CVS)([\/\\]+|\Z)/) + {# skipping hidden .svn, .git, .bzr, .hg and CVS directories + return 1; + } + return 0; +} + +return 1; diff --git a/abi-compliance-checker-2.4/modules/Internals/GccAst.pm b/abi-compliance-checker-2.4/modules/Internals/GccAst.pm new file mode 100644 index 0000000..8a2a08c --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/GccAst.pm @@ -0,0 +1,3945 @@ +########################################################################### +# A module to parse GCC AST +# +# Copyright (C) 2015-2019 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; +use Storable qw(dclone); + +my %Cache; + +my $BYTE = 8; + +my %OperatorIndication = ( + "not" => "~", + "assign" => "=", + "andassign" => "&=", + "orassign" => "|=", + "xorassign" => "^=", + "or" => "|", + "xor" => "^", + "addr" => "&", + "and" => "&", + "lnot" => "!", + "eq" => "==", + "ne" => "!=", + "lt" => "<", + "lshift" => "<<", + "lshiftassign" => "<<=", + "rshiftassign" => ">>=", + "call" => "()", + "mod" => "%", + "modassign" => "%=", + "subs" => "[]", + "land" => "&&", + "lor" => "||", + "rshift" => ">>", + "ref" => "->", + "le" => "<=", + "deref" => "*", + "mult" => "*", + "preinc" => "++", + "delete" => " delete", + "vecnew" => " new[]", + "vecdelete" => " delete[]", + "predec" => "--", + "postinc" => "++", + "postdec" => "--", + "plusassign" => "+=", + "plus" => "+", + "minus" => "-", + "minusassign" => "-=", + "gt" => ">", + "ge" => ">=", + "new" => " new", + "multassign" => "*=", + "divassign" => "/=", + "div" => "/", + "neg" => "-", + "pos" => "+", + "memref" => "->*", + "compound" => "," +); + +my %NodeType= ( + "array_type" => "Array", + "binfo" => "Other", + "boolean_type" => "Intrinsic", + "complex_type" => "Intrinsic", + "const_decl" => "Other", + "enumeral_type" => "Enum", + "field_decl" => "Other", + "function_decl" => "Other", + "function_type" => "FunctionType", + "identifier_node" => "Other", + "integer_cst" => "Other", + "integer_type" => "Intrinsic", + "vector_type" => "Vector", + "method_type" => "MethodType", + "namespace_decl" => "Other", + "parm_decl" => "Other", + "pointer_type" => "Pointer", + "real_cst" => "Other", + "real_type" => "Intrinsic", + "record_type" => "Struct", + "reference_type" => "Ref", + "string_cst" => "Other", + "template_decl" => "Other", + "template_type_parm" => "TemplateParam", + "typename_type" => "TypeName", + "sizeof_expr" => "SizeOf", + "tree_list" => "Other", + "tree_vec" => "Other", + "type_decl" => "Other", + "union_type" => "Union", + "var_decl" => "Other", + "void_type" => "Intrinsic", + "nop_expr" => "Other", + "addr_expr" => "Other", + "offset_type" => "Other" +); + +my %UnQual = ( + "r"=>"restrict", + "v"=>"volatile", + "c"=>"const", + "cv"=>"const volatile" +); + +my %IntrinsicNames = map {$_=>1} ( + "void", + "bool", + "wchar_t", + "char", + "signed char", + "unsigned char", + "short", + "unsigned short", + "int", + "unsigned int", + "long", + "unsigned long", + "long long", + "__int64", + "unsigned long long", + "__int128", + "unsigned __int128", + "float", + "double", + "long double" , + "__float80", + "__float128", + "..." +); + +my %ConstantSuffix = ( + "unsigned int"=>"u", + "long"=>"l", + "unsigned long"=>"ul", + "long long"=>"ll", + "unsigned long long"=>"ull" +); + +my %DefaultStdArgs = map {$_=>1} ( + "_Alloc", + "_Compare", + "_Traits", + "_Rx_traits", + "_InIter", + "_OutIter" +); + +my %LibInfo; +my %UnknownOperator; +my %Typedef_Tr; +my %Typedef_Eq; +my %StdCxxTypedef; +my %MissedTypedef; +my %MissedBase; +my %MissedBase_R; +my %CheckedTypeInfo; +my %TemplateInstance; +my %BasicTemplate; +my %TemplateArg; +my %TemplateDecl; +my %TemplateMap; +my %EnumMembName_Id; +my %TypedefToAnon; +my %MangledNames; +my $MAX_ID = 0; + +my $V = undef; + +# Aliases +my (%SymbolInfo, %TypeInfo, %TName_Tid) = (); + +foreach (1, 2) +{ + $SymbolInfo{$_} = $In::ABI{$_}{"SymbolInfo"}; + $TypeInfo{$_} = $In::ABI{$_}{"TypeInfo"}; + $TName_Tid{$_} = $In::ABI{$_}{"TName_Tid"}; +} + +sub readGccAst($$) +{ + my ($LVer, $DumpPath) = @_; + + $V = $LVer; + + open(TU_DUMP, $DumpPath); + local $/ = undef; + my $Content = ; + close(TU_DUMP); + + unlink($DumpPath); + + $Content=~s/\n[ ]+/ /g; + my @Lines = split(/\n/, $Content); + + # clean memory + undef $Content; + + $MAX_ID = $#Lines+1; # number of lines == number of nodes + + foreach (0 .. $#Lines) + { + if($Lines[$_]=~/\A\@(\d+)[ ]+([a-z_]+)[ ]+(.+)\Z/i) + { # get a number and attributes of a node + next if(not $NodeType{$2}); + $LibInfo{$V}{"info_type"}{$1} = $2; + $LibInfo{$V}{"info"}{$1} = $3." "; + } + + # clean memory + delete($Lines[$_]); + } + + # clean memory + undef @Lines; + + # processing info + setTemplateParams_All(); + + if($In::Opt{"ExtraDump"}) { + setAnonTypedef_All(); + } + + getTypeInfo_All(); + simplifyNames(); + simplifyConstants(); + getVarInfo_All(); + getSymbolInfo_All(); + + # clean memory + %LibInfo = (); + %TemplateInstance = (); + %BasicTemplate = (); + %MangledNames = (); + %TemplateDecl = (); + %StdCxxTypedef = (); + %MissedTypedef = (); + %Typedef_Tr = (); + %Typedef_Eq = (); + %TypedefToAnon = (); + + # clean cache + delete($Cache{"getTypeAttr"}); + delete($Cache{"getTypeDeclId"}); + + if($In::Opt{"ExtraDump"}) + { + remove_Unused($V, "Extra"); + } + else + { # remove unused types + if($In::Opt{"BinOnly"} and not $In::Opt{"ExtendedCheck"}) + { # --binary + remove_Unused($V, "All"); + } + else { + remove_Unused($V, "Extended"); + } + } + + if($In::Opt{"CheckInfo"}) + { + foreach my $Tid (keys(%{$TypeInfo{$V}})) { + checkCompleteness($TypeInfo{$V}{$Tid}); + } + + foreach my $Sid (keys(%{$SymbolInfo{$V}})) { + checkCompleteness($SymbolInfo{$V}{$Sid}); + } + } +} + +sub checkCompleteness($) +{ + my $Info = $_[0]; + + # data types + if(defined $Info->{"Memb"}) + { + foreach my $Pos (keys(%{$Info->{"Memb"}})) + { + if(defined $Info->{"Memb"}{$Pos}{"type"}) { + checkTypeInfo($Info->{"Memb"}{$Pos}{"type"}); + } + } + } + if(defined $Info->{"Base"}) + { + foreach my $Bid (keys(%{$Info->{"Base"}})) { + checkTypeInfo($Bid); + } + } + if(defined $Info->{"BaseType"}) { + checkTypeInfo($Info->{"BaseType"}); + } + if(defined $Info->{"TParam"}) + { + foreach my $Pos (keys(%{$Info->{"TParam"}})) + { + my $TName = $Info->{"TParam"}{$Pos}{"name"}; + if($TName=~/\A\(.+\)(true|false|\d.*)\Z/) { + next; + } + if($TName eq "_BoolType") { + next; + } + if($TName=~/\Asizeof\(/) { + next; + } + if(my $Tid = $TName_Tid{$V}{$TName}) { + checkTypeInfo($Tid); + } + else + { + if(defined $In::Opt{"Debug"}) { + printMsg("WARNING", "missed type $TName"); + } + } + } + } + + # symbols + if(defined $Info->{"Param"}) + { + foreach my $Pos (keys(%{$Info->{"Param"}})) + { + if(defined $Info->{"Param"}{$Pos}{"type"}) { + checkTypeInfo($Info->{"Param"}{$Pos}{"type"}); + } + } + } + if(defined $Info->{"Return"}) { + checkTypeInfo($Info->{"Return"}); + } + if(defined $Info->{"Class"}) { + checkTypeInfo($Info->{"Class"}); + } +} + +sub checkTypeInfo($) +{ + my $Tid = $_[0]; + + if(defined $CheckedTypeInfo{$V}{$Tid}) { + return; + } + $CheckedTypeInfo{$V}{$Tid} = 1; + + if(defined $TypeInfo{$V}{$Tid}) + { + if(not $TypeInfo{$V}{$Tid}{"Name"}) { + printMsg("ERROR", "missed type name ($Tid)"); + } + checkCompleteness($TypeInfo{$V}{$Tid}); + } + else { + printMsg("ERROR", "missed type id $Tid"); + } +} + +sub getSymbolInfo_All() +{ + foreach (sort {$b<=>$a} keys(%{$LibInfo{$V}{"info"}})) + { # reverse order + if($LibInfo{$V}{"info_type"}{$_} eq "function_decl") { + getSymbolInfo($_); + } + } + + if($In::Opt{"AddTemplateInstances"}) + { + # templates + foreach my $Sid (sort {$a<=>$b} keys(%{$SymbolInfo{$V}})) + { + my %Map = (); + + if(my $ClassId = $SymbolInfo{$V}{$Sid}{"Class"}) + { + if(defined $TemplateMap{$V}{$ClassId}) + { + foreach (keys(%{$TemplateMap{$V}{$ClassId}})) { + $Map{$_} = $TemplateMap{$V}{$ClassId}{$_}; + } + } + } + + if(defined $TemplateMap{$V}{$Sid}) + { + foreach (keys(%{$TemplateMap{$V}{$Sid}})) { + $Map{$_} = $TemplateMap{$V}{$Sid}{$_}; + } + } + + if(defined $SymbolInfo{$V}{$Sid}{"Param"}) + { + foreach (sort {$a<=>$b} keys(%{$SymbolInfo{$V}{$Sid}{"Param"}})) + { + my $PTid = $SymbolInfo{$V}{$Sid}{"Param"}{$_}{"type"}; + $SymbolInfo{$V}{$Sid}{"Param"}{$_}{"type"} = instType(\%Map, $PTid); + } + } + if(my $Return = $SymbolInfo{$V}{$Sid}{"Return"}) { + $SymbolInfo{$V}{$Sid}{"Return"} = instType(\%Map, $Return); + } + } + } +} + +sub getVarInfo_All() +{ + foreach (sort {$b<=>$a} keys(%{$LibInfo{$V}{"info"}})) + { # reverse order + if($LibInfo{$V}{"info_type"}{$_} eq "var_decl") { + getVarInfo($_); + } + } +} + +sub getTypeInfo_All() +{ + if(not checkGcc("4.5")) + { # support for GCC < 4.5 + # missed typedefs: QStyle::State is typedef to QFlags + # but QStyleOption.state is of type QFlags in the TU dump + # FIXME: check GCC versions + addMissedTypes_Pre(); + } + + foreach (sort {$a<=>$b} keys(%{$LibInfo{$V}{"info"}})) + { # forward order only + my $IType = $LibInfo{$V}{"info_type"}{$_}; + if($IType=~/_type\Z/ and $IType ne "function_type" + and $IType ne "method_type") { + getTypeInfo("$_"); + } + } + + # add "..." type + $TypeInfo{$V}{"-1"} = { + "Name" => "...", + "Type" => "Intrinsic", + "Tid" => "-1" + }; + $TName_Tid{$V}{"..."} = "-1"; + + if(not checkGcc("4.5")) + { # support for GCC < 4.5 + addMissedTypes_Post(); + } + + if($In::Opt{"AddTemplateInstances"}) + { + # templates + foreach my $Tid (sort {$a<=>$b} keys(%{$TypeInfo{$V}})) + { + if(defined $TemplateMap{$V}{$Tid} + and not defined $TypeInfo{$V}{$Tid}{"Template"}) + { + if(defined $TypeInfo{$V}{$Tid}{"Memb"}) + { + foreach my $Pos (sort {$a<=>$b} keys(%{$TypeInfo{$V}{$Tid}{"Memb"}})) + { + if(my $MembTypeId = $TypeInfo{$V}{$Tid}{"Memb"}{$Pos}{"type"}) + { + if(my $MAttr = getTypeAttr($MembTypeId)) + { + $TypeInfo{$V}{$Tid}{"Memb"}{$Pos}{"algn"} = $MAttr->{"Algn"}; + $MembTypeId = $TypeInfo{$V}{$Tid}{"Memb"}{$Pos}{"type"} = instType($TemplateMap{$V}{$Tid}, $MembTypeId); + } + } + } + } + if(defined $TypeInfo{$V}{$Tid}{"Base"}) + { + foreach my $Bid (sort {$a<=>$b} keys(%{$TypeInfo{$V}{$Tid}{"Base"}})) + { + my $NBid = instType($TemplateMap{$V}{$Tid}, $Bid); + + if($NBid ne $Bid + and $NBid ne $Tid) + { + %{$TypeInfo{$V}{$Tid}{"Base"}{$NBid}} = %{$TypeInfo{$V}{$Tid}{"Base"}{$Bid}}; + delete($TypeInfo{$V}{$Tid}{"Base"}{$Bid}); + } + } + } + } + } + } +} + +sub getVarInfo($) +{ + my $InfoId = $_[0]; + if(my $NSid = getTreeAttr_Scpe($InfoId)) + { + my $NSInfoType = $LibInfo{$V}{"info_type"}{$NSid}; + if($NSInfoType and $NSInfoType eq "function_decl") { + return; + } + } + + if(not $SymbolInfo{$V}{$InfoId}) { + $SymbolInfo{$V}{$InfoId} = {}; + } + + my $SInfo = $SymbolInfo{$V}{$InfoId}; + + ($SInfo->{"Header"}, $SInfo->{"Line"}) = getLocation($InfoId); + if(not $SInfo->{"Header"} + or isBuiltIn($SInfo->{"Header"})) { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + my $ShortName = getTreeStr(getTreeAttr_Name($InfoId)); + if(not $ShortName) { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + if($ShortName=~/\Atmp_add_class_\d+\Z/) { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + $SInfo->{"ShortName"} = $ShortName; + if(my $MnglName = getTreeStr(getTreeAttr_Mngl($InfoId))) + { + if($In::Opt{"OS"} eq "windows") + { # cut the offset + $MnglName=~s/\@\d+\Z//g; + } + $SInfo->{"MnglName"} = $MnglName; + } + if($SInfo->{"MnglName"} + and index($SInfo->{"MnglName"}, "_Z")!=0) + { # validate mangled name + delete($SymbolInfo{$V}{$InfoId}); + return; + } + if(not $SInfo->{"MnglName"} + and index($ShortName, "_Z")==0) + { # _ZTS, etc. + $SInfo->{"MnglName"} = $ShortName; + } + if(isPrivateData($SInfo->{"MnglName"})) + { # non-public global data + delete($SymbolInfo{$V}{$InfoId}); + return; + } + $SInfo->{"Data"} = 1; + if(my $Rid = getTypeId($InfoId)) + { + if(not defined $TypeInfo{$V}{$Rid} + or not $TypeInfo{$V}{$Rid}{"Name"}) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + $SInfo->{"Return"} = $Rid; + my $Val = getDataVal($InfoId, $Rid); + if(defined $Val) { + $SInfo->{"Value"} = $Val; + } + } + setClassAndNs($InfoId); + if(my $ClassId = $SInfo->{"Class"}) + { + if(not defined $TypeInfo{$V}{$ClassId} + or not $TypeInfo{$V}{$ClassId}{"Name"}) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + } + + if(my $ClassId = $SInfo->{"Class"}) + { + if(not $In::Opt{"StdcxxTesting"}) + { # stdc++ data + if(index($TypeInfo{$V}{$ClassId}{"Name"}, "std::")==0) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + } + + if($TypeInfo{$V}{$ClassId}{"NameSpace"}) + { + if(index($TypeInfo{$V}{$ClassId}{"NameSpace"}, "__gnu_cxx")==0) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + } + } + + if($SInfo->{"NameSpace"}) + { + if(index($SInfo->{"NameSpace"}, "__gnu_cxx")==0) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + } + + if($LibInfo{$V}{"info"}{$InfoId}=~/ lang:[ ]*C /i) + { # extern "C" + $SInfo->{"Lang"} = "C"; + $SInfo->{"MnglName"} = $ShortName; + } + if($In::Opt{"UserLang"} eq "C") + { # --lang=C option + $SInfo->{"MnglName"} = $ShortName; + } + if(not $In::Opt{"CheckHeadersOnly"}) + { + if(not $SInfo->{"Class"}) + { + if(not $SInfo->{"MnglName"} + or not linkSymbol($SInfo->{"MnglName"}, $V, "-Deps")) + { + if(linkSymbol($ShortName, $V, "-Deps")) + { # "const" global data is mangled as _ZL... in the TU dump + # but not mangled when compiling a C shared library + $SInfo->{"MnglName"} = $ShortName; + } + } + } + } + if($In::ABI{$V}{"Language"} eq "C++") + { + if(not $SInfo->{"MnglName"}) + { # for some symbols (_ZTI) the short name is the mangled name + if(index($ShortName, "_Z")==0) { + $SInfo->{"MnglName"} = $ShortName; + } + } + + if(not $SInfo->{"MnglName"} + or $In::Opt{"Target"} eq "windows") + { # try to mangle symbol (link with libraries) + if(my $Mangled = linkWithSymbol($InfoId)) { + $SInfo->{"MnglName"} = $Mangled; + } + } + } + if(not $SInfo->{"MnglName"}) + { + if($SInfo->{"Class"}) { + return; + } + $SInfo->{"MnglName"} = $ShortName; + } + if(my $Symbol = $SInfo->{"MnglName"}) + { + if(not selectSymbol($Symbol, $SInfo, "Dump", $V)) + { # non-target symbols + delete($SymbolInfo{$V}{$InfoId}); + return; + } + } + if(my $Rid = $SInfo->{"Return"}) + { + if(defined $MissedTypedef{$V}{$Rid}) + { + if(my $AddedTid = $MissedTypedef{$V}{$Rid}{"Tid"}) { + $SInfo->{"Return"} = $AddedTid; + } + } + } + setFuncAccess($InfoId); + if(index($SInfo->{"MnglName"}, "_ZTV")==0) { + delete($SInfo->{"Return"}); + } + if($ShortName=~/\A(_Z|\?)/) { + delete($SInfo->{"ShortName"}); + } + + if($In::Opt{"ExtraDump"}) { + $SInfo->{"Header"} = guessHeader($InfoId); + } +} + +sub getSymbolInfo($) +{ + my $InfoId = $_[0]; + if(isInternal($InfoId)) { + return; + } + + if(not $SymbolInfo{$V}{$InfoId}) { + $SymbolInfo{$V}{$InfoId} = {}; + } + + my $SInfo = $SymbolInfo{$V}{$InfoId}; + + ($SInfo->{"Header"}, $SInfo->{"Line"}) = getLocation($InfoId); + if(not $SInfo->{"Header"} + or isBuiltIn($SInfo->{"Header"})) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + setFuncAccess($InfoId); + setFuncKind($InfoId); + if($SInfo->{"PseudoTemplate"}) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + + $SInfo->{"Type"} = getFuncType($InfoId); + if(my $Return = getFuncReturn($InfoId)) + { + if(not defined $TypeInfo{$V}{$Return} + or not $TypeInfo{$V}{$Return}{"Name"}) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + $SInfo->{"Return"} = $Return; + } + if(my $Rid = $SInfo->{"Return"}) + { + if(defined $MissedTypedef{$V}{$Rid}) + { + if(my $AddedTid = $MissedTypedef{$V}{$Rid}{"Tid"}) { + $SInfo->{"Return"} = $AddedTid; + } + } + } + if(not $SInfo->{"Return"}) { + delete($SInfo->{"Return"}); + } + my $Orig = getFuncOrig($InfoId); + $SInfo->{"ShortName"} = getFuncShortName($Orig); + if(index($SInfo->{"ShortName"}, "\._")!=-1) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + + if(index($SInfo->{"ShortName"}, "tmp_add_func")==0) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + + if(defined $TemplateInstance{$V}{"Func"}{$Orig}) + { + my $Tmpl = $BasicTemplate{$V}{$InfoId}; + + my @TParams = getTParams($Orig, "Func"); + if(not @TParams) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + foreach my $Pos (0 .. $#TParams) + { + my $Val = $TParams[$Pos]; + $SInfo->{"TParam"}{$Pos}{"name"} = $Val; + + if($Tmpl) + { + if(my $Arg = $TemplateArg{$V}{$Tmpl}{$Pos}) + { + $TemplateMap{$V}{$InfoId}{$Arg} = $Val; + } + } + } + + if($Tmpl) + { + foreach my $Pos (sort {$a<=>$b} keys(%{$TemplateArg{$V}{$Tmpl}})) + { + if($Pos>$#TParams) + { + my $Arg = $TemplateArg{$V}{$Tmpl}{$Pos}; + $TemplateMap{$V}{$InfoId}{$Arg} = ""; + } + } + } + + if($SInfo->{"ShortName"}=~/\Aoperator\W+\Z/) + { # operator<< , operator>> + $SInfo->{"ShortName"} .= " "; + } + + if(@TParams) { + $SInfo->{"ShortName"} .= "<".join(", ", @TParams).">"; + } + else { + $SInfo->{"ShortName"} .= "<...>"; + } + + $SInfo->{"ShortName"} = formatName($SInfo->{"ShortName"}, "S"); + } + else + { # support for GCC 3.4 + $SInfo->{"ShortName"}=~s/<.+>\Z//; + } + if(my $MnglName = getTreeStr(getTreeAttr_Mngl($InfoId))) + { + if($In::Opt{"OS"} eq "windows") + { # cut the offset + $MnglName=~s/\@\d+\Z//g; + } + $SInfo->{"MnglName"} = $MnglName; + + # NOTE: mangling of some symbols may change depending on GCC version + # GCC 4.6: _ZN28QExplicitlySharedDataPointerI11QPixmapDataEC2IT_EERKS_IT_E + # GCC 4.7: _ZN28QExplicitlySharedDataPointerI11QPixmapDataEC2ERKS1_ + } + + if($SInfo->{"MnglName"} + and index($SInfo->{"MnglName"}, "_Z")!=0) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + if(not $SInfo->{"Destructor"}) + { # destructors have an empty parameter list + my $Skip = setFuncParams($InfoId); + if($Skip) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + } + if($LibInfo{$V}{"info"}{$InfoId}=~/ artificial /i) { + $SInfo->{"Artificial"} = 1; + } + + if(setClassAndNs($InfoId)) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + + if(my $ClassId = $SInfo->{"Class"}) + { + if(not defined $TypeInfo{$V}{$ClassId} + or not $TypeInfo{$V}{$ClassId}{"Name"}) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + } + + if($SInfo->{"Constructor"}) + { + my $CShort = getFuncShortName($InfoId); + + if($CShort eq "__comp_ctor") { + $SInfo->{"Constructor"} = "C1"; + } + elsif($CShort eq "__base_ctor") { + $SInfo->{"Constructor"} = "C2"; + } + } + elsif($SInfo->{"Destructor"}) + { + my $DShort = getFuncShortName($InfoId); + + if($DShort eq "__deleting_dtor") { + $SInfo->{"Destructor"} = "D0"; + } + elsif($DShort eq "__comp_dtor") { + $SInfo->{"Destructor"} = "D1"; + } + elsif($DShort eq "__base_dtor") { + $SInfo->{"Destructor"} = "D2"; + } + } + + if(not $SInfo->{"Constructor"} + and my $Spec = getVirtSpec($Orig)) + { # identify virtual and pure virtual functions + # NOTE: constructors cannot be virtual + # NOTE: in GCC 4.7 D1 destructors have no virtual spec + # in the TU dump, so taking it from the original symbol + if(not ($SInfo->{"Destructor"} + and $SInfo->{"Destructor"} eq "D2")) + { # NOTE: D2 destructors are not present in a v-table + $SInfo->{$Spec} = 1; + } + } + + if(isInline($InfoId)) { + $SInfo->{"InLine"} = 1; + } + + if(hasThrow($InfoId)) { + $SInfo->{"Throw"} = 1; + } + + if($SInfo->{"Constructor"} + and my $ClassId = $SInfo->{"Class"}) + { + if(not $SInfo->{"InLine"} + and not $SInfo->{"Artificial"}) + { # inline or auto-generated constructor + delete($TypeInfo{$V}{$ClassId}{"Copied"}); + } + } + + if(my $ClassId = $SInfo->{"Class"}) + { + if(not $In::Opt{"StdcxxTesting"}) + { # stdc++ interfaces + if(not $SInfo->{"Virt"} and not $SInfo->{"PureVirt"}) + { + if(index($TypeInfo{$V}{$ClassId}{"Name"}, "std::")==0) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + } + } + + if($TypeInfo{$V}{$ClassId}{"NameSpace"}) + { + if(index($TypeInfo{$V}{$ClassId}{"NameSpace"}, "__gnu_cxx")==0) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + } + } + + if($SInfo->{"NameSpace"}) + { + if(index($SInfo->{"NameSpace"}, "__gnu_cxx")==0) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + } + + if($LibInfo{$V}{"info"}{$InfoId}=~/ lang:[ ]*C /i) + { # extern "C" + $SInfo->{"Lang"} = "C"; + $SInfo->{"MnglName"} = $SInfo->{"ShortName"}; + } + if($In::Opt{"UserLang"} eq "C") + { # --lang=C option + $SInfo->{"MnglName"} = $SInfo->{"ShortName"}; + } + + if($In::ABI{$V}{"Language"} eq "C++") + { # correct mangled & short names + # C++ or --headers-only mode + if($SInfo->{"ShortName"}=~/\A__(comp|base|deleting)_(c|d)tor\Z/) + { # support for old GCC versions: reconstruct real names for constructors and destructors + $SInfo->{"ShortName"} = getNameByInfo(getTypeDeclId($SInfo->{"Class"})); + $SInfo->{"ShortName"}=~s/<.+>\Z//; + } + + if(not $SInfo->{"MnglName"} + or $In::Opt{"Target"} eq "windows") + { # try to mangle symbol (link with libraries) + if(my $Mangled = linkWithSymbol($InfoId)) { + $SInfo->{"MnglName"} = $Mangled; + } + } + } + else + { # not mangled in C + $SInfo->{"MnglName"} = $SInfo->{"ShortName"}; + } + if(not $In::Opt{"CheckHeadersOnly"} + and $SInfo->{"Type"} eq "Function" + and not $SInfo->{"Class"}) + { + my $Incorrect = 0; + + if($SInfo->{"MnglName"}) + { + if(index($SInfo->{"MnglName"}, "_Z")==0 + and not linkSymbol($SInfo->{"MnglName"}, $V, "-Deps")) + { # mangled in the TU dump, but not mangled in the library + $Incorrect = 1; + } + } + else + { + if($SInfo->{"Lang"} ne "C") + { # all C++ functions are not mangled in the TU dump + $Incorrect = 1; + } + } + if($Incorrect) + { + if(linkSymbol($SInfo->{"ShortName"}, $V, "-Deps")) { + $SInfo->{"MnglName"} = $SInfo->{"ShortName"}; + } + } + } + + if(not $SInfo->{"MnglName"}) + { # can't detect symbol name + delete($SymbolInfo{$V}{$InfoId}); + return; + } + + if(my $Symbol = $SInfo->{"MnglName"}) + { + if(not $In::Opt{"ExtraDump"}) + { + if(not selectSymbol($Symbol, $SInfo, "Dump", $V)) + { # non-target symbols + delete($SymbolInfo{$V}{$InfoId}); + return; + } + } + } + if($SInfo->{"Type"} eq "Method" + or $SInfo->{"Constructor"} + or $SInfo->{"Destructor"} + or $SInfo->{"Class"}) + { + if($SInfo->{"MnglName"}!~/\A(_Z|\?)/) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + } + if($SInfo->{"MnglName"}) + { + if($MangledNames{$V}{$SInfo->{"MnglName"}}) + { # one instance for one mangled name only + delete($SymbolInfo{$V}{$InfoId}); + return; + } + else { + $MangledNames{$V}{$SInfo->{"MnglName"}} = 1; + } + } + if($SInfo->{"Constructor"} + or $SInfo->{"Destructor"}) { + delete($SInfo->{"Return"}); + } + if($SInfo->{"MnglName"}=~/\A(_Z|\?)/ + and $SInfo->{"Class"}) + { + if($SInfo->{"Type"} eq "Function") + { # static methods + $SInfo->{"Static"} = 1; + } + } + if(getFuncLink($InfoId) eq "Static") { + $SInfo->{"Static"} = 1; + } + if($SInfo->{"MnglName"}=~/\A(_Z|\?)/) + { + if(my $Unmangled = getUnmangled($SInfo->{"MnglName"}, $V)) + { + if($Unmangled=~/\.\_\d/) + { + delete($SymbolInfo{$V}{$InfoId}); + return; + } + } + } + + if($SInfo->{"MnglName"}=~/\A_ZN(V|)K/) { + $SInfo->{"Const"} = 1; + } + if($SInfo->{"MnglName"}=~/\A_ZN(K|)V/) { + $SInfo->{"Volatile"} = 1; + } + + if($In::ABI{$V}{"WeakSymbols"}{$SInfo->{"MnglName"}}) { + $SInfo->{"Weak"} = 1; + } + + if($In::Opt{"ExtraDump"}) { + $SInfo->{"Header"} = guessHeader($InfoId); + } +} + +sub getTypeInfo($) +{ + my $TypeId = $_[0]; + $TypeInfo{$V}{$TypeId} = getTypeAttr($TypeId); + my $TName = $TypeInfo{$V}{$TypeId}{"Name"}; + if(not $TName) { + delete($TypeInfo{$V}{$TypeId}); + } +} + +sub getTypeAttr($) +{ + my $TypeId = $_[0]; + + if(defined $TypeInfo{$V}{$TypeId} + and $TypeInfo{$V}{$TypeId}{"Name"}) + { # already created + return $TypeInfo{$V}{$TypeId}; + } + elsif($Cache{"getTypeAttr"}{$V}{$TypeId}) + { # incomplete type + return {}; + } + $Cache{"getTypeAttr"}{$V}{$TypeId} = 1; + + my %TypeAttr = (); + + my $TypeDeclId = getTypeDeclId($TypeId); + $TypeAttr{"Tid"} = $TypeId; + + if(not $MissedBase{$V}{$TypeId} and isTypedef($TypeId)) + { + if(my $Info = $LibInfo{$V}{"info"}{$TypeId}) + { + if($Info=~/qual[ ]*:/) + { + my $NewId = ++$MAX_ID; + + $MissedBase{$V}{$TypeId} = "$NewId"; + $MissedBase_R{$V}{$NewId} = $TypeId; + $LibInfo{$V}{"info"}{$NewId} = $LibInfo{$V}{"info"}{$TypeId}; + $LibInfo{$V}{"info_type"}{$NewId} = $LibInfo{$V}{"info_type"}{$TypeId}; + } + } + $TypeAttr{"Type"} = "Typedef"; + } + else { + $TypeAttr{"Type"} = getTypeType($TypeId); + } + + if(my $ScopeId = getTreeAttr_Scpe($TypeDeclId)) + { + if($LibInfo{$V}{"info_type"}{$ScopeId} eq "function_decl") + { # local code + return {}; + } + } + + if($TypeAttr{"Type"} eq "Unknown") { + return {}; + } + elsif($TypeAttr{"Type"}=~/(Func|Method|Field)Ptr/) + { + my $MemPtrAttr = getMemPtrAttr(pointTo($TypeId), $TypeId, $TypeAttr{"Type"}); + if(my $TName = $MemPtrAttr->{"Name"}) + { + $TypeInfo{$V}{$TypeId} = $MemPtrAttr; + $TName_Tid{$V}{$TName} = $TypeId; + return $MemPtrAttr; + } + + return {}; + } + elsif($TypeAttr{"Type"} eq "Array") + { + my ($BTid, $BTSpec) = selectBaseType($TypeId); + if(not $BTid) { + return {}; + } + if(my $Algn = getAlgn($TypeId)) { + $TypeAttr{"Algn"} = $Algn/$BYTE; + } + $TypeAttr{"BaseType"} = $BTid; + if(my $BTAttr = getTypeAttr($BTid)) + { + if(not $BTAttr->{"Name"}) { + return {}; + } + if(my $NElems = getArraySize($TypeId, $BTAttr->{"Name"})) + { + if(my $Size = getSize($TypeId)) { + $TypeAttr{"Size"} = $Size/$BYTE; + } + if($BTAttr->{"Name"}=~/\A([^\[\]]+)(\[(\d+|)\].*)\Z/) { + $TypeAttr{"Name"} = $1."[$NElems]".$2; + } + else { + $TypeAttr{"Name"} = $BTAttr->{"Name"}."[$NElems]"; + } + } + else + { + $TypeAttr{"Size"} = $In::ABI{$V}{"WordSize"}; # pointer + if($BTAttr->{"Name"}=~/\A([^\[\]]+)(\[(\d+|)\].*)\Z/) { + $TypeAttr{"Name"} = $1."[]".$2; + } + else { + $TypeAttr{"Name"} = $BTAttr->{"Name"}."[]"; + } + } + $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}, "T"); + if($BTAttr->{"Header"}) { + $TypeAttr{"Header"} = $BTAttr->{"Header"}; + } + + $TName_Tid{$V}{$TypeAttr{"Name"}} = $TypeId; + $TypeInfo{$V}{$TypeId} = \%TypeAttr; + return \%TypeAttr; + } + return {}; + } + elsif($TypeAttr{"Type"}=~/\A(Intrinsic|Union|Struct|Enum|Class|Vector)\Z/) + { + my $TrivialAttr = getTrivialTypeAttr($TypeId); + if($TrivialAttr->{"Name"}) + { + $TypeInfo{$V}{$TypeId} = $TrivialAttr; + + if(not defined $IntrinsicNames{$TrivialAttr->{"Name"}} + or getTypeDeclId($TrivialAttr->{"Tid"})) + { # NOTE: register only one int: with built-in decl + if(not $TName_Tid{$V}{$TrivialAttr->{"Name"}}) { + $TName_Tid{$V}{$TrivialAttr->{"Name"}} = $TypeId; + } + } + + return $TrivialAttr; + } + + return {}; + } + elsif($TypeAttr{"Type"}=~/TemplateParam|TypeName/) + { + my $TrivialAttr = getTrivialTypeAttr($TypeId); + if($TrivialAttr->{"Name"}) + { + $TypeInfo{$V}{$TypeId} = $TrivialAttr; + if(not $TName_Tid{$V}{$TrivialAttr->{"Name"}}) { + $TName_Tid{$V}{$TrivialAttr->{"Name"}} = $TypeId; + } + return $TrivialAttr; + } + + return {}; + } + elsif($TypeAttr{"Type"} eq "SizeOf") + { + $TypeAttr{"BaseType"} = getTreeAttr_Type($TypeId); + my $BTAttr = getTypeAttr($TypeAttr{"BaseType"}); + $TypeAttr{"Name"} = "sizeof(".$BTAttr->{"Name"}.")"; + if($TypeAttr{"Name"}) + { + $TypeInfo{$V}{$TypeId} = \%TypeAttr; + return \%TypeAttr; + } + + return {}; + } + else + { # derived types + my ($BTid, $BTSpec) = selectBaseType($TypeId); + if(not $BTid) { + return {}; + } + $TypeAttr{"BaseType"} = $BTid; + if(defined $MissedTypedef{$V}{$BTid}) + { + if(my $MissedTDid = $MissedTypedef{$V}{$BTid}{"TDid"}) + { + if($MissedTDid ne $TypeDeclId) { + $TypeAttr{"BaseType"} = $MissedTypedef{$V}{$BTid}{"Tid"}; + } + } + } + my $BTAttr = getTypeAttr($TypeAttr{"BaseType"}); + if(not $BTAttr->{"Name"}) + { # templates + return {}; + } + if($BTAttr->{"Type"} eq "Typedef") + { # relinking typedefs + my %BaseBase = getType($BTAttr->{"BaseType"}, $V); + if($BTAttr->{"Name"} eq $BaseBase{"Name"}) { + $TypeAttr{"BaseType"} = $BaseBase{"Tid"}; + } + } + if($BTSpec) + { + if($TypeAttr{"Type"} eq "Pointer" + and $BTAttr->{"Name"}=~/\([\*]+\)/) + { + $TypeAttr{"Name"} = $BTAttr->{"Name"}; + $TypeAttr{"Name"}=~s/\(([*]+)\)/($1*)/g; + } + else { + $TypeAttr{"Name"} = $BTAttr->{"Name"}." ".$BTSpec; + } + } + else { + $TypeAttr{"Name"} = $BTAttr->{"Name"}; + } + if($TypeAttr{"Type"} eq "Typedef") + { + $TypeAttr{"Name"} = getNameByInfo($TypeDeclId); + + if(index($TypeAttr{"Name"}, "tmp_add_type")==0) { + return {}; + } + + if(isAnon($TypeAttr{"Name"})) + { # anon typedef to anon type: ._N + return {}; + } + + if($LibInfo{$V}{"info"}{$TypeDeclId}=~/ artificial /i) + { # artificial typedef of "struct X" to "X" + $TypeAttr{"Artificial"} = 1; + } + + if(my $NS = getNameSpace($TypeDeclId)) + { + my $TypeName = $TypeAttr{"Name"}; + if($NS=~/\A(struct |union |class |)((.+)::|)\Q$TypeName\E\Z/) + { # "some_type" is the typedef to "struct some_type" in C++ + if($3) { + $TypeAttr{"Name"} = $3."::".$TypeName; + } + } + else + { + $TypeAttr{"NameSpace"} = $NS; + $TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"}; + + if($TypeAttr{"NameSpace"}=~/\Astd(::|\Z)/ + and $TypeAttr{"Name"}!~/>(::\w+)+\Z/) + { + if($BTAttr->{"NameSpace"} + and $BTAttr->{"NameSpace"}=~/\Astd(::|\Z)/ and $BTAttr->{"Name"}=~/" are + # not covered by typedefs in the TU dump + # so trying to add such typedefs manually + $StdCxxTypedef{$V}{$BTAttr->{"Name"}}{$TypeAttr{"Name"}} = 1; + if(length($TypeAttr{"Name"})<=length($BTAttr->{"Name"})) + { + if(($BTAttr->{"Name"}!~/\A(std|boost)::/ or $TypeAttr{"Name"}!~/\A[a-z]+\Z/)) + { # skip "other" in "std" and "type" in "boost" + $Typedef_Eq{$V}{$BTAttr->{"Name"}} = $TypeAttr{"Name"}; + } + } + } + } + } + } + if($TypeAttr{"Name"} ne $BTAttr->{"Name"} and not $TypeAttr{"Artificial"} + and $TypeAttr{"Name"}!~/>(::\w+)+\Z/ and $BTAttr->{"Name"}!~/>(::\w+)+\Z/) + { + $In::ABI{$V}{"TypedefBase"}{$TypeAttr{"Name"}} = $BTAttr->{"Name"}; + if($BTAttr->{"Name"}=~/{"Name"}!~/\A(std|boost)::/ or $TypeAttr{"Name"}!~/\A[a-z]+\Z/)) { + $Typedef_Tr{$V}{$BTAttr->{"Name"}}{$TypeAttr{"Name"}} = 1; + } + } + } + ($TypeAttr{"Header"}, $TypeAttr{"Line"}) = getLocation($TypeDeclId); + } + if(not $TypeAttr{"Size"}) + { + if($TypeAttr{"Type"} eq "Pointer") { + $TypeAttr{"Size"} = $In::ABI{$V}{"WordSize"}; + } + elsif($BTAttr->{"Size"}) { + $TypeAttr{"Size"} = $BTAttr->{"Size"}; + } + } + if(my $Algn = getAlgn($TypeId)) { + $TypeAttr{"Algn"} = $Algn/$BYTE; + } + $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}, "T"); + if(not $TypeAttr{"Header"} and $BTAttr->{"Header"}) { + $TypeAttr{"Header"} = $BTAttr->{"Header"}; + } + %{$TypeInfo{$V}{$TypeId}} = %TypeAttr; + if($TypeAttr{"Name"} ne $BTAttr->{"Name"}) + { # typedef to "class Class" + # should not be registered in TName_Tid + if(not $TName_Tid{$V}{$TypeAttr{"Name"}}) { + $TName_Tid{$V}{$TypeAttr{"Name"}} = $TypeId; + } + } + return \%TypeAttr; + } +} + +sub getTrivialName($$) +{ + my ($TypeInfoId, $TypeId) = @_; + my %TypeAttr = (); + $TypeAttr{"Name"} = getNameByInfo($TypeInfoId); + if(not $TypeAttr{"Name"}) { + $TypeAttr{"Name"} = getTreeTypeName($TypeId); + } + ($TypeAttr{"Header"}, $TypeAttr{"Line"}) = getLocation($TypeInfoId); + $TypeAttr{"Type"} = getTypeType($TypeId); + $TypeAttr{"Name"}=~s/<(.+)\Z//g; # GCC 3.4.4 add template params to the name + if(isAnon($TypeAttr{"Name"})) + { + my $NameSpaceId = $TypeId; + while(my $NSId = getTreeAttr_Scpe(getTypeDeclId($NameSpaceId))) + { # searching for a first not anon scope + if($NSId eq $NameSpaceId) { + last; + } + else + { + $TypeAttr{"NameSpace"} = getNameSpace(getTypeDeclId($TypeId)); + if(not $TypeAttr{"NameSpace"} + or not isAnon($TypeAttr{"NameSpace"})) { + last; + } + } + $NameSpaceId = $NSId; + } + } + else + { + if(my $NameSpaceId = getTreeAttr_Scpe($TypeInfoId)) + { + if($NameSpaceId ne $TypeId) { + $TypeAttr{"NameSpace"} = getNameSpace($TypeInfoId); + } + } + } + if($TypeAttr{"NameSpace"} and not isAnon($TypeAttr{"Name"})) { + $TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"}; + } + $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}, "T"); + if(isAnon($TypeAttr{"Name"})) + { # anon-struct-header.h-line + $TypeAttr{"Name"} = "anon-".lc($TypeAttr{"Type"})."-"; + $TypeAttr{"Name"} .= $TypeAttr{"Header"}."-".$TypeAttr{"Line"}; + if($TypeAttr{"NameSpace"}) { + $TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"}; + } + } + if(defined $TemplateInstance{$V}{"Type"}{$TypeId} + and getTypeDeclId($TypeId) eq $TypeInfoId) + { + if(my @TParams = getTParams($TypeId, "Type")) { + $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}."< ".join(", ", @TParams)." >", "T"); + } + else { + $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}."<...>", "T"); + } + } + return ($TypeAttr{"Name"}, $TypeAttr{"NameSpace"}); +} + +sub getTrivialTypeAttr($) +{ + my $TypeId = $_[0]; + my $TypeInfoId = getTypeDeclId($_[0]); + + my %TypeAttr = (); + + if($TemplateDecl{$V}{$TypeId}) + { # template_decl + $TypeAttr{"Template"} = 1; + } + + setTypeAccess($TypeId, \%TypeAttr); + ($TypeAttr{"Header"}, $TypeAttr{"Line"}) = getLocation($TypeInfoId); + if(isBuiltIn($TypeAttr{"Header"})) + { + delete($TypeAttr{"Header"}); + delete($TypeAttr{"Line"}); + } + + $TypeAttr{"Type"} = getTypeType($TypeId); + ($TypeAttr{"Name"}, $TypeAttr{"NameSpace"}) = getTrivialName($TypeInfoId, $TypeId); + if(not $TypeAttr{"Name"}) { + return {}; + } + if(not $TypeAttr{"NameSpace"}) { + delete($TypeAttr{"NameSpace"}); + } + + if($TypeAttr{"Type"} eq "Intrinsic") + { + if(defined $TypeAttr{"Header"}) + { + if($TypeAttr{"Header"}=~/\Adump[1-2]\.[ih]\Z/) + { # support for SUSE 11.2 + # integer_type has srcp dump{1-2}.i + delete($TypeAttr{"Header"}); + } + } + } + + my $Tmpl = undef; + + if(defined $TemplateInstance{$V}{"Type"}{$TypeId}) + { + $Tmpl = $BasicTemplate{$V}{$TypeId}; + + if(my @TParams = getTParams($TypeId, "Type")) + { + foreach my $Pos (0 .. $#TParams) + { + my $Val = $TParams[$Pos]; + $TypeAttr{"TParam"}{$Pos}{"name"} = $Val; + + if(not defined $TypeAttr{"Template"}) + { + my %Base = getBaseType($TemplateInstance{$V}{"Type"}{$TypeId}{$Pos}, $V); + + if($Base{"Type"} eq "TemplateParam" + or defined $Base{"Template"}) { + $TypeAttr{"Template"} = 1; + } + } + + if($Tmpl) + { + if(my $Arg = $TemplateArg{$V}{$Tmpl}{$Pos}) + { + $TemplateMap{$V}{$TypeId}{$Arg} = $Val; + + if($Val eq $Arg) { + $TypeAttr{"Template"} = 1; + } + } + } + } + + if($Tmpl) + { + foreach my $Pos (sort {$a<=>$b} keys(%{$TemplateArg{$V}{$Tmpl}})) + { + if($Pos>$#TParams) + { + my $Arg = $TemplateArg{$V}{$Tmpl}{$Pos}; + $TemplateMap{$V}{$TypeId}{$Arg} = ""; + } + } + } + } + + if($In::Opt{"AddTemplateInstances"}) + { + if($Tmpl) + { + if(my $MainInst = getTreeAttr_Type($Tmpl)) + { + if(not getTreeAttr_Flds($TypeId)) + { + if(my $Flds = getTreeAttr_Flds($MainInst)) { + $LibInfo{$V}{"info"}{$TypeId} .= " flds: \@$Flds "; + } + } + if(not getTreeAttr_Binf($TypeId)) + { + if(my $Binf = getTreeAttr_Binf($MainInst)) { + $LibInfo{$V}{"info"}{$TypeId} .= " binf: \@$Binf "; + } + } + } + } + } + } + + my $StaticFields = setTypeMemb($TypeId, \%TypeAttr); + + if(my $Size = getSize($TypeId)) + { + $Size = $Size/$BYTE; + $TypeAttr{"Size"} = "$Size"; + } + else + { + if($In::Opt{"ExtraDump"}) + { + if(not defined $TypeAttr{"Memb"} + and not $Tmpl) + { # declaration only + $TypeAttr{"Forward"} = 1; + } + } + } + + if($TypeAttr{"Type"} eq "Struct" + and ($StaticFields or detectLang($TypeId))) + { + $TypeAttr{"Type"} = "Class"; + $TypeAttr{"Copied"} = 1; # default, will be changed in getSymbolInfo() + } + if($TypeAttr{"Type"} eq "Struct" + or $TypeAttr{"Type"} eq "Class") + { + my $Skip = setBaseClasses($TypeId, \%TypeAttr); + if($Skip) { + return {}; + } + } + if(my $Algn = getAlgn($TypeId)) { + $TypeAttr{"Algn"} = $Algn/$BYTE; + } + setSpec($TypeId, \%TypeAttr); + + if($TypeAttr{"Type"}=~/\A(Struct|Union|Enum)\Z/) + { + if(not $TypedefToAnon{$TypeId} + and not defined $TemplateInstance{$V}{"Type"}{$TypeId}) + { + if(not isAnon($TypeAttr{"Name"})) { + $TypeAttr{"Name"} = lc($TypeAttr{"Type"})." ".$TypeAttr{"Name"}; + } + } + } + + $TypeAttr{"Tid"} = $TypeId; + + if(my $VTable = $In::ABI{$V}{"ClassVTable_Content"}{$TypeAttr{"Name"}}) + { + my @Entries = split(/\n/, $VTable); + foreach (1 .. $#Entries) + { + my $Entry = $Entries[$_]; + if($Entry=~/\A(\d+)\s+(.+)\Z/) { + $TypeAttr{"VTable"}{$1} = simplifyVTable($2); + } + } + } + + if($TypeAttr{"Type"} eq "Enum") + { + if(not $TypeAttr{"NameSpace"}) + { + foreach my $Pos (keys(%{$TypeAttr{"Memb"}})) + { + my $MName = $TypeAttr{"Memb"}{$Pos}{"name"}; + my $MVal = $TypeAttr{"Memb"}{$Pos}{"value"}; + $In::ABI{$V}{"EnumConstants"}{$MName} = { + "Value"=>$MVal, + "Header"=>$TypeAttr{"Header"} + }; + if(isAnon($TypeAttr{"Name"})) + { + if($In::Opt{"ExtraDump"} or isTargetHeader($TypeAttr{"Header"}, $V) + or isTargetSource($TypeAttr{"Source"}, $V)) + { + $In::ABI{$V}{"Constants"}{$MName} = { + "Value" => $MVal, + "Header" => $TypeAttr{"Header"} + }; + } + } + } + } + } + if($In::Opt{"ExtraDump"}) + { + if(defined $TypedefToAnon{$TypeId}) { + $TypeAttr{"AnonTypedef"} = 1; + } + } + + return \%TypeAttr; +} + +sub setAnonTypedef_All() +{ + foreach my $InfoId (keys(%{$LibInfo{$V}{"info"}})) + { + if($LibInfo{$V}{"info_type"}{$InfoId} eq "type_decl") + { + if(isAnon(getNameByInfo($InfoId))) { + $TypedefToAnon{getTypeId($InfoId)} = 1; + } + } + } +} + +sub setTemplateParams_All() +{ + foreach (sort {$a<=>$b} keys(%{$LibInfo{$V}{"info"}})) + { + if($LibInfo{$V}{"info_type"}{$_} eq "template_decl") { + setTemplateParams($_); + } + } +} + +sub setTemplateParams($) +{ + my $Tid = getTypeId($_[0]); + if(my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/(inst|spcs)[ ]*:[ ]*@(\d+) /) + { + my $TmplInst_Id = $2; + setTemplateInstParams($_[0], $TmplInst_Id); + while($TmplInst_Id = getNextElem($TmplInst_Id)) { + setTemplateInstParams($_[0], $TmplInst_Id); + } + } + + $BasicTemplate{$V}{$Tid} = $_[0]; + + if(my $Prms = getTreeAttr_Prms($_[0])) + { + if(my $Valu = getTreeAttr_Valu($Prms)) + { + my $Vector = getTreeVec($Valu); + foreach my $Pos (sort {$a<=>$b} keys(%{$Vector})) + { + if(my $Val = getTreeAttr_Valu($Vector->{$Pos})) + { + if(my $Name = getNameByInfo($Val)) + { + $TemplateArg{$V}{$_[0]}{$Pos} = $Name; + if($LibInfo{$V}{"info_type"}{$Val} eq "parm_decl") { + $TemplateInstance{$V}{"Type"}{$Tid}{$Pos} = $Val; + } + else { + $TemplateInstance{$V}{"Type"}{$Tid}{$Pos} = getTreeAttr_Type($Val); + } + } + } + } + } + } + } + if(my $TypeId = getTreeAttr_Type($_[0])) + { + if(my $IType = $LibInfo{$V}{"info_type"}{$TypeId}) + { + if($IType eq "record_type") { + $TemplateDecl{$V}{$TypeId} = 1; + } + } + } +} + +sub setTemplateInstParams($$) +{ + my ($Tmpl, $Inst) = @_; + + if(my $Info = $LibInfo{$V}{"info"}{$Inst}) + { + my ($Params_InfoId, $ElemId) = (); + if($Info=~/purp[ ]*:[ ]*@(\d+) /) { + $Params_InfoId = $1; + } + if($Info=~/valu[ ]*:[ ]*@(\d+) /) { + $ElemId = $1; + } + if($Params_InfoId and $ElemId) + { + my $Params_Info = $LibInfo{$V}{"info"}{$Params_InfoId}; + while($Params_Info=~s/ (\d+)[ ]*:[ ]*\@(\d+) / /) + { + my ($PPos, $PTypeId) = ($1, $2); + if(my $PType = $LibInfo{$V}{"info_type"}{$PTypeId}) + { + if($PType eq "template_type_parm") { + $TemplateDecl{$V}{$ElemId} = 1; + } + } + if($LibInfo{$V}{"info_type"}{$ElemId} eq "function_decl") + { # functions + $TemplateInstance{$V}{"Func"}{$ElemId}{$PPos} = $PTypeId; + $BasicTemplate{$V}{$ElemId} = $Tmpl; + } + else + { # types + $TemplateInstance{$V}{"Type"}{$ElemId}{$PPos} = $PTypeId; + $BasicTemplate{$V}{$ElemId} = $Tmpl; + } + } + } + } +} + +sub getTypeDeclId($) +{ + my $Id = $_[0]; + if($Id) + { + if(defined $Cache{"getTypeDeclId"}{$V}{$Id}) { + return $Cache{"getTypeDeclId"}{$V}{$Id}; + } + if(my $Info = $LibInfo{$V}{"info"}{$Id}) + { + if($Info=~/name[ ]*:[ ]*@(\d+)/) { + return ($Cache{"getTypeDeclId"}{$V}{$Id} = $1); + } + } + } + return ($Cache{"getTypeDeclId"}{$V}{$Id} = 0); +} + +sub addMissedTypes_Pre() +{ + my %MissedTypes = (); + foreach my $MissedTDid (sort {$a<=>$b} keys(%{$LibInfo{$V}{"info"}})) + { # detecting missed typedefs + if($LibInfo{$V}{"info_type"}{$MissedTDid} eq "type_decl") + { + my $TypeId = getTreeAttr_Type($MissedTDid); + next if(not $TypeId); + my $TypeType = getTypeType($TypeId); + if($TypeType eq "Unknown") + { # template_type_parm + next; + } + my $TypeDeclId = getTypeDeclId($TypeId); + if($TypeDeclId eq $MissedTDid) { + next; + } + if(my $TypedefName = getNameByInfo($MissedTDid)) + { + if($TypedefName eq "__float80" or isAnon($TypedefName)) { + next; + } + + if(not $TypeDeclId + or getNameByInfo($TypeDeclId) ne $TypedefName) { + $MissedTypes{$V}{$TypeId}{$MissedTDid} = 1; + } + } + } + } + my %AddTypes = (); + foreach my $Tid (sort {$a<=>$b} keys(%{$MissedTypes{$V}})) + { # add missed typedefs + my @Missed = sort {$a<=>$b} keys(%{$MissedTypes{$V}{$Tid}}); + if(not @Missed or $#Missed>=1) { + next; + } + my $MissedTDid = $Missed[0]; + my ($TypedefName, $TypedefNS) = getTrivialName($MissedTDid, $Tid); + if(not $TypedefName) { + next; + } + my $NewId = ++$MAX_ID; + my %MissedInfo = ( # typedef info + "Name" => $TypedefName, + "NameSpace" => $TypedefNS, + "BaseType" => $Tid, + "Type" => "Typedef", + "Tid" => "$NewId" ); + my ($H, $L) = getLocation($MissedTDid); + $MissedInfo{"Header"} = $H; + $MissedInfo{"Line"} = $L; + if($TypedefName=~/\*|\&|\s/) + { # other types + next; + } + if($TypedefName=~/>(::\w+)+\Z/) + { # QFlags::enum_type + next; + } + if(getTypeType($Tid)=~/\A(Intrinsic|Union|Struct|Enum|Class)\Z/) + { # double-check for the name of typedef + my ($TName, $TNS) = getTrivialName(getTypeDeclId($Tid), $Tid); # base type info + next if(not $TName); + if(length($TypedefName)>=length($TName)) + { # too long typedef + next; + } + if($TName=~/\A\Q$TypedefName\E$b} keys(%{$TemplateInstance{$V}{$Kind}{$TypeId}}); + foreach my $Pos (@Positions) + { + my $Param_TypeId = $TemplateInstance{$V}{$Kind}{$TypeId}{$Pos}; + my $NodeType = $LibInfo{$V}{"info_type"}{$Param_TypeId}; + if(not $NodeType) + { # typename_type + return (); + } + if($NodeType eq "tree_vec") + { + if($Pos!=$#Positions) + { # select last vector of parameters ( ns::type ) + next; + } + } + my @Params = getTemplateParam($Pos, $Param_TypeId); + foreach my $P (@Params) + { + if($P eq "") { + return (); + } + elsif($P ne "\@skip\@") { + @TmplParams = (@TmplParams, $P); + } + } + } + return @TmplParams; +} + +sub getTreeVec($) +{ + my %Vector = (); + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + while($Info=~s/ (\d+)[ ]*:[ ]*\@(\d+) / /) + { # string length is N-1 because of the null terminator + $Vector{$1} = $2; + } + } + return \%Vector; +} + +sub getTemplateParam($$) +{ + my ($Pos, $Type_Id) = @_; + return () if(not $Type_Id); + my $NodeType = $LibInfo{$V}{"info_type"}{$Type_Id}; + return () if(not $NodeType); + if($NodeType eq "integer_cst") + { # int (1), unsigned (2u), char ('c' as 99), ... + my $CstTid = getTreeAttr_Type($Type_Id); + my $CstType = getTypeAttr($CstTid); # without recursion + my $Num = getNodeIntCst($Type_Id); + if(my $CstSuffix = $ConstantSuffix{$CstType->{"Name"}}) { + return ($Num.$CstSuffix); + } + else { + return ("(".$CstType->{"Name"}.")".$Num); + } + } + elsif($NodeType eq "string_cst") { + return (getNodeStrCst($Type_Id)); + } + elsif($NodeType eq "tree_vec") + { + my $Vector = getTreeVec($Type_Id); + my @Params = (); + foreach my $P1 (sort {$a<=>$b} keys(%{$Vector})) + { + foreach my $P2 (getTemplateParam($Pos, $Vector->{$P1})) { + push(@Params, $P2); + } + } + return @Params; + } + elsif($NodeType eq "parm_decl") + { + return (getNameByInfo($Type_Id)); + } + else + { + my $ParamAttr = getTypeAttr($Type_Id); + my $PName = $ParamAttr->{"Name"}; + if(not $PName) { + return (); + } + if($PName=~/\>/) + { + if(my $Cover = coverStdcxxTypedef($PName)) { + $PName = $Cover; + } + } + if($Pos>=1 and + isDefaultStd($PName)) + { # template > + # template + # template > + # template > + # template > + # template > + return ("\@skip\@"); + } + return ($PName); + } +} + +sub coverStdcxxTypedef($) +{ + my $TypeName = $_[0]; + if(my @Covers = sort {length($a)<=>length($b)} + sort keys(%{$StdCxxTypedef{$V}{$TypeName}})) + { # take the shortest typedef + # FIXME: there may be more than + # one typedefs to the same type + return $Covers[0]; + } + my $Covered = $TypeName; + while($TypeName=~s/(>)[ ]*(const|volatile|restrict| |\*|\&)\Z/$1/g){}; + if(my @Covers = sort {length($a)<=>length($b)} sort keys(%{$StdCxxTypedef{$V}{$TypeName}})) + { + if(my $Cover = $Covers[0]) + { + $Covered=~s/\b\Q$TypeName\E(\W|\Z)/$Cover$1/g; + $Covered=~s/\b\Q$TypeName\E(\w|\Z)/$Cover $1/g; + } + } + return formatName($Covered, "T"); +} + +sub getNodeIntCst($) +{ + my $CstId = $_[0]; + my $CstTypeId = getTreeAttr_Type($CstId); + if($EnumMembName_Id{$V}{$CstId}) { + return $EnumMembName_Id{$V}{$CstId}; + } + elsif((my $Value = getTreeValue($CstId)) ne "") + { + if($Value eq "0") + { + if($LibInfo{$V}{"info_type"}{$CstTypeId} eq "boolean_type") { + return "false"; + } + else { + return "0"; + } + } + elsif($Value eq "1") + { + if($LibInfo{$V}{"info_type"}{$CstTypeId} eq "boolean_type") { + return "true"; + } + else { + return "1"; + } + } + else { + return $Value; + } + } + return ""; +} + +sub getNodeStrCst($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/strg[ ]*: (.+) lngt:[ ]*(\d+)/) + { + if($LibInfo{$V}{"info_type"}{$_[0]} eq "string_cst") + { # string length is N-1 because of the null terminator + return substr($1, 0, $2-1); + } + else + { # identifier_node + return substr($1, 0, $2); + } + } + } + return ""; +} + +sub getMemPtrAttr($$$) +{ # function, method and field pointers + my ($PtrId, $TypeId, $Type) = @_; + my $MemInfo = $LibInfo{$V}{"info"}{$PtrId}; + if($Type eq "FieldPtr") { + $MemInfo = $LibInfo{$V}{"info"}{$TypeId}; + } + my $MemInfo_Type = $LibInfo{$V}{"info_type"}{$PtrId}; + my $MemPtrName = ""; + my %TypeAttr = ("Size"=>$In::ABI{$V}{"WordSize"}, "Type"=>$Type, "Tid"=>$TypeId); + if($Type eq "MethodPtr") + { # size of "method pointer" may be greater than WORD size + if(my $Size = getSize($TypeId)) + { + $Size/=$BYTE; + $TypeAttr{"Size"} = "$Size"; + } + } + if(my $Algn = getAlgn($TypeId)) { + $TypeAttr{"Algn"} = $Algn/$BYTE; + } + # Return + if($Type eq "FieldPtr") + { + my $ReturnAttr = getTypeAttr($PtrId); + if($ReturnAttr->{"Name"}) { + $MemPtrName .= $ReturnAttr->{"Name"}; + } + $TypeAttr{"Return"} = $PtrId; + } + else + { + if($MemInfo=~/retn[ ]*:[ ]*\@(\d+) /) + { + my $ReturnTypeId = $1; + my $ReturnAttr = getTypeAttr($ReturnTypeId); + if(not $ReturnAttr->{"Name"}) + { # templates + return {}; + } + $MemPtrName .= $ReturnAttr->{"Name"}; + $TypeAttr{"Return"} = $ReturnTypeId; + } + } + # Class + if($MemInfo=~/(clas|cls)[ ]*:[ ]*@(\d+) /) + { + $TypeAttr{"Class"} = $2; + my $ClassAttr = getTypeAttr($TypeAttr{"Class"}); + if($ClassAttr->{"Name"}) { + $MemPtrName .= " (".$ClassAttr->{"Name"}."\:\:*)"; + } + else { + $MemPtrName .= " (*)"; + } + } + else { + $MemPtrName .= " (*)"; + } + # Parameters + if($Type eq "FuncPtr" + or $Type eq "MethodPtr") + { + my @ParamTypeName = (); + if($MemInfo=~/prms[ ]*:[ ]*@(\d+) /) + { + my $PTypeInfoId = $1; + my ($Pos, $PPos) = (0, 0); + while($PTypeInfoId) + { + my $PTypeInfo = $LibInfo{$V}{"info"}{$PTypeInfoId}; + if($PTypeInfo=~/valu[ ]*:[ ]*@(\d+) /) + { + my $PTypeId = $1; + my $ParamAttr = getTypeAttr($PTypeId); + if(not $ParamAttr->{"Name"}) + { # templates (template_type_parm), etc. + return {}; + } + if($ParamAttr->{"Name"} eq "void") { + last; + } + if($Pos!=0 or $Type ne "MethodPtr") + { + $TypeAttr{"Param"}{$PPos++}{"type"} = $PTypeId; + push(@ParamTypeName, $ParamAttr->{"Name"}); + } + if($PTypeInfoId = getNextElem($PTypeInfoId)) { + $Pos+=1; + } + else { + last; + } + } + else { + last; + } + } + } + $MemPtrName .= " (".join(", ", @ParamTypeName).")"; + } + $TypeAttr{"Name"} = formatName($MemPtrName, "T"); + return \%TypeAttr; +} + +sub getTreeTypeName($) +{ + my $TypeId = $_[0]; + if(my $Info = $LibInfo{$V}{"info"}{$TypeId}) + { + if($LibInfo{$V}{"info_type"}{$_[0]} eq "integer_type") + { + if(my $Name = getNameByInfo($TypeId)) + { # bit_size_type + return $Name; + } + elsif($Info=~/unsigned/) { + return "unsigned int"; + } + else { + return "int"; + } + } + elsif($Info=~/name[ ]*:[ ]*@(\d+) /) { + return getNameByInfo($1); + } + } + return ""; +} + +sub isFuncPtr($) +{ + my $Ptd = pointTo($_[0]); + return 0 if(not $Ptd); + if(my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/unql[ ]*:/ and $Info!~/qual[ ]*:/) { + return 0; + } + } + if(my $InfoT1 = $LibInfo{$V}{"info_type"}{$_[0]} + and my $InfoT2 = $LibInfo{$V}{"info_type"}{$Ptd}) + { + if($InfoT1 eq "pointer_type" + and $InfoT2 eq "function_type") { + return 1; + } + } + return 0; +} + +sub isMethodPtr($) +{ + my $Ptd = pointTo($_[0]); + return 0 if(not $Ptd); + if(my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($LibInfo{$V}{"info_type"}{$_[0]} eq "record_type" + and $LibInfo{$V}{"info_type"}{$Ptd} eq "method_type" + and $Info=~/ ptrmem /) { + return 1; + } + } + return 0; +} + +sub isFieldPtr($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($LibInfo{$V}{"info_type"}{$_[0]} eq "offset_type" + and $Info=~/ ptrmem /) { + return 1; + } + } + return 0; +} + +sub pointTo($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/ptd[ ]*:[ ]*@(\d+)/) { + return $1; + } + } + return ""; +} + +sub getTypeTypeByTypeId($) +{ + my $TypeId = $_[0]; + if(my $TType = $LibInfo{$V}{"info_type"}{$TypeId}) + { + my $NType = $NodeType{$TType}; + if($NType eq "Intrinsic") { + return $NType; + } + elsif(isFuncPtr($TypeId)) { + return "FuncPtr"; + } + elsif(isMethodPtr($TypeId)) { + return "MethodPtr"; + } + elsif(isFieldPtr($TypeId)) { + return "FieldPtr"; + } + elsif($NType ne "Other") { + return $NType; + } + } + return "Unknown"; +} + +sub getQual($) +{ + my $TypeId = $_[0]; + if(my $Info = $LibInfo{$V}{"info"}{$TypeId}) + { + my ($Qual, $To) = (); + if($Info=~/qual[ ]*:[ ]*(r|c|v|cv) /) { + $Qual = $UnQual{$1}; + } + if($Info=~/unql[ ]*:[ ]*\@(\d+)/) { + $To = $1; + } + if($Qual and $To) { + return ($Qual, $To); + } + } + return (); +} + +sub getQualType($) +{ + if($_[0] eq "const volatile") { + return "ConstVolatile"; + } + return ucfirst($_[0]); +} + +sub getTypeType($) +{ + my $TypeId = $_[0]; + my $TypeDeclId = getTypeDeclId($TypeId); + if(defined $MissedTypedef{$V}{$TypeId}) + { # support for old GCC versions + if($MissedTypedef{$V}{$TypeId}{"TDid"} eq $TypeDeclId) { + return "Typedef"; + } + } + my $Info = $LibInfo{$V}{"info"}{$TypeId}; + my ($Qual, $To) = getQual($TypeId); + if(($Qual or $To) and $TypeDeclId + and (getTypeId($TypeDeclId) ne $TypeId)) + { # qualified types (special) + return getQualType($Qual); + } + elsif(not $MissedBase_R{$V}{$TypeId} + and isTypedef($TypeId)) { + return "Typedef"; + } + elsif($Qual) + { # qualified types + return getQualType($Qual); + } + + if($Info=~/unql[ ]*:[ ]*\@(\d+)/) + { # typedef struct { ... } name + $In::ABI{$V}{"TypeTypedef"}{$TypeId} = $1; + } + + my $TypeType = getTypeTypeByTypeId($TypeId); + if($TypeType eq "Struct") + { + if($TypeDeclId + and $LibInfo{$V}{"info_type"}{$TypeDeclId} eq "template_decl") { + return "Template"; + } + } + return $TypeType; +} + +sub isTypedef($) +{ + if($_[0]) + { + if($LibInfo{$V}{"info_type"}{$_[0]} eq "vector_type") + { # typedef float La_x86_64_xmm __attribute__ ((__vector_size__ (16))); + return 0; + } + if(my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if(my $TDid = getTypeDeclId($_[0])) + { + if(getTypeId($TDid) eq $_[0] + and getNameByInfo($TDid)) + { + if($Info=~/unql[ ]*:[ ]*\@(\d+) /) { + return $1; + } + } + } + } + } + return 0; +} + +sub selectBaseType($) +{ + my $TypeId = $_[0]; + if(defined $MissedTypedef{$V}{$TypeId}) + { # add missed typedefs + if($MissedTypedef{$V}{$TypeId}{"TDid"} eq getTypeDeclId($TypeId)) { + return ($TypeId, ""); + } + } + my $Info = $LibInfo{$V}{"info"}{$TypeId}; + my $InfoType = $LibInfo{$V}{"info_type"}{$TypeId}; + + my $MB_R = $MissedBase_R{$V}{$TypeId}; + my $MB = $MissedBase{$V}{$TypeId}; + + my ($Qual, $To) = getQual($TypeId); + if(($Qual or $To) and $Info=~/name[ ]*:[ ]*\@(\d+) / + and (getTypeId($1) ne $TypeId) + and (not $MB_R or getTypeId($1) ne $MB_R)) + { # qualified types (special) + return (getTypeId($1), $Qual); + } + elsif($MB) + { # add base + return ($MB, ""); + } + elsif(not $MB_R and my $Bid = isTypedef($TypeId)) + { # typedefs + return ($Bid, ""); + } + elsif($Qual or $To) + { # qualified types + return ($To, $Qual); + } + elsif($InfoType eq "reference_type") + { + if($Info=~/refd[ ]*:[ ]*@(\d+) /) { + return ($1, "&"); + } + } + elsif($InfoType eq "array_type") + { + if($Info=~/elts[ ]*:[ ]*@(\d+) /) { + return ($1, ""); + } + } + elsif($InfoType eq "pointer_type") + { + if($Info=~/ptd[ ]*:[ ]*@(\d+) /) { + return ($1, "*"); + } + } + + return (0, ""); +} + +sub detectLang($) +{ + my $TypeId = $_[0]; + my $Info = $LibInfo{$V}{"info"}{$TypeId}; + + if(checkGcc("8")) + { + if(getTreeAttr_VFld($TypeId)) { + return 1; + } + + if(my $Chain = getTreeAttr_Flds($TypeId)) + { + while(1) + { + if($LibInfo{$V}{"info_type"}{$Chain} eq "function_decl") { + return 1; + } + + $Chain = getTreeAttr_Chain($Chain); + + if(not $Chain) { + last; + } + } + } + } + + if(checkGcc("4")) + { # GCC 4 fncs-node points to only non-artificial methods + return ($Info=~/(fncs)[ ]*:[ ]*@(\d+) /); + } + else + { # GCC 3 + my $Fncs = getTreeAttr_Fncs($TypeId); + while($Fncs) + { + if($LibInfo{$V}{"info"}{$Fncs}!~/artificial/) { + return 1; + } + $Fncs = getTreeAttr_Chan($Fncs); + } + } + + return 0; +} + +sub setSpec($$) +{ + my ($TypeId, $TypeAttr) = @_; + my $Info = $LibInfo{$V}{"info"}{$TypeId}; + if($Info=~/\s+spec\s+/) { + $TypeAttr->{"Spec"} = 1; + } +} + +sub setBaseClasses($$) +{ + my ($TypeId, $TypeAttr) = @_; + my $Info = $LibInfo{$V}{"info"}{$TypeId}; + if(my $Binf = getTreeAttr_Binf($TypeId)) + { + my $Info = $LibInfo{$V}{"info"}{$Binf}; + my $Pos = 0; + while($Info=~s/(pub|public|prot|protected|priv|private|)[ ]+binf[ ]*:[ ]*@(\d+) //) + { + my ($Access, $BInfoId) = ($1, $2); + my $ClassId = getBinfClassId($BInfoId); + + if($ClassId eq $TypeId) + { # class A:public A + next; + } + + my $CType = $LibInfo{$V}{"info_type"}{$ClassId}; + if(not $CType or $CType eq "template_type_parm" + or $CType eq "typename_type") + { # skip + # return 1; + } + my $BaseInfo = $LibInfo{$V}{"info"}{$BInfoId}; + if($Access=~/prot/) { + $TypeAttr->{"Base"}{$ClassId}{"access"} = "protected"; + } + elsif($Access=~/priv/) { + $TypeAttr->{"Base"}{$ClassId}{"access"} = "private"; + } + $TypeAttr->{"Base"}{$ClassId}{"pos"} = "$Pos"; + if($BaseInfo=~/virt/) + { # virtual base + $TypeAttr->{"Base"}{$ClassId}{"virtual"} = 1; + } + + $Pos += 1; + } + } + return 0; +} + +sub getBinfClassId($) +{ + my $Info = $LibInfo{$V}{"info"}{$_[0]}; + if($Info=~/type[ ]*:[ ]*@(\d+) /) { + return $1; + } + + return ""; +} + +sub isInternal($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/mngl[ ]*:[ ]*@(\d+) /) + { + if($LibInfo{$V}{"info"}{$1}=~/\*[ ]*INTERNAL[ ]*\*/) + { # _ZN7mysqlpp8DateTimeC1ERKS0_ *INTERNAL* + return 1; + } + } + } + return 0; +} + +sub getDataVal($$) +{ + my ($InfoId, $TypeId) = @_; + if(my $Info = $LibInfo{$V}{"info"}{$InfoId}) + { + if($Info=~/init[ ]*:[ ]*@(\d+) /) + { + if(defined $LibInfo{$V}{"info_type"}{$1} + and $LibInfo{$V}{"info_type"}{$1} eq "nop_expr") + { + if(my $Nop = getTreeAttr_Op($1)) + { + if(defined $LibInfo{$V}{"info_type"}{$Nop} + and $LibInfo{$V}{"info_type"}{$Nop} eq "addr_expr") + { + if(my $Addr = getTreeAttr_Op($1)) { + return getInitVal($Addr, $TypeId); + } + } + } + } + else { + return getInitVal($1, $TypeId); + } + } + } + return undef; +} + +sub getInitVal($$) +{ + my ($InfoId, $TypeId) = @_; + if(my $Info = $LibInfo{$V}{"info"}{$InfoId}) + { + if(my $InfoType = $LibInfo{$V}{"info_type"}{$InfoId}) + { + if($InfoType eq "integer_cst") + { + my $Val = getNodeIntCst($InfoId); + if($TypeId and $TypeInfo{$V}{$TypeId}{"Name"}=~/\Achar(| const)\Z/) + { # characters + $Val = chr($Val); + } + return $Val; + } + elsif($InfoType eq "string_cst") { + return getNodeStrCst($InfoId); + } + elsif($InfoType eq "var_decl") + { + if(my $Name = getNodeStrCst(getTreeAttr_Mngl($InfoId))) { + return $Name; + } + } + } + } + return undef; +} + +sub setClassAndNs($) +{ + my $InfoId = $_[0]; + my $SInfo = $SymbolInfo{$V}{$InfoId}; + + if(my $Info = $LibInfo{$V}{"info"}{$InfoId}) + { + if($Info=~/scpe[ ]*:[ ]*@(\d+) /) + { + my $NSInfoId = $1; + if(my $InfoType = $LibInfo{$V}{"info_type"}{$NSInfoId}) + { + if($InfoType eq "namespace_decl") { + $SInfo->{"NameSpace"} = getNameSpace($InfoId); + } + elsif($InfoType eq "record_type") { + $SInfo->{"Class"} = $NSInfoId; + } + } + } + } + if($SInfo->{"Class"} + or $SInfo->{"NameSpace"}) + { + if($In::ABI{$V}{"Language"} ne "C++") + { # skip + return 1; + } + } + + return 0; +} + +sub isInline($) +{ # "body: undefined" in the tree + # -fkeep-inline-functions GCC option should be specified + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/ undefined /i) { + return 0; + } + } + return 1; +} + +sub hasThrow($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/type[ ]*:[ ]*@(\d+) /) { + return getTreeAttr_Unql($1, "unql"); + } + } + return 1; +} + +sub getTypeId($) +{ + my $Id = $_[0]; + if($Id and my $Info = $LibInfo{$V}{"info"}{$Id}) + { + if($Info=~/type[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub setTypeMemb($$) +{ + my ($TypeId, $TypeAttr) = @_; + my $TypeType = $TypeAttr->{"Type"}; + my ($Pos, $UnnamedPos) = (0, 0); + my $StaticFields = 0; + if($TypeType eq "Enum") + { + my $MInfoId = getTreeAttr_Csts($TypeId); + while($MInfoId) + { + $TypeAttr->{"Memb"}{$Pos}{"value"} = getEnumMembVal($MInfoId); + my $MembName = getTreeStr(getTreeAttr_Purp($MInfoId)); + $TypeAttr->{"Memb"}{$Pos}{"name"} = $MembName; + if(my $EMId = getTreeAttr_Valu($MInfoId)) + { + if($TypeAttr->{"NameSpace"}) { + $EnumMembName_Id{$V}{$EMId} = $TypeAttr->{"NameSpace"}."::".$MembName; + } + else { + $EnumMembName_Id{$V}{$EMId} = $MembName; + } + } + $MInfoId = getNextElem($MInfoId); + $Pos += 1; + } + } + elsif($TypeType=~/\A(Struct|Class|Union)\Z/) + { + my $MInfoId = getTreeAttr_Flds($TypeId); + while($MInfoId) + { + my $IType = $LibInfo{$V}{"info_type"}{$MInfoId}; + my $MInfo = $LibInfo{$V}{"info"}{$MInfoId}; + if(not $IType or $IType ne "field_decl") + { # search for fields, skip other stuff in the declaration + + if($IType eq "var_decl") + { # static field + $StaticFields = 1; + } + + $MInfoId = getNextElem($MInfoId); + next; + } + my $StructMembName = getTreeStr(getTreeAttr_Name($MInfoId)); + if(index($StructMembName, "_vptr.")==0) + { # virtual tables + $StructMembName = "_vptr"; + } + if(not $StructMembName) + { # unnamed fields + if(index($TypeAttr->{"Name"}, "_type_info_pseudo")==-1) + { + my $UnnamedTid = getTreeAttr_Type($MInfoId); + my $UnnamedTName = getNameByInfo(getTypeDeclId($UnnamedTid)); + if(isAnon($UnnamedTName)) + { # rename unnamed fields to unnamed0, unnamed1, ... + $StructMembName = "unnamed".($UnnamedPos++); + } + } + } + if(not $StructMembName) + { # unnamed fields and base classes + $MInfoId = getNextElem($MInfoId); + next; + } + my $MembTypeId = getTreeAttr_Type($MInfoId); + if(defined $MissedTypedef{$V}{$MembTypeId}) + { + if(my $AddedTid = $MissedTypedef{$V}{$MembTypeId}{"Tid"}) { + $MembTypeId = $AddedTid; + } + } + + $TypeAttr->{"Memb"}{$Pos}{"type"} = $MembTypeId; + $TypeAttr->{"Memb"}{$Pos}{"name"} = $StructMembName; + if((my $Access = getTreeAccess($MInfoId)) ne "public") + { # marked only protected and private, public by default + $TypeAttr->{"Memb"}{$Pos}{"access"} = $Access; + } + if($MInfo=~/spec:\s*mutable /) + { # mutable fields + $TypeAttr->{"Memb"}{$Pos}{"mutable"} = 1; + } + if(my $Algn = getAlgn($MInfoId)) { + $TypeAttr->{"Memb"}{$Pos}{"algn"} = $Algn; + } + if(my $BFSize = getBitField($MInfoId)) + { # in bits + $TypeAttr->{"Memb"}{$Pos}{"bitfield"} = $BFSize; + } + else + { # in bytes + if($TypeAttr->{"Memb"}{$Pos}{"algn"}==1) + { # template + delete($TypeAttr->{"Memb"}{$Pos}{"algn"}); + } + else { + $TypeAttr->{"Memb"}{$Pos}{"algn"} /= $BYTE; + } + } + + $MInfoId = getNextElem($MInfoId); + $Pos += 1; + } + } + + return $StaticFields; +} + +sub setFuncParams($) +{ + my $InfoId = $_[0]; + my $ParamInfoId = getTreeAttr_Args($InfoId); + + my $FType = getFuncType($InfoId); + my $SInfo = $SymbolInfo{$V}{$InfoId}; + + if($FType eq "Method") + { # check type of "this" pointer + my $ObjectTypeId = getTreeAttr_Type($ParamInfoId); + if(my $ObjectName = $TypeInfo{$V}{$ObjectTypeId}{"Name"}) + { + if($ObjectName=~/\bconst(| volatile)\*const\b/) { + $SInfo->{"Const"} = 1; + } + if($ObjectName=~/\bvolatile\b/) { + $SInfo->{"Volatile"} = 1; + } + } + else + { # skip + return 1; + } + # skip "this"-parameter + # $ParamInfoId = getNextElem($ParamInfoId); + } + my ($Pos, $PPos, $Vtt_Pos) = (0, 0, -1); + while($ParamInfoId) + { # formal args + my $ParamTypeId = getTreeAttr_Type($ParamInfoId); + my $ParamName = getTreeStr(getTreeAttr_Name($ParamInfoId)); + if(not $ParamName) + { # unnamed + $ParamName = "p".($PPos+1); + } + if(defined $MissedTypedef{$V}{$ParamTypeId}) + { + if(my $AddedTid = $MissedTypedef{$V}{$ParamTypeId}{"Tid"}) { + $ParamTypeId = $AddedTid; + } + } + my $PType = $TypeInfo{$V}{$ParamTypeId}{"Type"}; + if(not $PType or $PType eq "Unknown") { + return 1; + } + my $PTName = $TypeInfo{$V}{$ParamTypeId}{"Name"}; + if(not $PTName) { + return 1; + } + if($PTName eq "void") { + last; + } + if($ParamName eq "__vtt_parm" + and $TypeInfo{$V}{$ParamTypeId}{"Name"} eq "void const**") + { + $Vtt_Pos = $Pos; + $ParamInfoId = getNextElem($ParamInfoId); + next; + } + $SInfo->{"Param"}{$Pos}{"type"} = $ParamTypeId; + + if(my %Base = getBaseType($ParamTypeId, $V)) + { + if(defined $Base{"Template"}) { + return 1; + } + } + + $SInfo->{"Param"}{$Pos}{"name"} = $ParamName; + if(my $Algn = getAlgn($ParamInfoId)) { + $SInfo->{"Param"}{$Pos}{"algn"} = $Algn/$BYTE; + } + if($LibInfo{$V}{"info"}{$ParamInfoId}=~/spec:\s*register /) + { # foo(register type arg) + $SInfo->{"Param"}{$Pos}{"reg"} = 1; + } + $ParamInfoId = getNextElem($ParamInfoId); + $Pos += 1; + if($ParamName ne "this" or $FType ne "Method") { + $PPos += 1; + } + } + if(setFuncArgs($InfoId, $Vtt_Pos)) { + $SInfo->{"Param"}{$Pos}{"type"} = "-1"; + } + return 0; +} + +sub setFuncArgs($$) +{ + my ($InfoId, $Vtt_Pos) = @_; + my $FuncTypeId = getFuncTypeId($InfoId); + my $ParamListElemId = getTreeAttr_Prms($FuncTypeId); + my $FType = getFuncType($InfoId); + my $SInfo = $SymbolInfo{$V}{$InfoId}; + + if($FType eq "Method") + { + # skip "this"-parameter + # $ParamListElemId = getNextElem($ParamListElemId); + } + if(not $ParamListElemId) + { # foo(...) + return 1; + } + my $HaveVoid = 0; + my ($Pos, $PPos) = (0, 0); + while($ParamListElemId) + { # actual params: may differ from formal args + # formal int*const + # actual: int* + if($Vtt_Pos!=-1 and $Pos==$Vtt_Pos) + { + $Vtt_Pos=-1; + $ParamListElemId = getNextElem($ParamListElemId); + next; + } + my $ParamTypeId = getTreeAttr_Valu($ParamListElemId); + if($TypeInfo{$V}{$ParamTypeId}{"Name"} eq "void") + { + $HaveVoid = 1; + last; + } + else + { + if(not defined $SInfo->{"Param"}{$Pos}{"type"}) + { + $SInfo->{"Param"}{$Pos}{"type"} = $ParamTypeId; + if(not $SInfo->{"Param"}{$Pos}{"name"}) + { # unnamed + $SInfo->{"Param"}{$Pos}{"name"} = "p".($PPos+1); + } + } + elsif(my $OldId = $SInfo->{"Param"}{$Pos}{"type"}) + { + if($Pos>0 or getFuncType($InfoId) ne "Method") + { # params + if($OldId ne $ParamTypeId) + { + my %Old_Pure = getPureType($OldId, $V); + my %New_Pure = getPureType($ParamTypeId, $V); + + if($Old_Pure{"Name"} ne $New_Pure{"Name"}) { + $SInfo->{"Param"}{$Pos}{"type"} = $ParamTypeId; + } + } + } + } + } + if(my $PurpId = getTreeAttr_Purp($ParamListElemId)) + { # default arguments + if(my $PurpType = $LibInfo{$V}{"info_type"}{$PurpId}) + { + if($PurpType eq "nop_expr") + { # func ( const char* arg = (const char*)(void*)0 ) + $PurpId = getTreeAttr_Op($PurpId); + } + my $Val = getInitVal($PurpId, $ParamTypeId); + if(defined $Val) { + $SInfo->{"Param"}{$Pos}{"default"} = $Val; + } + } + } + $ParamListElemId = getNextElem($ParamListElemId); + if($Pos!=0 or $FType ne "Method") { + $PPos += 1; + } + $Pos += 1; + } + return ($Pos>=1 and not $HaveVoid); +} + +sub getTreeAttr_Chan($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/chan[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub getTreeAttr_Chain($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/chain[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub getTreeAttr_Unql($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/unql[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub getTreeAttr_Scpe($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/scpe[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub getTreeAttr_Type($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/type[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub getTreeAttr_Name($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/name[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub getTreeAttr_Mngl($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/mngl[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub getTreeAttr_Prms($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/prms[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub getTreeAttr_Fncs($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/fncs[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub getTreeAttr_Csts($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/csts[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub getTreeAttr_Purp($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/purp[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub getTreeAttr_Op($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/op 0[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub getTreeAttr_Valu($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/valu[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub getTreeAttr_Flds($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/flds[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub getTreeAttr_VFld($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/vfld[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub getTreeAttr_Binf($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/binf[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub getTreeAttr_Args($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/args[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return ""; +} + +sub getTreeValue($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/(low|int)[ ]*:[ ]*([^ ]+) /) { + return $2; + } + } + return ""; +} + +sub getTreeAccess($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/accs[ ]*:[ ]*([a-zA-Z]+) /) + { + my $Access = $1; + if($Access eq "prot") { + return "protected"; + } + elsif($Access eq "priv") { + return "private"; + } + } + elsif($Info=~/ protected /) + { # support for old GCC versions + return "protected"; + } + elsif($Info=~/ private /) + { # support for old GCC versions + return "private"; + } + } + return "public"; +} + +sub setFuncAccess($) +{ + my $Access = getTreeAccess($_[0]); + if($Access eq "protected") { + $SymbolInfo{$V}{$_[0]}{"Protected"} = 1; + } + elsif($Access eq "private") { + $SymbolInfo{$V}{$_[0]}{"Private"} = 1; + } +} + +sub setTypeAccess($$) +{ + my ($TypeId, $TypeAttr) = @_; + my $Access = getTreeAccess($TypeId); + if($Access eq "protected") { + $TypeAttr->{"Protected"} = 1; + } + elsif($Access eq "private") { + $TypeAttr->{"Private"} = 1; + } +} + +sub setFuncKind($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/pseudo tmpl/) { + $SymbolInfo{$V}{$_[0]}{"PseudoTemplate"} = 1; + } + elsif($Info=~/ constructor /) { + $SymbolInfo{$V}{$_[0]}{"Constructor"} = 1; + } + elsif($Info=~/ destructor /) { + $SymbolInfo{$V}{$_[0]}{"Destructor"} = 1; + } + } +} + +sub getVirtSpec($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/spec[ ]*:[ ]*pure /) { + return "PureVirt"; + } + elsif($Info=~/spec[ ]*:[ ]*virt /) { + return "Virt"; + } + elsif($Info=~/ pure\s+virtual /) + { # support for old GCC versions + return "PureVirt"; + } + elsif($Info=~/ virtual /) + { # support for old GCC versions + return "Virt"; + } + } + return ""; +} + +sub getFuncLink($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/link[ ]*:[ ]*static /) { + return "Static"; + } + elsif($Info=~/link[ ]*:[ ]*([a-zA-Z]+) /) { + return $1; + } + } + return ""; +} + +sub getNameSpace($) +{ + my $InfoId = $_[0]; + if(my $NSInfoId = getTreeAttr_Scpe($InfoId)) + { + if(my $InfoType = $LibInfo{$V}{"info_type"}{$NSInfoId}) + { + if($InfoType eq "namespace_decl") + { + if($LibInfo{$V}{"info"}{$NSInfoId}=~/name[ ]*:[ ]*@(\d+) /) + { + my $NameSpace = getTreeStr($1); + if($NameSpace eq "::") + { # global namespace + return ""; + } + if(my $BaseNameSpace = getNameSpace($NSInfoId)) { + $NameSpace = $BaseNameSpace."::".$NameSpace; + } + $In::ABI{$V}{"NameSpaces"}{$NameSpace} = 1; + return $NameSpace; + } + else { + return ""; + } + } + elsif($InfoType ne "function_decl") + { # inside data type + my ($Name, $NameNS) = getTrivialName(getTypeDeclId($NSInfoId), $NSInfoId); + return $Name; + } + } + } + return ""; +} + +sub getEnumMembVal($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/valu[ ]*:[ ]*\@(\d+)/) + { + if(my $VInfo = $LibInfo{$V}{"info"}{$1}) + { + if($VInfo=~/cnst[ ]*:[ ]*\@(\d+)/) + { # in newer versions of GCC the value is in the "const_decl->cnst" node + return getTreeValue($1); + } + else + { # some old versions of GCC (3.3) have the value in the "integer_cst" node + return getTreeValue($1); + } + } + } + } + return ""; +} + +sub getSize($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/size[ ]*:[ ]*\@(\d+)/) { + return getTreeValue($1); + } + } + return 0; +} + +sub getAlgn($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/algn[ ]*:[ ]*(\d+) /) { + return $1; + } + } + return ""; +} + +sub getBitField($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/ bitfield /) { + return getSize($_[0]); + } + } + return 0; +} + +sub getNextElem($) +{ + if(my $Chan = getTreeAttr_Chan($_[0])) { + return $Chan; + } + elsif(my $Chain = getTreeAttr_Chain($_[0])) { + return $Chain; + } + return ""; +} + +sub getLocation($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/srcp[ ]*:[ ]*([\w\-\<\>\.\+\/\\]+):(\d+) /) { + return (pathFmt($1), $2); + } + } + return (); +} + +sub getNameByInfo($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/name[ ]*:[ ]*@(\d+) /) + { + if(my $NInfo = $LibInfo{$V}{"info"}{$1}) + { + if($NInfo=~/strg[ ]*:[ ]*(.*?)[ ]+lngt/) + { # short unsigned int (may include spaces) + my $Str = $1; + if($In::Desc{$V}{"CppMode"} + and index($Str, "c99_")==0 + and $Str=~/\Ac99_(.+)\Z/) { + $Str = $1; + } + return $Str; + } + } + } + } + return ""; +} + +sub getTreeStr($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/strg[ ]*:[ ]*([^ ]*)/) + { + my $Str = $1; + if($In::Desc{$V}{"CppMode"} + and index($Str, "c99_")==0 + and $Str=~/\Ac99_(.+)\Z/) + { + $Str = $1; + } + return $Str; + } + } + return ""; +} + +sub getFuncShortName($) +{ + if(my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if(index($Info, " operator ")!=-1) + { + if(index($Info, " conversion ")!=-1) + { + if(my $Rid = $SymbolInfo{$V}{$_[0]}{"Return"}) + { + if(my $RName = $TypeInfo{$V}{$Rid}{"Name"}) { + return "operator ".$RName; + } + } + } + else + { + if($Info=~/ operator[ ]+([a-zA-Z]+) /) + { + if(my $Ind = $OperatorIndication{$1}) { + return "operator".$Ind; + } + elsif(not $UnknownOperator{$1}) + { + printMsg("WARNING", "unknown operator $1"); + $UnknownOperator{$1} = 1; + } + } + } + } + else + { + if($Info=~/name[ ]*:[ ]*@(\d+) /) { + return getTreeStr($1); + } + } + } + return ""; +} + +sub getFuncReturn($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/type[ ]*:[ ]*@(\d+) /) + { + if($LibInfo{$V}{"info"}{$1}=~/retn[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + } + return ""; +} + +sub getFuncOrig($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/orig[ ]*:[ ]*@(\d+) /) { + return $1; + } + } + return $_[0]; +} + +sub getFuncType($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/type[ ]*:[ ]*@(\d+) /) + { + if(my $Type = $LibInfo{$V}{"info_type"}{$1}) + { + if($Type eq "method_type") { + return "Method"; + } + elsif($Type eq "function_type") { + return "Function"; + } + else { + return "Other"; + } + } + } + } + return ""; +} + +sub getFuncTypeId($) +{ + if($_[0] and my $Info = $LibInfo{$V}{"info"}{$_[0]}) + { + if($Info=~/type[ ]*:[ ]*@(\d+)( |\Z)/) { + return $1; + } + } + return 0; +} + + + +sub guessHeader($) +{ + my $InfoId = $_[0]; + my $SInfo = $SymbolInfo{$V}{$InfoId}; + + my $ShortName = $SInfo->{"ShortName"}; + my $ClassName = ""; + if(my $ClassId = $SInfo->{"Class"}) { + $ClassName = getShortClass($ClassId, $V); + } + my $Header = $SInfo->{"Header"}; + + if(my $HPath = $In::ABI{$V}{"SymbolHeader"}{$ClassName}{$ShortName}) + { + if(getFilename($HPath) eq $Header) + { + my $HDir = getFilename(getDirname($HPath)); + if($HDir ne "include" + and $HDir=~/\A[a-z]+\Z/i) { + return join_P($HDir, $Header); + } + } + } + return $Header; +} + +sub linkWithSymbol($) +{ # link symbols from shared libraries + # with the symbols from header files + my $InfoId = $_[0]; + + if($In::Opt{"Target"} eq "windows") + { # link MS C++ symbols from library with GCC symbols from headers + if(my $Mangled1 = getMangled_MSVC(modelUnmangled($InfoId, "MSVC", $V), $V)) + { # exported symbols + return $Mangled1; + } + elsif(my $Mangled2 = mangleSymbol($InfoId, "MSVC", $V)) + { # pure virtual symbols + return $Mangled2; + } + } + + # GCC 3.x doesn't mangle class methods names in the TU dump (only functions and global data) + # GCC 4.x doesn't mangle C++ functions in the TU dump (only class methods) except extern "C" functions + # GCC 4.8.[012] and 6.[12].0 don't mangle anything + + # try to mangle symbol + if((not checkGcc("4") and $SymbolInfo{$V}{$InfoId}{"Class"}) + or (checkGcc("4") and not $SymbolInfo{$V}{$InfoId}{"Class"}) + or $In::Opt{"GccMissedMangling"}) + { + if(not $In::Opt{"CheckHeadersOnly"}) + { + if(my $Mangled = getMangled_GCC(modelUnmangled($InfoId, "GCC", $V), $V)) { + return correctIncharge($InfoId, $V, $Mangled); + } + } + + if(my $Mangled = mangleSymbol($InfoId, "GCC", $V)) { + return correctIncharge($InfoId, $V, $Mangled); + } + } + + return undef; +} + +sub simplifyNames() +{ + foreach my $Base (keys(%{$Typedef_Tr{$V}})) + { + if($Typedef_Eq{$V}{$Base}) { + next; + } + my @Translations = sort keys(%{$Typedef_Tr{$V}{$Base}}); + if($#Translations==0) + { + if(length($Translations[0])<=length($Base)) { + $Typedef_Eq{$V}{$Base} = $Translations[0]; + } + } + else + { # select most appropriate + foreach my $Tr (@Translations) + { + if($Base=~/\A\Q$Tr\E/) + { + $Typedef_Eq{$V}{$Base} = $Tr; + last; + } + } + } + } + + foreach my $TypeId (sort {$a<=>$b} keys(%{$TypeInfo{$V}})) + { # template instances only + my $TypeName = $TypeInfo{$V}{$TypeId}{"Name"}; + if(not $TypeName) { + next; + } + if(index($TypeName, "<")==-1) { + next; + } + if($TypeName=~/>(::\w+)+\Z/) + { # skip unused types + next; + } + + my $TypeName_N = $TypeName; + + foreach my $Base (sort {length($b)<=>length($a)} + sort {$b cmp $a} keys(%{$Typedef_Eq{$V}})) + { + next if(not $Base); + if(index($TypeName_N, $Base)==-1) { + next; + } + if(length($TypeName_N) - length($Base) <= 3) { + next; + } + + if(my $Typedef = $Typedef_Eq{$V}{$Base}) + { + if($TypeName_N=~s/(\<|\,)\Q$Base\E(\W|\Z)/$1$Typedef$2/g + or $TypeName_N=~s/(\<|\,)\Q$Base\E(\w|\Z)/$1$Typedef $2/g) + { + if(defined $TypeInfo{$V}{$TypeId}{"TParam"}) + { + foreach my $TPos (keys(%{$TypeInfo{$V}{$TypeId}{"TParam"}})) + { + if(my $TPName = $TypeInfo{$V}{$TypeId}{"TParam"}{$TPos}{"name"}) + { + if(index($TPName, $Base)==-1) { + next; + } + if($TPName=~s/\A\Q$Base\E(\W|\Z)/$Typedef$1/g + or $TPName=~s/\A\Q$Base\E(\w|\Z)/$Typedef $1/g) { + $TypeInfo{$V}{$TypeId}{"TParam"}{$TPos}{"name"} = formatName($TPName, "T"); + } + } + } + } + } + } + } + + if($TypeName_N ne $TypeName) + { + $TypeName_N = formatName($TypeName_N, "T"); + $TypeInfo{$V}{$TypeId}{"Name"} = $TypeName_N; + + if(not defined $TName_Tid{$V}{$TypeName_N}) { + $TName_Tid{$V}{$TypeName_N} = $TypeId; + } + } + } +} + +sub createType($) +{ + my $Attr = $_[0]; + my $NewId = ++$MAX_ID; + + $Attr->{"Tid"} = $NewId; + $TypeInfo{$V}{$NewId} = $Attr; + $TName_Tid{$V}{formatName($Attr->{"Name"}, "T")} = $NewId; + + return "$NewId"; +} + +sub instType($$) +{ # create template instances + my ($Map, $Tid) = @_; + + my $TInfoRef = $TypeInfo{$V}; + + if(not $TInfoRef->{$Tid}) { + return undef; + } + my $Attr = dclone($TInfoRef->{$Tid}); + + foreach my $Key (sort keys(%{$Map})) + { + if(my $Val = $Map->{$Key}) + { + $Attr->{"Name"}=~s/\b$Key\b/$Val/g; + + if(defined $Attr->{"NameSpace"}) { + $Attr->{"NameSpace"}=~s/\b$Key\b/$Val/g; + } + foreach (keys(%{$Attr->{"TParam"}})) { + $Attr->{"TParam"}{$_}{"name"}=~s/\b$Key\b/$Val/g; + } + } + else + { # remove absent + # _Traits, etc. + $Attr->{"Name"}=~s/,\s*\b$Key(,|>)/$1/g; + if(defined $Attr->{"NameSpace"}) { + $Attr->{"NameSpace"}=~s/,\s*\b$Key(,|>)/$1/g; + } + foreach (keys(%{$Attr->{"TParam"}})) + { + if($Attr->{"TParam"}{$_}{"name"} eq $Key) { + delete($Attr->{"TParam"}{$_}); + } + else { + $Attr->{"TParam"}{$_}{"name"}=~s/,\s*\b$Key(,|>)/$1/g; + } + } + } + } + + my $Tmpl = 0; + + if(defined $Attr->{"TParam"}) + { + foreach (sort {$a<=>$b} keys(%{$Attr->{"TParam"}})) + { + my $PName = $Attr->{"TParam"}{$_}{"name"}; + + if(my $PTid = $TName_Tid{$V}{$PName}) + { + my %Base = getBaseType($PTid, $V); + + if($Base{"Type"} eq "TemplateParam" + or defined $Base{"Template"}) + { + $Tmpl = 1; + last + } + } + } + } + + if(my $Id = getTypeIdByName($Attr->{"Name"}, $V)) { + return "$Id"; + } + else + { + if(not $Tmpl) { + delete($Attr->{"Template"}); + } + + my $New = createType($Attr); + + my %EMap = (); + if(defined $TemplateMap{$V}{$Tid}) { + %EMap = %{$TemplateMap{$V}{$Tid}}; + } + foreach (keys(%{$Map})) { + $EMap{$_} = $Map->{$_}; + } + + if(defined $TInfoRef->{$New}{"BaseType"}) { + $TInfoRef->{$New}{"BaseType"} = instType(\%EMap, $TInfoRef->{$New}{"BaseType"}); + } + if(defined $TInfoRef->{$New}{"Base"}) + { + foreach my $Bid (sort {$a<=>$b} keys(%{$TInfoRef->{$New}{"Base"}})) + { + my $NBid = instType(\%EMap, $Bid); + + if($NBid ne $Bid + and $NBid ne $New) + { + %{$TInfoRef->{$New}{"Base"}{$NBid}} = %{$TInfoRef->{$New}{"Base"}{$Bid}}; + delete($TInfoRef->{$New}{"Base"}{$Bid}); + } + } + } + + if(defined $TInfoRef->{$New}{"Memb"}) + { + foreach (sort {$a<=>$b} keys(%{$TInfoRef->{$New}{"Memb"}})) + { + if(defined $TInfoRef->{$New}{"Memb"}{$_}{"type"}) { + $TInfoRef->{$New}{"Memb"}{$_}{"type"} = instType(\%EMap, $TInfoRef->{$New}{"Memb"}{$_}{"type"}); + } + } + } + + if(defined $TInfoRef->{$New}{"Param"}) + { + foreach (sort {$a<=>$b} keys(%{$TInfoRef->{$New}{"Param"}})) { + $TInfoRef->{$New}{"Param"}{$_}{"type"} = instType(\%EMap, $TInfoRef->{$New}{"Param"}{$_}{"type"}); + } + } + + if(defined $TInfoRef->{$New}{"Return"}) { + $TInfoRef->{$New}{"Return"} = instType(\%EMap, $TInfoRef->{$New}{"Return"}); + } + + return $New; + } +} + +sub correctIncharge($$$) +{ + my ($InfoId, $V, $Mangled) = @_; + if($In::ABI{$V}{"SymbolInfo"}{$InfoId}{"Constructor"}) + { + if($MangledNames{$V}{$Mangled}) { + $Mangled=~s/C1([EI])/C2$1/; + } + } + elsif($In::ABI{$V}{"SymbolInfo"}{$InfoId}{"Destructor"}) + { + if($MangledNames{$V}{$Mangled}) { + $Mangled=~s/D0([EI])/D1$1/; + } + if($MangledNames{$V}{$Mangled}) { + $Mangled=~s/D1([EI])/D2$1/; + } + } + return $Mangled; +} + +sub simplifyConstants() +{ + my $CRef = $In::ABI{$V}{"Constants"}; + foreach my $Constant (keys(%{$CRef})) + { + if(defined $CRef->{$Constant}{"Header"}) + { + my $Value = $CRef->{$Constant}{"Value"}; + if(defined $In::ABI{$V}{"EnumConstants"}{$Value}) { + $CRef->{$Constant}{"Value"} = $In::ABI{$V}{"EnumConstants"}{$Value}{"Value"}; + } + } + } +} + +sub simplifyVTable($) +{ + my $Content = $_[0]; + if($Content=~s/ \[with (.+)]//) + { # std::basic_streambuf<_CharT, _Traits>::imbue [with _CharT = char, _Traits = std::char_traits] + if(my @Elems = sepParams($1, 0, 0)) + { + foreach my $Elem (@Elems) + { + if($Elem=~/\A(.+?)\s*=\s*(.+?)\Z/) + { + my ($Arg, $Val) = ($1, $2); + + if(defined $DefaultStdArgs{$Arg}) { + $Content=~s/,\s*$Arg\b//g; + } + else { + $Content=~s/\b$Arg\b/$Val/g; + } + } + } + } + } + + return $Content; +} + +return 1; diff --git a/abi-compliance-checker-2.4/modules/Internals/Input.pm b/abi-compliance-checker-2.4/modules/Internals/Input.pm new file mode 100644 index 0000000..b363b9e --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/Input.pm @@ -0,0 +1,34 @@ +########################################################################### +# A module to handle input data +# +# Copyright (C) 2015-2018 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +package In; + +# Input options +our %Opt; + +# Descriptor of input data +our %Desc; + +# ABI +our %ABI; + +return 1; diff --git a/abi-compliance-checker-2.4/modules/Internals/Logging.pm b/abi-compliance-checker-2.4/modules/Internals/Logging.pm new file mode 100644 index 0000000..63c9291 --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/Logging.pm @@ -0,0 +1,174 @@ +########################################################################### +# A module for logging +# +# Copyright (C) 2015-2018 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; + +my (%LOG_PATH, %DEBUG_DIR); + +my %ERROR_CODE = ( + # Compatible verdict + "Compatible"=>0, + "Success"=>0, + # Incompatible verdict + "Incompatible"=>1, + # Undifferentiated error code + "Error"=>2, + # System command is not found + "Not_Found"=>3, + # Cannot access input files + "Access_Error"=>4, + # Cannot compile header files + "Cannot_Compile"=>5, + # Header compiled with errors + "Compile_Error"=>6, + # Invalid input ABI dump + "Invalid_Dump"=>7, + # Incompatible version of ABI dump + "Dump_Version"=>8, + # Cannot find a module + "Module_Error"=>9, + # Empty intersection between + # headers and shared objects + "Empty_Intersection"=>10, + # Empty set of symbols in headers + "Empty_Set"=>11 +); + +sub exitStatus($$) +{ + my ($Code, $Msg) = @_; + printMsg("ERROR", $Msg); + exit($ERROR_CODE{$Code}); +} + +sub getErrorCode($) { + return $ERROR_CODE{$_[0]}; +} + +sub getCodeError($) +{ + my %CODE_ERROR = reverse(%ERROR_CODE); + return $CODE_ERROR{$_[0]}; +} + +sub printMsg($$) +{ + my ($Type, $Msg) = @_; + if($Type!~/\AINFO/) { + $Msg = $Type.": ".$Msg; + } + if($Type!~/_C\Z/) { + $Msg .= "\n"; + } + if($In::Opt{"Quiet"}) + { # --quiet option + appendFile($In::Opt{"DefaultLog"}, $Msg); + } + else + { + if($Type eq "ERROR") { + print STDERR $Msg; + } + else { + print $Msg; + } + } +} + +sub initLogging($) +{ + my $LVer = $_[0]; + + # create log directory + my ($LogDir, $LogFile) = ("logs/".$In::Opt{"TargetLib"}."/".$In::Desc{$LVer}{"Version"}, "log.txt"); + if(my $LogPath = $In::Desc{$LVer}{"OutputLogPath"}) + { # user-defined by -log-path option + ($LogDir, $LogFile) = sepPath($LogPath); + } + if($In::Opt{"LogMode"} ne "n") { + mkpath($LogDir); + } + $LOG_PATH{$LVer} = join_P(getAbsPath($LogDir), $LogFile); + if($In::Opt{"Debug"}) { + initDebugging($LVer); + } + + resetLogging($LVer); + resetDebugging($LVer); +} + +sub initDebugging($) +{ + my $LVer = $_[0]; + + # debug directory + $DEBUG_DIR{$LVer} = "debug/".$In::Opt{"TargetLib"}."/".$In::Desc{$LVer}{"Version"}; +} + +sub getDebugDir($) { + return $DEBUG_DIR{$_[0]}; +} + +sub getExtraDir($) { + return $DEBUG_DIR{$_[0]}."/extra-info"; +} + +sub writeLog($$) +{ + my ($LVer, $Msg) = @_; + if($In::Opt{"LogMode"} ne "n") { + appendFile($LOG_PATH{$LVer}, $Msg); + } +} + +sub resetLogging($) +{ + my $LVer = $_[0]; + if($In::Opt{"LogMode"}!~/a|n/) + { # remove old log + unlink($LOG_PATH{$LVer}); + } +} + +sub resetDebugging($) +{ + my $LVer = $_[0]; + if($In::Opt{"Debug"}) + { + if(-d $DEBUG_DIR{$LVer}) + { + rmtree($DEBUG_DIR{$LVer}); + } + + mkpath($DEBUG_DIR{$LVer}); + } +} + +sub printErrorLog($) +{ + my $LVer = $_[0]; + if($In::Opt{"LogMode"} ne "n") { + printMsg("ERROR", "see log for details:\n ".$LOG_PATH{$LVer}."\n"); + } +} + +return 1; diff --git a/abi-compliance-checker-2.4/modules/Internals/Mangling.pm b/abi-compliance-checker-2.4/modules/Internals/Mangling.pm new file mode 100644 index 0000000..7c5495c --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/Mangling.pm @@ -0,0 +1,1052 @@ +########################################################################### +# A module to mangle C++ symbols +# +# Copyright (C) 2015-2018 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; + +my %Cache; + +my %IntrinsicMangling = ( + "void" => "v", + "bool" => "b", + "wchar_t" => "w", + "char" => "c", + "signed char" => "a", + "unsigned char" => "h", + "short" => "s", + "unsigned short" => "t", + "int" => "i", + "unsigned int" => "j", + "long" => "l", + "unsigned long" => "m", + "long long" => "x", + "__int64" => "x", + "unsigned long long" => "y", + "__int128" => "n", + "unsigned __int128" => "o", + "float" => "f", + "double" => "d", + "long double" => "e", + "__float80" => "e", + "__float128" => "g", + "..." => "z" +); + +my %StdcxxMangling = ( + "3std"=>"St", + "3std9allocator"=>"Sa", + "3std12basic_string"=>"Sb", + "3std12basic_stringIcE"=>"Ss", + "3std13basic_istreamIcE"=>"Si", + "3std13basic_ostreamIcE"=>"So", + "3std14basic_iostreamIcE"=>"Sd" +); + +my $DEFAULT_STD_PARMS = "std::(allocator|less|char_traits|regex_traits|istreambuf_iterator|ostreambuf_iterator)"; + +my %ConstantSuffixR = ( + "u"=>"unsigned int", + "l"=>"long", + "ul"=>"unsigned long", + "ll"=>"long long", + "ull"=>"unsigned long long" +); + +my %OperatorMangling = ( + "~" => "co", + "=" => "aS", + "|" => "or", + "^" => "eo", + "&" => "an",#ad (addr) + "==" => "eq", + "!" => "nt", + "!=" => "ne", + "<" => "lt", + "<=" => "le", + "<<" => "ls", + "<<=" => "lS", + ">" => "gt", + ">=" => "ge", + ">>" => "rs", + ">>=" => "rS", + "()" => "cl", + "%" => "rm", + "[]" => "ix", + "&&" => "aa", + "||" => "oo", + "*" => "ml",#de (deref) + "++" => "pp",# + "--" => "mm",# + "new" => "nw", + "delete" => "dl", + "new[]" => "na", + "delete[]" => "da", + "+=" => "pL", + "+" => "pl",#ps (pos) + "-" => "mi",#ng (neg) + "-=" => "mI", + "*=" => "mL", + "/=" => "dV", + "&=" => "aN", + "|=" => "oR", + "%=" => "rM", + "^=" => "eO", + "/" => "dv", + "->*" => "pm", + "->" => "pt",#rf (ref) + "," => "cm", + "?" => "qu", + "." => "dt", + "sizeof"=> "sz"#st +); + +my $MAX_CMD_ARG = 4096; +my $MAX_CPPFILT_INPUT = 50000; +my $CPPFILT_SUPPORT_FILE = undef; + +my %TrName; +my %GccMangledName; +my %MangledName; + +my $DisabledUnmangle_MSVC = undef; + +sub mangleSymbol($$$) +{ # mangling for simple methods + # see gcc-4.6.0/gcc/cp/mangle.c + my ($InfoId, $Compiler, $LVer) = @_; + if($Cache{"mangleSymbol"}{$LVer}{$InfoId}{$Compiler}) { + return $Cache{"mangleSymbol"}{$LVer}{$InfoId}{$Compiler}; + } + my $Mangled = ""; + if($Compiler eq "GCC") { + $Mangled = mangleSymbol_GCC($InfoId, $LVer); + } + elsif($Compiler eq "MSVC") { + $Mangled = mangleSymbol_MSVC($InfoId, $LVer); + } + return ($Cache{"mangleSymbol"}{$LVer}{$InfoId}{$Compiler} = $Mangled); +} + +sub mangleSymbol_MSVC($$) +{ # TODO + my ($InfoId, $LVer) = @_; + return ""; +} + +sub mangleSymbol_GCC($$) +{ # see gcc-4.6.0/gcc/cp/mangle.c + my ($InfoId, $LVer) = @_; + + my $SInfo = $In::ABI{$LVer}{"SymbolInfo"}{$InfoId}; + + my ($Mangled, $ClassId, $NameSpace) = ("_Z", 0, ""); + + my $Return = $SInfo->{"Return"}; + my %Repl = (); # SN_ replacements + if($ClassId = $SInfo->{"Class"}) + { + my $MangledClass = mangleParam($ClassId, $LVer, \%Repl); + if($MangledClass!~/\AN/) { + $MangledClass = "N".$MangledClass; + } + else { + $MangledClass=~s/E\Z//; + } + if($SInfo->{"Const"}) { + $MangledClass=~s/\AN/NK/; + } + if($SInfo->{"Volatile"}) { + $MangledClass=~s/\AN/NV/; + } + $Mangled .= $MangledClass; + } + elsif($NameSpace = $SInfo->{"NameSpace"}) + { # mangled by name due to the absence of structured info + my $MangledNS = mangleNs($NameSpace, $LVer, \%Repl); + if($MangledNS!~/\AN/) { + $MangledNS = "N".$MangledNS; + } + else { + $MangledNS=~s/E\Z//; + } + $Mangled .= $MangledNS; + } + my ($ShortName, $TmplParams) = templateBase($SInfo->{"ShortName"}); + my @TParams = (); + if(my @TPos = sort {$a<=>$b} keys(%{$SInfo->{"TParam"}})) + { # parsing mode + foreach my $PPos (@TPos) { + push(@TParams, $SInfo->{"TParam"}{$PPos}{"name"}); + } + } + elsif($TmplParams) + { # remangling mode + # support for old ABI dumps + @TParams = sepParams($TmplParams, 0, 0); + } + if(my $Ctor = $SInfo->{"Constructor"}) + { + if($Ctor ne "1") { + $Mangled .= $Ctor; + } + else { + $Mangled .= "C1"; + } + } + elsif(my $Dtor = $SInfo->{"Destructor"}) + { + if($Dtor ne "1") { + $Mangled .= $Dtor; + } + else { + $Mangled .= "D0"; + } + } + elsif($ShortName) + { + if($SInfo->{"Data"}) + { + if(not $SInfo->{"Class"} + and isConstType($Return, $LVer)) + { # "const" global data is mangled as _ZL... + $Mangled .= "L"; + } + } + if($ShortName=~/\Aoperator(\W.*)\Z/) + { + my $Op = $1; + $Op=~s/\A[ ]+//g; + if(my $OpMngl = $OperatorMangling{$Op}) { + $Mangled .= $OpMngl; + } + else { # conversion operator + $Mangled .= "cv".mangleParam(getTypeIdByName($Op, $LVer), $LVer, \%Repl); + } + } + else { + $Mangled .= length($ShortName).$ShortName; + } + if(@TParams) + { # templates + $Mangled .= "I"; + foreach my $TParam (@TParams) { + $Mangled .= mangleTemplateParam($TParam, $LVer, \%Repl); + } + $Mangled .= "E"; + } + if(not $ClassId and @TParams) { + addSubst($ShortName, \%Repl, 0); + } + } + if($ClassId or $NameSpace) { + $Mangled .= "E"; + } + if(@TParams) + { + if($Return) { + $Mangled .= mangleParam($Return, $LVer, \%Repl); + } + } + if(not $SInfo->{"Data"}) + { + my @Params = (); + if(defined $SInfo->{"Param"} + and not $SInfo->{"Destructor"}) + { + @Params = sort {$a<=>$b} keys(%{$SInfo->{"Param"}}); + + if($SInfo->{"Class"} + and not $SInfo->{"Static"}) + { + if($SInfo->{"Param"}{"0"}{"name"} eq "this") { + shift(@Params); + } + } + } + foreach my $PPos (sort {$a<=>$b} @Params) + { # checking parameters + my $PTid = $SInfo->{"Param"}{$PPos}{"type"}; + $Mangled .= mangleParam($PTid, $LVer, \%Repl); + } + if(not @Params) { + $Mangled .= "v"; + } + } + $Mangled = writeCxxSubstitution($Mangled); + if($Mangled eq "_Z") { + return ""; + } + return $Mangled; +} + +sub templateBase($) +{ # NOTE: std::_Vector_base::_Vector_impl + # NOTE: operators: >>, << + my $Name = $_[0]; + if($Name!~/>\Z/ or $Name!~/ + $TParams = substr($TParams, $CPos); + } + if($TParams=~s/\A<(.+)>\Z/$1/) { + $Name=~s/<\Q$TParams\E>\Z//; + } + else + { # error + $TParams = ""; + } + return ($Name, $TParams); +} + +sub getSubNs($) +{ + my $Name = $_[0]; + my @NS = (); + while(my $CPos = findCenter($Name, ":")) + { + push(@NS, substr($Name, 0, $CPos)); + $Name = substr($Name, $CPos); + $Name=~s/\A:://; + } + return (join("::", @NS), $Name); +} + +sub mangleNs($$$) +{ + my ($Name, $LVer, $Repl) = @_; + if(my $Tid = $In::ABI{$LVer}{"TName_Tid"}{$Name}) + { + my $Mangled = mangleParam($Tid, $LVer, $Repl); + $Mangled=~s/\AN(.+)E\Z/$1/; + return $Mangled; + + } + else + { + my ($MangledNS, $SubNS) = ("", ""); + ($SubNS, $Name) = getSubNs($Name); + if($SubNS) { + $MangledNS .= mangleNs($SubNS, $LVer, $Repl); + } + $MangledNS .= length($Name).$Name; + addSubst($MangledNS, $Repl, 0); + return $MangledNS; + } +} + +sub mangleParam($$$) +{ + my ($PTid, $LVer, $Repl) = @_; + my ($MPrefix, $Mangled) = ("", ""); + my %ReplCopy = %{$Repl}; + my %BaseType = getBaseType($PTid, $LVer); + my $BaseType_Name = $BaseType{"Name"}; + $BaseType_Name=~s/\A(struct|union|enum) //g; + if(not $BaseType_Name) { + return ""; + } + my ($ShortName, $TmplParams) = templateBase($BaseType_Name); + my $Suffix = getBaseTypeQual($PTid, $LVer); + while($Suffix=~s/\s*(const|volatile|restrict)\Z//g){}; + while($Suffix=~/(&|\*|const)\Z/) + { + if($Suffix=~s/[ ]*&\Z//) { + $MPrefix .= "R"; + } + if($Suffix=~s/[ ]*\*\Z//) { + $MPrefix .= "P"; + } + if($Suffix=~s/[ ]*const\Z//) + { + if($MPrefix=~/R|P/ + or $Suffix=~/&|\*/) { + $MPrefix .= "K"; + } + } + if($Suffix=~s/[ ]*volatile\Z//) { + $MPrefix .= "V"; + } + #if($Suffix=~s/[ ]*restrict\Z//) { + #$MPrefix .= "r"; + #} + } + if(my $Token = $IntrinsicMangling{$BaseType_Name}) { + $Mangled .= $Token; + } + elsif($BaseType{"Type"}=~/(Class|Struct|Union|Enum)/) + { + my @TParams = (); + if(my @TPos = sort {$a<=>$b} keys(%{$BaseType{"TParam"}})) + { # parsing mode + foreach (@TPos) { + push(@TParams, $BaseType{"TParam"}{$_}{"name"}); + } + } + elsif($TmplParams) + { # remangling mode + # support for old ABI dumps + @TParams = sepParams($TmplParams, 0, 0); + } + my $MangledNS = ""; + my ($SubNS, $SName) = getSubNs($ShortName); + if($SubNS) { + $MangledNS .= mangleNs($SubNS, $LVer, $Repl); + } + $MangledNS .= length($SName).$SName; + if(@TParams) { + addSubst($MangledNS, $Repl, 0); + } + $Mangled .= "N".$MangledNS; + if(@TParams) + { # templates + $Mangled .= "I"; + foreach my $TParam (@TParams) { + $Mangled .= mangleTemplateParam($TParam, $LVer, $Repl); + } + $Mangled .= "E"; + } + $Mangled .= "E"; + } + elsif($BaseType{"Type"}=~/(FuncPtr|MethodPtr)/) + { + if($BaseType{"Type"} eq "MethodPtr") { + $Mangled .= "M".mangleParam($BaseType{"Class"}, $LVer, $Repl)."F"; + } + else { + $Mangled .= "PF"; + } + $Mangled .= mangleParam($BaseType{"Return"}, $LVer, $Repl); + my @Params = sort {$a<=>$b} keys(%{$BaseType{"Param"}}); + foreach my $Num (@Params) { + $Mangled .= mangleParam($BaseType{"Param"}{$Num}{"type"}, $LVer, $Repl); + } + if(not @Params) { + $Mangled .= "v"; + } + $Mangled .= "E"; + } + elsif($BaseType{"Type"} eq "FieldPtr") + { + $Mangled .= "M".mangleParam($BaseType{"Class"}, $LVer, $Repl); + $Mangled .= mangleParam($BaseType{"Return"}, $LVer, $Repl); + } + $Mangled = $MPrefix.$Mangled; # add prefix (RPK) + if(my $Optimized = writeSubstitution($Mangled, \%ReplCopy)) + { + if($Mangled eq $Optimized) + { + if($ShortName!~/::/) + { # remove "N ... E" + if($MPrefix) { + $Mangled=~s/\A($MPrefix)N(.+)E\Z/$1$2/g; + } + else { + $Mangled=~s/\AN(.+)E\Z/$1/g; + } + } + } + else { + $Mangled = $Optimized; + } + } + addSubst($Mangled, $Repl, 1); + return $Mangled; +} + +sub mangleTemplateParam($$$) +{ # types + literals + my ($TParam, $LVer, $Repl) = @_; + if(my $TPTid = $In::ABI{$LVer}{"TName_Tid"}{$TParam}) { + return mangleParam($TPTid, $LVer, $Repl); + } + elsif($TParam=~/\A(\d+)(\w+)\Z/) + { # class_name<1u>::method(...) + return "L".$IntrinsicMangling{$ConstantSuffixR{$2}}.$1."E"; + } + elsif($TParam=~/\A\(([\w ]+)\)(\d+)\Z/) + { # class_name<(signed char)1>::method(...) + return "L".$IntrinsicMangling{$1}.$2."E"; + } + elsif($TParam eq "true") + { # class_name::method(...) + return "Lb1E"; + } + elsif($TParam eq "false") + { # class_name::method(...) + return "Lb0E"; + } + else { # internal error + return length($TParam).$TParam; + } +} + +sub addSubst($$$) +{ + my ($Value, $Repl, $Rec) = @_; + if($Rec) + { # subtypes + my @Subs = ($Value); + while($Value=~s/\A(R|P|K)//) { + push(@Subs, $Value); + } + foreach (reverse(@Subs)) { + addSubst($_, $Repl, 0); + } + return; + } + if($Value=~/\AS(\d*)_\Z/) { + return; + } + $Value=~s/\AN(.+)E\Z/$1/g; + if(defined $Repl->{$Value}) { + return; + } + if(length($Value)<=1) { + return; + } + if($StdcxxMangling{$Value}) { + return; + } + # check for duplicates + my $Base = $Value; + foreach my $Type (sort {$Repl->{$a}<=>$Repl->{$b}} sort keys(%{$Repl})) + { + my $Num = $Repl->{$Type}; + my $Replace = macroMangle($Num); + $Base=~s/\Q$Replace\E/$Type/; + } + if(my $OldNum = $Repl->{$Base}) + { + $Repl->{$Value} = $OldNum; + return; + } + my @Repls = sort {$b<=>$a} values(%{$Repl}); + if(@Repls) { + $Repl->{$Value} = $Repls[0]+1; + } + else { + $Repl->{$Value} = -1; + } + # register duplicates + # upward + $Base = $Value; + foreach my $Type (sort {$Repl->{$a}<=>$Repl->{$b}} sort keys(%{$Repl})) + { + if($Base eq $Type) { + next; + } + my $Num = $Repl->{$Type}; + my $Replace = macroMangle($Num); + $Base=~s/\Q$Type\E/$Replace/; + $Repl->{$Base} = $Repl->{$Value}; + } +} + +sub macroMangle($) +{ + my $Num = $_[0]; + if($Num==-1) { + return "S_"; + } + else + { + my $Code = ""; + if($Num<10) + { # S0_, S1_, S2_, ... + $Code = $Num; + } + elsif($Num>=10 and $Num<=35) + { # SA_, SB_, SC_, ... + $Code = chr(55+$Num); + } + else + { # S10_, S11_, S12_ + $Code = $Num-26; # 26 is length of english alphabet + } + return "S".$Code."_"; + } +} + +sub writeCxxSubstitution($) +{ + my $Mangled = $_[0]; + if($StdcxxMangling{$Mangled}) { + return $StdcxxMangling{$Mangled}; + } + else + { + my @Repls = sort {$b cmp $a} keys(%StdcxxMangling); + @Repls = sort {length($b)<=>length($a)} @Repls; + foreach my $MangledType (@Repls) + { + my $Replace = $StdcxxMangling{$MangledType}; + #if($Mangled!~/$Replace/) { + $Mangled=~s/N\Q$MangledType\EE/$Replace/g; + $Mangled=~s/\Q$MangledType\E/$Replace/g; + #} + } + } + return $Mangled; +} + +sub writeSubstitution($$) +{ + my ($Mangled, $Repl) = @_; + if(defined $Repl->{$Mangled} + and my $MnglNum = $Repl->{$Mangled}) { + $Mangled = macroMangle($MnglNum); + } + else + { + my @Repls = keys(%{$Repl}); + + # @Repls = sort {$Repl->{$a}<=>$Repl->{$b}} @Repls; + # FIXME: how to apply replacements? by num or by pos + + @Repls = sort {length($b)<=>length($a)} sort {$b cmp $a} @Repls; + foreach my $MangledType (@Repls) + { + my $Replace = macroMangle($Repl->{$MangledType}); + if($Mangled!~/$Replace/) { + $Mangled=~s/N\Q$MangledType\EE/$Replace/g; + $Mangled=~s/\Q$MangledType\E/$Replace/g; + } + } + } + return $Mangled; +} + +sub isDefaultStd($) { + return ($_[0]=~/\A$DEFAULT_STD_PARMS\ >*) + # to be TIFFStreamOpen(char const*, std::basic_ostream*) + my ($Name, $Type) = @_; + + # single + while($Name=~/([^<>,]+),\s*$DEFAULT_STD_PARMS<([^<>,]+)>\s*/ and $1 eq $3) + { + my $P = $1; + $Name=~s/\Q$P\E,\s*$DEFAULT_STD_PARMS<\Q$P\E>\s*/$P/g; + } + + # double + if($Name=~/$DEFAULT_STD_PARMS/) + { + if($Type eq "S") + { + my ($ShortName, $FuncParams) = splitSignature($Name); + + foreach my $FParam (sepParams($FuncParams, 0, 0)) + { + if(index($FParam, "<")!=-1) + { + $FParam=~s/>([^<>]+)\Z/>/; # remove quals + my $FParam_N = canonifyName($FParam, "T"); + if($FParam_N ne $FParam) { + $Name=~s/\Q$FParam\E/$FParam_N/g; + } + } + } + } + elsif($Type eq "T") + { + my ($ShortTmpl, $TmplParams) = templateBase($Name); + + my @TParams = sepParams($TmplParams, 0, 0); + if($#TParams>=1) + { + my $FParam = $TParams[0]; + foreach my $Pos (1 .. $#TParams) + { + my $TParam = $TParams[$Pos]; + if($TParam=~/\A$DEFAULT_STD_PARMS<\Q$FParam\E\s*>\Z/) { + $Name=~s/\Q$FParam, $TParam\E\s*/$FParam/g; + } + } + } + } + } + if($Type eq "S") { + return formatName($Name, "S"); + } + return $Name; +} + +sub getUnmangled($$) +{ + if(defined $TrName{$_[1]}{$_[0]}) { + return $TrName{$_[1]}{$_[0]}; + } + + return undef; +} + +sub getMangled_GCC($$) +{ + if(defined $GccMangledName{$_[1]}{$_[0]}) { + return $GccMangledName{$_[1]}{$_[0]}; + } + + return undef; +} + +sub getMangled_MSVC($$) +{ + if(defined $MangledName{$_[1]}{$_[0]}) { + return $MangledName{$_[1]}{$_[0]}; + } + + return $_[0]; +} + +sub translateSymbols(@) +{ + my $LVer = pop(@_); + my (@MnglNames1, @MnglNames2, @ZNames, @UnmangledNames) = (); + my %Versioned = (); + + foreach my $Symbol (sort @_) + { + if(index($Symbol, "_Z")==0) + { + push(@ZNames, $Symbol); + if($TrName{$LVer}{$Symbol}) + { # already unmangled + next; + } + if($Symbol=~s/([\@\$]+.*)\Z//) { + $Versioned{$Symbol}{$Symbol.$1} = 1; + } + else { + $Versioned{$Symbol}{$Symbol} = 1; + } + push(@MnglNames1, $Symbol); + } + elsif(index($Symbol, "?")==0) + { + push(@MnglNames2, $Symbol); + } + } + if($#MnglNames1 > -1) + { # GCC names + @UnmangledNames = reverse(unmangleArray(@MnglNames1)); + foreach my $MnglName (@MnglNames1) + { + if(my $Unmangled = pop(@UnmangledNames)) + { + foreach my $M (keys(%{$Versioned{$MnglName}})) + { + $TrName{$LVer}{$M} = canonifyName($Unmangled, "S"); + if(not $GccMangledName{$LVer}{$TrName{$LVer}{$M}}) { + $GccMangledName{$LVer}{$TrName{$LVer}{$M}} = $M; + } + } + } + } + + foreach my $Symbol (@ZNames) + { + if(index($Symbol, "_ZTV")==0 + and $TrName{$LVer}{$Symbol}=~/vtable for (.+)/) + { # bind class name and v-table symbol + $In::ABI{$LVer}{"ClassVTable"}{$1} = $Symbol; + } + } + } + if($#MnglNames2 > -1) + { # MSVC names + @UnmangledNames = reverse(unmangleArray(@MnglNames2)); + foreach my $MnglName (@MnglNames2) + { + if(my $Unmangled = pop(@UnmangledNames)) + { + $TrName{$LVer}{$MnglName} = formatName($Unmangled, "S"); + $MangledName{$LVer}{$TrName{$LVer}{$MnglName}} = $MnglName; + } + } + } + return \%{$TrName{$LVer}}; +} + +sub unmangleArray(@) +{ + if($_[0]=~/\A\?/) + { # MSVC mangling + if(defined $DisabledUnmangle_MSVC) { + return @_; + } + my $UndNameCmd = getCmdPath("undname"); + if(not $UndNameCmd) + { + if($In::Opt{"OS"} eq "windows") { + exitStatus("Not_Found", "can't find \"undname\""); + } + elsif(not defined $DisabledUnmangle_MSVC) + { + printMsg("WARNING", "can't find \"undname\", disable MSVC unmangling"); + $DisabledUnmangle_MSVC = 1; + return @_; + } + } + my $TmpDir = $In::Opt{"Tmp"}; + writeFile("$TmpDir/unmangle", join("\n", @_)); + return split(/\n/, `$UndNameCmd 0x8386 \"$TmpDir/unmangle\"`); + } + else + { # GCC mangling + my $CppFiltCmd = getCmdPath("c++filt"); + if(not $CppFiltCmd) { + exitStatus("Not_Found", "can't find c++filt in PATH"); + } + if(not defined $CPPFILT_SUPPORT_FILE) + { + my $Info = `$CppFiltCmd -h 2>&1`; + $CPPFILT_SUPPORT_FILE = ($Info=~/\@/); + } + my $NoStrip = ""; + + if($In::Opt{"OS"}=~/macos|windows/) { + $NoStrip = "-n"; + } + + if($CPPFILT_SUPPORT_FILE) + { # new versions of c++filt can take a file + if($#_>$MAX_CPPFILT_INPUT) + { # c++filt <= 2.22 may crash on large files (larger than 8mb) + # this is fixed in the oncoming version of Binutils + my @Half = splice(@_, 0, ($#_+1)/2); + return (unmangleArray(@Half), unmangleArray(@_)) + } + else + { + my $TmpDir = $In::Opt{"Tmp"}; + writeFile("$TmpDir/unmangle", join("\n", @_)); + my $Res = `$CppFiltCmd $NoStrip \@\"$TmpDir/unmangle\"`; + if($?==139) + { # segmentation fault + printMsg("ERROR", "internal error - c++filt crashed, try to reduce MAX_CPPFILT_FILE_SIZE constant"); + } + return split(/\n/, $Res); + } + } + else + { # old-style unmangling + if($#_>$MAX_CMD_ARG) + { + my @Half = splice(@_, 0, ($#_+1)/2); + return (unmangleArray(@Half), unmangleArray(@_)) + } + else + { + my $Strings = join(" ", @_); + my $Res = `$CppFiltCmd $NoStrip $Strings`; + if($?==139) + { # segmentation fault + printMsg("ERROR", "internal error - c++filt crashed, try to reduce MAX_COMMAND_LINE_ARGUMENTS constant"); + } + return split(/\n/, $Res); + } + } + } +} + +sub debugMangling($) +{ + my $LVer = $_[0]; + + printMsg("INFO", "Debug model mangling and unmangling ($LVer)"); + + my %Mangled = (); + foreach my $InfoId (keys(%{$In::ABI{$LVer}{"SymbolInfo"}})) + { + my $SInfo = $In::ABI{$LVer}{"SymbolInfo"}{$InfoId}; + + if(my $Mngl = $SInfo->{"MnglName"}) + { + if(my $Class = $SInfo->{"Class"}) + { + if(defined $In::ABI{$LVer}{"TypeInfo"}{$Class}{"TParam"}) + { # mngl names are not equal because of default tmpl args + next; + } + + if(index($In::ABI{$LVer}{"TypeInfo"}{$Class}{"Name"}, "...")!=-1) + { # no info about tmpl args + next; + } + } + + if($Mngl=~/\A(_Z|\?)/) { + $Mangled{$Mngl} = $InfoId; + } + } + } + + translateSymbols(keys(%Mangled), $LVer); + + my $Total = keys(%Mangled); + my ($GoodMangling, $GoodUnmangling) = (0, 0); + + foreach my $Mngl (sort keys(%Mangled)) + { + my $InfoId = $Mangled{$Mngl}; + + my $U1 = getUnmangled($Mngl, $LVer); + my $U2 = modelUnmangled($InfoId, "GCC", $LVer); + my $U3 = mangleSymbol($InfoId, "GCC", $LVer); + + if($U1 ne $U2) { + printMsg("INFO", "Bad model unmangling:\n Orig: $Mngl\n Unmgl: $U1\n Model: $U2\n"); + } + else { + $GoodUnmangling += 1; + } + + if($Mngl ne $U3) { + printMsg("INFO", "Bad model mangling:\n Orig: $Mngl\n Model: $U3\n"); + } + else { + $GoodMangling += 1; + } + } + + printMsg("INFO", "Model unmangling: $GoodUnmangling/$Total"); + printMsg("INFO", "Model mangling: $GoodMangling/$Total"); +} + +sub modelUnmangled($$$) +{ + my ($InfoId, $Compiler, $LVer) = @_; + if($Cache{"modelUnmangled"}{$LVer}{$Compiler}{$InfoId}) { + return $Cache{"modelUnmangled"}{$LVer}{$Compiler}{$InfoId}; + } + + my $SInfo = $In::ABI{$LVer}{"SymbolInfo"}{$InfoId}; + + my $PureSignature = $SInfo->{"ShortName"}; + if($SInfo->{"Destructor"}) { + $PureSignature = "~".$PureSignature; + } + if(not $SInfo->{"Data"}) + { + my (@Params, @ParamTypes) = (); + if(defined $SInfo->{"Param"} + and not $SInfo->{"Destructor"}) + { + @Params = sort {$a<=>$b} keys(%{$SInfo->{"Param"}}); + + if($SInfo->{"Class"} + and not $SInfo->{"Static"}) + { + if($SInfo->{"Param"}{"0"}{"name"} eq "this") { + shift(@Params); + } + } + } + foreach my $ParamPos (@Params) + { # checking parameters + my $PTid = $SInfo->{"Param"}{$ParamPos}{"type"}; + my $PTName = $In::ABI{$LVer}{"TypeInfo"}{$PTid}{"Name"}; + + $PTName = unmangledFormat($PTName, $LVer); + $PTName=~s/\b(restrict|register)\b//g; + + if($Compiler eq "MSVC") { + $PTName=~s/\blong long\b/__int64/; + } + @ParamTypes = (@ParamTypes, $PTName); + } + if(@ParamTypes) { + $PureSignature .= "(".join(", ", @ParamTypes).")"; + } + else + { + if($Compiler eq "MSVC") { + $PureSignature .= "(void)"; + } + else + { # GCC + $PureSignature .= "()"; + } + } + $PureSignature = deleteKeywords($PureSignature); + } + if(my $ClassId = $SInfo->{"Class"}) + { + my $ClassName = unmangledFormat($In::ABI{$LVer}{"TypeInfo"}{$ClassId}{"Name"}, $LVer); + $PureSignature = $ClassName."::".$PureSignature; + } + elsif(my $NS = $SInfo->{"NameSpace"}) { + $PureSignature = $NS."::".$PureSignature; + } + if($SInfo->{"Const"}) { + $PureSignature .= " const"; + } + if($SInfo->{"Volatile"}) { + $PureSignature .= " volatile"; + } + my $ShowReturn = 0; + if($Compiler eq "MSVC" + and $SInfo->{"Data"}) { + $ShowReturn = 1; + } + elsif(index($SInfo->{"ShortName"}, "<")!=-1) + { # template instance + $ShowReturn = 1; + } + if($ShowReturn) + { # mangled names for template function specializations include return type + if(my $ReturnId = $SInfo->{"Return"}) + { + my %RType = getPureType($ReturnId, $LVer); + my $ReturnName = unmangledFormat($RType{"Name"}, $LVer); + $PureSignature = $ReturnName." ".$PureSignature; + } + } + return ($Cache{"modelUnmangled"}{$LVer}{$Compiler}{$InfoId} = formatName($PureSignature, "S")); +} + +sub unmangledFormat($$) +{ + my ($Name, $LibVersion) = @_; + $Name = uncoverTypedefs($Name, $LibVersion); + while($Name=~s/([^\w>])(const|volatile)(,|>|\Z)/$1$3/g){}; + $Name=~s/\(\w+\)(\d)/$1/; + return $Name; +} + +sub isConstType($$) +{ + my ($TypeId, $LVer) = @_; + my %Base = getType($TypeId, $LVer); + while(defined $Base{"Type"} and $Base{"Type"} eq "Typedef") { + %Base = getOneStepBaseType($Base{"Tid"}, $LVer); + } + return ($Base{"Type"} eq "Const"); +} + +return 1; diff --git a/abi-compliance-checker-2.4/modules/Internals/Path.pm b/abi-compliance-checker-2.4/modules/Internals/Path.pm new file mode 100644 index 0000000..bc7d1c2 --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/Path.pm @@ -0,0 +1,91 @@ +########################################################################### +# A module with functions to handle paths +# +# Copyright (C) 2017-2018 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; +use Cwd qw(realpath); + +sub pathFmt(@) +{ + my $Path = shift(@_); + my $Fmt = $In::Opt{"OS"}; + if(@_) { + $Fmt = shift(@_); + } + + $Path=~s/[\/\\]+\.?\Z//g; + if($Fmt eq "windows") + { + $Path=~s/\//\\/g; + $Path = lc($Path); + } + else + { # forward slash to pass into MinGW GCC + $Path=~s/\\/\//g; + } + + $Path=~s/[\/\\]+\Z//g; + + return $Path; +} + +sub getAbsPath($) +{ # abs_path() should NOT be called for absolute inputs + # because it can change them + my $Path = $_[0]; + if(not isAbsPath($Path)) { + $Path = abs_path($Path); + } + return pathFmt($Path); +} + +sub realpath_F($) +{ + my $Path = $_[0]; + return pathFmt(realpath($Path)); +} + +sub classifyPath($) +{ + my $Path = $_[0]; + if($Path=~/[\*\+\(\[\|]/) + { # pattern + return ($Path, "Pattern"); + } + elsif($Path=~/[\/\\]/) + { # directory or relative path + return (pathFmt($Path), "Path"); + } + else { + return ($Path, "Name"); + } +} + +sub join_P($$) +{ + my $S = "/"; + if($In::Opt{"OS"} eq "windows") { + $S = "\\"; + } + return join($S, @_); +} + +return 1; diff --git a/abi-compliance-checker-2.4/modules/Internals/RegTests.pm b/abi-compliance-checker-2.4/modules/Internals/RegTests.pm new file mode 100644 index 0000000..a09ae5b --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/RegTests.pm @@ -0,0 +1,5201 @@ +########################################################################### +# A module with regression test suite +# +# Copyright (C) 2009-2011 Institute for System Programming, RAS +# Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies) +# Copyright (C) 2012-2018 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; + +loadModule("ElfTools"); + +sub testTool() +{ + if($In::Opt{"UserLang"} ne "C++") { + testC(); + } + + if($In::Opt{"UserLang"} ne "C") { + testCpp(); + } +} + +sub testCpp() +{ + printMsg("INFO", "Verifying detectable C++ library changes"); + my ($HEADER1, $SOURCE1, $HEADER2, $SOURCE2) = (); + + my $DECL_SPEC = ""; + my $EXTERN = ""; + + if($In::Opt{"OS"} eq "windows") + { + $DECL_SPEC = "__declspec( dllexport )"; + $EXTERN = "extern "; # add "extern" for CL compiler + } + + # Class outside namespace + $HEADER1 .= " + class $DECL_SPEC OutsideNS { + public: + int someMethod(); + int field; + };"; + $SOURCE1 .= " + int OutsideNS::someMethod() { return 0; }"; + + $HEADER2 .= " + class $DECL_SPEC OutsideNS { + public: + int someMethod(); + int field; + int field2; + };"; + $SOURCE2 .= " + int OutsideNS::someMethod() { return 0; }"; + + # Begin namespace + $HEADER1 .= "namespace TestNS {\n"; + $HEADER2 .= "namespace TestNS {\n"; + $SOURCE1 .= "namespace TestNS {\n"; + $SOURCE2 .= "namespace TestNS {\n"; + + # Changed template internals + # $HEADER1 .= " + # template + # class $DECL_SPEC ChangedTemplate { + # public: + # T value; + # T*const field; + # T array[_P]; + # typedef int My; + # My var; + # }; + # ChangedTemplate* changedTemplate();"; + # $SOURCE1 .= " + # ChangedTemplate* changedTemplate() { return new ChangedTemplate(); }"; + # + # $HEADER2 .= " + # template + # class $DECL_SPEC ChangedTemplate { + # public: + # double value; + # T* field; + # double array[_P]; + # typedef int My; + # My var; + # }; + # ChangedTemplate* changedTemplate();"; + # $SOURCE2 .= " + # ChangedTemplate* changedTemplate() { return new ChangedTemplate(); }"; + + # Removed inline method + $HEADER1 .= " + class $DECL_SPEC RemovedInlineMethod { + public: + int someMethod(); + inline int removedMethod() { return 0; }; + int field; + };"; + $SOURCE1 .= " + int RemovedInlineMethod::someMethod() { return removedMethod(); }"; + + $HEADER2 .= " + class $DECL_SPEC RemovedInlineMethod { + public: + int someMethod(); + int field; + };"; + $SOURCE2 .= " + int RemovedInlineMethod::someMethod() { return 0; }"; + + # Pure_Virtual_Replacement + $HEADER1 .= " + class $DECL_SPEC PureVirtualReplacement { + public: + virtual int methodOld(int param) = 0; + int otherMethod(); + }; + + class $DECL_SPEC PureVirtualReplacement_Derived: public PureVirtualReplacement { + public: + int methodOld(int param); + };"; + $SOURCE1 .= " + int PureVirtualReplacement::otherMethod() { return 0; } + int PureVirtualReplacement_Derived::methodOld(int param) { return 0; }"; + + $HEADER2 .= " + class $DECL_SPEC PureVirtualReplacement { + public: + virtual int methodNew(int param) = 0; + int otherMethod(); + }; + + class $DECL_SPEC PureVirtualReplacement_Derived: public PureVirtualReplacement { + public: + int methodNew(int param); + };"; + $SOURCE2 .= " + int PureVirtualReplacement::otherMethod() { return 0; } + int PureVirtualReplacement_Derived::methodNew(int param) { return 0; }"; + + # Virtual_Replacement + $HEADER1 .= " + class $DECL_SPEC VirtualReplacement { + public: + virtual int methodOld(int param); + };"; + $SOURCE1 .= " + int VirtualReplacement::methodOld(int param) { return 0; }"; + + $HEADER2 .= " + class $DECL_SPEC VirtualReplacement { + public: + virtual int methodNew(int param); + };"; + $SOURCE2 .= " + int VirtualReplacement::methodNew(int param) { return 0; }"; + + # Removed_Symbol (renamed, source-compatible) + $HEADER1 .= " + int $DECL_SPEC renamedFunc(int param);"; + $SOURCE1 .= " + int renamedFunc(int param) { return 0; }"; + + $HEADER2 .= " + int $DECL_SPEC renamedFunc_NewName(int param); + #define renamedFunc renamedFunc_NewName"; + $SOURCE2 .= " + int renamedFunc_NewName(int param) { return 0; }"; + + # Removed_Symbol + $HEADER1 .= " + int $DECL_SPEC functionBecameInline(int param);"; + $SOURCE1 .= " + int functionBecameInline(int param) { return 0; }"; + + $HEADER2 .= " + inline int functionBecameInline(int param) { return 0; }"; + + # Removed_Symbol (safe) + $HEADER1 .= " + inline int removedInlineFunction(int param) { return 0; }"; + + # Became Non-Opaque + $HEADER1 .= " + struct OpaqueStruct; + int paramBecameNonOpaque(OpaqueStruct* p);"; + $SOURCE1 .= " + int paramBecameNonOpaque(OpaqueStruct* p) { return 0; }"; + + $HEADER2 .= " + struct OpaqueStruct + { + int i; + short j; + OpaqueStruct(); + }; + int paramBecameNonOpaque(OpaqueStruct* p);"; + $SOURCE2 .= " + int paramBecameNonOpaque(OpaqueStruct* p) { return 0; }"; + + # Field_Became_Const + # Typedef + $HEADER1 .= " + typedef int*const CONST_INT_PTR; + class $DECL_SPEC FieldBecameConstTypedef { + public: + int* f; + int*const f2; + int method(CONST_INT_PTR p); + };"; + $SOURCE1 .= " + int FieldBecameConstTypedef::method(CONST_INT_PTR p) { return 0; }"; + + $HEADER2 .= " + typedef int*const CONST_INT_PTR; + class $DECL_SPEC FieldBecameConstTypedef { + public: + CONST_INT_PTR f; + int*const f2; + int method(CONST_INT_PTR p); + };"; + $SOURCE2 .= " + int FieldBecameConstTypedef::method(CONST_INT_PTR p) { return 0; }"; + + # Field_Removed_Const + $HEADER1 .= " + class $DECL_SPEC FieldRemovedConst { + public: + int*const*const f; + int method(); + };"; + $SOURCE1 .= " + int FieldRemovedConst::method() { return 0; }"; + + $HEADER2 .= " + class $DECL_SPEC FieldRemovedConst { + public: + int**const f; + int method(); + };"; + $SOURCE2 .= " + int FieldRemovedConst::method() { return 0; }"; + + # Field_Became_Const + $HEADER1 .= " + class $DECL_SPEC FieldBecameConst { + public: + int* f; + int method(); + };"; + $SOURCE1 .= " + int FieldBecameConst::method() { return 0; }"; + + $HEADER2 .= " + class $DECL_SPEC FieldBecameConst { + public: + int*const f; + int method(); + };"; + $SOURCE2 .= " + int FieldBecameConst::method() { return 0; }"; + + # Field_Became_Private + $HEADER1 .= " + class $DECL_SPEC FieldBecamePrivate { + public: + int* f; + int method(); + };"; + $SOURCE1 .= " + int FieldBecamePrivate::method() { return 0; }"; + + $HEADER2 .= " + class $DECL_SPEC FieldBecamePrivate { + private: + int* f; + public: + int method(); + };"; + $SOURCE2 .= " + int FieldBecamePrivate::method() { return 0; }"; + + # Field_Became_Protected + $HEADER1 .= " + class $DECL_SPEC FieldBecameProtected { + public: + int* f; + int method(); + };"; + $SOURCE1 .= " + int FieldBecameProtected::method() { return 0; }"; + + $HEADER2 .= " + class $DECL_SPEC FieldBecameProtected { + protected: + int* f; + public: + int method(); + };"; + $SOURCE2 .= " + int FieldBecameProtected::method() { return 0; }"; + + # Global_Data_Became_Private + $HEADER1 .= " + class $DECL_SPEC GlobalDataBecamePrivate { + public: + static int data; + + };"; + $SOURCE1 .= " + int GlobalDataBecamePrivate::data = 10;"; + + $HEADER2 .= " + class $DECL_SPEC GlobalDataBecamePrivate { + private: + static int data; + + };"; + $SOURCE2 .= " + int GlobalDataBecamePrivate::data = 10;"; + + # Method_Became_Private + $HEADER1 .= " + class $DECL_SPEC MethodBecamePrivate { + public: + int method(); + };"; + $SOURCE1 .= " + int MethodBecamePrivate::method() { return 0; }"; + + $HEADER2 .= " + class $DECL_SPEC MethodBecamePrivate { + private: + int method(); + };"; + $SOURCE2 .= " + int MethodBecamePrivate::method() { return 0; }"; + + # Method_Became_Protected + $HEADER1 .= " + class $DECL_SPEC MethodBecameProtected { + public: + int method(); + };"; + $SOURCE1 .= " + int MethodBecameProtected::method() { return 0; }"; + + $HEADER2 .= " + class $DECL_SPEC MethodBecameProtected { + protected: + int method(); + };"; + $SOURCE2 .= " + int MethodBecameProtected::method() { return 0; }"; + + # Method_Became_Public + $HEADER1 .= " + class $DECL_SPEC MethodBecamePublic { + protected: + int method(); + };"; + $SOURCE1 .= " + int MethodBecamePublic::method() { return 0; }"; + + $HEADER2 .= " + class $DECL_SPEC MethodBecamePublic { + public: + int method(); + };"; + $SOURCE2 .= " + int MethodBecamePublic::method() { return 0; }"; + + # Removed_Const_Overload + $HEADER1 .= " + class $DECL_SPEC RemovedConstOverload { + public: + int removed(); + int removed() const; + };"; + $SOURCE1 .= " + int RemovedConstOverload::removed() { return 0; } + int RemovedConstOverload::removed() const { return 0; }"; + + $HEADER2 .= " + class $DECL_SPEC RemovedConstOverload { + public: + int removed(); + };"; + $SOURCE2 .= " + int RemovedConstOverload::removed() { return 0; }"; + + # Inline method + $HEADER1 .= " + class $DECL_SPEC InlineMethod { + public: + inline int foo() { return 0; } + };"; + + $HEADER2 .= " + class $DECL_SPEC InlineMethod { + public: + inline long foo() { return 0; } + };"; + + # Global_Data_Became_Non_Const + $HEADER1 .= " + $EXTERN $DECL_SPEC const int globalDataBecameNonConst = 10;"; + + $HEADER2 .= " + extern $DECL_SPEC int globalDataBecameNonConst;"; + $SOURCE2 .= " + int globalDataBecameNonConst = 15;"; + + # Global_Data_Became_Non_Const + # Class Member + $HEADER1 .= " + class $DECL_SPEC GlobalDataBecameNonConst { + public: + static const int data; + };"; + $SOURCE1 .= " + const int GlobalDataBecameNonConst::data = 10;"; + + $HEADER2 .= " + class $DECL_SPEC GlobalDataBecameNonConst { + public: + static int data; + };"; + $SOURCE2 .= " + int GlobalDataBecameNonConst::data = 10;"; + + # Global_Data_Became_Const + $HEADER1 .= " + extern $DECL_SPEC int globalDataBecameConst;"; + $SOURCE1 .= " + int globalDataBecameConst = 10;"; + + $HEADER2 .= " + $EXTERN $DECL_SPEC const int globalDataBecameConst = 15;"; + + # Global_Data_Became_Const + # Class Member + $HEADER1 .= " + class $DECL_SPEC GlobalDataBecameConst { + public: + static int Data; + };"; + $SOURCE1 .= " + int GlobalDataBecameConst::Data = 10;"; + + $HEADER2 .= " + class $DECL_SPEC GlobalDataBecameConst { + public: + static const int Data = 15; + };"; + + # Global_Data_Value_Changed + $HEADER1 .= " + class $DECL_SPEC GlobalDataValue { + public: + static const int Integer = 10; + static const char Char = \'o\'; + };"; + + $HEADER2 .= " + class $DECL_SPEC GlobalDataValue { + public: + static const int Integer = 15; + static const char Char = \'N\'; + };"; + + # Global_Data_Value_Changed + # Integer + $HEADER1 .= " + $EXTERN $DECL_SPEC const int globalDataValue_Integer = 10;"; + + $HEADER2 .= " + $EXTERN $DECL_SPEC const int globalDataValue_Integer = 15;"; + + # Global_Data_Value_Changed + # Character + $HEADER1 .= " + $EXTERN $DECL_SPEC const char globalDataValue_Char = \'o\';"; + + $HEADER2 .= " + $EXTERN $DECL_SPEC const char globalDataValue_Char = \'N\';"; + + # Parameter_Became_Restrict + $HEADER1 .= " + class $DECL_SPEC ParameterBecameRestrict { + public: + int method(int* param); + };"; + $SOURCE1 .= " + int ParameterBecameRestrict::method(int* param) { return 0; }"; + + $HEADER2 .= " + class $DECL_SPEC ParameterBecameRestrict { + public: + int method(int* __restrict param); + };"; + $SOURCE2 .= " + int ParameterBecameRestrict::method(int* __restrict param) { return 0; }"; + + # Parameter_Became_Non_Restrict + $HEADER1 .= " + class $DECL_SPEC ParameterBecameNonRestrict { + public: + int method(int* __restrict param); + };"; + $SOURCE1 .= " + int ParameterBecameNonRestrict::method(int* __restrict param) { return 0; }"; + + $HEADER2 .= " + class $DECL_SPEC ParameterBecameNonRestrict { + public: + int method(int* param); + };"; + $SOURCE2 .= " + int ParameterBecameNonRestrict::method(int* param) { return 0; }"; + + # Field_Became_Volatile + $HEADER1 .= " + class $DECL_SPEC FieldBecameVolatile { + public: + int method(int param); + int f; + };"; + $SOURCE1 .= " + int FieldBecameVolatile::method(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC FieldBecameVolatile { + public: + int method(int param); + volatile int f; + };"; + $SOURCE2 .= " + int FieldBecameVolatile::method(int param) { return param; }"; + + # Field_Became_Non_Volatile + $HEADER1 .= " + class $DECL_SPEC FieldBecameNonVolatile { + public: + int method(int param); + volatile int f; + };"; + $SOURCE1 .= " + int FieldBecameNonVolatile::method(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC FieldBecameNonVolatile { + public: + int method(int param); + int f; + };"; + $SOURCE2 .= " + int FieldBecameNonVolatile::method(int param) { return param; }"; + + # Field_Became_Mutable + $HEADER1 .= " + class $DECL_SPEC FieldBecameMutable { + public: + int method(int param); + int f; + };"; + $SOURCE1 .= " + int FieldBecameMutable::method(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC FieldBecameMutable { + public: + int method(int param); + mutable int f; + };"; + $SOURCE2 .= " + int FieldBecameMutable::method(int param) { return param; }"; + + # Field_Became_Non_Mutable + $HEADER1 .= " + class $DECL_SPEC FieldBecameNonMutable { + public: + int method(int param); + mutable int f; + };"; + $SOURCE1 .= " + int FieldBecameNonMutable::method(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC FieldBecameNonMutable { + public: + int method(int param); + int f; + };"; + $SOURCE2 .= " + int FieldBecameNonMutable::method(int param) { return param; }"; + + # Method_Became_Const + # Method_Became_Volatile + $HEADER1 .= " + class $DECL_SPEC MethodBecameConstVolatile { + public: + int method(int param); + };"; + $SOURCE1 .= " + int MethodBecameConstVolatile::method(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC MethodBecameConstVolatile { + public: + int method(int param) volatile const; + };"; + $SOURCE2 .= " + int MethodBecameConstVolatile::method(int param) volatile const { return param; }"; + + # Method_Became_Const + $HEADER1 .= " + class $DECL_SPEC MethodBecameConst { + public: + int method(int param); + };"; + $SOURCE1 .= " + int MethodBecameConst::method(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC MethodBecameConst { + public: + int method(int param) const; + };"; + $SOURCE2 .= " + int MethodBecameConst::method(int param) const { return param; }"; + + # Method_Became_Non_Const + $HEADER1 .= " + class $DECL_SPEC MethodBecameNonConst { + public: + int method(int param) const; + };"; + $SOURCE1 .= " + int MethodBecameNonConst::method(int param) const { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC MethodBecameNonConst { + public: + int method(int param); + };"; + $SOURCE2 .= " + int MethodBecameNonConst::method(int param) { return param; }"; + + # Method_Became_Volatile + $HEADER1 .= " + class $DECL_SPEC MethodBecameVolatile { + public: + int method(int param); + };"; + $SOURCE1 .= " + int MethodBecameVolatile::method(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC MethodBecameVolatile { + public: + int method(int param) volatile; + };"; + $SOURCE2 .= " + int MethodBecameVolatile::method(int param) volatile { return param; }"; + + # Virtual_Method_Position + # Multiple bases + $HEADER1 .= " + class $DECL_SPEC PrimaryBase + { + public: + virtual ~PrimaryBase(); + virtual void foo(); + }; + class $DECL_SPEC SecondaryBase + { + public: + virtual ~SecondaryBase(); + virtual void bar(); + }; + class UnsafeVirtualOverride: public PrimaryBase, public SecondaryBase + { + public: + UnsafeVirtualOverride(); + ~UnsafeVirtualOverride(); + void foo(); + };"; + $SOURCE1 .= " + PrimaryBase::~PrimaryBase() { } + void PrimaryBase::foo() { } + + SecondaryBase::~SecondaryBase() { } + void SecondaryBase::bar() { } + + UnsafeVirtualOverride::UnsafeVirtualOverride() { } + UnsafeVirtualOverride::~UnsafeVirtualOverride() { } + void UnsafeVirtualOverride::foo() { }"; + + $HEADER2 .= " + class $DECL_SPEC PrimaryBase + { + public: + virtual ~PrimaryBase(); + virtual void foo(); + }; + class $DECL_SPEC SecondaryBase + { + public: + virtual ~SecondaryBase(); + virtual void bar(); + }; + class UnsafeVirtualOverride: public PrimaryBase, public SecondaryBase + { + public: + UnsafeVirtualOverride(); + ~UnsafeVirtualOverride(); + void foo(); + void bar(); + };"; + $SOURCE2 .= " + PrimaryBase::~PrimaryBase() { } + void PrimaryBase::foo() { } + + SecondaryBase::~SecondaryBase() { } + void SecondaryBase::bar() { } + + UnsafeVirtualOverride::UnsafeVirtualOverride() { } + UnsafeVirtualOverride::~UnsafeVirtualOverride() { } + void UnsafeVirtualOverride::foo() { } + void UnsafeVirtualOverride::bar() { }"; + + # Removed_Interface (inline virtual d-tor) + $HEADER1 .= " + template + class $DECL_SPEC BaseTemplate { + public: + BaseTemplate() { } + virtual int method(int param) { return param; }; + virtual ~BaseTemplate() { }; + }; + class $DECL_SPEC RemovedVirtualDestructor: public BaseTemplate { + public: + RemovedVirtualDestructor() { }; + virtual int method2(int param); + };"; + $SOURCE1 .= " + int RemovedVirtualDestructor::method2(int param) { return param; }"; + + $HEADER2 .= " + template + class $DECL_SPEC BaseTemplate { + public: + BaseTemplate() { } + virtual int method(int param) { return param; }; + //virtual ~BaseTemplate() { }; + }; + class $DECL_SPEC RemovedVirtualDestructor: public BaseTemplate { + public: + RemovedVirtualDestructor() { }; + virtual int method2(int param); + };"; + $SOURCE2 .= " + int RemovedVirtualDestructor::method2(int param) { return param; }"; + + # Added_Virtual_Method_At_End + $HEADER1 .= " + class $DECL_SPEC DefaultConstructor { + public: + DefaultConstructor() { } + virtual int method(int param); + };"; + $SOURCE1 .= " + int DefaultConstructor::method(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC DefaultConstructor { + public: + DefaultConstructor() { } + virtual int method(int param); + virtual int addedMethod(int param); + };"; + $SOURCE2 .= " + int DefaultConstructor::method(int param) { return addedMethod(param); } + int DefaultConstructor::addedMethod(int param) { return param; }"; + + # Added_Enum_Member + $HEADER1 .= " + enum AddedEnumMember { + OldMember + }; + $DECL_SPEC int addedEnumMember(enum AddedEnumMember param);"; + $SOURCE1 .= " + int addedEnumMember(enum AddedEnumMember param) { return 0; }"; + + $HEADER2 .= " + enum AddedEnumMember { + OldMember, + NewMember + }; + $DECL_SPEC int addedEnumMember(enum AddedEnumMember param);"; + $SOURCE2 .= " + int addedEnumMember(enum AddedEnumMember param) { return 0; }"; + + # Parameter_Type_Format (Safe) + $HEADER1 .= " + struct DType + { + int i; + double j; + }; + $DECL_SPEC int parameterTypeFormat_Safe(struct DType param);"; + $SOURCE1 .= " + int parameterTypeFormat_Safe(struct DType param) { return 0; }"; + + $HEADER2 .= " + class DType + { + int i; + double j; + }; + $DECL_SPEC int parameterTypeFormat_Safe(class DType param);"; + $SOURCE2 .= " + int parameterTypeFormat_Safe(class DType param) { return 0; }"; + + # Type_Became_Opaque (Struct) + $HEADER1 .= " + struct StructBecameOpaque + { + int i, j; + }; + $DECL_SPEC int structBecameOpaque(struct StructBecameOpaque* param);"; + $SOURCE1 .= " + int structBecameOpaque(struct StructBecameOpaque* param) { return 0; }"; + + $HEADER2 .= " + struct StructBecameOpaque; + $DECL_SPEC int structBecameOpaque(struct StructBecameOpaque* param);"; + $SOURCE2 .= " + int structBecameOpaque(struct StructBecameOpaque* param) { return 0; }"; + + # Type_Became_Opaque (Union) + $HEADER1 .= " + union UnionBecameOpaque + { + int i, j; + }; + $DECL_SPEC int unionBecameOpaque(union UnionBecameOpaque* param);"; + $SOURCE1 .= " + int unionBecameOpaque(union UnionBecameOpaque* param) { return 0; }"; + + $HEADER2 .= " + union UnionBecameOpaque; + $DECL_SPEC int unionBecameOpaque(union UnionBecameOpaque* param);"; + $SOURCE2 .= " + int unionBecameOpaque(union UnionBecameOpaque* param) { return 0; }"; + + # Field_Type_Format + $HEADER1 .= " + struct DType1 + { + int i; + double j[7]; + }; + struct FieldTypeFormat + { + int i; + struct DType1 j; + }; + $DECL_SPEC int fieldTypeFormat(struct FieldTypeFormat param);"; + $SOURCE1 .= " + int fieldTypeFormat(struct FieldTypeFormat param) { return 0; }"; + + $HEADER2 .= " + struct DType2 + { + double i[7]; + int j; + }; + struct FieldTypeFormat + { + int i; + struct DType2 j; + }; + $DECL_SPEC int fieldTypeFormat(struct FieldTypeFormat param);"; + $SOURCE2 .= " + int fieldTypeFormat(struct FieldTypeFormat param) { return 0; }"; + + # Field_Type_Format (func ptr) + $HEADER1 .= " + typedef void (*FuncPtr_Old) (int a); + struct FieldTypeFormat_FuncPtr + { + int i; + FuncPtr_Old j; + }; + $DECL_SPEC int fieldTypeFormat_FuncPtr(struct FieldTypeFormat_FuncPtr param);"; + $SOURCE1 .= " + int fieldTypeFormat_FuncPtr(struct FieldTypeFormat_FuncPtr param) { return 0; }"; + + $HEADER2 .= " + typedef void (*FuncPtr_New) (int a, int b); + struct FieldTypeFormat_FuncPtr + { + int i; + FuncPtr_New j; + }; + $DECL_SPEC int fieldTypeFormat_FuncPtr(struct FieldTypeFormat_FuncPtr param);"; + $SOURCE2 .= " + int fieldTypeFormat_FuncPtr(struct FieldTypeFormat_FuncPtr param) { return 0; }"; + + # Removed_Virtual_Method (inline) + $HEADER1 .= " + class $DECL_SPEC RemovedInlineVirtualFunction { + public: + RemovedInlineVirtualFunction(); + virtual int removedMethod(int param) { return 0; } + virtual int method(int param); + };"; + $SOURCE1 .= " + int RemovedInlineVirtualFunction::method(int param) { return param; } + RemovedInlineVirtualFunction::RemovedInlineVirtualFunction() { }"; + + $HEADER2 .= " + class $DECL_SPEC RemovedInlineVirtualFunction { + public: + RemovedInlineVirtualFunction(); + virtual int method(int param); + };"; + $SOURCE2 .= " + int RemovedInlineVirtualFunction::method(int param) { return param; } + RemovedInlineVirtualFunction::RemovedInlineVirtualFunction() { }"; + + # MethodPtr + $HEADER1 .= " + class TestMethodPtr { + public: + typedef void (TestMethodPtr::*Method)(int*); + Method _method; + TestMethodPtr(); + void method(); + };"; + $SOURCE1 .= " + TestMethodPtr::TestMethodPtr() { } + void TestMethodPtr::method() { }"; + + $HEADER2 .= " + class TestMethodPtr { + public: + typedef void (TestMethodPtr::*Method)(int*, void*); + Method _method; + TestMethodPtr(); + void method(); + };"; + $SOURCE2 .= " + TestMethodPtr::TestMethodPtr() { } + void TestMethodPtr::method() { }"; + + # FieldPtr + $HEADER1 .= " + class TestFieldPtr { + public: + typedef void* (TestFieldPtr::*Field); + Field _field; + TestFieldPtr(); + void method(void*); + };"; + $SOURCE1 .= " + TestFieldPtr::TestFieldPtr(){ } + void TestFieldPtr::method(void*) { }"; + + $HEADER2 .= " + class TestFieldPtr { + public: + typedef int (TestFieldPtr::*Field); + Field _field; + TestFieldPtr(); + void method(void*); + };"; + $SOURCE2 .= " + TestFieldPtr::TestFieldPtr(){ } + void TestFieldPtr::method(void*) { }"; + + # Removed_Symbol (Template Specializations) + $HEADER1 .= " + template + class Template { + public: + char const *field; + }; + template + class TestRemovedTemplate { + public: + char const *field; + void method(int); + }; + template <> + class TestRemovedTemplate<7, char> { + public: + char const *field; + void method(int); + };"; + $SOURCE1 .= " + void TestRemovedTemplate<7, char>::method(int){ }"; + + # Removed_Symbol (Template Specializations) + $HEADER1 .= " + template + int removedTemplateSpec(TName); + + template <> int removedTemplateSpec(char);"; + $SOURCE1 .= " + template <> int removedTemplateSpec(char){return 0;}"; + + # Removed_Field (Ref) + $HEADER1 .= " + struct TestRefChange { + int a, b, c; + }; + $DECL_SPEC int paramRefChange(const TestRefChange & p1, int p2);"; + $SOURCE1 .= " + int paramRefChange(const TestRefChange & p1, int p2) { return p2; }"; + + $HEADER2 .= " + struct TestRefChange { + int a, b; + }; + $DECL_SPEC int paramRefChange(const TestRefChange & p1, int p2);"; + $SOURCE2 .= " + int paramRefChange(const TestRefChange & p1, int p2) { return p2; }"; + + # Removed_Parameter + $HEADER1 .= " + $DECL_SPEC int removedParameter(int param, int removed_param);"; + $SOURCE1 .= " + int removedParameter(int param, int removed_param) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC int removedParameter(int param);"; + $SOURCE2 .= " + int removedParameter(int param) { return 0; }"; + + # Added_Parameter + $HEADER1 .= " + $DECL_SPEC int addedParameter(int param);"; + $SOURCE1 .= " + int addedParameter(int param) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC int addedParameter(int param, int added_param);"; + $SOURCE2 .= " + int addedParameter(int param, int added_param) { return 0; }"; + + # Added + $HEADER2 .= " + typedef int (*FUNCPTR_TYPE)(int a, int b); + $DECL_SPEC int addedFunc(FUNCPTR_TYPE*const** f);"; + $SOURCE2 .= " + int addedFunc(FUNCPTR_TYPE*const** f) { return 0; }"; + + # Added (3) + $HEADER2 .= " + struct DStruct + { + int i, j, k; + }; + int addedFunc3(struct DStruct* p);"; + $SOURCE2 .= " + int addedFunc3(struct DStruct* p) { return 0; }"; + + # Added_Virtual_Method + $HEADER1 .= " + class $DECL_SPEC AddedVirtualMethod { + public: + virtual int method(int param); + };"; + $SOURCE1 .= " + int AddedVirtualMethod::method(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC AddedVirtualMethod { + public: + virtual int addedMethod(int param); + virtual int method(int param); + };"; + $SOURCE2 .= " + int AddedVirtualMethod::addedMethod(int param) { + return param; + } + int AddedVirtualMethod::method(int param) { return param; }"; + + # Added_Virtual_Method (added "virtual" attribute) + $HEADER1 .= " + class $DECL_SPEC BecameVirtualMethod { + public: + int becameVirtual(int param); + virtual int method(int param); + };"; + $SOURCE1 .= " + int BecameVirtualMethod::becameVirtual(int param) { return param; } + int BecameVirtualMethod::method(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC BecameVirtualMethod { + public: + virtual int becameVirtual(int param); + virtual int method(int param); + };"; + $SOURCE2 .= " + int BecameVirtualMethod::becameVirtual(int param) { return param; } + int BecameVirtualMethod::method(int param) { return param; }"; + + # Added_Pure_Virtual_Method + $HEADER1 .= " + class $DECL_SPEC AddedPureVirtualMethod { + public: + virtual int method(int param); + int otherMethod(int param); + };"; + $SOURCE1 .= " + int AddedPureVirtualMethod::method(int param) { return param; } + int AddedPureVirtualMethod::otherMethod(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC AddedPureVirtualMethod { + public: + virtual int addedMethod(int param)=0; + virtual int method(int param); + int otherMethod(int param); + };"; + $SOURCE2 .= " + int AddedPureVirtualMethod::method(int param) { return param; } + int AddedPureVirtualMethod::otherMethod(int param) { return param; }"; + + # Added_Virtual_Method_At_End (Safe) + $HEADER1 .= " + class $DECL_SPEC AddedVirtualMethodAtEnd { + public: + AddedVirtualMethodAtEnd(); + int method1(int param); + virtual int method2(int param); + };"; + $SOURCE1 .= " + AddedVirtualMethodAtEnd::AddedVirtualMethodAtEnd() { } + int AddedVirtualMethodAtEnd::method1(int param) { return param; } + int AddedVirtualMethodAtEnd::method2(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC AddedVirtualMethodAtEnd { + public: + AddedVirtualMethodAtEnd(); + int method1(int param); + virtual int method2(int param); + virtual int addedMethod(int param); + };"; + $SOURCE2 .= " + AddedVirtualMethodAtEnd::AddedVirtualMethodAtEnd() { } + int AddedVirtualMethodAtEnd::method1(int param) { return param; } + int AddedVirtualMethodAtEnd::method2(int param) { return param; } + int AddedVirtualMethodAtEnd::addedMethod(int param) { return param; }"; + + # Added_Virtual_Method_At_End (With Default Constructor) + $HEADER1 .= " + class $DECL_SPEC AddedVirtualMethodAtEnd_DefaultConstructor { + public: + int method1(int param); + virtual int method2(int param); + };"; + $SOURCE1 .= " + int AddedVirtualMethodAtEnd_DefaultConstructor::method1(int param) { return param; } + int AddedVirtualMethodAtEnd_DefaultConstructor::method2(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC AddedVirtualMethodAtEnd_DefaultConstructor { + public: + int method1(int param); + virtual int method2(int param); + virtual int addedMethod(int param); + };"; + $SOURCE2 .= " + int AddedVirtualMethodAtEnd_DefaultConstructor::method1(int param) { return param; } + int AddedVirtualMethodAtEnd_DefaultConstructor::method2(int param) { return param; } + int AddedVirtualMethodAtEnd_DefaultConstructor::addedMethod(int param) { return param; }"; + + # Added_First_Virtual_Method + $HEADER1 .= " + class $DECL_SPEC AddedFirstVirtualMethod { + public: + int method(int param); + };"; + $SOURCE1 .= " + int AddedFirstVirtualMethod::method(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC AddedFirstVirtualMethod { + public: + int method(int param); + virtual int addedMethod(int param); + };"; + $SOURCE2 .= " + int AddedFirstVirtualMethod::method(int param) { return param; } + int AddedFirstVirtualMethod::addedMethod(int param) { return param; }"; + + # Removed_Virtual_Method + $HEADER1 .= " + class $DECL_SPEC RemovedVirtualFunction { + public: + int a, b, c; + virtual int removedMethod(int param); + virtual int vMethod(int param); + };"; + $SOURCE1 .= " + int RemovedVirtualFunction::removedMethod(int param) { return param; } + int RemovedVirtualFunction::vMethod(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC RemovedVirtualFunction { + public: + int a, b, c; + int removedMethod(int param); + virtual int vMethod(int param); + };"; + $SOURCE2 .= " + int RemovedVirtualFunction::removedMethod(int param) { return param; } + int RemovedVirtualFunction::vMethod(int param) { return param; }"; + + # Removed_Virtual_Method (Pure, From the End) + $HEADER1 .= " + class $DECL_SPEC RemovedPureVirtualMethodFromEnd { + public: + virtual int method(int param); + virtual int removedMethod(int param)=0; + };"; + $SOURCE1 .= " + int RemovedPureVirtualMethodFromEnd::method(int param) { return param; } + int RemovedPureVirtualMethodFromEnd::removedMethod(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC RemovedPureVirtualMethodFromEnd + { + public: + virtual int method(int param); + int removedMethod(int param); + };"; + $SOURCE2 .= " + int RemovedPureVirtualMethodFromEnd::method(int param) { return param; } + int RemovedPureVirtualMethodFromEnd::removedMethod(int param) { return param; }"; + + # Removed_Symbol (Pure with Implementation) + $HEADER1 .= " + class $DECL_SPEC RemovedPureSymbol { + public: + virtual int method(int param); + virtual int removedMethod(int param)=0; + };"; + $SOURCE1 .= " + int RemovedPureSymbol::method(int param) { return param; } + int RemovedPureSymbol::removedMethod(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC RemovedPureSymbol + { + public: + virtual int method(int param); + };"; + $SOURCE2 .= " + int RemovedPureSymbol::method(int param) { return param; }"; + + # Removed_Virtual_Method (From the End) + $HEADER1 .= " + class $DECL_SPEC RemovedVirtualMethodFromEnd { + public: + virtual int method(int param); + virtual int removedMethod(int param); + };"; + $SOURCE1 .= " + int RemovedVirtualMethodFromEnd::method(int param) { return param; } + int RemovedVirtualMethodFromEnd::removedMethod(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC RemovedVirtualMethodFromEnd + { + public: + virtual int method(int param); + int removedMethod(int param); + };"; + $SOURCE2 .= " + int RemovedVirtualMethodFromEnd::method(int param) { return param; } + int RemovedVirtualMethodFromEnd::removedMethod(int param) { return param; }"; + + # Removed_Virtual_Method + $HEADER1 .= " + class $DECL_SPEC RemovedVirtualSymbol { + public: + virtual int method(int param); + virtual int removedMethod(int param); + };"; + $SOURCE1 .= " + int RemovedVirtualSymbol::method(int param) { return param; } + int RemovedVirtualSymbol::removedMethod(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC RemovedVirtualSymbol + { + public: + virtual int method(int param); + };"; + $SOURCE2 .= " + int RemovedVirtualSymbol::method(int param) { return param; }"; + + # Removed_Virtual_Method (Private) + $HEADER1 .= " + class $DECL_SPEC RemovedPrivateVirtualSymbol { + public: + virtual int method(int param); + private: + virtual int removedMethod(int param); + };"; + $SOURCE1 .= " + int RemovedPrivateVirtualSymbol::method(int param) { return param; } + int RemovedPrivateVirtualSymbol::removedMethod(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC RemovedPrivateVirtualSymbol + { + public: + virtual int method(int param); + };"; + $SOURCE2 .= " + int RemovedPrivateVirtualSymbol::method(int param) { return param; }"; + + # Added_Virtual_Method (Private) + $HEADER1 .= " + class $DECL_SPEC AddedPrivateVirtualSymbol + { + public: + AddedPrivateVirtualSymbol(); + virtual int method(int param); + }; + + class $DECL_SPEC AddedPrivateVirtualSymbol_Derived: public AddedPrivateVirtualSymbol + { + public: + virtual int method1(int param); + };"; + $SOURCE1 .= " + AddedPrivateVirtualSymbol::AddedPrivateVirtualSymbol() {}; + int AddedPrivateVirtualSymbol::method(int param) { return param; } + int AddedPrivateVirtualSymbol_Derived::method1(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC AddedPrivateVirtualSymbol { + public: + AddedPrivateVirtualSymbol(); + virtual int method(int param); + private: + virtual int addedMethod(int param); + }; + + class $DECL_SPEC AddedPrivateVirtualSymbol_Derived: public AddedPrivateVirtualSymbol + { + public: + virtual int method1(int param); + };"; + $SOURCE2 .= " + AddedPrivateVirtualSymbol::AddedPrivateVirtualSymbol() {}; + int AddedPrivateVirtualSymbol::method(int param) { return param; } + int AddedPrivateVirtualSymbol::addedMethod(int param) { return param; } + int AddedPrivateVirtualSymbol_Derived::method1(int param) { return param; }"; + + # Removed and added virtual method + $HEADER1 .= " + class $DECL_SPEC RemovedAddedVirtualSymbol { + public: + virtual int removedMethod(int param); + };"; + $SOURCE1 .= " + int RemovedAddedVirtualSymbol::removedMethod(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC RemovedAddedVirtualSymbol + { + public: + virtual int addedMethod(int param); + };"; + $SOURCE2 .= " + int RemovedAddedVirtualSymbol::addedMethod(int param) { return param; }"; + + # Removed_Last_Virtual_Method + $HEADER1 .= " + class $DECL_SPEC RemovedLastVirtualMethod + { + public: + int method(int param); + virtual int removedMethod(int param); + };"; + $SOURCE1 .= " + int RemovedLastVirtualMethod::method(int param) { return param; }"; + $SOURCE1 .= " + int RemovedLastVirtualMethod::removedMethod(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC RemovedLastVirtualMethod + { + public: + int method(int param); + int removedMethod(int param); + };"; + $SOURCE2 .= " + int RemovedLastVirtualMethod::method(int param) { return param; }"; + $SOURCE2 .= " + int RemovedLastVirtualMethod::removedMethod(int param) { return param; }"; + + # Removed_Last_Virtual_Method + $HEADER1 .= " + class $DECL_SPEC RemovedLastVirtualSymbol + { + public: + int method(int param); + virtual int removedMethod(int param); + };"; + $SOURCE1 .= " + int RemovedLastVirtualSymbol::method(int param) { return param; }"; + $SOURCE1 .= " + int RemovedLastVirtualSymbol::removedMethod(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC RemovedLastVirtualSymbol + { + public: + int method(int param); + };"; + $SOURCE2 .= " + int RemovedLastVirtualSymbol::method(int param) { return param; }"; + + # Virtual_Table_Size + $HEADER1 .= " + class $DECL_SPEC VirtualTableSize + { + public: + virtual int method1(int param); + virtual int method2(int param); + }; + class $DECL_SPEC VirtualTableSize_SubClass: public VirtualTableSize + { + public: + virtual int method3(int param); + virtual int method4(int param); + };"; + $SOURCE1 .= " + int VirtualTableSize::method1(int param) { return param; } + int VirtualTableSize::method2(int param) { return param; } + int VirtualTableSize_SubClass::method3(int param) { return param; } + int VirtualTableSize_SubClass::method4(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC VirtualTableSize + { + public: + virtual int method1(int param); + virtual int method2(int param); + virtual int addedMethod(int param); + }; + class $DECL_SPEC VirtualTableSize_SubClass: public VirtualTableSize + { + public: + virtual int method3(int param); + virtual int method4(int param); + };"; + $SOURCE2 .= " + int VirtualTableSize::method1(int param) { return param; } + int VirtualTableSize::method2(int param) { return param; } + int VirtualTableSize::addedMethod(int param) { return param; } + int VirtualTableSize_SubClass::method3(int param) { return param; } + int VirtualTableSize_SubClass::method4(int param) { return param; }"; + + # Virtual_Method_Position + $HEADER1 .= " + class $DECL_SPEC VirtualMethodPosition + { + public: + virtual int method1(int param); + virtual int method2(int param); + };"; + $SOURCE1 .= " + int VirtualMethodPosition::method1(int param) { return param; }"; + $SOURCE1 .= " + int VirtualMethodPosition::method2(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC VirtualMethodPosition + { + public: + virtual int method2(int param); + virtual int method1(int param); + };"; + $SOURCE2 .= " + int VirtualMethodPosition::method1(int param) { return param; }"; + $SOURCE2 .= " + int VirtualMethodPosition::method2(int param) { return param; }"; + + # Pure_Virtual_Method_Position + $HEADER1 .= " + class $DECL_SPEC PureVirtualFunctionPosition { + public: + virtual int method1(int param)=0; + virtual int method2(int param)=0; + int method3(int param); + };"; + $SOURCE1 .= " + int PureVirtualFunctionPosition::method3(int param) { return method1(7)+method2(7); }"; + + $HEADER2 .= " + class $DECL_SPEC PureVirtualFunctionPosition { + public: + virtual int method2(int param)=0; + virtual int method1(int param)=0; + int method3(int param); + };"; + $SOURCE2 .= " + int PureVirtualFunctionPosition::method3(int param) { return method1(7)+method2(7); }"; + + # Virtual_Method_Position + $HEADER1 .= " + class $DECL_SPEC VirtualFunctionPosition { + public: + virtual int method1(int param); + virtual int method2(int param); + };"; + $SOURCE1 .= " + int VirtualFunctionPosition::method1(int param) { return 1; } + int VirtualFunctionPosition::method2(int param) { return 2; }"; + + $HEADER2 .= " + class $DECL_SPEC VirtualFunctionPosition { + public: + virtual int method2(int param); + virtual int method1(int param); + };"; + $SOURCE2 .= " + int VirtualFunctionPosition::method1(int param) { return 1; } + int VirtualFunctionPosition::method2(int param) { return 2; }"; + + # Virtual_Method_Position (safe) + $HEADER1 .= " + class $DECL_SPEC VirtualFunctionPositionSafe_Base { + public: + virtual int method1(int param); + virtual int method2(int param); + }; + class $DECL_SPEC VirtualFunctionPositionSafe: public VirtualFunctionPositionSafe_Base { + public: + virtual int method1(int param); + virtual int method2(int param); + };"; + $SOURCE1 .= " + int VirtualFunctionPositionSafe_Base::method1(int param) { return param; } + int VirtualFunctionPositionSafe_Base::method2(int param) { return param; } + int VirtualFunctionPositionSafe::method1(int param) { return param; } + int VirtualFunctionPositionSafe::method2(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC VirtualFunctionPositionSafe_Base { + public: + virtual int method1(int param); + virtual int method2(int param); + }; + class $DECL_SPEC VirtualFunctionPositionSafe: public VirtualFunctionPositionSafe_Base { + public: + virtual int method2(int param); + virtual int method1(int param); + };"; + $SOURCE2 .= " + int VirtualFunctionPositionSafe_Base::method1(int param) { return param; } + int VirtualFunctionPositionSafe_Base::method2(int param) { return param; } + int VirtualFunctionPositionSafe::method1(int param) { return param; } + int VirtualFunctionPositionSafe::method2(int param) { return param; }"; + + # Overridden_Virtual_Method + $HEADER1 .= " + class $DECL_SPEC OverriddenVirtualMethod_Base { + public: + virtual int method1(int param); + virtual int method2(int param); + }; + class $DECL_SPEC OverriddenVirtualMethod: public OverriddenVirtualMethod_Base { + public: + OverriddenVirtualMethod(); + virtual int method3(int param); + };"; + $SOURCE1 .= " + int OverriddenVirtualMethod_Base::method1(int param) { return param; } + int OverriddenVirtualMethod_Base::method2(int param) { return param; } + OverriddenVirtualMethod::OverriddenVirtualMethod() {} + int OverriddenVirtualMethod::method3(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC OverriddenVirtualMethod_Base { + public: + virtual int method1(int param); + virtual int method2(int param); + }; + class $DECL_SPEC OverriddenVirtualMethod:public OverriddenVirtualMethod_Base { + OverriddenVirtualMethod(); + virtual int method2(int param); + virtual int method3(int param); + };"; + $SOURCE2 .= " + int OverriddenVirtualMethod_Base::method1(int param) { return param; } + int OverriddenVirtualMethod_Base::method2(int param) { return param; } + OverriddenVirtualMethod::OverriddenVirtualMethod() {} + int OverriddenVirtualMethod::method2(int param) { return param; } + int OverriddenVirtualMethod::method3(int param) { return param; }"; + + # Overridden_Virtual_Method_B (+ removed) + $HEADER1 .= " + + class $DECL_SPEC OverriddenVirtualMethodB: public OverriddenVirtualMethod_Base { + public: + OverriddenVirtualMethodB(); + virtual int method2(int param); + virtual int method3(int param); + };"; + $SOURCE1 .= " + OverriddenVirtualMethodB::OverriddenVirtualMethodB() {} + int OverriddenVirtualMethodB::method2(int param) { return param; } + int OverriddenVirtualMethodB::method3(int param) { return param; }"; + + $HEADER2 .= " + + class $DECL_SPEC OverriddenVirtualMethodB:public OverriddenVirtualMethod_Base { + public: + OverriddenVirtualMethodB(); + virtual int method3(int param); + };"; + $SOURCE2 .= " + OverriddenVirtualMethodB::OverriddenVirtualMethodB() {} + int OverriddenVirtualMethodB::method3(int param) { return param; }"; + + # Size + $HEADER1 .= " + struct $DECL_SPEC TypeSize + { + public: + TypeSize method(TypeSize param); + int i[5]; + long j; + double k; + TypeSize* p; + };"; + $SOURCE1 .= " + TypeSize TypeSize::method(TypeSize param) { return param; }"; + + $HEADER2 .= " + struct $DECL_SPEC TypeSize + { + public: + TypeSize method(TypeSize param); + int i[15]; + long j; + double k; + TypeSize* p; + int added_member; + };"; + $SOURCE2 .= " + TypeSize TypeSize::method(TypeSize param) { return param; }"; + + # Size_Of_Allocable_Class_Increased + $HEADER1 .= " + class $DECL_SPEC AllocableClassSize + { + public: + AllocableClassSize(); + int method(); + double p[5]; + };"; + $SOURCE1 .= " + AllocableClassSize::AllocableClassSize() { }"; + $SOURCE1 .= " + int AllocableClassSize::method() { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC AllocableClassSize + { + public: + AllocableClassSize(); + int method(); + double p[15]; + };"; + $SOURCE2 .= " + AllocableClassSize::AllocableClassSize() { }"; + $SOURCE2 .= " + int AllocableClassSize::method() { return 0; }"; + + # Size_Of_Allocable_Class_Decreased (decreased size, has derived class, has public members) + $HEADER1 .= " + class $DECL_SPEC DecreasedClassSize + { + public: + DecreasedClassSize(); + int method(); + double p[15]; + };"; + $SOURCE1 .= " + DecreasedClassSize::DecreasedClassSize() { }"; + $SOURCE1 .= " + int DecreasedClassSize::method() { return 0; }"; + $HEADER1 .= " + class $DECL_SPEC DecreasedClassSize_SubClass: public DecreasedClassSize + { + public: + DecreasedClassSize_SubClass(); + int method(); + int f; + };"; + $SOURCE1 .= " + DecreasedClassSize_SubClass::DecreasedClassSize_SubClass() { f=7; }"; + $SOURCE1 .= " + int DecreasedClassSize_SubClass::method() { return f; }"; + + $HEADER2 .= " + struct $DECL_SPEC DecreasedClassSize + { + public: + DecreasedClassSize(); + int method(); + double p[5]; + };"; + $SOURCE2 .= " + DecreasedClassSize::DecreasedClassSize() { }"; + $SOURCE2 .= " + int DecreasedClassSize::method() { return 0; }"; + $HEADER2 .= " + class $DECL_SPEC DecreasedClassSize_SubClass: public DecreasedClassSize + { + public: + DecreasedClassSize_SubClass(); + int method(); + int f; + };"; + $SOURCE2 .= " + DecreasedClassSize_SubClass::DecreasedClassSize_SubClass() { f=7; }"; + $SOURCE2 .= " + int DecreasedClassSize_SubClass::method() { return f; }"; + + # Size_Of_Copying_Class + $HEADER1 .= " + class $DECL_SPEC CopyingClassSize + { + public: + int method(); + int p[5]; + };"; + $SOURCE1 .= " + int CopyingClassSize::method() { return p[4]; }"; + + $HEADER2 .= " + struct $DECL_SPEC CopyingClassSize + { + public: + int method(); + int p[15]; + };"; + $SOURCE2 .= " + int CopyingClassSize::method() { return p[10]; }"; + + # Base_Class_Became_Virtually_Inherited + $HEADER1 .= " + class $DECL_SPEC BecameVirtualBase + { + public: + BecameVirtualBase(); + int method(); + double p[5]; + };"; + $SOURCE1 .= " + BecameVirtualBase::BecameVirtualBase() { }"; + $SOURCE1 .= " + int BecameVirtualBase::method() { return 0; }"; + $HEADER1 .= " + class $DECL_SPEC AddedVirtualBase1:public BecameVirtualBase + { + public: + AddedVirtualBase1(); + int method(); + };"; + $SOURCE1 .= " + AddedVirtualBase1::AddedVirtualBase1() { }"; + $SOURCE1 .= " + int AddedVirtualBase1::method() { return 0; }"; + $HEADER1 .= " + class $DECL_SPEC AddedVirtualBase2: public BecameVirtualBase + { + public: + AddedVirtualBase2(); + int method(); + };"; + $SOURCE1 .= " + AddedVirtualBase2::AddedVirtualBase2() { }"; + $SOURCE1 .= " + int AddedVirtualBase2::method() { return 0; }"; + $HEADER1 .= " + class $DECL_SPEC BaseClassBecameVirtuallyInherited:public AddedVirtualBase1, public AddedVirtualBase2 + { + public: + BaseClassBecameVirtuallyInherited(); + };"; + $SOURCE1 .= " + BaseClassBecameVirtuallyInherited::BaseClassBecameVirtuallyInherited() { }"; + + $HEADER2 .= " + class $DECL_SPEC BecameVirtualBase + { + public: + BecameVirtualBase(); + int method(); + double p[5]; + };"; + $SOURCE2 .= " + BecameVirtualBase::BecameVirtualBase() { }"; + $SOURCE2 .= " + int BecameVirtualBase::method() { return 0; }"; + $HEADER2 .= " + class $DECL_SPEC AddedVirtualBase1:public virtual BecameVirtualBase + { + public: + AddedVirtualBase1(); + int method(); + };"; + $SOURCE2 .= " + AddedVirtualBase1::AddedVirtualBase1() { }"; + $SOURCE2 .= " + int AddedVirtualBase1::method() { return 0; }"; + $HEADER2 .= " + class $DECL_SPEC AddedVirtualBase2: public virtual BecameVirtualBase + { + public: + AddedVirtualBase2(); + int method(); + };"; + $SOURCE2 .= " + AddedVirtualBase2::AddedVirtualBase2() { }"; + $SOURCE2 .= " + int AddedVirtualBase2::method() { return 0; }"; + $HEADER2 .= " + class $DECL_SPEC BaseClassBecameVirtuallyInherited:public AddedVirtualBase1, public AddedVirtualBase2 + { + public: + BaseClassBecameVirtuallyInherited(); + };"; + $SOURCE2 .= " + BaseClassBecameVirtuallyInherited::BaseClassBecameVirtuallyInherited() { }"; + + # Added_Base_Class, Removed_Base_Class + $HEADER1 .= " + class $DECL_SPEC BaseClass + { + public: + BaseClass(); + int method(); + double p[5]; + }; + class $DECL_SPEC RemovedBaseClass + { + public: + RemovedBaseClass(); + int method(); + }; + class $DECL_SPEC ChangedBaseClass:public BaseClass, public RemovedBaseClass + { + public: + ChangedBaseClass(); + };"; + $SOURCE1 .= " + BaseClass::BaseClass() { } + int BaseClass::method() { return 0; } + RemovedBaseClass::RemovedBaseClass() { } + int RemovedBaseClass::method() { return 0; } + ChangedBaseClass::ChangedBaseClass() { }"; + + $HEADER2 .= " + class $DECL_SPEC BaseClass + { + public: + BaseClass(); + int method(); + double p[5]; + }; + class $DECL_SPEC AddedBaseClass + { + public: + AddedBaseClass(); + int method(); + }; + class $DECL_SPEC ChangedBaseClass:public BaseClass, public AddedBaseClass + { + public: + ChangedBaseClass(); + };"; + $SOURCE2 .= " + BaseClass::BaseClass() { } + int BaseClass::method() { return 0; } + AddedBaseClass::AddedBaseClass() { } + int AddedBaseClass::method() { return 0; } + ChangedBaseClass::ChangedBaseClass() { }"; + + # Added_Base_Class_And_Shift, Removed_Base_Class_And_Shift + $HEADER1 .= " + struct $DECL_SPEC BaseClass2 + { + BaseClass2(); + int method(); + double p[15]; + }; + class $DECL_SPEC ChangedBaseClassAndSize:public BaseClass + { + public: + ChangedBaseClassAndSize(); + };"; + $SOURCE1 .= " + BaseClass2::BaseClass2() { } + int BaseClass2::method() { return 0; } + ChangedBaseClassAndSize::ChangedBaseClassAndSize() { }"; + + $HEADER2 .= " + struct $DECL_SPEC BaseClass2 + { + BaseClass2(); + int method(); + double p[15]; + }; + class $DECL_SPEC ChangedBaseClassAndSize:public BaseClass2 + { + public: + ChangedBaseClassAndSize(); + };"; + $SOURCE2 .= " + BaseClass2::BaseClass2() { } + int BaseClass2::method() { return 0; } + ChangedBaseClassAndSize::ChangedBaseClassAndSize() { }"; + + # Added_Field_And_Size + $HEADER1 .= " + struct $DECL_SPEC AddedFieldAndSize + { + int method(AddedFieldAndSize param); + double i, j, k; + AddedFieldAndSize* p; + };"; + $SOURCE1 .= " + int AddedFieldAndSize::method(AddedFieldAndSize param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC AddedFieldAndSize + { + int method(AddedFieldAndSize param); + double i, j, k; + AddedFieldAndSize* p; + int added_member1; + long long added_member2; + };"; + $SOURCE2 .= " + int AddedFieldAndSize::method(AddedFieldAndSize param) { return 0; }"; + + # Added_Field + $HEADER1 .= " + class $DECL_SPEC ObjectAddedMember + { + public: + int method(int param); + double i, j, k; + AddedFieldAndSize* p; + };"; + $SOURCE1 .= " + int ObjectAddedMember::method(int param) { return param; }"; + + $HEADER2 .= " + class $DECL_SPEC ObjectAddedMember + { + public: + int method(int param); + double i, j, k; + AddedFieldAndSize* p; + int added_member1; + long long added_member2; + };"; + $SOURCE2 .= " + int ObjectAddedMember::method(int param) { return param; }"; + + # Added_Field (safe) + $HEADER1 .= " + struct $DECL_SPEC AddedBitfield + { + int method(AddedBitfield param); + double i, j, k; + int b1 : 32; + int b2 : 31; + AddedBitfield* p; + };"; + $SOURCE1 .= " + int AddedBitfield::method(AddedBitfield param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC AddedBitfield + { + int method(AddedBitfield param); + double i, j, k; + int b1 : 32; + int b2 : 31; + int added_bitfield : 1; + int added_bitfield2 : 1; + AddedBitfield* p; + };"; + $SOURCE2 .= " + int AddedBitfield::method(AddedBitfield param) { return 0; }"; + + # Bit_Field_Size + $HEADER1 .= " + struct $DECL_SPEC BitfieldSize + { + int method(BitfieldSize param); + short changed_bitfield : 1; + };"; + $SOURCE1 .= " + int BitfieldSize::method(BitfieldSize param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC BitfieldSize + { + int method(BitfieldSize param); + short changed_bitfield : 7; + };"; + $SOURCE2 .= " + int BitfieldSize::method(BitfieldSize param) { return 0; }"; + + # Removed_Field + $HEADER1 .= " + struct $DECL_SPEC RemovedBitfield + { + int method(RemovedBitfield param); + double i, j, k; + int b1 : 32; + int b2 : 31; + int removed_bitfield : 1; + RemovedBitfield* p; + };"; + $SOURCE1 .= " + int RemovedBitfield::method(RemovedBitfield param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC RemovedBitfield + { + int method(RemovedBitfield param); + double i, j, k; + int b1 : 32; + int b2 : 31; + RemovedBitfield* p; + };"; + $SOURCE2 .= " + int RemovedBitfield::method(RemovedBitfield param) { return 0; }"; + + # Removed_Middle_Field + $HEADER1 .= " + struct $DECL_SPEC RemovedMiddleBitfield + { + int method(RemovedMiddleBitfield param); + double i, j, k; + int b1 : 32; + int removed_middle_bitfield : 1; + int b2 : 31; + RemovedMiddleBitfield* p; + };"; + $SOURCE1 .= " + int RemovedMiddleBitfield::method(RemovedMiddleBitfield param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC RemovedMiddleBitfield + { + int method(RemovedMiddleBitfield param); + double i, j, k; + int b1 : 32; + int b2 : 31; + RemovedMiddleBitfield* p; + };"; + $SOURCE2 .= " + int RemovedMiddleBitfield::method(RemovedMiddleBitfield param) { return 0; }"; + + # Added_Middle_Field_And_Size + $HEADER1 .= " + struct $DECL_SPEC AddedMiddleFieldAndSize + { + int method(AddedMiddleFieldAndSize param); + int i; + long j; + double k; + AddedMiddleFieldAndSize* p; + };"; + $SOURCE1 .= " + int AddedMiddleFieldAndSize::method(AddedMiddleFieldAndSize param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC AddedMiddleFieldAndSize + { + int method(AddedMiddleFieldAndSize param); + int i; + int added_middle_member; + long j; + double k; + AddedMiddleFieldAndSize* p; + };"; + $SOURCE2 .= " + int AddedMiddleFieldAndSize::method(AddedMiddleFieldAndSize param) { return 0; }"; + + # Added_Field (padding) + $HEADER1 .= " + struct $DECL_SPEC AddedMiddlePaddedField + { + int method(int param); + short i; + long j; + double k; + };"; + $SOURCE1 .= " + int AddedMiddlePaddedField::method(int param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC AddedMiddlePaddedField + { + int method(int param); + short i; + short added_padded_field; + long j; + double k; + };"; + $SOURCE2 .= " + int AddedMiddlePaddedField::method(int param) { return 0; }"; + + # Added_Field (tail padding) + $HEADER1 .= " + struct $DECL_SPEC AddedTailField + { + int method(int param); + int i1, i2, i3, i4, i5, i6, i7; + short s; + };"; + $SOURCE1 .= " + int AddedTailField::method(int param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC AddedTailField + { + int method(int param); + int i1, i2, i3, i4, i5, i6, i7; + short s; + short added_tail_field; + };"; + $SOURCE2 .= " + int AddedTailField::method(int param) { return 0; }"; + + # Test Alignment + $HEADER1 .= " + struct $DECL_SPEC TestAlignment + { + int method(int param); + short s:9; + short j:9; + char c; + short t:9; + short u:9; + char d; + };"; + $SOURCE1 .= " + int TestAlignment::method(int param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC TestAlignment + { + int method(int param); + short s:9; + short j:9; + char c; + short t:9; + short u:9; + char d; + };"; + $SOURCE2 .= " + int TestAlignment::method(int param) { return 0; }"; + + # Renamed_Field + $HEADER1 .= " + struct $DECL_SPEC RenamedField + { + int method(RenamedField param); + long i; + long j; + double k; + RenamedField* p; + };"; + $SOURCE1 .= " + int RenamedField::method(RenamedField param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC RenamedField + { + int method(RenamedField param); + long renamed_member; + long j; + double k; + RenamedField* p; + };"; + $SOURCE2 .= " + int RenamedField::method(RenamedField param) { return 0; }"; + + # Removed_Field_And_Size + $HEADER1 .= " + struct $DECL_SPEC RemovedFieldAndSize + { + int method(RemovedFieldAndSize param); + double i, j, k; + RemovedFieldAndSize* p; + int removed_member1; + long removed_member2; + };"; + $SOURCE1 .= " + int RemovedFieldAndSize::method(RemovedFieldAndSize param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC RemovedFieldAndSize + { + int method(RemovedFieldAndSize param); + double i, j, k; + RemovedFieldAndSize* p; + };"; + $SOURCE2 .= " + int RemovedFieldAndSize::method(RemovedFieldAndSize param) { return 0; }"; + + # Field Position + $HEADER1 .= " + struct $DECL_SPEC MovedField + { + int method(int param); + double i; + int j; + };"; + $SOURCE1 .= " + int MovedField::method(int param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC MovedField + { + int method(int param); + int j; + double i; + };"; + $SOURCE2 .= " + int MovedField::method(int param) { return 0; }"; + + # Removed_Middle_Field_And_Size + $HEADER1 .= " + struct $DECL_SPEC RemovedMiddleFieldAndSize + { + int method(RemovedMiddleFieldAndSize param); + int i; + int removed_middle_member; + long j; + double k; + RemovedMiddleFieldAndSize* p; + };"; + $SOURCE1 .= " + int RemovedMiddleFieldAndSize::method(RemovedMiddleFieldAndSize param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC RemovedMiddleFieldAndSize + { + int method(RemovedMiddleFieldAndSize param); + int i; + long j; + double k; + RemovedMiddleFieldAndSize* p; + };"; + $SOURCE2 .= " + int RemovedMiddleFieldAndSize::method(RemovedMiddleFieldAndSize param) { return 0; }"; + + # Enum_Member_Value + $HEADER1 .= " + enum EnumMemberValue + { + MEMBER_1=1, + MEMBER_2=2 + };"; + $HEADER1 .= " + $DECL_SPEC int enumMemberValueChange(enum EnumMemberValue param);"; + $SOURCE1 .= " + int enumMemberValueChange(enum EnumMemberValue param) { return 0; }"; + + $HEADER2 .= " + enum EnumMemberValue + { + MEMBER_1=2, + MEMBER_2=1 + };"; + $HEADER2 .= " + $DECL_SPEC int enumMemberValueChange(enum EnumMemberValue param);"; + $SOURCE2 .= " + int enumMemberValueChange(enum EnumMemberValue param) { return 0; }"; + + # Enum_Member_Name + $HEADER1 .= " + enum EnumMemberRename + { + BRANCH_1=1, + BRANCH_2=2 + };"; + $HEADER1 .= " + $DECL_SPEC int enumMemberRename(enum EnumMemberRename param);"; + $SOURCE1 .= " + int enumMemberRename(enum EnumMemberRename param) { return 0; }"; + + $HEADER2 .= " + enum EnumMemberRename + { + BRANCH_FIRST=1, + BRANCH_SECOND=2 + };"; + $HEADER2 .= " + $DECL_SPEC int enumMemberRename(enum EnumMemberRename param);"; + $SOURCE2 .= " + int enumMemberRename(enum EnumMemberRename param) { return 0; }"; + + # Field_Type_And_Size + $HEADER1 .= " + struct $DECL_SPEC FieldTypeAndSize + { + int method(FieldTypeAndSize param); + int i; + long j; + double k; + FieldTypeAndSize* p; + };"; + $SOURCE1 .= " + int FieldTypeAndSize::method(FieldTypeAndSize param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC FieldTypeAndSize + { + int method(FieldTypeAndSize param); + long long i; + long j; + double k; + FieldTypeAndSize* p; + };"; + $SOURCE2 .= " + int FieldTypeAndSize::method(FieldTypeAndSize param) { return 0; }"; + + # Member_Type + $HEADER1 .= " + struct $DECL_SPEC MemberType + { + int method(MemberType param); + int i; + long j; + double k; + MemberType* p; + };"; + $SOURCE1 .= " + int MemberType::method(MemberType param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC MemberType + { + int method(MemberType param); + float i; + long j; + double k; + MemberType* p; + };"; + $SOURCE2 .= " + int MemberType::method(MemberType param) { return 0; }"; + + # Field_BaseType + $HEADER1 .= " + struct $DECL_SPEC FieldBaseType + { + int method(FieldBaseType param); + int *i; + long j; + double k; + FieldBaseType* p; + };"; + $SOURCE1 .= " + int FieldBaseType::method(FieldBaseType param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC FieldBaseType + { + int method(FieldBaseType param); + long long *i; + long j; + double k; + FieldBaseType* p; + };"; + $SOURCE2 .= " + int FieldBaseType::method(FieldBaseType param) { return 0; }"; + + # Field_PointerLevel_Increased (and size) + $HEADER1 .= " + struct $DECL_SPEC FieldPointerLevelAndSize + { + int method(FieldPointerLevelAndSize param); + long long i; + long j; + double k; + FieldPointerLevelAndSize* p; + };"; + $SOURCE1 .= " + int FieldPointerLevelAndSize::method(FieldPointerLevelAndSize param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC FieldPointerLevelAndSize + { + int method(FieldPointerLevelAndSize param); + long long *i; + long j; + double k; + FieldPointerLevelAndSize* p; + };"; + $SOURCE2 .= " + int FieldPointerLevelAndSize::method(FieldPointerLevelAndSize param) { return 0; }"; + + # Field_PointerLevel + $HEADER1 .= " + struct $DECL_SPEC FieldPointerLevel + { + int method(FieldPointerLevel param); + int **i; + long j; + double k; + FieldPointerLevel* p; + };"; + $SOURCE1 .= " + int FieldPointerLevel::method(FieldPointerLevel param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC FieldPointerLevel + { + int method(FieldPointerLevel param); + int *i; + long j; + double k; + FieldPointerLevel* p; + };"; + $SOURCE2 .= " + int FieldPointerLevel::method(FieldPointerLevel param) { return 0; }"; + + # Added_Interface (method) + $HEADER1 .= " + struct $DECL_SPEC AddedInterface + { + int method(AddedInterface param); + int i; + long j; + double k; + AddedInterface* p; + };"; + $SOURCE1 .= " + int AddedInterface::method(AddedInterface param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC AddedInterface + { + int method(AddedInterface param); + int added_func(AddedInterface param); + int i; + long j; + double k; + AddedInterface* p; + };"; + $SOURCE2 .= " + int AddedInterface::method(AddedInterface param) { return 0; }"; + $SOURCE2 .= " + int AddedInterface::added_func(AddedInterface param) { return 0; }"; + + # Added_Interface (function) + $HEADER2 .= " + $DECL_SPEC int addedFunc2(void *** param);"; + $SOURCE2 .= " + int addedFunc2(void *** param) { return 0; }"; + + # Added_Interface (global variable) + $HEADER1 .= " + struct $DECL_SPEC AddedVariable + { + int method(AddedVariable param); + int i1, i2; + long j; + double k; + AddedVariable* p; + };"; + $SOURCE1 .= " + int AddedVariable::method(AddedVariable param) { + return i1; + }"; + + $HEADER2 .= " + struct $DECL_SPEC AddedVariable + { + int method(AddedVariable param); + static int i1; + static int i2; + long j; + double k; + AddedVariable* p; + };"; + $SOURCE2 .= " + int AddedVariable::method(AddedVariable param) { return AddedVariable::i1; }"; + $SOURCE2 .= " + int AddedVariable::i1=0;"; + $SOURCE2 .= " + int AddedVariable::i2=0;"; + + # Removed_Interface (method) + $HEADER1 .= " + struct $DECL_SPEC RemovedInterface + { + int method(RemovedInterface param); + int removed_func(RemovedInterface param); + int i; + long j; + double k; + RemovedInterface* p; + };"; + $SOURCE1 .= " + int RemovedInterface::method(RemovedInterface param) { return 0; }"; + $SOURCE1 .= " + int RemovedInterface::removed_func(RemovedInterface param) { return 0; }"; + + $HEADER2 .= " + struct $DECL_SPEC RemovedInterface + { + int method(RemovedInterface param); + int i; + long j; + double k; + RemovedInterface* p; + };"; + $SOURCE2 .= " + int RemovedInterface::method(RemovedInterface param) { return 0; }"; + + # Removed_Interface (function) + $HEADER1 .= " + $DECL_SPEC int removedFunc2(void *** param);"; + $SOURCE1 .= " + int removedFunc2(void *** param) { return 0; }"; + + # Method_Became_Static + $HEADER1 .= " + struct $DECL_SPEC MethodBecameStatic + { + MethodBecameStatic becameStatic(MethodBecameStatic param); + int **i; + long j; + double k; + MethodBecameStatic* p; + };"; + $SOURCE1 .= " + MethodBecameStatic MethodBecameStatic::becameStatic(MethodBecameStatic param) { return param; }"; + + $HEADER2 .= " + struct $DECL_SPEC MethodBecameStatic + { + static MethodBecameStatic becameStatic(MethodBecameStatic param); + int **i; + long j; + double k; + MethodBecameStatic* p; + };"; + $SOURCE2 .= " + MethodBecameStatic MethodBecameStatic::becameStatic(MethodBecameStatic param) { return param; }"; + + # Method_Became_Non_Static + $HEADER1 .= " + struct $DECL_SPEC MethodBecameNonStatic + { + static MethodBecameNonStatic becameNonStatic(MethodBecameNonStatic param); + int **i; + long j; + double k; + MethodBecameNonStatic* p; + };"; + $SOURCE1 .= " + MethodBecameNonStatic MethodBecameNonStatic::becameNonStatic(MethodBecameNonStatic param) { return param; }"; + + $HEADER2 .= " + struct $DECL_SPEC MethodBecameNonStatic + { + MethodBecameNonStatic becameNonStatic(MethodBecameNonStatic param); + int **i; + long j; + double k; + MethodBecameNonStatic* p; + };"; + $SOURCE2 .= " + MethodBecameNonStatic MethodBecameNonStatic::becameNonStatic(MethodBecameNonStatic param) { return param; }"; + + # Parameter_Type_And_Size + $HEADER1 .= " + $DECL_SPEC int funcParameterTypeAndSize(int param, int other_param);"; + $SOURCE1 .= " + int funcParameterTypeAndSize(int param, int other_param) { return other_param; }"; + + $HEADER2 .= " + $DECL_SPEC int funcParameterTypeAndSize(long long param, int other_param);"; + $SOURCE2 .= " + int funcParameterTypeAndSize(long long param, int other_param) { return other_param; }"; + + # Parameter_Type + $HEADER1 .= " + $DECL_SPEC int funcParameterType(int param, int other_param);"; + $SOURCE1 .= " + int funcParameterType(int param, int other_param) { return other_param; }"; + + $HEADER2 .= " + $DECL_SPEC int funcParameterType(float param, int other_param);"; + $SOURCE2 .= " + int funcParameterType(float param, int other_param) { return other_param; }"; + + # Parameter_BaseType + $HEADER1 .= " + $DECL_SPEC int funcParameterBaseType(int *param);"; + $SOURCE1 .= " + int funcParameterBaseType(int *param) { return sizeof(*param); }"; + + $HEADER2 .= " + $DECL_SPEC int funcParameterBaseType(long long *param);"; + $SOURCE2 .= " + int funcParameterBaseType(long long *param) { return sizeof(*param); }"; + + # Parameter_PointerLevel + $HEADER1 .= " + $DECL_SPEC long long funcParameterPointerLevelAndSize(long long param);"; + $SOURCE1 .= " + long long funcParameterPointerLevelAndSize(long long param) { return param; }"; + + $HEADER2 .= " + $DECL_SPEC long long funcParameterPointerLevelAndSize(long long *param);"; + $SOURCE2 .= " + long long funcParameterPointerLevelAndSize(long long *param) { return param[5]; }"; + + # Parameter_PointerLevel + $HEADER1 .= " + $DECL_SPEC int funcParameterPointerLevel(int *param);"; + $SOURCE1 .= " + int funcParameterPointerLevel(int *param) { return param[5]; }"; + + $HEADER2 .= " + $DECL_SPEC int funcParameterPointerLevel(int **param);"; + $SOURCE2 .= " + int funcParameterPointerLevel(int **param) { return param[5][5]; }"; + + # Return_Type_And_Size + $HEADER1 .= " + $DECL_SPEC int funcReturnTypeAndSize(int param);"; + $SOURCE1 .= " + int funcReturnTypeAndSize(int param) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC long long funcReturnTypeAndSize(int param);"; + $SOURCE2 .= " + long long funcReturnTypeAndSize(int param) { return 0; }"; + + # Return_Type + $HEADER1 .= " + $DECL_SPEC int funcReturnType(int param);"; + $SOURCE1 .= " + int funcReturnType(int param) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC float funcReturnType(int param);"; + $SOURCE2 .= " + float funcReturnType(int param) { return 0.7; }"; + + # Return_Type_Became_Void ("int" to "void") + $HEADER1 .= " + $DECL_SPEC int funcReturnTypeBecameVoid(int param);"; + $SOURCE1 .= " + int funcReturnTypeBecameVoid(int param) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC void funcReturnTypeBecameVoid(int param);"; + $SOURCE2 .= " + void funcReturnTypeBecameVoid(int param) { return; }"; + + # Return_BaseType + $HEADER1 .= " + $DECL_SPEC int* funcReturnBaseType(int param);"; + $SOURCE1 .= " + int* funcReturnBaseType(int param) { + int *x = new int[10]; + return x; + }"; + + $HEADER2 .= " + $DECL_SPEC long long* funcReturnBaseType(int param);"; + $SOURCE2 .= " + long long* funcReturnBaseType(int param) { + long long *x = new long long[10]; + return x; + }"; + + # Return_PointerLevel + $HEADER1 .= " + $DECL_SPEC long long funcReturnPointerLevelAndSize(int param);"; + $SOURCE1 .= " + long long funcReturnPointerLevelAndSize(int param) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC long long* funcReturnPointerLevelAndSize(int param);"; + $SOURCE2 .= " + long long* funcReturnPointerLevelAndSize(int param) { return new long long[10]; }"; + + # Return_PointerLevel + $HEADER1 .= " + $DECL_SPEC int* funcReturnPointerLevel(int param);"; + $SOURCE1 .= " + int* funcReturnPointerLevel(int param) { return new int[10]; }"; + + $HEADER2 .= " + $DECL_SPEC int** funcReturnPointerLevel(int param);"; + $SOURCE2 .= " + int** funcReturnPointerLevel(int param) { return new int*[10]; }"; + + # Size (anon type) + $HEADER1 .= " + typedef struct { + int i; + long j; + double k; + } AnonTypedef; + $DECL_SPEC int funcAnonTypedef(AnonTypedef param);"; + $SOURCE1 .= " + int funcAnonTypedef(AnonTypedef param) { return 0; }"; + + $HEADER2 .= " + typedef struct { + int i; + long j; + double k; + union { + int dummy[256]; + struct { + char q_skiptable[256]; + const char *p; + int l; + } p; + }; + } AnonTypedef; + $DECL_SPEC int funcAnonTypedef(AnonTypedef param);"; + $SOURCE2 .= " + int funcAnonTypedef(AnonTypedef param) { return 0; }"; + + # Added_Field (safe: opaque) + $HEADER1 .= " + struct $DECL_SPEC OpaqueType + { + public: + OpaqueType method(OpaqueType param); + int i; + long j; + double k; + OpaqueType* p; + };"; + $SOURCE1 .= " + OpaqueType OpaqueType::method(OpaqueType param) { return param; }"; + + $HEADER2 .= " + struct $DECL_SPEC OpaqueType + { + public: + OpaqueType method(OpaqueType param); + int i; + long j; + double k; + OpaqueType* p; + int added_member; + };"; + $SOURCE2 .= " + OpaqueType OpaqueType::method(OpaqueType param) { return param; }"; + + # Added_Field (safe: internal) + $HEADER1 .= " + struct $DECL_SPEC InternalType { + InternalType method(InternalType param); + int i; + long j; + double k; + InternalType* p; + };"; + $SOURCE1 .= " + InternalType InternalType::method(InternalType param) { return param; }"; + + $HEADER2 .= " + struct $DECL_SPEC InternalType { + InternalType method(InternalType param); + int i; + long j; + double k; + InternalType* p; + int added_member; + };"; + $SOURCE2 .= " + InternalType InternalType::method(InternalType param) { return param; }"; + + # Size (unnamed struct/union fields within structs/unions) + $HEADER1 .= " + typedef struct { + int a; + struct { + int u1; + float u2; + }; + int d; + } UnnamedTypeSize; + $DECL_SPEC int unnamedTypeSize(UnnamedTypeSize param);"; + $SOURCE1 .= " + int unnamedTypeSize(UnnamedTypeSize param) { return 0; }"; + + $HEADER2 .= " + typedef struct { + int a; + struct { + long double u1; + float u2; + }; + int d; + } UnnamedTypeSize; + $DECL_SPEC int unnamedTypeSize(UnnamedTypeSize param);"; + $SOURCE2 .= " + int unnamedTypeSize(UnnamedTypeSize param) { return 0; }"; + + # Changed_Constant + $HEADER1 .= " + #define PUBLIC_CONSTANT \"old_value\""; + $HEADER2 .= " + #define PUBLIC_CONSTANT \"new_value\""; + + $HEADER1 .= " + #define PUBLIC_VERSION \"1.2 (3.4)\""; + $HEADER2 .= " + #define PUBLIC_VERSION \"1.2 (3.5)\""; + + $HEADER1 .= " + #define PRIVATE_CONSTANT \"old_value\" + #undef PRIVATE_CONSTANT"; + $HEADER2 .= " + #define PRIVATE_CONSTANT \"new_value\" + #undef PRIVATE_CONSTANT"; + + # Added_Field (union) + $HEADER1 .= " + union UnionAddedField { + int a; + struct { + int b; + float c; + }; + int d; + }; + $DECL_SPEC int unionAddedField(UnionAddedField param);"; + $SOURCE1 .= " + int unionAddedField(UnionAddedField param) { return 0; }"; + + $HEADER2 .= " + union UnionAddedField { + int a; + struct { + long double x, y; + } new_field; + struct { + int b; + float c; + }; + int d; + }; + $DECL_SPEC int unionAddedField(UnionAddedField param);"; + $SOURCE2 .= " + int unionAddedField(UnionAddedField param) { return 0; }"; + + # Removed_Field (union) + $HEADER1 .= " + union UnionRemovedField { + int a; + struct { + long double x, y; + } removed_field; + struct { + int b; + float c; + }; + int d; + }; + $DECL_SPEC int unionRemovedField(UnionRemovedField param);"; + $SOURCE1 .= " + int unionRemovedField(UnionRemovedField param) { return 0; }"; + + $HEADER2 .= " + union UnionRemovedField { + int a; + struct { + int b; + float c; + }; + int d; + }; + $DECL_SPEC int unionRemovedField(UnionRemovedField param);"; + $SOURCE2 .= " + int unionRemovedField(UnionRemovedField param) { return 0; }"; + + # Added (typedef change) + $HEADER1 .= " + typedef float TYPEDEF_TYPE; + $DECL_SPEC int parameterTypedefChange(TYPEDEF_TYPE param);"; + $SOURCE1 .= " + int parameterTypedefChange(TYPEDEF_TYPE param) { return 1; }"; + + $HEADER2 .= " + typedef int TYPEDEF_TYPE; + $DECL_SPEC int parameterTypedefChange(TYPEDEF_TYPE param);"; + $SOURCE2 .= " + int parameterTypedefChange(TYPEDEF_TYPE param) { return 1; }"; + + # Parameter_Default_Value_Changed (safe) + # Converted from void* to const char* + $HEADER1 .= " + $DECL_SPEC int paramDefaultValue_Converted(const char* arg = 0); "; + $SOURCE1 .= " + int paramDefaultValue_Converted(const char* arg) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC int paramDefaultValue_Converted(const char* arg = (const char*)((void*) 0)); "; + $SOURCE2 .= " + int paramDefaultValue_Converted(const char* arg) { return 0; }"; + + # Parameter_Default_Value_Changed + # Integer + $HEADER1 .= " + $DECL_SPEC int paramDefaultValueChanged_Integer(int param = 0xf00f); "; + $SOURCE1 .= " + int paramDefaultValueChanged_Integer(int param) { return param; }"; + + $HEADER2 .= " + $DECL_SPEC int paramDefaultValueChanged_Integer(int param = 0xf00b); "; + $SOURCE2 .= " + int paramDefaultValueChanged_Integer(int param) { return param; }"; + + # Parameter_Default_Value_Changed + # String + $HEADER1 .= " + $DECL_SPEC int paramDefaultValueChanged_String(char const* param = \" str 1 \"); "; + $SOURCE1 .= " + int paramDefaultValueChanged_String(char const* param) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC int paramDefaultValueChanged_String(char const* param = \" str 2 \"); "; + $SOURCE2 .= " + int paramDefaultValueChanged_String(char const* param) { return 0; }"; + + # Parameter_Default_Value_Changed + # Character + $HEADER1 .= " + $DECL_SPEC int paramDefaultValueChanged_Char(char param = \'A\'); "; + $SOURCE1 .= " + int paramDefaultValueChanged_Char(char param) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC int paramDefaultValueChanged_Char(char param = \'B\'); "; + $SOURCE2 .= " + int paramDefaultValueChanged_Char(char param) { return 0; }"; + + # Parameter_Default_Value_Changed + # Bool + $HEADER1 .= " + $DECL_SPEC int paramDefaultValueChanged_Bool(bool param = true); "; + $SOURCE1 .= " + int paramDefaultValueChanged_Bool(bool param) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC int paramDefaultValueChanged_Bool(bool param = false); "; + $SOURCE2 .= " + int paramDefaultValueChanged_Bool(bool param) { return 0; }"; + + # Parameter_Default_Value_Removed + $HEADER1 .= " + $DECL_SPEC int parameterDefaultValueRemoved(int param = 15); + "; + $SOURCE1 .= " + int parameterDefaultValueRemoved(int param) { return param; }"; + + $HEADER2 .= " + $DECL_SPEC int parameterDefaultValueRemoved(int param);"; + $SOURCE2 .= " + int parameterDefaultValueRemoved(int param) { return param; }"; + + # Parameter_Default_Value_Added + $HEADER1 .= " + $DECL_SPEC int parameterDefaultValueAdded(int param); + "; + $SOURCE1 .= " + int parameterDefaultValueAdded(int param) { return param; }"; + + $HEADER2 .= " + $DECL_SPEC int parameterDefaultValueAdded(int param = 15);"; + $SOURCE2 .= " + int parameterDefaultValueAdded(int param) { return param; }"; + + # Field_Type (typedefs in member type) + $HEADER1 .= " + typedef float TYPEDEF_TYPE_2; + struct $DECL_SPEC FieldTypedefChange{ + public: + TYPEDEF_TYPE_2 m; + TYPEDEF_TYPE_2 n; + }; + $DECL_SPEC int fieldTypedefChange(FieldTypedefChange param);"; + $SOURCE1 .= " + int fieldTypedefChange(FieldTypedefChange param) { return 1; }"; + + $HEADER2 .= " + typedef int TYPEDEF_TYPE_2; + struct $DECL_SPEC FieldTypedefChange{ + public: + TYPEDEF_TYPE_2 m; + TYPEDEF_TYPE_2 n; + }; + $DECL_SPEC int fieldTypedefChange(FieldTypedefChange param);"; + $SOURCE2 .= " + int fieldTypedefChange(FieldTypedefChange param) { return 1; }"; + + # Callback (testCallback symbol should be affected + # instead of callback1 and callback2) + $HEADER1 .= " + class $DECL_SPEC Callback { + public: + virtual int callback1(int x, int y)=0; + virtual int callback2(int x, int y)=0; + }; + $DECL_SPEC int testCallback(Callback* p);"; + $SOURCE1 .= " + int testCallback(Callback* p) { + p->callback2(1, 2); + return 0; + }"; + + $HEADER2 .= " + class $DECL_SPEC Callback { + public: + virtual int callback1(int x, int y)=0; + virtual int added_callback(int x, int y)=0; + virtual int callback2(int x, int y)=0; + }; + $DECL_SPEC int testCallback(Callback* p);"; + $SOURCE2 .= " + int testCallback(Callback* p) { + p->callback2(1, 2); + return 0; + }"; + + # End namespace + $HEADER1 .= "\n}\n"; + $HEADER2 .= "\n}\n"; + $SOURCE1 .= "\n}\n"; + $SOURCE2 .= "\n}\n"; + + runTests("libsample_cpp", "C++", $HEADER1, $SOURCE1, $HEADER2, $SOURCE2, "TestNS::OpaqueType", "_ZN6TestNS12InternalType6methodES0_"); +} + +sub testC() +{ + printMsg("INFO", "\nVerifying detectable C library changes"); + my ($HEADER1, $SOURCE1, $HEADER2, $SOURCE2) = (); + + my $DECL_SPEC = ""; + my $EXTERN = ""; + + if($In::Opt{"OS"} eq "windows") + { + $DECL_SPEC = "__declspec( dllexport )"; + $EXTERN = "extern "; # add "extern" for CL compiler + } + + # Array size + $HEADER1 .= " + $DECL_SPEC int arraySize(int p[10]);"; + $SOURCE1 .= " + int arraySize(int p[10]) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC int arraySize(int p[15]);"; + $SOURCE2 .= " + int arraySize(int p[15]) { return 0; }"; + + # Struct to union + $HEADER1 .= " + typedef struct StructToUnion { + unsigned char A[64]; + } StructToUnion; + + $DECL_SPEC int structToUnion(StructToUnion *p);"; + $SOURCE1 .= " + int structToUnion(StructToUnion *p) { return 0; }"; + + $HEADER2 .= " + typedef union StructToUnion { + unsigned char A[64]; + void *p; + } StructToUnion; + + $DECL_SPEC int structToUnion(StructToUnion *p);"; + $SOURCE2 .= " + int structToUnion(StructToUnion *p) { return 0; }"; + + # Typedef to function + $HEADER1 .= " + typedef int(TypedefToFunction)(int pX); + + $DECL_SPEC int typedefToFunction(TypedefToFunction* p);"; + $SOURCE1 .= " + int typedefToFunction(TypedefToFunction* p) { return 0; }"; + + $HEADER2 .= " + typedef int(TypedefToFunction)(int pX, int pY); + + $DECL_SPEC int typedefToFunction(TypedefToFunction* p);"; + $SOURCE2 .= " + int typedefToFunction(TypedefToFunction* p) { return 0; }"; + + # Used_Reserved + $HEADER1 .= " + typedef struct { + int f; + void* reserved0; + void* reserved1; + } UsedReserved; + + $DECL_SPEC int usedReserved(UsedReserved p);"; + $SOURCE1 .= " + int usedReserved(UsedReserved p) { return 0; }"; + + $HEADER2 .= " + typedef struct { + int f; + void* f0; + void* f1; + } UsedReserved; + + $DECL_SPEC int usedReserved(UsedReserved p);"; + $SOURCE2 .= " + int usedReserved(UsedReserved p) { return 0; }"; + + # Parameter_Type_And_Register + $HEADER1 .= " + typedef struct { + int a[4]; + } ARRAY; + $DECL_SPEC void callConv5 (ARRAY i, int j);"; + $SOURCE1 .= " + void callConv5 (ARRAY i, int j) { }"; + + $HEADER2 .= " + typedef struct { + int a[4]; + } ARRAY; + $DECL_SPEC void callConv5 (ARRAY i, double j);"; + $SOURCE2 .= " + void callConv5 (ARRAY i, double j) { }"; + + # Parameter_Type_And_Register + $HEADER1 .= " + typedef union { + int a; + double b; + } UNION; + $DECL_SPEC void callConv4 (UNION i, int j);"; + $SOURCE1 .= " + void callConv4 (UNION i, int j) { }"; + + $HEADER2 .= " + typedef union { + int a; + double b; + } UNION; + $DECL_SPEC void callConv4 (UNION i, double j);"; + $SOURCE2 .= " + void callConv4 (UNION i, double j) { }"; + + # Parameter_Type_And_Register + $HEADER1 .= " + typedef struct { + long a:4; + long b:16; + } POD2; + $DECL_SPEC void callConv3 (POD2 i, int j);"; + $SOURCE1 .= " + void callConv3 (POD2 i, int j) { }"; + + $HEADER2 .= " + typedef struct { + long a:4; + long b:16; + } POD2; + $DECL_SPEC void callConv3 (POD2 i, double j);"; + $SOURCE2 .= " + void callConv3 (POD2 i, double j) { }"; + + # Parameter_Type_And_Register + $HEADER1 .= " + typedef struct { + short s:9; + int j:9; + char c; + short t:9; + short u:9; + char d; + } POD; + $DECL_SPEC void callConv2 (POD i, int j);"; + $SOURCE1 .= " + void callConv2 (POD i, int j) { }"; + + $HEADER2 .= " + typedef struct { + short s:9; + int j:9; + char c; + short t:9; + short u:9; + char d; + } POD; + $DECL_SPEC void callConv2 (POD i, double j);"; + $SOURCE2 .= " + void callConv2 (POD i, double j) { }"; + + # Parameter_Type_And_Register + $HEADER1 .= " + typedef struct { + int a, b; + double d; + } POD1; + $DECL_SPEC void callConv (int e, int f, POD1 s, int g, int h, long double ld, double m, double n, int i, int j, int k);"; + $SOURCE1 .= " + void callConv(int e, int f, POD1 s, int g, int h, long double ld, double m, double n, int i, int j, int k) { }"; + + $HEADER2 .= " + typedef struct { + int a, b; + double d; + } POD1; + $DECL_SPEC void callConv (int e, int f, POD1 s, int g, int h, long double ld, double m, double n, int i, int j, double k);"; + $SOURCE2 .= " + void callConv(int e, int f, POD1 s, int g, int h, long double ld, double m, double n, int i, int j, double k) { }"; + + # Parameter_Type (int to "int const") + $HEADER1 .= " + $DECL_SPEC void parameterBecameConstInt(int arg);"; + $SOURCE1 .= " + void parameterBecameConstInt(int arg) { }"; + + $HEADER2 .= " + $DECL_SPEC void parameterBecameConstInt(const int arg);"; + $SOURCE2 .= " + void parameterBecameConstInt(const int arg) { }"; + + # Parameter_Type ("int const" to int) + $HEADER1 .= " + $DECL_SPEC void parameterBecameNonConstInt(const int arg);"; + $SOURCE1 .= " + void parameterBecameNonConstInt(const int arg) { }"; + + $HEADER2 .= " + $DECL_SPEC void parameterBecameNonConstInt(int arg);"; + $SOURCE2 .= " + void parameterBecameNonConstInt(int arg) { }"; + + # Parameter_Became_Register + $HEADER1 .= " + $DECL_SPEC void parameterBecameRegister(int arg);"; + $SOURCE1 .= " + void parameterBecameRegister(int arg) { }"; + + $HEADER2 .= " + $DECL_SPEC void parameterBecameRegister(register int arg);"; + $SOURCE2 .= " + void parameterBecameRegister(register int arg) { }"; + + # Return_Type_Became_Const + $HEADER1 .= " + $DECL_SPEC char* returnTypeBecameConst(int param);"; + $SOURCE1 .= " + char* returnTypeBecameConst(int param) { return (char*)malloc(256); }"; + + $HEADER2 .= " + $DECL_SPEC const char* returnTypeBecameConst(int param);"; + $SOURCE2 .= " + const char* returnTypeBecameConst(int param) { return \"abc\"; }"; + + # Return_Type_Became_Const (2) + $HEADER1 .= " + $DECL_SPEC char* returnTypeBecameConst2(int param);"; + $SOURCE1 .= " + char* returnTypeBecameConst2(int param) { return (char*)malloc(256); }"; + + $HEADER2 .= " + $DECL_SPEC char*const returnTypeBecameConst2(int param);"; + $SOURCE2 .= " + char*const returnTypeBecameConst2(int param) { return (char*const)malloc(256); }"; + + # Return_Type_Became_Const (3) + $HEADER1 .= " + $DECL_SPEC char* returnTypeBecameConst3(int param);"; + $SOURCE1 .= " + char* returnTypeBecameConst3(int param) { return (char*)malloc(256); }"; + + $HEADER2 .= " + $DECL_SPEC char const*const returnTypeBecameConst3(int param);"; + $SOURCE2 .= " + char const*const returnTypeBecameConst3(int param) { return (char const*const)malloc(256); }"; + + # Return_Type_Became_Volatile + $HEADER1 .= " + $DECL_SPEC char* returnTypeBecameVolatile(int param);"; + $SOURCE1 .= " + char* returnTypeBecameVolatile(int param) { return (char*)malloc(256); }"; + + $HEADER2 .= " + $DECL_SPEC volatile char* returnTypeBecameVolatile(int param);"; + $SOURCE2 .= " + volatile char* returnTypeBecameVolatile(int param) { return \"abc\"; }"; + + # Added_Enum_Member + $HEADER1 .= " + enum AddedEnumMember { + OldMember + }; + $DECL_SPEC int addedEnumMember(enum AddedEnumMember param);"; + $SOURCE1 .= " + int addedEnumMember(enum AddedEnumMember param) { return 0; }"; + + $HEADER2 .= " + enum AddedEnumMember { + OldMember, + NewMember + }; + $DECL_SPEC int addedEnumMember(enum AddedEnumMember param);"; + $SOURCE2 .= " + int addedEnumMember(enum AddedEnumMember param) { return 0; }"; + + # Parameter_Type (Array) + $HEADER1 .= " + $DECL_SPEC int arrayParameterType(int param[5]);"; + $SOURCE1 .= " + int arrayParameterType(int param[5]) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC int arrayParameterType(int param[7]);"; + $SOURCE2 .= " + int arrayParameterType(int param[7]) { return 0; }"; + + # Field_Type + $HEADER1 .= " + struct ArrayFieldType + { + int f; + int i[1]; + }; + $DECL_SPEC int arrayFieldType(struct ArrayFieldType param);"; + $SOURCE1 .= " + int arrayFieldType(struct ArrayFieldType param) { return param.i[0]; }"; + + $HEADER2 .= " + struct ArrayFieldType + { + int f; + int i[]; + }; + $DECL_SPEC int arrayFieldType(struct ArrayFieldType param);"; + $SOURCE2 .= " + int arrayFieldType(struct ArrayFieldType param) { return param.i[0]; }"; + + # Field_Type_And_Size (Array) + $HEADER1 .= " + struct ArrayFieldSize + { + int i[5]; + }; + $DECL_SPEC int arrayFieldSize(struct ArrayFieldSize param);"; + $SOURCE1 .= " + int arrayFieldSize(struct ArrayFieldSize param) { return 0; }"; + + $HEADER2 .= " + struct ArrayFieldSize + { + int i[7]; + }; + $DECL_SPEC int arrayFieldSize(struct ArrayFieldSize param);"; + $SOURCE2 .= " + int arrayFieldSize(struct ArrayFieldSize param) { return 0; }"; + + # Parameter_Became_Non_VaList + $HEADER1 .= " + $DECL_SPEC int parameterNonVaList(int param, ...);"; + $SOURCE1 .= " + int parameterNonVaList(int param, ...) { return param; }"; + + $HEADER2 .= " + $DECL_SPEC int parameterNonVaList(int param1, int param2);"; + $SOURCE2 .= " + int parameterNonVaList(int param1, int param2) { return param1; }"; + + # Parameter_Became_VaList + $HEADER1 .= " + $DECL_SPEC int parameterVaList(int param1, int param2);"; + $SOURCE1 .= " + int parameterVaList(int param1, int param2) { return param1; }"; + + $HEADER2 .= " + $DECL_SPEC int parameterVaList(int param, ...);"; + $SOURCE2 .= " + int parameterVaList(int param, ...) { return param; }"; + + # Field_Type_And_Size + $HEADER1 .= " + struct FieldSizePadded + { + int i; + char changed_field; + // padding (3 bytes) + int j; + }; + $DECL_SPEC int fieldSizePadded(struct FieldSizePadded param);"; + $SOURCE1 .= " + int fieldSizePadded(struct FieldSizePadded param) { return 0; }"; + + $HEADER2 .= " + struct FieldSizePadded + { + int i; + int changed_field; + int j; + }; + $DECL_SPEC int fieldSizePadded(struct FieldSizePadded param);"; + $SOURCE2 .= " + int fieldSizePadded(struct FieldSizePadded param) { return 0; }"; + + # Parameter_Type_Format + $HEADER1 .= " + struct DType1 + { + int i; + double j[7]; + }; + $DECL_SPEC int parameterTypeFormat(struct DType1 param);"; + $SOURCE1 .= " + int parameterTypeFormat(struct DType1 param) { return 0; }"; + + $HEADER2 .= " + struct DType2 + { + double i[7]; + int j; + }; + $DECL_SPEC int parameterTypeFormat(struct DType2 param);"; + $SOURCE2 .= " + int parameterTypeFormat(struct DType2 param) { return 0; }"; + + # Field_Type_Format + $HEADER1 .= " + struct FieldTypeFormat + { + int i; + struct DType1 j; + }; + $DECL_SPEC int fieldTypeFormat(struct FieldTypeFormat param);"; + $SOURCE1 .= " + int fieldTypeFormat(struct FieldTypeFormat param) { return 0; }"; + + $HEADER2 .= " + struct FieldTypeFormat + { + int i; + struct DType2 j; + }; + $DECL_SPEC int fieldTypeFormat(struct FieldTypeFormat param);"; + $SOURCE2 .= " + int fieldTypeFormat(struct FieldTypeFormat param) { return 0; }"; + + # Parameter_Type_Format (struct to union) + $HEADER1 .= " + struct DType + { + int i; + double j; + }; + $DECL_SPEC int parameterTypeFormat2(struct DType param);"; + $SOURCE1 .= " + int parameterTypeFormat2(struct DType param) { return 0; }"; + + $HEADER2 .= " + union DType + { + int i; + long double j; + }; + $DECL_SPEC int parameterTypeFormat2(union DType param);"; + $SOURCE2 .= " + int parameterTypeFormat2(union DType param) { return 0; }"; + + # Global_Data_Size + $HEADER1 .= " + struct GlobalDataSize { + int a; + }; + $EXTERN $DECL_SPEC struct GlobalDataSize globalDataSize;"; + + $HEADER2 .= " + struct GlobalDataSize { + int a, b; + }; + $EXTERN $DECL_SPEC struct GlobalDataSize globalDataSize;"; + + # Global_Data_Type + $HEADER1 .= " + $EXTERN $DECL_SPEC int globalDataType;"; + + $HEADER2 .= " + $EXTERN $DECL_SPEC float globalDataType;"; + + # Global_Data_Type_And_Size + $HEADER1 .= " + $EXTERN $DECL_SPEC int globalDataTypeAndSize;"; + + $HEADER2 .= " + $EXTERN $DECL_SPEC short globalDataTypeAndSize;"; + + # Global_Data_Value_Changed + # Integer + $HEADER1 .= " + $EXTERN $DECL_SPEC const int globalDataValue_Integer = 10;"; + + $HEADER2 .= " + $EXTERN $DECL_SPEC const int globalDataValue_Integer = 15;"; + + # Global_Data_Value_Changed + # Character + $HEADER1 .= " + $EXTERN $DECL_SPEC const char globalDataValue_Char = \'o\';"; + + $HEADER2 .= " + $EXTERN $DECL_SPEC const char globalDataValue_Char = \'N\';"; + + # Global_Data_Became_Non_Const + $HEADER1 .= " + $EXTERN $DECL_SPEC const int globalDataBecameNonConst = 10;"; + + $HEADER2 .= " + extern $DECL_SPEC int globalDataBecameNonConst;"; + $SOURCE2 .= " + int globalDataBecameNonConst = 15;"; + + # Global_Data_Became_Non_Const + # Typedef + $HEADER1 .= " + typedef const int CONST_INT; + $EXTERN $DECL_SPEC CONST_INT globalDataBecameNonConst_Typedef = 10;"; + + $HEADER2 .= " + extern $DECL_SPEC int globalDataBecameNonConst_Typedef;"; + $SOURCE2 .= " + int globalDataBecameNonConst_Typedef = 15;"; + + # Global_Data_Became_Const + $HEADER1 .= " + extern $DECL_SPEC int globalDataBecameConst;"; + $SOURCE1 .= " + int globalDataBecameConst = 10;"; + + $HEADER2 .= " + $EXTERN $DECL_SPEC const int globalDataBecameConst = 15;"; + + # Global_Data_Became_Non_Const + $HEADER1 .= " + struct GlobalDataType{int a;int b;struct GlobalDataType* p;}; + $EXTERN $DECL_SPEC const struct GlobalDataType globalStructDataBecameNonConst = { 1, 2, (struct GlobalDataType*)0 };"; + + $HEADER2 .= " + struct GlobalDataType{int a;int b;struct GlobalDataType* p;}; + $EXTERN $DECL_SPEC struct GlobalDataType globalStructDataBecameNonConst = { 1, 2, (struct GlobalDataType*)0 };"; + + # Removed_Parameter + $HEADER1 .= " + $DECL_SPEC int removedParameter(int param, int removed_param);"; + $SOURCE1 .= " + int removedParameter(int param, int removed_param) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC int removedParameter(int param);"; + $SOURCE2 .= " + int removedParameter(int param) { return 0; }"; + + # Added_Parameter + $HEADER1 .= " + $DECL_SPEC int addedParameter(int param);"; + $SOURCE1 .= " + int addedParameter(int param) { return param; }"; + + $HEADER2 .= " + $DECL_SPEC int addedParameter(int param, int added_param, int added_param2);"; + $SOURCE2 .= " + int addedParameter(int param, int added_param, int added_param2) { return added_param2; }"; + + # Added_Interface (typedef to funcptr parameter) + $HEADER2 .= " + typedef int (*FUNCPTR_TYPE)(int a, int b); + $DECL_SPEC int addedFunc(FUNCPTR_TYPE*const** f);"; + $SOURCE2 .= " + int addedFunc(FUNCPTR_TYPE*const** f) { return 0; }"; + + # Added_Interface (funcptr parameter) + $HEADER2 .= " + $DECL_SPEC int addedFunc2(int(*func)(int, int));"; + $SOURCE2 .= " + int addedFunc2(int(*func)(int, int)) { return 0; }"; + + # Added_Interface (no limited parameters) + $HEADER2 .= " + $DECL_SPEC int addedFunc3(float p1, ...);"; + $SOURCE2 .= " + int addedFunc3(float p1, ...) { return 0; }"; + + # Size + $HEADER1 .= " + struct TypeSize + { + long long i[5]; + long j; + double k; + struct TypeSize* p; + }; + $DECL_SPEC int testSize(struct TypeSize param, int param_2);"; + $SOURCE1 .= " + int testSize(struct TypeSize param, int param_2) { return param_2; }"; + + $HEADER2 .= " + struct TypeSize + { + long long i[15]; + long long j; + double k; + struct TypeSize* p; + }; + $DECL_SPEC int testSize(struct TypeSize param, int param_2);"; + $SOURCE2 .= " + int testSize(struct TypeSize param, int param_2) { return param_2; }"; + + # Added_Field_And_Size + $HEADER1 .= " + struct AddedFieldAndSize + { + int i; + long j; + double k; + struct AddedFieldAndSize* p; + }; + $DECL_SPEC int addedFieldAndSize(struct AddedFieldAndSize param, int param_2);"; + $SOURCE1 .= " + int addedFieldAndSize(struct AddedFieldAndSize param, int param_2) { return param_2; }"; + + $HEADER2 .= " + struct AddedFieldAndSize + { + int i; + long j; + double k; + struct AddedFieldAndSize* p; + int added_member1; + int added_member2; + }; + $DECL_SPEC int addedFieldAndSize(struct AddedFieldAndSize param, int param_2);"; + $SOURCE2 .= " + int addedFieldAndSize(struct AddedFieldAndSize param, int param_2) { return param_2; }"; + + # Added_Middle_Field_And_Size + $HEADER1 .= " + struct AddedMiddleFieldAndSize + { + int i; + long j; + double k; + struct AddedMiddleFieldAndSize* p; + }; + $DECL_SPEC int addedMiddleFieldAndSize(struct AddedMiddleFieldAndSize param, int param_2);"; + $SOURCE1 .= " + int addedMiddleFieldAndSize(struct AddedMiddleFieldAndSize param, int param_2) { return param_2; }"; + + $HEADER2 .= " + struct AddedMiddleFieldAndSize + { + int i; + int added_middle_member; + long j; + double k; + struct AddedMiddleFieldAndSize* p; + }; + $DECL_SPEC int addedMiddleFieldAndSize(struct AddedMiddleFieldAndSize param, int param_2);"; + $SOURCE2 .= " + int addedMiddleFieldAndSize(struct AddedMiddleFieldAndSize param, int param_2) { return param_2; }"; + + # Added_Middle_Field + $HEADER1 .= " + struct AddedMiddleField + { + unsigned char field1; + unsigned short field2; + }; + $DECL_SPEC int addedMiddleField(struct AddedMiddleField param, int param_2);"; + $SOURCE1 .= " + int addedMiddleField(struct AddedMiddleField param, int param_2) { return param_2; }"; + + $HEADER2 .= " + struct AddedMiddleField + { + unsigned char field1; + unsigned char added_field; + unsigned short field2; + }; + $DECL_SPEC int addedMiddleField(struct AddedMiddleField param, int param_2);"; + $SOURCE2 .= " + int addedMiddleField(struct AddedMiddleField param, int param_2) { return param_2; }"; + + # Renamed_Field + $HEADER1 .= " + struct RenamedField + { + long i; + long j; + double k; + struct RenamedField* p; + }; + $DECL_SPEC int renamedField(struct RenamedField param, int param_2);"; + $SOURCE1 .= " + int renamedField(struct RenamedField param, int param_2) { return param_2; }"; + + $HEADER2 .= " + struct RenamedField + { + long renamed_member; + long j; + double k; + struct RenamedField* p; + }; + $DECL_SPEC int renamedField(struct RenamedField param, int param_2);"; + $SOURCE2 .= " + int renamedField(struct RenamedField param, int param_2) { return param_2; }"; + + # Renamed_Field + $HEADER1 .= " + union RenamedUnionField + { + int renamed_from; + double j; + }; + $DECL_SPEC int renamedUnionField(union RenamedUnionField param);"; + $SOURCE1 .= " + int renamedUnionField(union RenamedUnionField param) { return 0; }"; + + $HEADER2 .= " + union RenamedUnionField + { + int renamed_to; + double j; + }; + $DECL_SPEC int renamedUnionField(union RenamedUnionField param);"; + $SOURCE2 .= " + int renamedUnionField(union RenamedUnionField param) { return 0; }"; + + # Removed_Field_And_Size + $HEADER1 .= " + struct RemovedFieldAndSize + { + int i; + long j; + double k; + struct RemovedFieldAndSize* p; + int removed_member1; + int removed_member2; + }; + $DECL_SPEC int removedFieldAndSize(struct RemovedFieldAndSize param, int param_2);"; + $SOURCE1 .= " + int removedFieldAndSize(struct RemovedFieldAndSize param, int param_2) { return param_2; }"; + + $HEADER2 .= " + struct RemovedFieldAndSize + { + int i; + long j; + double k; + struct RemovedFieldAndSize* p; + }; + $DECL_SPEC int removedFieldAndSize(struct RemovedFieldAndSize param, int param_2);"; + $SOURCE2 .= " + int removedFieldAndSize(struct RemovedFieldAndSize param, int param_2) { return param_2; }"; + + # Removed_Middle_Field + $HEADER1 .= " + struct RemovedMiddleField + { + int i; + int removed_middle_member; + long j; + double k; + struct RemovedMiddleField* p; + }; + $DECL_SPEC int removedMiddleField(struct RemovedMiddleField param, int param_2);"; + $SOURCE1 .= " + int removedMiddleField(struct RemovedMiddleField param, int param_2) { return param_2; }"; + + $HEADER2 .= " + struct RemovedMiddleField + { + int i; + long j; + double k; + struct RemovedMiddleField* p; + }; + $DECL_SPEC int removedMiddleField(struct RemovedMiddleField param, int param_2);"; + $SOURCE2 .= " + int removedMiddleField(struct RemovedMiddleField param, int param_2) { return param_2; }"; + + # Enum_Member_Value + $HEADER1 .= " + enum EnumMemberValue + { + MEMBER1=1, + MEMBER2=2 + }; + $DECL_SPEC int enumMemberValue(enum EnumMemberValue param);"; + $SOURCE1 .= " + int enumMemberValue(enum EnumMemberValue param) { return 0; }"; + + $HEADER2 .= " + enum EnumMemberValue + { + MEMBER1=2, + MEMBER2=1 + }; + $DECL_SPEC int enumMemberValue(enum EnumMemberValue param);"; + $SOURCE2 .= " + int enumMemberValue(enum EnumMemberValue param) { return 0; }"; + + # Enum_Member_Removed + $HEADER1 .= " + enum EnumMemberRemoved + { + MEMBER=1, + MEMBER_REMOVED=2 + }; + $DECL_SPEC int enumMemberRemoved(enum EnumMemberRemoved param);"; + $SOURCE1 .= " + int enumMemberRemoved(enum EnumMemberRemoved param) { return 0; }"; + + $HEADER2 .= " + enum EnumMemberRemoved + { + MEMBER=1 + }; + $DECL_SPEC int enumMemberRemoved(enum EnumMemberRemoved param);"; + $SOURCE2 .= " + int enumMemberRemoved(enum EnumMemberRemoved param) { return 0; }"; + + # Enum_Member_Removed (middle) + $HEADER1 .= " + enum EnumMiddleMemberRemoved + { + MEM_REMOVED, + MEM1, + MEM2 + }; + $DECL_SPEC int enumMiddleMemberRemoved(enum EnumMiddleMemberRemoved param);"; + $SOURCE1 .= " + int enumMiddleMemberRemoved(enum EnumMiddleMemberRemoved param) { return 0; }"; + + $HEADER2 .= " + enum EnumMiddleMemberRemoved + { + MEM1, + MEM2 + }; + $DECL_SPEC int enumMiddleMemberRemoved(enum EnumMiddleMemberRemoved param);"; + $SOURCE2 .= " + int enumMiddleMemberRemoved(enum EnumMiddleMemberRemoved param) { return 0; }"; + + # Enum_Member_Name + $HEADER1 .= " + enum EnumMemberName + { + BRANCH1=1, + BRANCH2=2 + }; + $DECL_SPEC int enumMemberName(enum EnumMemberName param);"; + $SOURCE1 .= " + int enumMemberName(enum EnumMemberName param) { return 0; }"; + + $HEADER2 .= " + enum EnumMemberName + { + BRANCH_FIRST=1, + BRANCH_SECOND=2 + }; + $DECL_SPEC int enumMemberName(enum EnumMemberName param);"; + $SOURCE2 .= " + int enumMemberName(enum EnumMemberName param) { return 0; }"; + + # Field_Type_And_Size + $HEADER1 .= " + struct FieldTypeAndSize + { + int i; + long j; + double k; + struct FieldTypeAndSize* p; + }; + $DECL_SPEC int fieldTypeAndSize(struct FieldTypeAndSize param, int param_2);"; + $SOURCE1 .= " + int fieldTypeAndSize(struct FieldTypeAndSize param, int param_2) { return param_2; }"; + + $HEADER2 .= " + struct FieldTypeAndSize + { + int i; + long long j; + double k; + struct FieldTypeAndSize* p; + }; + $DECL_SPEC int fieldTypeAndSize(struct FieldTypeAndSize param, int param_2);"; + $SOURCE2 .= " + int fieldTypeAndSize(struct FieldTypeAndSize param, int param_2) { return param_2; }"; + + # Field_Type + $HEADER1 .= " + struct FieldType + { + int i; + long j; + double k; + struct FieldType* p; + }; + $DECL_SPEC int fieldType(struct FieldType param, int param_2);"; + $SOURCE1 .= " + int fieldType(struct FieldType param, int param_2) { return param_2; }"; + + $HEADER2 .= " + struct FieldType + { + float i; + long j; + double k; + struct FieldType* p; + }; + $DECL_SPEC int fieldType(struct FieldType param, int param_2);"; + $SOURCE2 .= " + int fieldType(struct FieldType param, int param_2) { return param_2; }"; + + # Field_BaseType + $HEADER1 .= " + struct FieldBaseType + { + int i; + long *j; + double k; + struct FieldBaseType* p; + }; + $DECL_SPEC int fieldBaseType(struct FieldBaseType param, int param_2);"; + $SOURCE1 .= " + int fieldBaseType(struct FieldBaseType param, int param_2) { return param_2; }"; + + $HEADER2 .= " + struct FieldBaseType + { + int i; + long long *j; + double k; + struct FieldBaseType* p; + }; + $DECL_SPEC int fieldBaseType(struct FieldBaseType param, int param_2);"; + $SOURCE2 .= " + int fieldBaseType(struct FieldBaseType param, int param_2) { return param_2; }"; + + # Field_PointerLevel (and Size) + $HEADER1 .= " + struct FieldPointerLevelAndSize + { + int i; + long long j; + double k; + struct FieldPointerLevelAndSize* p; + }; + $DECL_SPEC int fieldPointerLevelAndSize(struct FieldPointerLevelAndSize param, int param_2);"; + $SOURCE1 .= " + int fieldPointerLevelAndSize(struct FieldPointerLevelAndSize param, int param_2) { return param_2; }"; + + $HEADER2 .= " + struct FieldPointerLevelAndSize + { + int i; + long long *j; + double k; + struct FieldPointerLevelAndSize* p; + }; + $DECL_SPEC int fieldPointerLevelAndSize(struct FieldPointerLevelAndSize param, int param_2);"; + $SOURCE2 .= " + int fieldPointerLevelAndSize(struct FieldPointerLevelAndSize param, int param_2) { return param_2; }"; + + # Field_PointerLevel + $HEADER1 .= " + struct FieldPointerLevel + { + int i; + long *j; + double k; + struct FieldPointerLevel* p; + }; + $DECL_SPEC int fieldPointerLevel(struct FieldPointerLevel param, int param_2);"; + $SOURCE1 .= " + int fieldPointerLevel(struct FieldPointerLevel param, int param_2) { return param_2; }"; + + $HEADER2 .= " + struct FieldPointerLevel + { + int i; + long **j; + double k; + struct FieldPointerLevel* p; + }; + $DECL_SPEC int fieldPointerLevel(struct FieldPointerLevel param, int param_2);"; + $SOURCE2 .= " + int fieldPointerLevel(struct FieldPointerLevel param, int param_2) { return param_2; }"; + + # Added_Interface + $HEADER2 .= " + $DECL_SPEC int addedFunc4(int param);"; + $SOURCE2 .= " + int addedFunc4(int param) { return param; }"; + + # Removed_Interface + $HEADER1 .= " + $DECL_SPEC int removedFunc(int param);"; + $SOURCE1 .= " + int removedFunc(int param) { return param; }"; + + # Parameter_Type_And_Size + $HEADER1 .= " + $DECL_SPEC int parameterTypeAndSize(int param, int other_param);"; + $SOURCE1 .= " + int parameterTypeAndSize(int param, int other_param) { return other_param; }"; + + $HEADER2 .= " + $DECL_SPEC int parameterTypeAndSize(long long param, int other_param);"; + $SOURCE2 .= " + int parameterTypeAndSize(long long param, int other_param) { return other_param; }"; + + # Parameter_Type_And_Size + Parameter_Became_Non_Const + $HEADER1 .= " + $DECL_SPEC int parameterTypeAndSizeBecameNonConst(int* const param, int other_param);"; + $SOURCE1 .= " + int parameterTypeAndSizeBecameNonConst(int* const param, int other_param) { return other_param; }"; + + $HEADER2 .= " + $DECL_SPEC int parameterTypeAndSizeBecameNonConst(long double param, int other_param);"; + $SOURCE2 .= " + int parameterTypeAndSizeBecameNonConst(long double param, int other_param) { return other_param; }"; + + # Parameter_Type_And_Size (test calling conventions) + $HEADER1 .= " + $DECL_SPEC int parameterCallingConvention(int p1, int p2, int p3);"; + $SOURCE1 .= " + int parameterCallingConvention(int p1, int p2, int p3) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC float parameterCallingConvention(char p1, int p2, int p3);"; + $SOURCE2 .= " + float parameterCallingConvention(char p1, int p2, int p3) { return 7.0f; }"; + + # Parameter_Type + $HEADER1 .= " + $DECL_SPEC int parameterType(int param, int other_param);"; + $SOURCE1 .= " + int parameterType(int param, int other_param) { return other_param; }"; + + $HEADER2 .= " + $DECL_SPEC int parameterType(float param, int other_param);"; + $SOURCE2 .= " + int parameterType(float param, int other_param) { return other_param; }"; + + # Parameter_Became_Non_Const + $HEADER1 .= " + $DECL_SPEC int parameterBecameNonConst(int const* param);"; + $SOURCE1 .= " + int parameterBecameNonConst(int const* param) { return *param; }"; + + $HEADER2 .= " + $DECL_SPEC int parameterBecameNonConst(int* param);"; + $SOURCE2 .= " + int parameterBecameNonConst(int* param) { + *param=10; + return *param; + }"; + + # Parameter_Became_Non_Const + Parameter_Became_Non_Volatile + $HEADER1 .= " + $DECL_SPEC int parameterBecameNonConstNonVolatile(int const volatile* param);"; + $SOURCE1 .= " + int parameterBecameNonConstNonVolatile(int const volatile* param) { return *param; }"; + + $HEADER2 .= " + $DECL_SPEC int parameterBecameNonConstNonVolatile(int* param);"; + $SOURCE2 .= " + int parameterBecameNonConstNonVolatile(int* param) { + *param=10; + return *param; + }"; + + # Parameter_BaseType (Typedef) + $HEADER1 .= " + typedef int* PARAM_TYPEDEF; + $DECL_SPEC int parameterBaseTypedefChange(PARAM_TYPEDEF param);"; + $SOURCE1 .= " + int parameterBaseTypedefChange(PARAM_TYPEDEF param) { return 0; }"; + + $HEADER2 .= " + typedef const int* PARAM_TYPEDEF; + $DECL_SPEC int parameterBaseTypedefChange(PARAM_TYPEDEF param);"; + $SOURCE2 .= " + int parameterBaseTypedefChange(PARAM_TYPEDEF param) { return 0; }"; + + # Parameter_BaseType + $HEADER1 .= " + $DECL_SPEC int parameterBaseTypeChange(int *param);"; + $SOURCE1 .= " + int parameterBaseTypeChange(int *param) { return sizeof(*param); }"; + + $HEADER2 .= " + $DECL_SPEC int parameterBaseTypeChange(long long *param);"; + $SOURCE2 .= " + int parameterBaseTypeChange(long long *param) { return sizeof(*param); }"; + + # Parameter_PointerLevel + $HEADER1 .= " + $DECL_SPEC long long parameterPointerLevelAndSize(long long param);"; + $SOURCE1 .= " + long long parameterPointerLevelAndSize(long long param) { return param; }"; + + $HEADER2 .= " + $DECL_SPEC long long parameterPointerLevelAndSize(long long *param);"; + $SOURCE2 .= " + long long parameterPointerLevelAndSize(long long *param) { return param[5]; }"; + + # Parameter_PointerLevel + $HEADER1 .= " + $DECL_SPEC int parameterPointerLevel(int *param);"; + $SOURCE1 .= " + int parameterPointerLevel(int *param) { return param[5]; }"; + + $HEADER2 .= " + $DECL_SPEC int parameterPointerLevel(int **param);"; + $SOURCE2 .= " + int parameterPointerLevel(int **param) { return param[5][5]; }"; + + # Return_Type_And_Size + $HEADER1 .= " + $DECL_SPEC int returnTypeAndSize(int param);"; + $SOURCE1 .= " + int returnTypeAndSize(int param) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC long long returnTypeAndSize(int param);"; + $SOURCE2 .= " + long long returnTypeAndSize(int param) { return 0; }"; + + # Return_Type + $HEADER1 .= " + $DECL_SPEC int returnType(int param);"; + $SOURCE1 .= " + int returnType(int param) { return 1; }"; + + $HEADER2 .= " + $DECL_SPEC float returnType(int param);"; + $SOURCE2 .= " + float returnType(int param) { return 1; }"; + + # Return_Type_Became_Void ("int" to "void") + $HEADER1 .= " + $DECL_SPEC int returnTypeChangeToVoid(int param);"; + $SOURCE1 .= " + int returnTypeChangeToVoid(int param) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC void returnTypeChangeToVoid(int param);"; + $SOURCE2 .= " + void returnTypeChangeToVoid(int param) { return; }"; + + # Return_Type ("struct" to "void*") + $HEADER1 .= " + struct SomeStruct { + int a; + double b, c, d; + }; + $DECL_SPEC struct SomeStruct* returnTypeChangeToVoidPtr(int param);"; + $SOURCE1 .= " + struct SomeStruct* returnTypeChangeToVoidPtr(int param) { return (struct SomeStruct*)0; }"; + + $HEADER2 .= " + struct SomeStruct { + int a; + double b, c, d; + }; + $DECL_SPEC void* returnTypeChangeToVoidPtr(int param);"; + $SOURCE2 .= " + void* returnTypeChangeToVoidPtr(int param) { return (void*)0; }"; + + # Return_Type (structure change) + $HEADER1 .= " + struct SomeStruct2 { + int a; + int b; + }; + $DECL_SPEC struct SomeStruct2 returnType2(int param);"; + $SOURCE1 .= " + struct SomeStruct2 returnType2(int param) { struct SomeStruct2 r = {1, 2};return r; }"; + + $HEADER2 .= " + struct SomeStruct2 { + int a; + }; + $DECL_SPEC struct SomeStruct2 returnType2(int param);"; + $SOURCE2 .= " + struct SomeStruct2 returnType2(int param) { struct SomeStruct2 r = {1};return r; }"; + + # Return_Type (structure change) + $HEADER1 .= " + struct SomeStruct3 { + int a; + int b; + }; + $DECL_SPEC struct SomeStruct3 returnType3(int param);"; + $SOURCE1 .= " + struct SomeStruct3 returnType3(int param) { struct SomeStruct3 r = {1, 2};return r; }"; + + $HEADER2 .= " + struct SomeStruct3 { + int a; + long double b; + }; + $DECL_SPEC struct SomeStruct3 returnType3(int param);"; + $SOURCE2 .= " + struct SomeStruct3 returnType3(int param) { struct SomeStruct3 r = {1, 2.0L};return r; }"; + + # Return_Type_From_Void_And_Stack_Layout ("void" to "struct") + $HEADER1 .= " + $DECL_SPEC void returnTypeChangeFromVoidToStruct(int param);"; + $SOURCE1 .= " + void returnTypeChangeFromVoidToStruct(int param) { return; }"; + + $HEADER2 .= " + $DECL_SPEC struct SomeStruct returnTypeChangeFromVoidToStruct(int param);"; + $SOURCE2 .= " + struct SomeStruct returnTypeChangeFromVoidToStruct(int param) { + struct SomeStruct obj = {1,2}; + return obj; + }"; + + # Return_Type_Became_Void_And_Stack_Layout ("struct" to "void") + $HEADER1 .= " + $DECL_SPEC struct SomeStruct returnTypeChangeFromStructToVoid(int param);"; + $SOURCE1 .= " + struct SomeStruct returnTypeChangeFromStructToVoid(int param) { + struct SomeStruct obj = {1,2}; + return obj; + }"; + + $HEADER2 .= " + $DECL_SPEC void returnTypeChangeFromStructToVoid(int param);"; + $SOURCE2 .= " + void returnTypeChangeFromStructToVoid(int param) { return; }"; + + # Return_Type_From_Void_And_Stack_Layout (safe, "void" to "long") + $HEADER1 .= " + $DECL_SPEC void returnTypeChangeFromVoidToLong(int param);"; + $SOURCE1 .= " + void returnTypeChangeFromVoidToLong(int param) { return; }"; + + $HEADER2 .= " + $DECL_SPEC long returnTypeChangeFromVoidToLong(int param);"; + $SOURCE2 .= " + long returnTypeChangeFromVoidToLong(int param) { return 0; }"; + + # Return_Type_From_Void_And_Stack_Layout (safe, "void" to "void*") + $HEADER1 .= " + $DECL_SPEC void returnTypeChangeFromVoidToVoidPtr(int param);"; + $SOURCE1 .= " + void returnTypeChangeFromVoidToVoidPtr(int param) { return; }"; + + $HEADER2 .= " + $DECL_SPEC void* returnTypeChangeFromVoidToVoidPtr(int param);"; + $SOURCE2 .= " + void* returnTypeChangeFromVoidToVoidPtr(int param) { return 0; }"; + + # Return_Type_From_Register_To_Stack ("int" to "struct") + $HEADER1 .= " + $DECL_SPEC int returnTypeChangeFromIntToStruct(int param);"; + $SOURCE1 .= " + int returnTypeChangeFromIntToStruct(int param) { return param; }"; + + $HEADER2 .= " + $DECL_SPEC struct SomeStruct returnTypeChangeFromIntToStruct(int param);"; + $SOURCE2 .= " + struct SomeStruct returnTypeChangeFromIntToStruct(int param) { + struct SomeStruct obj = {1,2}; + return obj; + }"; + + # Return_Type_From_Stack_To_Register (from struct to int) + $HEADER1 .= " + $DECL_SPEC struct SomeStruct returnTypeChangeFromStructToInt(int param);"; + $SOURCE1 .= " + struct SomeStruct returnTypeChangeFromStructToInt(int param) { + struct SomeStruct obj = {1,2}; + return obj; + }"; + + $HEADER2 .= " + $DECL_SPEC int returnTypeChangeFromStructToInt(int param);"; + $SOURCE2 .= " + int returnTypeChangeFromStructToInt(int param) { return param; }"; + + # Return_Type_From_Stack_To_Register (from struct to int, without parameters) + $HEADER1 .= " + $DECL_SPEC struct SomeStruct returnTypeChangeFromStructToIntWithNoParams();"; + $SOURCE1 .= " + struct SomeStruct returnTypeChangeFromStructToIntWithNoParams() { + struct SomeStruct obj = {1,2}; + return obj; + }"; + + $HEADER2 .= " + $DECL_SPEC int returnTypeChangeFromStructToIntWithNoParams();"; + $SOURCE2 .= " + int returnTypeChangeFromStructToIntWithNoParams() { return 0; }"; + + # Return_BaseType + $HEADER1 .= " + $DECL_SPEC int *returnBaseTypeChange(int param);"; + $SOURCE1 .= " + int *returnBaseTypeChange(int param) { return (int*)0; }"; + + $HEADER2 .= " + $DECL_SPEC long long *returnBaseTypeChange(int param);"; + $SOURCE2 .= " + long long *returnBaseTypeChange(int param) { return (long long*)0; }"; + + # Return_PointerLevel + $HEADER1 .= " + $DECL_SPEC long long returnPointerLevelAndSize(int param);"; + $SOURCE1 .= " + long long returnPointerLevelAndSize(int param) { return 100; }"; + + $HEADER2 .= " + $DECL_SPEC long long *returnPointerLevelAndSize(int param);"; + $SOURCE2 .= " + long long *returnPointerLevelAndSize(int param) { return (long long *)0; }"; + + # Return_PointerLevel + $HEADER1 .= " + $DECL_SPEC long long *returnPointerLevel(int param);"; + $SOURCE1 .= " + long long *returnPointerLevel(int param) { return (long long *)0; }"; + + $HEADER2 .= " + $DECL_SPEC long long **returnPointerLevel(int param);"; + $SOURCE2 .= " + long long **returnPointerLevel(int param) { return (long long **)0; }"; + + # Size (typedef to anon structure) + $HEADER1 .= " + typedef struct + { + int i; + long j; + double k; + } AnonTypedef; + $DECL_SPEC int anonTypedef(AnonTypedef param);"; + $SOURCE1 .= " + int anonTypedef(AnonTypedef param) { return 0; }"; + + $HEADER2 .= " + typedef struct + { + int i; + long j; + double k; + union { + int dummy[256]; + struct { + char q_skiptable[256]; + const char *p; + int l; + } p; + }; + } AnonTypedef; + $DECL_SPEC int anonTypedef(AnonTypedef param);"; + $SOURCE2 .= " + int anonTypedef(AnonTypedef param) { return 0; }"; + + # Size (safe: opaque) + $HEADER1 .= " + struct OpaqueType + { + long long i[5]; + long j; + double k; + struct OpaqueType* p; + }; + $DECL_SPEC int opaqueTypeUse(struct OpaqueType param, int param_2);"; + $SOURCE1 .= " + int opaqueTypeUse(struct OpaqueType param, int param_2) { return param_2; }"; + + $HEADER2 .= " + struct OpaqueType + { + long long i[5]; + long long j; + double k; + struct OpaqueType* p; + }; + $DECL_SPEC int opaqueTypeUse(struct OpaqueType param, int param_2);"; + $SOURCE2 .= " + int opaqueTypeUse(struct OpaqueType param, int param_2) { return param_2; }"; + + # Size (safe: internal) + $HEADER1 .= " + struct InternalType + { + long long i[5]; + long j; + double k; + struct InternalType* p; + }; + $DECL_SPEC int internalTypeUse(struct InternalType param, int param_2);"; + $SOURCE1 .= " + int internalTypeUse(struct InternalType param, int param_2) { return param_2; }"; + + $HEADER2 .= " + struct InternalType + { + long long i[5]; + long long j; + double k; + struct InternalType* p; + }; + $DECL_SPEC int internalTypeUse(struct InternalType param, int param_2);"; + $SOURCE2 .= " + int internalTypeUse(struct InternalType param, int param_2) { return param_2; }"; + + if($In::Opt{"OS"} eq "linux") + { + # Changed version + $HEADER1 .= " + $DECL_SPEC int changedVersion(int param); + $DECL_SPEC int changedDefaultVersion(int param);"; + $SOURCE1 .= " + int changedVersion(int param) { return 0; } + __asm__(\".symver changedVersion,changedVersion\@VERSION_2.0\"); + int changedDefaultVersion(int param) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC int changedVersion(int param); + $DECL_SPEC int changedDefaultVersion(long param);"; + $SOURCE2 .= " + int changedVersion(int param) { return 0; } + __asm__(\".symver changedVersion,changedVersion\@VERSION_3.0\"); + int changedDefaultVersion(long param) { return 0; }"; + + # Unchanged version + $HEADER1 .= " + $DECL_SPEC int unchangedVersion(int param); + $DECL_SPEC int unchangedDefaultVersion(int param);"; + $SOURCE1 .= " + int unchangedVersion(int param) { return 0; } + __asm__(\".symver unchangedVersion,unchangedVersion\@VERSION_1.0\"); + int unchangedDefaultVersion(int param) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC int unchangedVersion(int param); + $DECL_SPEC int unchangedDefaultVersion(int param);"; + $SOURCE2 .= " + int unchangedVersion(int param) { return 0; } + __asm__(\".symver unchangedVersion,unchangedVersion\@VERSION_1.0\"); + int unchangedDefaultVersion(int param) { return 0; }"; + + # Non-Default to Default + $HEADER1 .= " + $DECL_SPEC int changedVersionToDefault(int param);"; + $SOURCE1 .= " + int changedVersionToDefault(int param) { return 0; } + __asm__(\".symver changedVersionToDefault,changedVersionToDefault\@VERSION_1.0\");"; + + $HEADER2 .= " + $DECL_SPEC int changedVersionToDefault(long param);"; + $SOURCE2 .= " + int changedVersionToDefault(long param) { return 0; }"; + + # Default to Non-Default + $HEADER1 .= " + $DECL_SPEC int changedVersionToNonDefault(int param);"; + $SOURCE1 .= " + int changedVersionToNonDefault(int param) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC int changedVersionToNonDefault(long param);"; + $SOURCE2 .= " + int changedVersionToNonDefault(long param) { return 0; } + __asm__(\".symver changedVersionToNonDefault,changedVersionToNonDefault\@VERSION_3.0\");"; + + # Added version + $HEADER1 .= " + $DECL_SPEC int addedVersion(int param); + $DECL_SPEC int addedDefaultVersion(int param);"; + $SOURCE1 .= " + int addedVersion(int param) { return 0; } + int addedDefaultVersion(int param) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC int addedVersion(int param); + $DECL_SPEC int addedDefaultVersion(int param);"; + $SOURCE2 .= " + int addedVersion(int param) { return 0; } + __asm__(\".symver addedVersion,addedVersion\@VERSION_2.0\"); + int addedDefaultVersion(int param) { return 0; }"; + + # Removed version + $HEADER1 .= " + $DECL_SPEC int removedVersion(int param); + $DECL_SPEC int removedVersion2(int param); + $DECL_SPEC int removedDefaultVersion(int param);"; + $SOURCE1 .= " + int removedVersion(int param) { return 0; } + __asm__(\".symver removedVersion,removedVersion\@VERSION_1.0\"); + int removedVersion2(int param) { return 0; } + __asm__(\".symver removedVersion2,removedVersion\@VERSION_3.0\"); + int removedDefaultVersion(int param) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC int removedVersion(int param); + $DECL_SPEC int removedVersion2(int param); + $DECL_SPEC int removedDefaultVersion(int param);"; + $SOURCE2 .= " + int removedVersion(int param) { return 0; } + int removedVersion2(int param) { return 0; } + __asm__(\".symver removedVersion2,removedVersion\@VERSION_3.0\"); + int removedDefaultVersion(int param) { return 0; }"; + + # Return_Type (good versioning) + $HEADER1 .= " + $DECL_SPEC int goodVersioning(int param);"; + $SOURCE1 .= " + int goodVersioning(int param) { return 0; } + __asm__(\".symver goodVersioning,goodVersioning\@VERSION_1.0\");"; + + $HEADER2 .= " + $DECL_SPEC int goodVersioningOld(int param);"; + $SOURCE2 .= " + int goodVersioningOld(int param) { return 0; } + __asm__(\".symver goodVersioningOld,goodVersioning\@VERSION_1.0\");"; + + $HEADER2 .= " + $DECL_SPEC float goodVersioning(int param);"; + $SOURCE2 .= " + float goodVersioning(int param) { return 0.7; } + __asm__(\".symver goodVersioning,goodVersioning\@VERSION_2.0\");"; + + # Return_Type (bad versioning) + $HEADER1 .= " + $DECL_SPEC int badVersioning(int param);"; + $SOURCE1 .= " + int badVersioning(int param) { return 0; } + __asm__(\".symver badVersioning,badVersioning\@VERSION_1.0\");"; + + $HEADER2 .= " + $DECL_SPEC float badVersioningOld(int param);"; + $SOURCE2 .= " + float badVersioningOld(int param) { return 0.7; } + __asm__(\".symver badVersioningOld,badVersioning\@VERSION_1.0\");"; + + $HEADER2 .= " + $DECL_SPEC float badVersioning(int param);"; + $SOURCE2 .= " + float badVersioning(int param) { return 0.7; } + __asm__(\".symver badVersioning,badVersioning\@VERSION_2.0\");"; + } + # unnamed struct/union fields within structs/unions + $HEADER1 .= " + typedef struct + { + int a; + union { + int b; + float c; + }; + int d; + } UnnamedTypeSize; + $DECL_SPEC int unnamedTypeSize(UnnamedTypeSize param);"; + $SOURCE1 .= " + int unnamedTypeSize(UnnamedTypeSize param) { return 0; }"; + + $HEADER2 .= " + typedef struct + { + int a; + union { + long double b; + float c; + }; + int d; + } UnnamedTypeSize; + $DECL_SPEC int unnamedTypeSize(UnnamedTypeSize param);"; + $SOURCE2 .= " + int unnamedTypeSize(UnnamedTypeSize param) { return 0; }"; + + # Changed_Constant (#define) + $HEADER1 .= " + #define PUBLIC_CONSTANT \"old_value\""; + $HEADER2 .= " + #define PUBLIC_CONSTANT \"new_value\""; + + # Changed_Constant (Safe) + $HEADER1 .= " + #define INTEGER_CONSTANT 0x01"; + $HEADER2 .= " + #define INTEGER_CONSTANT 1"; + + # Changed_Constant (Safe) + $HEADER1 .= " + #define PRIVATE_CONSTANT \"old_value\" + #undef PRIVATE_CONSTANT"; + $HEADER2 .= " + #define PRIVATE_CONSTANT \"new_value\" + #undef PRIVATE_CONSTANT"; + + # Changed_Constant (enum) + $HEADER1 .= " + enum { + SOME_CONSTANT=0x1 + };"; + $HEADER2 .= " + enum { + SOME_CONSTANT=0x2 + };"; + + # Added_Constant (#define) + $HEADER2 .= " + #define ADDED_CNST \"value\""; + + # Added_Constant (enum) + $HEADER1 .= " + enum { + CONSTANT1 + };"; + $HEADER2 .= " + enum { + CONSTANT1, + ADDED_CONSTANT + };"; + + # Removed_Constant (#define) + $HEADER1 .= " + #define REMOVED_CNST \"value\""; + + # Removed_Constant (enum) + $HEADER1 .= " + enum { + CONSTANT2, + REMOVED_CONSTANT + };"; + $HEADER2 .= " + enum { + CONSTANT2 + };"; + + # Added_Field (union) + $HEADER1 .= " + union UnionTypeAddedField + { + int a; + struct { + int b; + float c; + }; + int d; + }; + $DECL_SPEC int unionTypeAddedField(union UnionTypeAddedField param);"; + $SOURCE1 .= " + int unionTypeAddedField(union UnionTypeAddedField param) { return 0; }"; + + $HEADER2 .= " + union UnionTypeAddedField + { + int a; + struct { + long double x, y; + } new_field; + struct { + int b; + float c; + }; + int d; + }; + $DECL_SPEC int unionTypeAddedField(union UnionTypeAddedField param);"; + $SOURCE2 .= " + int unionTypeAddedField(union UnionTypeAddedField param) { return 0; }"; + + # Prameter_BaseType (typedef) + $HEADER1 .= " + typedef float TYPEDEF_TYPE; + $DECL_SPEC int parameterTypedefChange(TYPEDEF_TYPE param);"; + $SOURCE1 .= " + int parameterTypedefChange(TYPEDEF_TYPE param) { return 1.0; }"; + + $HEADER2 .= " + typedef int TYPEDEF_TYPE; + $DECL_SPEC int parameterTypedefChange(TYPEDEF_TYPE param);"; + $SOURCE2 .= " + int parameterTypedefChange(TYPEDEF_TYPE param) { return 1; }"; + + # Field_BaseType (typedef in member type) + $HEADER1 .= " + typedef float TYPEDEF_TYPE_2; + struct FieldBaseTypedefChange { + TYPEDEF_TYPE_2 m; + }; + $DECL_SPEC int fieldBaseTypedefChange(struct FieldBaseTypedefChange param);"; + $SOURCE1 .= " + int fieldBaseTypedefChange(struct FieldBaseTypedefChange param) { return 1; }"; + + $HEADER2 .= " + typedef int TYPEDEF_TYPE_2; + struct FieldBaseTypedefChange { + TYPEDEF_TYPE_2 m; + }; + $DECL_SPEC int fieldBaseTypedefChange(struct FieldBaseTypedefChange param);"; + $SOURCE2 .= " + int fieldBaseTypedefChange(struct FieldBaseTypedefChange param) { return 1; }"; + + # C++ keywords in C code + $HEADER1 .= " + $DECL_SPEC int testCppKeywords1(int class, int virtual, int (*new)(int));"; + $SOURCE1 .= " + $DECL_SPEC int testCppKeywords1(int class, int virtual, int (*new)(int)) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC int testCppKeywords1(int class, int virtual); + $DECL_SPEC int testCppKeywords2(int operator, int other); + $DECL_SPEC int testCppKeywords3(int operator); + $DECL_SPEC int operator(int class, int this); + $DECL_SPEC int delete(int virtual, int* this); + $DECL_SPEC int testCppKeywords4(int* param, int export); + struct CppKeywords { + int bool: 8; + //int*this; + }; + #ifdef __cplusplus + class TestCppKeywords { + void operator delete(void*); + void operator ()(int); + void operator,(int); + void delete() { + delete this; + }; + }; + #endif"; + $SOURCE2 .= " + $DECL_SPEC int testCppKeywords1(int class, int virtual) { return 0; } + $DECL_SPEC int testCppKeywords2(int operator, int other) { return 0; } + $DECL_SPEC int testCppKeywords3(int operator) { return 0; } + $DECL_SPEC int operator(int class, int this) { return 0; } + $DECL_SPEC int delete(int virtual, int* this) { return 0; } + $DECL_SPEC int testCppKeywords4(int* param, int export) { return 0; }"; + + # Regression + $HEADER1 .= " + $DECL_SPEC int* testRegression(int *pointer, char const *name, ...);"; + $SOURCE1 .= " + int* testRegression(int *pointer, char const *name, ...) { return 0; }"; + + $HEADER2 .= " + $DECL_SPEC int* testRegression(int *pointer, char const *name, ...);"; + $SOURCE2 .= " + int* testRegression(int *pointer, char const *name, ...) { return 0; }"; + + runTests("libsample_c", "C", $HEADER1, $SOURCE1, $HEADER2, $SOURCE2, "struct OpaqueType", "internalTypeUse"); +} + +sub runTests($$$$$$$$) +{ + my ($LibName, $Lang, $HEADER1, $SOURCE1, $HEADER2, $SOURCE2, $Opaque, $Private) = @_; + + my $LExt = $In::Opt{"Ext"}; + my $GccPath = $In::Opt{"GccPath"}; + + my $SrcE = ($Lang eq "C++")?"cpp":"c"; + rmtree($LibName); + + my $ObjName = "libsample"; + + # creating test suite + my $Path_v1 = "$LibName/$ObjName.v1"; + my $Path_v2 = "$LibName/$ObjName.v2"; + mkpath($Path_v1); + mkpath($Path_v2); + writeFile("$Path_v1/$ObjName.h", $HEADER1."\n"); + writeFile("$Path_v1/$ObjName.$SrcE", "#include \"$ObjName.h\"\n".$SOURCE1."\n"); + + writeFile("$LibName/v1.xml", " + + 1.0 + + + + ".getAbsPath($Path_v1)." + + + + ".getAbsPath($Path_v1)." + + + + ".getAbsPath($Path_v1)." + + "); + + writeFile("$Path_v1/test.$SrcE", " + #include \"$ObjName.h\" + #include + ".($Lang eq "C++"?"using namespace TestNS;":"")." + int main() + { + int ret = 0; + printf(\"\%d\\n\", ret); + return 0; + } + "); + + writeFile("$Path_v2/$ObjName.h", $HEADER2."\n"); + writeFile("$Path_v2/$ObjName.$SrcE", "#include \"$ObjName.h\"\n".$SOURCE2."\n"); + writeFile("$LibName/v2.xml", " + + 2.0 + + + + ".getAbsPath($Path_v2)." + + + + ".getAbsPath($Path_v2)." + + + + ".getAbsPath($Path_v2)." + + "); + + writeFile("$Path_v2/test.$SrcE", " + #include \"$ObjName.h\" + #include + ".($Lang eq "C++"?"using namespace TestNS;":"")." + int main() + { + int ret = 0; + printf(\"\%d\\n\", ret); + return 0; + } + "); + + writeFile("$LibName/filt.xml", " + + $Opaque + + + + $Private + + "); + + my ($BuildCmd, $BuildCmd_Test) = ("", ""); + + if($In::Opt{"OS"} eq "linux") + { + if($Lang eq "C") + { # tests for symbol versioning + writeFile("$Path_v1/version", " + VERSION_1.0 { + unchangedDefaultVersion; + removedDefaultVersion; + }; + VERSION_2.0 { + changedDefaultVersion; + }; + VERSION_3.0 { + changedVersionToNonDefault; + }; + "); + writeFile("$Path_v2/version", " + VERSION_1.0 { + unchangedDefaultVersion; + changedVersionToDefault; + }; + VERSION_2.0 { + addedDefaultVersion; + }; + VERSION_3.0 { + changedDefaultVersion; + }; + "); + $BuildCmd = $GccPath." -Wl,--version-script version -shared $ObjName.$SrcE -o $ObjName.$LExt -g -Og"; + $BuildCmd_Test = $GccPath." -Wl,--version-script version test.$SrcE -Wl,$ObjName.$LExt -o test"; + } + else + { + $BuildCmd = $GccPath." -shared -x c++ $ObjName.$SrcE -lstdc++ -o $ObjName.$LExt -g -Og"; + $BuildCmd_Test = $GccPath." -x c++ test.$SrcE -lstdc++ -Wl,$ObjName.$LExt -o test"; + } + if(getArch_GCC(1)=~/\A(arm|aarch64|x86_64)\Z/i) + { # relocation R_ARM_MOVW_ABS_NC against `a local symbol' can not be used when making a shared object; recompile with -fPIC + $BuildCmd .= " -fPIC -DPIC"; + $BuildCmd_Test .= " -fPIC -DPIC"; + } + } + elsif($In::Opt{"OS"} eq "macos") + { # using GCC -dynamiclib + if($Lang eq "C") + { + $BuildCmd = $GccPath." -dynamiclib $ObjName.$SrcE -o $ObjName.$LExt"; + $BuildCmd_Test = $GccPath." test.$SrcE $ObjName.$LExt -o test"; + } + else + { # C++ + $BuildCmd = $GccPath." -dynamiclib -x c++ $ObjName.$SrcE -lstdc++ -o $ObjName.$LExt"; + $BuildCmd_Test = $GccPath." -x c++ test.$SrcE $ObjName.$LExt -o test"; + } + } + elsif($In::Opt{"OS"} eq "windows") + { + checkWin32Env(); # to run MS VC++ compiler + my $CL = getCmdPath("cl"); + + if(not $CL) { + exitStatus("Not_Found", "can't find \"cl\" compiler"); + } + $BuildCmd = "$CL /LD $ObjName.$SrcE >build_log.txt 2>&1"; + $BuildCmd_Test = "$CL test.$SrcE $ObjName.$LExt"; + } + else + { # default unix-like + if($Lang eq "C") + { + $BuildCmd = $GccPath." -shared $ObjName.$SrcE -o $ObjName.$LExt -g -Og"; + $BuildCmd_Test = $GccPath." test.$SrcE -Wl,$ObjName.$LExt -o test"; + } + else + { # C++ + $BuildCmd = $GccPath." -shared -x c++ $ObjName.$SrcE -lstdc++ -o $ObjName.$LExt -g -Og"; + $BuildCmd_Test = $GccPath." -x c++ test.$SrcE -Wl,$ObjName.$LExt -o test"; + } + + if(getArch_GCC(1)=~/\A(arm|x86_64)\Z/i) + { + $BuildCmd .= " -fPIC -DPIC"; + $BuildCmd_Test .= " -fPIC -DPIC"; + } + } + + if(my $Opts = getGccOptions(1)) + { # user-defined options + $BuildCmd .= " ".$Opts; + $BuildCmd_Test .= " ".$Opts; + } + + my $MkContent = "all:\n\t$BuildCmd\ntest:\n\t$BuildCmd_Test\n"; + if($In::Opt{"OS"} eq "windows") { + $MkContent .= "clean:\n\tdel test $ObjName.so\n"; + } + else { + $MkContent .= "clean:\n\trm test $ObjName.so\n"; + } + writeFile("$Path_v1/Makefile", $MkContent); + writeFile("$Path_v2/Makefile", $MkContent); + system("cd $Path_v1 && $BuildCmd >build-log.txt 2>&1"); + if($?) + { + my $Msg = "can't compile $LibName v.1: \'$Path_v1/build-log.txt\'"; + if(readFile("$Path_v1/build-log.txt")=~/error trying to exec \W+cc1plus\W+/) { + $Msg .= "\nDid you install G++?"; + } + exitStatus("Error", $Msg); + } + system("cd $Path_v2 && $BuildCmd >build-log.txt 2>&1"); + if($?) { + exitStatus("Error", "can't compile $LibName v.2: \'$Path_v2/build-log.txt\'"); + } + + # executing the tool + my @Cmd = ("perl", $0, "-l", $LibName); + + if($In::Opt{"TestABIDumper"} + and $In::Opt{"OS"} eq "linux") + { + my @Cmd_d1 = ("abi-dumper", $Path_v1."/".$ObjName.".".$LExt, "-o", $LibName."/ABIv1.dump"); + @Cmd_d1 = (@Cmd_d1, "-public-headers", $Path_v1, "-lver", "1.0"); + if($In::Opt{"Debug"}) + { # debug mode + printMsg("INFO", "Executing @Cmd_d1"); + } + system(@Cmd_d1); + printMsg("INFO", ""); + + my @Cmd_d2 = ("abi-dumper", $Path_v2."/".$ObjName.".".$LExt, "-o", $LibName."/ABIv2.dump"); + @Cmd_d2 = (@Cmd_d2, "-public-headers", $Path_v2, "-lver", "2.0"); + if($In::Opt{"Debug"}) + { # debug mode + printMsg("INFO", "Executing @Cmd_d2"); + } + system(@Cmd_d2); + printMsg("INFO", ""); + + @Cmd = (@Cmd, "-old", $LibName."/ABIv1.dump", "-new", $LibName."/ABIv2.dump"); + } + else + { + @Cmd = (@Cmd, "-old", "$LibName/v1.xml", "-new", "$LibName/v2.xml"); + } + + @Cmd = (@Cmd, "-filter", "$LibName/filt.xml"); + + if($Lang eq "C") { + @Cmd = (@Cmd, "-cxx-incompatible"); + } + + @Cmd = (@Cmd, "-lang", $Lang); + + if($In::Opt{"TestDump"}) + { + @Cmd = (@Cmd, "-use-dumps"); + if($In::Opt{"SortDump"}) { + @Cmd = (@Cmd, "-sort"); + } + } + if($In::Opt{"DumpFormat"} and $In::Opt{"DumpFormat"} ne "perl") + { # Perl Data::Dumper is default format + @Cmd = (@Cmd, "-dump-format", $In::Opt{"DumpFormat"}); + } + if($GccPath ne "gcc") { + @Cmd = (@Cmd, "-cross-gcc", $GccPath); + } + if($In::Opt{"Quiet"}) + { # quiet mode + @Cmd = (@Cmd, "-quiet"); + @Cmd = (@Cmd, "-logging-mode", "a"); + } + elsif($In::Opt{"LogMode"} + and $In::Opt{"LogMode"} ne "w") + { # "w" is default + @Cmd = (@Cmd, "-logging-mode", $In::Opt{"LogMode"}); + } + if($In::Opt{"ExtendedCheck"}) + { # extended mode + @Cmd = (@Cmd, "-extended"); + if($Lang eq "C") { + @Cmd = (@Cmd, "-lang", "C"); + } + } + if($In::Opt{"ReportFormat"} + and $In::Opt{"ReportFormat"} ne "html") + { # HTML is default format + @Cmd = (@Cmd, "-report-format", $In::Opt{"ReportFormat"}); + } + if($In::Opt{"CheckHeadersOnly"}) { + @Cmd = (@Cmd, "-headers-only"); + } + if($In::Opt{"OldStyle"}) { + @Cmd = (@Cmd, "-old-style"); + } + + if($In::Opt{"DebugMangling"}) { + @Cmd = (@Cmd, "-debug-mangling"); + } + + if($In::Opt{"Debug"}) + { # debug mode + @Cmd = (@Cmd, "-debug"); + printMsg("INFO", "Executing @Cmd"); + } + + my $RPath = "compat_reports/$LibName/1.0_to_2.0/compat_report.".$In::Opt{"ReportFormat"}; + + if(-f $RPath) { + unlink($RPath); + } + + system(@Cmd); + + my $ECode = $?>>8; + + if($ECode!~/\A[016]\Z/ or not -f $RPath) + { # error + exitStatus("Error", "analysis has failed ($ECode)"); + } + + my $NProblems = 0; + if($In::Opt{"ReportFormat"} eq "xml") + { + my $Content = readFile($RPath); + # binary + if(my $PSummary = parseTag(\$Content, "problem_summary")) + { + $NProblems += int(parseTag(\$PSummary, "removed_symbols")); + if(my $TProblems = parseTag(\$PSummary, "problems_with_types")) + { + $NProblems += int(parseTag(\$TProblems, "high")); + $NProblems += int(parseTag(\$TProblems, "medium")); + } + if(my $IProblems = parseTag(\$PSummary, "problems_with_symbols")) + { + $NProblems += int(parseTag(\$IProblems, "high")); + $NProblems += int(parseTag(\$IProblems, "medium")); + } + } + # source + if(my $PSummary = parseTag(\$Content, "problem_summary")) + { + $NProblems += int(parseTag(\$PSummary, "removed_symbols")); + if(my $TProblems = parseTag(\$PSummary, "problems_with_types")) + { + $NProblems += int(parseTag(\$TProblems, "high")); + $NProblems += int(parseTag(\$TProblems, "medium")); + } + + if(my $IProblems = parseTag(\$PSummary, "problems_with_symbols")) + { + $NProblems += int(parseTag(\$IProblems, "high")); + $NProblems += int(parseTag(\$IProblems, "medium")); + } + } + } + else + { + my $BReport = readAttributes($RPath, 0); + $NProblems += $BReport->{"removed"}; + $NProblems += $BReport->{"type_problems_high"}+$BReport->{"type_problems_medium"}; + $NProblems += $BReport->{"interface_problems_high"}+$BReport->{"interface_problems_medium"}; + + my $SReport = readAttributes($RPath, 1); + $NProblems += $SReport->{"removed"}; + $NProblems += $SReport->{"type_problems_high"}+$SReport->{"type_problems_medium"}; + $NProblems += $SReport->{"interface_problems_high"}+$SReport->{"interface_problems_medium"}; + } + + if(($LibName eq "libsample_c" and $NProblems>70) + or ($LibName eq "libsample_cpp" and $NProblems>150)) { + printMsg("INFO", "Test result: SUCCESS ($NProblems problems found)\n"); + } + else { + printMsg("ERROR", "Test result: FAILED ($NProblems problems found)\n"); + } +} + +return 1; diff --git a/abi-compliance-checker-2.4/modules/Internals/Scripts/Sections.js b/abi-compliance-checker-2.4/modules/Internals/Scripts/Sections.js new file mode 100644 index 0000000..29cde81 --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/Scripts/Sections.js @@ -0,0 +1,16 @@ +function showContent(header, id) +{ + e = document.getElementById(id); + if(e.style.display == 'none') + { + e.style.display = 'block'; + e.style.visibility = 'visible'; + header.innerHTML = header.innerHTML.replace(/\[[^0-9 ]\]/gi,"[−]"); + } + else + { + e.style.display = 'none'; + e.style.visibility = 'hidden'; + header.innerHTML = header.innerHTML.replace(/\[[^0-9 ]\]/gi,"[+]"); + } +} \ No newline at end of file diff --git a/abi-compliance-checker-2.4/modules/Internals/Scripts/Tabs.js b/abi-compliance-checker-2.4/modules/Internals/Scripts/Tabs.js new file mode 100644 index 0000000..b444e98 --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/Scripts/Tabs.js @@ -0,0 +1,61 @@ +function initTabs() +{ + var url = window.location.href; + if(url.indexOf('_Source_')!=-1 || url.indexOf('#Source')!=-1) + { + var tab1 = document.getElementById('BinaryID'); + var tab2 = document.getElementById('SourceID'); + tab1.className='tab disabled'; + tab2.className='tab active'; + } + var sets = document.getElementsByTagName('div'); + for (var i = 0; i < sets.length; i++) + { + if (sets[i].className.indexOf('tabset') != -1) + { + var tabs = []; + var links = sets[i].getElementsByTagName('a'); + for (var j = 0; j < links.length; j++) + { + if (links[j].className.indexOf('tab') != -1) + { + tabs.push(links[j]); + links[j].tabs = tabs; + var tab = document.getElementById(links[j].href.substr(links[j].href.indexOf('#') + 1)); + //reset all tabs on start + if (tab) + { + if (links[j].className.indexOf('active')!=-1) { + tab.style.display = 'block'; + } + else { + tab.style.display = 'none'; + } + } + links[j].onclick = function() + { + var tab = document.getElementById(this.href.substr(this.href.indexOf('#') + 1)); + if (tab) + { + //reset all tabs before change + for (var k = 0; k < this.tabs.length; k++) + { + document.getElementById(this.tabs[k].href.substr(this.tabs[k].href.indexOf('#') + 1)).style.display = 'none'; + this.tabs[k].className = this.tabs[k].className.replace('active', 'disabled'); + } + this.className = 'tab active'; + tab.style.display = 'block'; + // window.location.hash = this.id.replace('ID', ''); + return false; + } + } + } + } + } + } + if(url.indexOf('#')!=-1) { + location.href=location.href; + } +} +if (window.addEventListener) window.addEventListener('load', initTabs, false); +else if (window.attachEvent) window.attachEvent('onload', initTabs); \ No newline at end of file diff --git a/abi-compliance-checker-2.4/modules/Internals/Styles/CmpSystems.css b/abi-compliance-checker-2.4/modules/Internals/Styles/CmpSystems.css new file mode 100644 index 0000000..7552249 --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/Styles/CmpSystems.css @@ -0,0 +1,84 @@ +body { + font-family:Arial; + background-color:White; + color:Black; +} +hr { + color:Black; + background-color:Black; + height:1px; + border:0; +} +h1 { + margin-bottom:0px; + padding-bottom:0px; + font-size:1.625em; +} +table.summary, table.legend { + font-family:"DejaVu Sans Mono", "Monaco", monospace; + font-size:0.94em; + border-collapse:collapse; + table-layout:fixed; +} +table.summary tr, table.summary td, table.summary th, table.legend td +{ + border:1px solid #777777; + border-collapse:collapse; + padding:0.2em; + padding-left:7px; + padding-right:7px; + text-align:center; +} +table.summary th { + background:#f2f2f2; + white-space:nowrap; +} +table.summary td { + padding-top:20px; + padding-bottom:20px; + white-space:nowrap; +} +table.summary td.object { + text-align:left; + max-width:10em; + white-space:normal; + word-wrap:break-word; +} +table.summary td.ver, table.summary th.ver { + max-width:10em; + white-space:normal; + word-wrap:break-word; +} +td.passed { + background-color:#CCFFCC; +} +td.warning { + background-color:#F4F4AF; +} +td.failed { + background-color:#FFCCCC; +} +td.new { + background-color:#C6DEFF; +} +.compatible { + background-color:#CCFFCC; +} +.almost_compatible { + background-color:#FFDAA3; +} +.incompatible { + background-color:#FFCCCC; +} +a.default { + color:#336699; +} +th.severity { + width:55px; +} +sup { + font-size:0.625em; +} +.footer { + font-size:0.75em; +} \ No newline at end of file diff --git a/abi-compliance-checker-2.4/modules/Internals/Styles/HeadersDiff.css b/abi-compliance-checker-2.4/modules/Internals/Styles/HeadersDiff.css new file mode 100644 index 0000000..a0426cb --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/Styles/HeadersDiff.css @@ -0,0 +1,49 @@ +body { + font-family:Arial; + background-color:White; + color:Black; +} +hr { + color:Black; + background-color:Black; + height:1px; + border:0; +} +h1 { + margin-bottom:0px; + padding-bottom:0px; + font-size:1.625em; +} + +.diff_tbl tr { } +.diff_tbl td { + white-space: pre; + font-family: "DejaVu Sans Mono", "Droid Sans Mono", Monaco, Monospace; + vertical-align: top; + font-size: 14px; +} + +.diff_tbl th { + font-size: 16px; +} + +.small { font-size: 0.6em; font-style: italic; font-family: Verdana, Helvetica, sans-serif; } +.left { background-color: #EEE; } +.right { background-color: #FFF; } +.diff { background-color: #CCF; } +.lblock { background-color: #BFB; } +.rblock { background-color: #FF8; } +.insert { background-color: #8FF; } +.delete { background-color: #ACF; } +.void { background-color: #FFB; } +.cont { background-color: #EEE; } +.linebr { background-color: #AAA; } +.lineno { color: red; background-color: #FFF; font-size: 0.7em; text-align: right; padding: 0 2px; } +.elipsis{ background-color: #AAA; } +.left .cont { background-color: #DDD; } +.right .cont { background-color: #EEE; } +.lblock .cont { background-color: #9D9; } +.rblock .cont { background-color: #DD6; } +.insert .cont { background-color: #0DD; } +.delete .cont { background-color: #8AD; } +.stats, .stats td, .stats th { background-color: #EEE; padding: 2px 0; } diff --git a/abi-compliance-checker-2.4/modules/Internals/Styles/Report.css b/abi-compliance-checker-2.4/modules/Internals/Styles/Report.css new file mode 100644 index 0000000..3a95194 --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/Styles/Report.css @@ -0,0 +1,254 @@ +body { + font-family:Arial, sans-serif; + background-color:White; + color:Black; +} +hr { + color:Black; + background-color:Black; + height:1px; + border:0; +} +h1 { + margin-bottom:0px; + padding-bottom:0px; + font-size:1.625em; +} +h2 { + margin-bottom:0px; + padding-bottom:0px; + font-size:1.25em; + white-space:nowrap; +} +span.section { + font-weight:bold; + cursor:pointer; + color:#003E69; + white-space:nowrap; + margin-left:0.3125em; +} +span.new_sign { + font-weight:bold; + margin-left:1.65em; + color:#003E69; +} +span.new_sign_lbl { + margin-left:3em; + font-size:1em; + color:Black; +} +span:hover.section { + color:#336699; +} +span.sect_aff { + cursor:pointer; + padding-left:1.55em; + font-size:0.875em; + color:#cc3300; +} +span.sect_info { + cursor:pointer; + padding-left:1.55em; + font-size:0.875em; + color:Black; +} +span.ext { + font-weight:normal; +} +span.h_name { + color:#cc3300; + font-size:0.875em; + font-weight:bold; +} +div.h_list, div.lib_list { + font-size:0.94em; + padding-left:0.4em; +} +span.ns { + color:#408080; + font-size:0.94em; +} +span.lib_name { + color:Green; + font-size:0.875em; + font-weight:bold; +} +span.iname { + font-weight:bold; + color:#003E69; + margin-left:0.3125em; +} +span.iname_b { + font-weight:bold; +} +span.iname_a { + color:#333333; + font-weight:bold; + font-size:0.94em; +} +span.sym_p { + font-weight:normal; + white-space:normal; +} +span.sym_pd { + white-space:normal; +} +span.sym_p span, span.sym_pd span { + white-space:nowrap; +} +div.affect { + padding-left:1em; + padding-bottom:10px; + font-size:0.87em; + font-style:italic; + line-height:0.9em; +} +div.affected { + padding-left:1.9em; + padding-top:10px; +} +table.ptable { + border-collapse:collapse; + border:1px outset black; + margin-left:0.95em; + margin-top:3px; + margin-bottom:3px; + width:56.25em; +} +table.ptable td { + border:1px solid gray; + padding:3px; + font-size:0.875em; + text-align:left; + vertical-align:top; + max-width:28em; + word-wrap:break-word; +} +table.ptable th.pn { + width:2%; +} +table.ptable th.chg { + width:47%; +} +table.vtable { + border-collapse:collapse; + border:1px outset black; + margin-left:1.9em; + margin-top:0.7em; +} +table.vtable td { + border:1px solid gray; + padding:3px; + font-size:0.875em; + vertical-align:top; + max-width:450px; + word-wrap:break-word; +} +table.ptable th, table.vtable th { + background-color:#eeeeee; + font-weight:bold; + color:#333333; + font-family:Verdana, Arial; + font-size:0.875em; + border:1px solid gray; + text-align:center; + vertical-align:top; + white-space:nowrap; + padding:3px; +} +table.summary { + border-collapse:collapse; + border:1px outset black; +} +table.summary th { + background-color:#eeeeee; + font-weight:normal; + text-align:left; + font-size:0.94em; + white-space:nowrap; + border:1px inset gray; + padding:3px; +} +table.summary td { + text-align:right; + white-space:nowrap; + border:1px inset gray; + padding:3px 5px 3px 10px; +} +span.mngl { + padding-left:1em; + font-size:0.875em; + cursor:text; + color:#444444; + font-weight:bold; +} +span.pleft { + padding-left:2.5em; +} +span.sym_ver { + color:#333333; + white-space:nowrap; + font-family:"DejaVu Sans Mono", Monospace; +} +span.attr { + color:#333333; + font-weight:normal; +} +span.color_p { + font-style:italic; + color:Brown; +} +span.p { + font-style:italic; +} +span.fp { + font-style:italic; + background-color:#DCDCDC; +} +span.ttype { + font-weight:normal; +} +span.nowrap { + white-space:nowrap; +} +span.value { + font-weight:bold; +} +.passed { + background-color:#CCFFCC; + font-weight:normal; +} +.warning { + background-color:#F4F4AF; + font-weight:normal; +} +.failed { + background-color:#FFCCCC; + font-weight:normal; +} +.new { + background-color:#C6DEFF; + font-weight:normal; +} +.compatible { + background-color:#CCFFCC; + font-weight:normal; +} +.almost_compatible { + background-color:#FFDAA3; + font-weight:normal; +} +.incompatible { + background-color:#FFCCCC; + font-weight:normal; +} +.gray { + background-color:#DCDCDC; + font-weight:normal; +} +.top_ref { + font-size:0.69em; +} +.footer { + font-size:0.75em; +} diff --git a/abi-compliance-checker-2.4/modules/Internals/Styles/SymbolsList.css b/abi-compliance-checker-2.4/modules/Internals/Styles/SymbolsList.css new file mode 100644 index 0000000..eaf12de --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/Styles/SymbolsList.css @@ -0,0 +1,79 @@ +body { + font-family:Arial, sans-serif; + background-color:White; + color:Black; +} +hr { + color:Black; + background-color:Black; + height:1px; + border:0; +} +h1 { + margin-bottom:0px; + padding-bottom:0px; + font-size:1.625em; +} +span.iname { + font-weight:bold; + color:#003E69; + margin-left:5px; +} +span.section { + font-weight:bold; + cursor:pointer; + color:#003E69; + white-space:nowrap; + margin-left:5px; +} +span:hover.section { + color:#336699; +} +span.h_name { + color:#cc3300; + font-size:0.875em; + font-weight:bold; +} +span.ns { + color:#408080; + font-size:0.94em; +} +span.lib_name { + color:Green; + font-size:0.875em; + font-weight:bold; +} +span.sym_p { + font-weight:normal; + white-space:normal; +} +span.sym_kind { + color:Black; + font-weight:normal; +} +span.mangled { + padding-left:15px; + font-size:0.875em; + cursor:text; + color:#444444; +} +span.sym_ver { + color:#333333; + white-space:nowrap; +} +span.color_p { + font-style:italic; + color:Brown; +} +span.param { + font-style:italic; +} +span.nowrap { + white-space:nowrap; +} +.top_ref { + font-size:0.69em; +} +.footer { + font-size:0.75em; +} \ No newline at end of file diff --git a/abi-compliance-checker-2.4/modules/Internals/Styles/Tabs.css b/abi-compliance-checker-2.4/modules/Internals/Styles/Tabs.css new file mode 100644 index 0000000..1a90231 --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/Styles/Tabs.css @@ -0,0 +1,34 @@ +.tabset { + float:left; +} +a.tab { + border:1px solid Black; + float:left; + margin:0px 5px -1px 0px; + padding:3px 5px 3px 5px; + position:relative; + font-size:0.875em; + background-color:#DDD; + text-decoration:none; + color:Black; +} +a.disabled:hover +{ + color:Black; + background:#EEE; +} +a.active:hover +{ + color:Black; + background:White; +} +a.active { + border-bottom-color:White; + background-color:White; +} +div.tab { + border-top:1px solid Black; + padding:0px; + width:100%; + clear:both; +} diff --git a/abi-compliance-checker-2.4/modules/Internals/SysCheck.pm b/abi-compliance-checker-2.4/modules/Internals/SysCheck.pm new file mode 100644 index 0000000..841ac7f --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/SysCheck.pm @@ -0,0 +1,2487 @@ +########################################################################### +# A module to compare operating systems +# +# Copyright (C) 2009-2011 Institute for System Programming, RAS +# Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies) +# Copyright (C) 2012-2018 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; +use File::Temp qw(tempdir); +use Cwd qw(abs_path cwd); + +loadModule("ElfTools"); +loadModule("ABIDump"); + +my %SysDesc; +my %NonPrefix; + +sub cmpSystems($$) +{ # -cmp-systems option handler + # should be used with -d1 and -d2 options + my ($SPath1, $SPath2) = @_; + + if(not $SPath1) { + exitStatus("Error", "the option -d1 should be specified"); + } + elsif(not -d $SPath1) { + exitStatus("Access_Error", "can't access directory \'".$SPath1."\'"); + } + elsif(not -d $SPath1."/abi_dumps") { + exitStatus("Access_Error", "can't access directory \'".$SPath1."/abi_dumps\'"); + } + if(not $SPath2) { + exitStatus("Error", "the option -d2 should be specified"); + } + elsif(not -d $SPath2) { + exitStatus("Access_Error", "can't access directory \'".$SPath2."\'"); + } + elsif(not -d $SPath2."/abi_dumps") { + exitStatus("Access_Error", "can't access directory \'".$SPath2."/abi_dumps\'"); + } + # sys_dumps///... + my $SystemName1 = getFilename(getDirname($SPath1)); + my $SystemName2 = getFilename(getDirname($SPath2)); + + my $SystemName1_P = $SystemName1; + my $SystemName2_P = $SystemName2; + + $SystemName1=~s/_/ /g; + $SystemName2=~s/_/ /g; + + # sys_dumps///... + my $ArchName = getFilename($SPath1); + if($ArchName ne getFilename($SPath2)) { + exitStatus("Error", "can't compare systems of different CPU architecture"); + } + + my $TmpDir = $In::Opt{"Tmp"}; + + if(my $OStarget_Dump = readFile($SPath1."/target.txt")) + { # change target + setTarget($OStarget_Dump); + } + + my $GroupByHeaders = 0; + if(my $Mode = readFile($SPath1."/mode.txt")) + { # change mode + if($Mode eq "headers-only") + { # -headers-only mode + $In::Opt{"CheckHeadersOnly"} = 1; + $GroupByHeaders = 1; + } + if($Mode eq "group-by-headers") { + $GroupByHeaders = 1; + } + } + my $SYS_REPORT_PATH = "sys_compat_reports/".$SystemName1_P."_to_".$SystemName2_P."/$ArchName"; + rmtree($SYS_REPORT_PATH); + my (%LibSoname1, %LibSoname2) = (); + foreach (split(/\n/, readFile($SPath1."/sonames.txt"))) + { + if(my ($LFName, $Soname) = split(/;/, $_)) + { + if($In::Opt{"Target"} eq "symbian") { + $Soname=~s/\{.+\}//; + } + $LibSoname1{$LFName} = $Soname; + } + } + foreach (split(/\n/, readFile($SPath2."/sonames.txt"))) + { + if(my ($LFName, $Soname) = split(/;/, $_)) + { + if($In::Opt{"Target"} eq "symbian") { + $Soname=~s/\{.+\}//; + } + $LibSoname2{$LFName} = $Soname; + } + } + my (%LibV1, %LibV2) = (); + foreach (split(/\n/, readFile($SPath1."/versions.txt"))) + { + if(my ($LFName, $V) = split(/;/, $_)) { + $LibV1{$LFName} = $V; + } + } + foreach (split(/\n/, readFile($SPath2."/versions.txt"))) + { + if(my ($LFName, $V) = split(/;/, $_)) { + $LibV2{$LFName} = $V; + } + } + my @Dumps1 = cmdFind($SPath1."/abi_dumps","f","*.abi",1); + my @Dumps2 = cmdFind($SPath2."/abi_dumps","f","*.abi",1); + + my (%LibVers1, %LibVers2) = (); + my (%ShortNames1, %ShortNames2) = (); + foreach my $DPath (@Dumps1) + { + if(my $Name = isDump($DPath)) + { + my ($Soname, $V) = ($LibSoname1{$Name}, $LibV1{$Name}); + if(not $V) { + $V = libPart($Name, "version"); + } + if($GroupByHeaders) { + $Soname = $Name; + } + $LibVers1{$Soname}{$V} = $DPath; + $ShortNames1{libPart($Soname, "short")}{$Soname} = 1; + } + } + foreach my $DPath (@Dumps2) + { + if(my $Name = isDump($DPath)) + { + my ($Soname, $V) = ($LibSoname2{$Name}, $LibV2{$Name}); + if(not $V) { + $V = libPart($Name, "version"); + } + if($GroupByHeaders) { + $Soname = $Name; + } + $LibVers2{$Soname}{$V} = $DPath; + $ShortNames2{libPart($Soname, "short")}{$Soname} = 1; + } + } + my (%Added, %Removed) = (); + my (%ChangedSoname, %TestResults) = (); + my (%AddedShort, %RemovedShort) = (); + if(not $GroupByHeaders) + { + my %ChangedSoname_Safe = (); + foreach my $LName (sort keys(%LibSoname2)) + { # libcurl.so.3 -> libcurl.so.4 (search for SONAME by the file name) + # OS #1 => OS #2 + if(defined $LibVers2{$LName}) + { # already registered + next; + } + my $Soname = $LibSoname2{$LName}; + if(defined $LibVers2{$Soname} + and defined $LibVers1{$LName}) + { + $LibVers2{$LName} = $LibVers2{$Soname}; + $ChangedSoname_Safe{$Soname}=$LName; + } + } + foreach my $LName (sort keys(%LibSoname1)) + { # libcurl.so.3 -> libcurl.so.4 (search for SONAME by the file name) + # OS #1 <= OS #2 + if(defined $LibVers1{$LName}) + { # already registered + next; + } + my $Soname = $LibSoname1{$LName}; + if(defined $LibVers1{$Soname} + and defined $LibVers2{$LName}) { + $LibVers1{$LName} = $LibVers1{$Soname}; + } + } + if(not $GroupByHeaders) { + printMsg("INFO", "Checking added/removed libs"); + } + foreach my $LName (sort {lc($a) cmp lc($b)} keys(%LibVers1)) + { # removed libs + if(not isTargetLib($LName)) { + next; + } + if(not defined $LibVers1{$LName}) { + next; + } + my @Versions1 = keys(%{$LibVers1{$LName}}); + if($#Versions1>=1) + { # should be only one version + next; + } + if(not defined $LibVers2{$LName} + or not keys(%{$LibVers2{$LName}})) + { # removed library + if(not $LibSoname2{$LName}) + { + my $LSName = libPart($LName, "short"); + $RemovedShort{$LSName}{$LName} = 1; + my $V = $Versions1[0]; + $Removed{$LName}{"version"} = $V; + + my $ListPath = "info/$LName/symbols.html"; + my $FV = $SystemName1; + if($V) { + $FV = $V."-".$FV; + } + createSymbolsList($LibVers1{$LName}{$V}, + $SYS_REPORT_PATH."/".$ListPath, $LName, $FV, $ArchName); + $Removed{$LName}{"list"} = $ListPath; + } + } + } + foreach my $LName (sort {lc($a) cmp lc($b)} keys(%LibVers2)) + { # added libs + if(not isTargetLib($LName)) { + next; + } + if(not defined $LibVers2{$LName}) { + next; + } + my @Versions2 = keys(%{$LibVers2{$LName}}); + if($#Versions2>=1) + { # should be only one version + next; + } + if($ChangedSoname_Safe{$LName}) + { # changed soname but added the symbolic link for old-version library + next; + } + if(not defined $LibVers1{$LName} + or not keys(%{$LibVers1{$LName}})) + { # added library + if(not $LibSoname1{$LName}) + { + my $LSName = libPart($LName, "short"); + $AddedShort{$LSName}{$LName} = 1; + my $V = $Versions2[0]; + $Added{$LName}{"version"} = $V; + + my $ListPath = "info/$LName/symbols.html"; + my $FV = $SystemName2; + if($V) { + $FV = $V."-".$FV; + } + createSymbolsList($LibVers2{$LName}{$V}, + $SYS_REPORT_PATH."/".$ListPath, $LName, $FV, $ArchName); + $Added{$LName}{"list"} = $ListPath; + } + } + } + foreach my $LSName (keys(%AddedShort)) + { # changed SONAME + my @AddedSonames = sort keys(%{$AddedShort{$LSName}}); + next if($#AddedSonames!=0); + + if(defined $RemovedShort{$LSName}) + { # removed old soname + my @RemovedSonames = sort keys(%{$RemovedShort{$LSName}}); + $ChangedSoname{$AddedSonames[0]} = $RemovedSonames[0]; + $ChangedSoname{$RemovedSonames[0]} = $AddedSonames[0]; + } + elsif(defined $ShortNames1{$LSName}) + { # saved old soname + my @Sonames = sort keys(%{$ShortNames1{$LSName}}); + $ChangedSoname{$AddedSonames[0]} = $Sonames[0]; + $ChangedSoname{$Sonames[0]} = $AddedSonames[0]; + } + } + } + + my %SONAME_Changed = (); + my %SONAME_Added = (); + + foreach my $LName (sort {lc($a) cmp lc($b)} keys(%LibVers1)) + { + if(not isTargetLib($LName)) { + next; + } + my @Versions1 = keys(%{$LibVers1{$LName}}); + if(not @Versions1 or $#Versions1>=1) + { # should be only one version + next; + } + my $LV1 = $Versions1[0]; + my $DPath1 = $LibVers1{$LName}{$LV1}; + my @Versions2 = keys(%{$LibVers2{$LName}}); + if($#Versions2>=1) + { # should be only one version + next; + } + my ($LV2, $LName2, $DPath2) = (); + my $LName_Short = libPart($LName, "name+ext"); + if($LName2 = $ChangedSoname{$LName}) + { # changed SONAME + @Versions2 = keys(%{$LibVers2{$LName2}}); + if(not @Versions2 or $#Versions2>=1) { + next; + } + $LV2 = $Versions2[0]; + $DPath2 = $LibVers2{$LName2}{$LV2}; + + if(defined $LibVers2{$LName}) + { # show old soname in the table + $TestResults{$LName}{"v1"} = $LV1; + $TestResults{$LName}{"v2"} = $LV1; + } + + if(defined $LibVers2{$LName}) + { # do not count results + $SONAME_Added{$LName_Short} = 1; + } + $SONAME_Changed{$LName_Short} = 1; + $LName = $LName_Short; + } + elsif(@Versions2) + { + $LV2 = $Versions2[0]; + $DPath2 = $LibVers2{$LName}{$LV2}; + } + else + { # removed + next; + } + my $ACC_compare = "perl $0 -l $LName -d1 \"$DPath1\" -d2 \"$DPath2\""; + + my $BinReportPath = "compat_reports/$LName/abi_compat_report.html"; + my $SrcReportPath = "compat_reports/$LName/src_compat_report.html"; + my $BinReportPath_Full = $SYS_REPORT_PATH."/".$BinReportPath; + my $SrcReportPath_Full = $SYS_REPORT_PATH."/".$SrcReportPath; + + if($In::Opt{"BinOnly"}) + { + $ACC_compare .= " -binary"; + $ACC_compare .= " -bin-report-path \"$BinReportPath_Full\""; + } + if($In::Opt{"SrcOnly"}) + { + $ACC_compare .= " -source"; + $ACC_compare .= " -src-report-path \"$SrcReportPath_Full\""; + } + + if($In::Opt{"CheckHeadersOnly"}) { + $ACC_compare .= " -headers-only"; + } + if($GroupByHeaders) { + $ACC_compare .= " -component header"; + } + + if($In::Opt{"DisableConstantsCheck"}) { + $ACC_compare .= " -disable-constants-check"; + } + + $ACC_compare .= " -skip-added-constants"; + $ACC_compare .= " -skip-removed-constants"; + + if($In::Opt{"Quiet"}) + { # quiet mode + $ACC_compare .= " -quiet"; + } + if($In::Opt{"LogMode"} eq "n") { + $ACC_compare .= " -logging-mode n"; + } + elsif($In::Opt{"Quiet"}) { + $ACC_compare .= " -logging-mode a"; + } + if($In::Opt{"Debug"}) + { # debug mode + $ACC_compare .= " -debug"; + printMsg("INFO", "$ACC_compare"); + } + printMsg("INFO_C", "Checking $LName: "); + system($ACC_compare." 1>$TmpDir/null 2>$TmpDir/$LName.stderr"); + if(-s "$TmpDir/$LName.stderr") + { + my $ErrorLog = readFile("$TmpDir/$LName.stderr"); + chomp($ErrorLog); + printMsg("INFO", "Failed ($ErrorLog)"); + } + else + { + printMsg("INFO", "Ok"); + if($In::Opt{"BinOnly"}) + { + $TestResults{$LName}{"Binary"} = readAttributes($BinReportPath_Full, 0); + $TestResults{$LName}{"Binary"}{"path"} = $BinReportPath; + } + if($In::Opt{"SrcOnly"}) + { + $TestResults{$LName}{"Source"} = readAttributes($SrcReportPath_Full, 0); + $TestResults{$LName}{"Source"}{"path"} = $SrcReportPath; + } + $TestResults{$LName}{"v1"} = $LV1; + $TestResults{$LName}{"v2"} = $LV2; + } + + my $HP1 = $SPath1."/headers/".$LName; + my $HP2 = $SPath2."/headers/".$LName; + + if(-d $HP1 + and -d $HP2 + and my $RfcDiff = getCmdPath("rfcdiff")) + { + my @Headers1 = cmdFind($HP1,"f"); + my @Headers2 = cmdFind($HP2,"f"); + + my (%Files1, %Files2) = (); + + foreach my $P (@Headers1) { + $Files1{getFilename($P)} = $P; + } + + foreach my $P (@Headers2) { + $Files2{getFilename($P)} = $P; + } + + my $Diff = ""; + foreach my $N (sort {lc($a) cmp lc($b)} keys(%Files1)) + { + my $Path1 = $Files1{$N}; + my $Path2 = undef; + + if(defined $Files2{$N}) { + $Path2 = $Files2{$N}; + } + else { + next; + } + + if(-s $Path1 == -s $Path2) + { + if(readFile($Path1) eq readFile($Path2)) { + next; + } + } + + my $DiffOut = $TmpDir."/rfcdiff"; + + if(-e $DiffOut) { + unlink($DiffOut); + } + + my $Cmd_R = $RfcDiff." --width 80 --stdout \"$Path1\" \"$Path2\" >$DiffOut 2>/dev/null"; + qx/$Cmd_R/; # execute + + if(-s $DiffOut) + { + my $Content = readFile($DiffOut); + if(length($Content)<3500 and $Content=~/The files are identical|No changes|Failed to create/i) { + next; + } + + $Content=~s/<\!--(.|\n)+?-->\s*//g; + $Content=~s/\A((.|\n)+)((.|\n)+)(<\/body>(.|\n)+)\Z/$3/; + $Content=~s/(]*>)(.+)(<\/td>)/$1$3/; + $Content=~s/(&&g; + $Content=~s&&&g; + $Content=~s&&&g; + $Content=~s&&&g; + + $Content=~s/(\Q$N\E)( )/$1 ($LV1-$SystemName1)$2/; + $Content=~s/(\Q$N\E)( )/$1 ($LV2-$SystemName2)$2/; + + if($Diff) { + $Diff .= "

\n"; + } + $Diff .= $Content; + } + } + + if($Diff) + { + my $Title = $LName.": headers diff between $LV1-$SystemName1 and $LV2-$SystemName2 versions"; + my $Keywords = $LName.", header, diff"; + my $Description = "Diff for header files between $LV1-$SystemName1 and $LV2-$SystemName2 versions of $LName"; + my $Styles = readModule("Styles", "HeadersDiff.css"); + + my $Link = "This html diff was produced by rfcdiff 1.41."; + + $Diff .= "
"; + $Diff .= "
$Link
\n"; + + $Diff = "

Headers diff for $LName between $LV1-$SystemName1 and $LV2-$SystemName2 versions



".$Diff; + + $Diff = "
$Diff
"; + + $Diff = composeHTML_Head($Title, $Keywords, $Description, $Styles, "", 1)."\n\n$Diff\n\n\n"; + + my $Output = $SYS_REPORT_PATH."/headers_diff/$LName"; + writeFile($Output."/diff.html", $Diff); + } + } + } + + my %TOTAL = (); + foreach my $LName (keys(%TestResults)) + { + if($SONAME_Changed{$LName}) { + next; + } + foreach my $Comp ("Binary", "Source") + { + if(not defined $TestResults{$LName}{$Comp}) { + next; + } + foreach my $Kind (keys(%{$TestResults{$LName}{$Comp}})) + { + if($Kind=~/_problems_(high|medium|low)/) { + $TOTAL{$LName}{$Comp} += $TestResults{$LName}{$Comp}{$Kind}; + } + } + } + } + + my %META_DATA = (); + my %STAT = (); + foreach my $Comp ("Binary", "Source") + { + $STAT{$Comp}{"total"} = keys(%TestResults) - keys(%SONAME_Changed); + $STAT{$Comp}{"added"} = keys(%Added); + $STAT{$Comp}{"removed"} = keys(%Removed); + + foreach ("added", "removed") + { + my $Kind = $_."_interfaces"; + foreach my $LName (keys(%TestResults)) + { + next if($SONAME_Changed{$LName}); + $STAT{$Comp}{$Kind} += $TestResults{$LName}{$Comp}{$_}; + } + push(@{$META_DATA{$Comp}}, $Kind.":".$STAT{$Comp}{$Kind}); + } + foreach my $T ("type", "interface") + { + foreach my $S ("high", "medium", "low") + { + my $Kind = $T."_problems_".$S; + foreach my $LName (keys(%TestResults)) + { + next if($SONAME_Changed{$LName}); + $STAT{$Comp}{$Kind} += $TestResults{$LName}{$Comp}{$Kind}; + } + push(@{$META_DATA{$Comp}}, $Kind.":".$STAT{$Comp}{$Kind}); + } + } + foreach my $LName (keys(%TestResults)) + { + next if($SONAME_Changed{$LName}); + foreach ("affected", "changed_constants") { + $STAT{$Comp}{$_} += $TestResults{$LName}{$Comp}{$_}; + } + if(not defined $STAT{$Comp}{"verdict"} + and $TestResults{$LName}{$Comp}{"verdict"} eq "incompatible") { + $STAT{$Comp}{"verdict"} = "incompatible"; + } + } + if(not defined $STAT{$Comp}{"verdict"}) { + $STAT{$Comp}{"verdict"} = "compatible"; + } + if($STAT{$Comp}{"total"}) { + $STAT{$Comp}{"affected"} /= $STAT{$Comp}{"total"}; + } + else { + $STAT{$Comp}{"affected"} = 0; + } + $STAT{$Comp}{"affected"} = showNum($STAT{$Comp}{"affected"}); + if($STAT{$Comp}{"verdict"}>1) { + $STAT{$Comp}{"verdict"} = 1; + } + push(@{$META_DATA{$Comp}}, "changed_constants:".$STAT{$Comp}{"changed_constants"}); + push(@{$META_DATA{$Comp}}, "tool_version:".dumpVersion("perl $0")); + foreach ("removed", "added", "total", "affected", "verdict") { + @{$META_DATA{$Comp}} = ($_.":".$STAT{$Comp}{$_}, @{$META_DATA{$Comp}}); + } + } + + my $SONAME_Title = "SONAME"; + if($In::Opt{"Target"} eq "windows") { + $SONAME_Title = "DLL"; + } + elsif($In::Opt{"Target"} eq "symbian") { + $SONAME_Title = "DSO"; + } + if($GroupByHeaders) + { # show the list of headers + $SONAME_Title = "Header File"; + } + + my $SYS_REPORT = "

"; + + if($In::Opt{"BinOnly"} + and $In::Opt{"SrcOnly"}) { + $SYS_REPORT .= "API compatibility"; + } + elsif($In::Opt{"BinOnly"}) { + $SYS_REPORT .= "Binary compatibility"; + } + elsif($In::Opt{"SrcOnly"}) { + $SYS_REPORT .= "Source compatibility"; + } + + $SYS_REPORT .= " report between $SystemName1 and $SystemName2"; + $SYS_REPORT .= " on ".showArch($ArchName)."\n"; + + $SYS_REPORT .= "

"; + $SYS_REPORT .= "
\n"; + + # legend + my $LEGEND = "\n"; + $LEGEND .= "\n"; + $LEGEND .= "\n"; + $LEGEND .= "\n"; + $LEGEND .= "\n"; + $LEGEND .= "\n"; + $LEGEND .= "
ADDEDCOMPATIBLE
WARNINGINCOMPATIBLE
\n"; + + $SYS_REPORT .= $LEGEND; + $SYS_REPORT .= "
\n"; + + my $Columns = 2; + + my $Total = (keys(%TestResults) + keys(%Added) + keys(%Removed) - keys(%SONAME_Changed)); + my $HDiff = $SYS_REPORT_PATH."/headers_diff"; + + $SYS_REPORT .= "\n"; + $SYS_REPORT .= "\n"; + $SYS_REPORT .= "\n"; + if(not $GroupByHeaders) { + $SYS_REPORT .= "\n"; + } + if($In::Opt{"BinOnly"} + and $In::Opt{"SrcOnly"}) { + $SYS_REPORT .= "\n"; + } + else { + $SYS_REPORT .= "\n"; + } + $SYS_REPORT .= "\n"; + $SYS_REPORT .= "\n"; + if(-d $HDiff) + { + $SYS_REPORT .= "\n"; + $Columns += 1; + } + $SYS_REPORT .= "\n"; + + $SYS_REPORT .= "\n"; + if(not $GroupByHeaders) { + $SYS_REPORT .= "\n"; + } + if($In::Opt{"BinOnly"} + and $In::Opt{"SrcOnly"}) { + $SYS_REPORT .= "\n"; + } + $SYS_REPORT .= "\n"; + my %RegisteredPairs = (); + + foreach my $LName (sort {lc($a) cmp lc($b)} (keys(%TestResults), keys(%Added), keys(%Removed))) + { + next if($SONAME_Changed{$LName}); + my $LName_Short = libPart($LName, "name+ext"); + my $Anchor = $LName; + $Anchor=~s/\+/p/g; # anchor for libFLAC++ is libFLACpp + $Anchor=~s/\~/-/g; # libqttracker.so.1~6 + + $SYS_REPORT .= "\n"; + $SYS_REPORT .= "\n"; + if(defined $Removed{$LName}) { + $SYS_REPORT .= "\n"; + } + elsif(defined $Added{$LName}) { + $SYS_REPORT .= "\n"; + } + elsif(not $GroupByHeaders) + { + $SYS_REPORT .= "\n"; + } + my $SONAME_report = "\n"; + + if(defined $Added{$LName}) + { # added library + $SYS_REPORT .= "\n"; + $SYS_REPORT .= "\n" if($In::Opt{"BinOnly"}); + $SYS_REPORT .= "\n" if($In::Opt{"SrcOnly"}); + if($RegisteredPairs{$LName}) { + # do nothing + } + elsif(my $To = $ChangedSoname{$LName}) + { + $RegisteredPairs{$To}=1; + $SYS_REPORT .= $SONAME_report; + } + else + { + foreach (1 .. $Columns) { + $SYS_REPORT .= "\n"; # colspan='5' + } + } + $SYS_REPORT .= "\n"; + next; + } + elsif(defined $Removed{$LName}) + { # removed library + $SYS_REPORT .= "\n"; + $SYS_REPORT .= "\n" if($In::Opt{"BinOnly"}); + $SYS_REPORT .= "\n" if($In::Opt{"SrcOnly"}); + if($RegisteredPairs{$LName}) { + # do nothing + } + elsif(my $To = $ChangedSoname{$LName}) + { + $RegisteredPairs{$To}=1; + $SYS_REPORT .= $SONAME_report; + } + else + { + foreach (1 .. $Columns) { + $SYS_REPORT .= "\n"; # colspan='5' + } + } + $SYS_REPORT .= "\n"; + next; + } + elsif(defined $ChangedSoname{$LName}) + { # added library + $SYS_REPORT .= "\n"; + $SYS_REPORT .= "\n" if($In::Opt{"BinOnly"}); + $SYS_REPORT .= "\n" if($In::Opt{"SrcOnly"}); + if($RegisteredPairs{$LName}) { + # do nothing + } + elsif(my $To = $ChangedSoname{$LName}) + { + $RegisteredPairs{$To}=1; + $SYS_REPORT .= $SONAME_report; + } + else + { + foreach (1 .. $Columns) { + $SYS_REPORT .= "\n"; # colspan='5' + } + } + $SYS_REPORT .= "\n"; + next; + } + elsif(not $GroupByHeaders) + { + $SYS_REPORT .= "\n"; + } + + my $BinCompatReport = $TestResults{$LName}{"Binary"}{"path"}; + my $SrcCompatReport = $TestResults{$LName}{"Source"}{"path"}; + + if($In::Opt{"BinOnly"}) + { + if($TestResults{$LName}{"Binary"}{"verdict"} eq "compatible") + { + my $Cl = "passed"; + if($TOTAL{$LName}{"Binary"}) { + $Cl = "warning"; + } + $SYS_REPORT .= "\n"; + } + else + { + my $Compatible = 100 - $TestResults{$LName}{"Binary"}{"affected"}; + my $Cl = "incompatible"; + if($Compatible>=90) { + $Cl = "warning"; + } + elsif($Compatible>=80) { + $Cl = "almost_compatible"; + } + $SYS_REPORT .= "\n"; + } + } + if($In::Opt{"SrcOnly"}) + { + if($TestResults{$LName}{"Source"}{"verdict"} eq "compatible") + { + my $Cl = "passed"; + if($TOTAL{$LName}{"Source"}) { + $Cl = "warning"; + } + $SYS_REPORT .= "\n"; + } + else + { + my $Compatible = 100 - $TestResults{$LName}{"Source"}{"affected"}; + my $Cl = "incompatible"; + if($Compatible>=90) { + $Cl = "warning"; + } + elsif($Compatible>=80) { + $Cl = "almost_compatible"; + } + $SYS_REPORT .= "\n"; + } + } + if($In::Opt{"BinOnly"}) + { # show added/removed symbols at binary level + # for joined and -binary-only reports + my $AddedSym=""; + if(my $Count = $TestResults{$LName}{"Binary"}{"added"}) { + $AddedSym="$Count new"; + } + if($AddedSym) { + $SYS_REPORT.="\n"; + } + else { + $SYS_REPORT.="\n"; + } + my $RemovedSym=""; + if(my $Count = $TestResults{$LName}{"Binary"}{"removed"}) { + $RemovedSym="$Count removed"; + } + if($RemovedSym) { + $SYS_REPORT.="\n"; + } + else { + $SYS_REPORT.="\n"; + } + } + elsif($In::Opt{"SrcOnly"}) + { + my $AddedSym=""; + if(my $Count = $TestResults{$LName}{"Source"}{"added"}) { + $AddedSym="$Count new"; + } + if($AddedSym) { + $SYS_REPORT.="\n"; + } + else { + $SYS_REPORT.="\n"; + } + my $RemovedSym=""; + if(my $Count = $TestResults{$LName}{"Source"}{"removed"}) { + $RemovedSym="$Count removed"; + } + if($RemovedSym) { + $SYS_REPORT.="\n"; + } + else { + $SYS_REPORT.="\n"; + } + } + + if(-d $HDiff) + { + if(-d $HDiff."/".$LName) { + $SYS_REPORT .= "\n"; + } + elsif(defined $Added{$LName} or defined $Removed{$LName}) { + $SYS_REPORT .= "\n"; + } + else { + $SYS_REPORT .= "\n"; + } + } + + $SYS_REPORT .= "\n"; + } + + $SYS_REPORT .= "
$SONAME_Title$TotalVersionCompatibilityCompatibilityAdded
Symbols
Removed
Symbols
Headers
Diff
$SystemName1$SystemName2BinarySource
$LName".printVer($Removed{$LName}{"version"})."added".printVer($TestResults{$LName}{"v1"})."\n"; + if($In::Opt{"BinOnly"} + and $In::Opt{"SrcOnly"}) { + $SONAME_report .= "SONAME has been changed (see binary and source compatibility reports)\n"; + } + elsif($In::Opt{"BinOnly"}) { + $SONAME_report .= "SONAME has been changed\n"; + } + elsif($In::Opt{"SrcOnly"}) { + $SONAME_report .= "SONAME has been changed\n"; + } + $SONAME_report .= "".printVer($Added{$LName}{"version"})."100%100%N/A
removed0%0%N/A
".printVer($TestResults{$LName}{"v2"})."100%100%N/A
".printVer($TestResults{$LName}{"v2"})."100%$Compatible%100%$Compatible%$AddedSym0$RemovedSym0$AddedSym0$RemovedSym0diffN/AEmpty
"; + + my $Title = "$SystemName1 vs $SystemName2 compatibility report"; + my $Keywords = "compatibility, $SystemName1, $SystemName2, API, changes"; + my $Description = "API compatibility report between $SystemName1 and $SystemName2 on ".showArch($ArchName); + my $Styles = readModule("Styles", "CmpSystems.css"); + + $SYS_REPORT = composeHTML_Head($Title, $Keywords, $Description, $Styles, "", 1)."\n\n
".$SYS_REPORT."
\n"; + $SYS_REPORT .= "

\n"; + $SYS_REPORT .= getReportFooter(); + $SYS_REPORT .= "\n"; + + if($In::Opt{"SrcOnly"}) { + $SYS_REPORT = "\n".$SYS_REPORT; + } + if($In::Opt{"BinOnly"}) { + $SYS_REPORT = "\n".$SYS_REPORT; + } + my $REPORT_PATH = $SYS_REPORT_PATH."/"; + if($In::Opt{"BinOnly"} and $In::Opt{"SrcOnly"}) { + $REPORT_PATH .= "compat_report.html"; + } + elsif($In::Opt{"BinOnly"}) { + $REPORT_PATH .= "abi_compat_report.html"; + } + elsif($In::Opt{"SrcOnly"}) { + $REPORT_PATH .= "src_compat_report.html"; + } + writeFile($REPORT_PATH, $SYS_REPORT); + printMsg("INFO", "\nSee detailed report:\n $REPORT_PATH"); +} + +sub printVer($) +{ + if($_[0] eq "") { + return 0; + } + return $_[0]; +} + +sub getPrefix_S($) +{ + my $Prefix = getPrefix($_[0]); + if(not $Prefix or defined $NonPrefix{lc($Prefix)}) { + return "NONE"; + } + return $Prefix; +} + +sub readSysDesc($) +{ + my $Content = $_[0]; + + $Content=~s/\/\*(.|\n)+?\*\///g; + $Content=~s/<\!--(.|\n)+?-->//g; + + $SysDesc{"Name"} = parseTag(\$Content, "name"); + + if(not $SysDesc{"Name"}) { + exitStatus("Error", "system name is not specified ( section)"); + } + foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "libs"))) + { # target libs + if(not -e $Path) { + exitStatus("Access_Error", "can't access \'$Path\'"); + } + $Path = getAbsPath($Path); + $SysDesc{"Libs"}{$Path} = 1; + } + foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_libs"))) + { # target libs + if(not -d $Path) { + exitStatus("Access_Error", "can't access directory \'$Path\'"); + } + $Path = getAbsPath($Path); + $SysDesc{"SearchLibs"}{$Path} = 1; + } + foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_libs"))) + { # skip libs + $SysDesc{"SkipLibs"}{$Path} = 1; + } + foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "headers"))) + { + if(not -e $Path) { + exitStatus("Access_Error", "can't access \'$Path\'"); + } + $Path = getAbsPath($Path); + $SysDesc{"Headers"}{$Path} = 1; + } + foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_headers"))) + { + if(not -d $Path) { + exitStatus("Access_Error", "can't access directory \'$Path\'"); + } + $Path = getAbsPath($Path); + $SysDesc{"SearchHeaders"}{$Path} = 1; + } + foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "tools"))) + { + if(not -d $Path) { + exitStatus("Access_Error", "can't access directory \'$Path\'"); + } + $Path = getAbsPath($Path); + $SysDesc{"Tools"}{$Path} = 1; + + $In::Opt{"TargetTools"}{$Path} = 1; + push_U($In::Opt{"SysPaths"}{"bin"}, $Path); + } + foreach my $Op (split(/\s*\n\s*/, parseTag(\$Content, "gcc_options"))) { + $SysDesc{"GccOpts"}{$Op} = 1; + } + if($SysDesc{"CrossPrefix"} = parseTag(\$Content, "cross_prefix")) + { # section of XML descriptor + $In::Opt{"CrossPrefix"} = $SysDesc{"CrossPrefix"}; + } + elsif($In::Opt{"CrossPrefix"}) + { # -cross-prefix option + $SysDesc{"CrossPrefix"} = $In::Opt{"CrossPrefix"}; + } + $SysDesc{"Defines"} = parseTag(\$Content, "defines"); + if($SysDesc{"Image"} = parseTag(\$Content, "image")) + { # + # FIXME: isn't implemented yet + if(not -f $SysDesc{"Image"}) { + exitStatus("Access_Error", "can't access \'".$SysDesc{"Image"}."\'"); + } + } +} + +sub readSysDesc_P($) +{ + my $Path = $_[0]; + my $Content = readFile($Path); + my %Tags = ( + "headers" => "mf", + "skip_headers" => "mf", + "skip_including" => "mf", + "skip_include_paths" => "mf", + "skip_libs" => "mf", + "include_preamble" => "mf", + "add_include_paths" => "mf", + "gcc_options" => "m", + "skip_symbols" => "m", + "skip_types" => "m", + "ignore_symbols" => "h", + "non_prefix" => "h", + "defines" => "s", + "cxx_incompatible" => "s" + ); + my %DInfo = (); + foreach my $Tag (keys(%Tags)) + { + if(my $TContent = parseTag(\$Content, $Tag)) + { + if($Tags{$Tag}=~/m/) + { # multi-line (+order) + my @Items = split(/\s*\n\s*/, $TContent); + $DInfo{$Tag} = []; + foreach my $Item (@Items) + { + if($Tags{$Tag}=~/f/) { + $Item = pathFmt($Item); + } + push(@{$DInfo{$Tag}}, $Item); + } + + } + elsif($Tags{$Tag}=~/s/) + { # single element + $DInfo{$Tag} = $TContent; + } + else + { # hash array + my @Items = split(/\s*\n\s*/, $TContent); + foreach my $Item (@Items) { + $DInfo{$Tag}{$Item}=1; + } + } + } + } + + if(defined $DInfo{"non_self_compiled"}) + { # support for old ABI dumps + $DInfo{"skip_including"} = $DInfo{"non_self_compiled"}; + } + + return \%DInfo; +} + +sub readSysInfo() +{ + my $TargetSysInfo = $In::Opt{"TargetSysInfo"}; + + # Library Specific Info + my %SysInfo = (); + if(-d $TargetSysInfo."/descriptors/") + { + foreach my $DPath (cmdFind($TargetSysInfo."/descriptors/","f","",1)) + { + my $LSName = getFilename($DPath); + $LSName=~s/\.xml\Z//; + $SysInfo{$LSName} = readSysDesc_P($DPath); + } + } + else { + printMsg("WARNING", "can't find \'$TargetSysInfo/descriptors\'"); + } + + # Exceptions + if(checkGcc("4.4")) + { # exception for libstdc++ + $SysInfo{"libstdc++"}{"gcc_options"} = ["-std=c++0x"]; + } + if($In::Opt{"Target"} eq "symbian") + { # exception for libstdcpp + $SysInfo{"libstdcpp"}{"defines"} = "namespace std { struct nothrow_t {}; }"; + } + if($SysDesc{"Name"}=~/maemo/i) + { # GL/gl.h: No such file + $SysInfo{"libSDL"}{"skip_headers"}=["SDL_opengl.h"]; + } + + # Common Info + my $SysCInfo = {}; + if(-f $TargetSysInfo."/common.xml") { + $SysCInfo = readSysDesc_P($TargetSysInfo."/common.xml"); + } + else { + printMsg("Module_Error", "can't find \'$TargetSysInfo/common.xml\'"); + } + + my @CompilerOpts = (); + if($SysDesc{"Name"}=~/maemo|meego/i) { + push(@CompilerOpts, "-DMAEMO_CHANGES", "-DM_APPLICATION_NAME=\\\"app\\\""); + } + if(my @Opts = keys(%{$SysDesc{"GccOpts"}})) { + push(@CompilerOpts, @Opts); + } + if(@CompilerOpts) + { + if(not $SysCInfo->{"gcc_options"}) { + $SysCInfo->{"gcc_options"} = []; + } + push(@{$SysCInfo->{"gcc_options"}}, @CompilerOpts); + } + return (\%SysInfo, $SysCInfo); +} + +sub dumpSystem() +{ # -dump-system option handler + # should be used with -sysroot and -cross-gcc options + my $TmpDir = $In::Opt{"Tmp"}; + my $LibExt = $In::Opt{"Ext"}; + + my $SysName_P = $SysDesc{"Name"}; + $SysName_P=~s/ /_/g; + + my $SystemRoot = $In::Opt{"SystemRoot"}; + + my $SYS_DUMP_PATH = "sys_dumps/".$SysName_P."/".getArch_GCC(1); + if(not $In::Opt{"TargetLib"}) { + rmtree($SYS_DUMP_PATH); + } + my (@SystemLibs, @SysHeaders) = (); + + foreach my $Path (keys(%{$SysDesc{"Libs"}})) + { + if(not -e $Path) { + exitStatus("Access_Error", "can't access \'$Path\'"); + } + if(-d $Path) + { + if(my @SubLibs = findLibs($Path,"",1)) { + push(@SystemLibs, @SubLibs); + } + $SysDesc{"SearchLibs"}{$Path} = 1; + } + else + { # single file + push(@SystemLibs, $Path); + $SysDesc{"SearchLibs"}{getDirname($Path)} = 1; + } + } + foreach my $Path (keys(%{$SysDesc{"Headers"}})) + { + if(not -e $Path) { + exitStatus("Access_Error", "can't access \'$Path\'"); + } + if(-d $Path) + { + if(my @SubHeaders = cmdFind($Path,"f","","")) { + push(@SysHeaders, @SubHeaders); + } + $SysDesc{"SearchHeaders"}{$Path}=1; + } + else + { # single file + push(@SysHeaders, $Path); + $SysDesc{"SearchHeaders"}{getDirname($Path)} = 1; + } + } + my $GroupByHeaders = 0; + if($In::Opt{"CheckHeadersOnly"}) + { # -headers-only + $GroupByHeaders = 1; + # @SysHeaders = optimize_set(@SysHeaders); + } + elsif($SysDesc{"Image"}) + { # one big image + $GroupByHeaders = 1; + @SystemLibs = ($SysDesc{"Image"}); + } + writeFile($SYS_DUMP_PATH."/target.txt", $In::Opt{"Target"}); + my (%SysLib_Symbols, %SymbolGroup, %Symbol_SysHeaders, + %SysHeader_Symbols, %SysLib_SysHeaders) = (); + my (%Skipped, %Failed) = (); + my (%SysHeaderDir_Used, %SysHeaderDir_SysHeaders) = (); + my (%SymbolCounter, %TotalLibs) = (); + my (%PrefixToLib, %LibPrefix, %PrefixSymbols) = (); + + my %Glibc = map {$_=>1} ( + "libc", + "libpthread" + ); + my ($SysInfo, $SysCInfo) = readSysInfo(); + + foreach (keys(%{$SysCInfo->{"non_prefix"}})) + { + $NonPrefix{$_} = 1; + $NonPrefix{$_."_"} = 1; + $NonPrefix{"_".$_} = 1; + $NonPrefix{"_".$_."_"} = 1; + } + + if(not $GroupByHeaders) + { + if($In::Opt{"Debug"}) { + printMsg("INFO", localtime(time)); + } + printMsg("INFO", "Indexing sonames ...\n"); + } + my (%LibSoname, %SysLibVersion) = (); + my %DevelPaths = map {$_=>1} @SystemLibs; + foreach my $Path (sort keys(%{$SysDesc{"SearchLibs"}})) + { + foreach my $LPath (findLibs($Path,"",1)) { + $DevelPaths{$LPath} = 1; + } + } + foreach my $LPath (keys(%DevelPaths)) + { # register SONAMEs + my $LName = getFilename($LPath); + if(not isTargetLib($LName)) { + next; + } + if($In::Opt{"Target"}=~/\A(linux|macos|freebsd|solaris)\Z/ + and $LName!~/\Alib/) { + next; + } + if(my $Soname = getSONAME($LPath)) + { + if($In::Opt{"Target"} eq "symbian") + { + if($Soname=~/[\/\\]/) + { # L://epoc32/release/armv5/lib/gfxtrans{000a0000}.dso + $Soname = getFilename($Soname); + } + $Soname = lc($Soname); + } + if(not defined $LibSoname{$LName}) { + $LibSoname{$LName}=$Soname; + } + if(-l $LPath and my $Path = realpath_F($LPath)) + { + my $Name = getFilename($Path); + if(not defined $LibSoname{$Name}) { + $LibSoname{$Name}=$Soname; + } + } + } + else + { # windows and others + $LibSoname{$LName}=$LName; + } + } + my $SONAMES = ""; + foreach (sort {lc($a) cmp lc($b)} keys(%LibSoname)) { + $SONAMES .= $_.";".$LibSoname{$_}."\n"; + } + if(not $GroupByHeaders) { + writeFile($SYS_DUMP_PATH."/sonames.txt", $SONAMES); + } + foreach my $LPath (sort keys(%DevelPaths)) + { # register VERSIONs + my $LName = getFilename($LPath); + if(not isTargetLib($LName) + and not isTargetLib($LibSoname{$LName})) { + next; + } + if(my $BV = getBinVer($LPath)) + { # binary version + $SysLibVersion{$LName} = $BV; + } + elsif(my $PV = libPart($LName, "version")) + { # source version + $SysLibVersion{$LName} = $PV; + } + elsif(my $SV = libPart(getSONAME($LPath), "version")) + { # soname version + $SysLibVersion{$LName} = $SV; + } + elsif($LName=~/(\d[\d\.\-\_]*)\.$LibExt\Z/) + { # libfreebl3.so + if($1 ne 32 and $1 ne 64) { + $SysLibVersion{$LName} = $1; + } + } + } + my $VERSIONS = ""; + foreach (sort {lc($a) cmp lc($b)} keys(%SysLibVersion)) { + $VERSIONS .= $_.";".$SysLibVersion{$_}."\n"; + } + if(not $GroupByHeaders) { + writeFile($SYS_DUMP_PATH."/versions.txt", $VERSIONS); + } + + # create target list + my @SkipLibs = keys(%{$SysDesc{"SkipLibs"}}); + if(my $CSkip = $SysCInfo->{"skip_libs"}) { + push(@SkipLibs, @{$CSkip}); + } + if(@SkipLibs and not $In::Opt{"TargetLib"}) + { + my %SkipLibs = map {$_ => 1} @SkipLibs; + my @Target = (); + foreach my $LPath (@SystemLibs) + { + my $LName = getFilename($LPath); + my $LName_Short = libPart($LName, "name+ext"); + if(not defined $SkipLibs{$LName_Short} + and not defined $SkipLibs{$LName} + and not checkList($LPath, \@SkipLibs)) { + push(@Target, $LName); + } + } + addTargetLibs(\@Target); + } + + my %SysLibs = (); + foreach my $LPath (sort @SystemLibs) + { + my $LName = getFilename($LPath); + my $LSName = libPart($LName, "short"); + my $LRelPath = cutPrefix($LPath, $SystemRoot); + if(not isTargetLib($LName)) { + next; + } + if($In::Opt{"Target"}=~/\A(linux|macos|freebsd|solaris)\Z/ + and $LName!~/\Alib/) { + next; + } + if($In::Opt{"Target"} eq "symbian") + { + if(my $V = libPart($LName, "version")) + { # skip qtcore.dso + # register qtcore{00040604}.dso + delete($SysLibs{getDirname($LPath)."\\".$LSName.".".$LibExt}); + my $MV = libPart($LibSoname{$LSName.".".$LibExt}, "version"); + if($MV and $V ne $MV) + { # skip other versions: + # qtcore{00040700}.dso + # qtcore{00040702}.dso + next; + } + } + } + if(-l $LPath) + { # symlinks + if(my $Path = realpath_F($LPath)) { + $SysLibs{$Path} = 1; + } + } + elsif(-f $LPath) + { + if($Glibc{$LSName} + and -T $LPath) + { # GNU ld scripts (libc.so, libpthread.so) + my @Candidates = cmdFind($SystemRoot."/lib","",$LSName.".".$LibExt."*","1"); + if(@Candidates) + { + my $Candidate = $Candidates[0]; + if(-l $Candidate + and my $Path = realpath_F($Candidate)) { + $Candidate = $Path; + } + $SysLibs{$Candidate} = 1; + } + } + else { + $SysLibs{$LPath} = 1; + } + } + } + @SystemLibs = (); # clear memory + + if(not keys(%SysLibs)) { + exitStatus("Error", "can't find libraries"); + } + + if(not $In::Opt{"CheckHeadersOnly"}) + { + if($In::Opt{"Debug"}) { + printMsg("INFO", localtime(time)); + } + if($SysDesc{"Image"}) { + printMsg("INFO", "Reading symbols from image ...\n"); + } + else { + printMsg("INFO", "Reading symbols from libraries ...\n"); + } + } + + my %Syms = (); + my @AllSyms = {}; + my %ShortestNames = (); + + foreach my $LPath (sort {lc($a) cmp lc($b)} keys(%SysLibs)) + { + my $LRelPath = cutPrefix($LPath, $SystemRoot); + my $LName = getFilename($LPath); + + $ShortestNames{$LPath} = libPart($LName, "shortest"); + + my $Res = readSymbols_Lib(1, $LPath, 0, "-Weak", 0, 0); + + if(not keys(%{$Res}) and $In::Opt{"TargetLib"}) { + exitStatus("Error", "can't find exported symbols in the library"); + } + + $Syms{$LPath} = $Res->{$LName}; + push(@AllSyms, keys(%{$Syms{$LPath}})); + } + + my $Translate = translateSymbols(@AllSyms, 1); + + my %DupSymbols = (); + + foreach my $LPath (sort {lc($a) cmp lc($b)} keys(%SysLibs)) + { + my $LRelPath = cutPrefix($LPath, $SystemRoot); + my $LName = getFilename($LPath); + foreach my $Symbol (keys(%{$Syms{$LPath}})) + { + $Symbol=~s/[\@\$]+(.*)\Z//g; + if($Symbol=~/\A(_Z|\?)/) + { + if(isPrivateData($Symbol)) { + next; + } + if($Symbol=~/(C1|C2|D0|D1|D2)E/) + { # do NOT analyze constructors + # and destructors + next; + } + my $Unmangled = $Translate->{$Symbol}; + $Unmangled=~s/<.+>//g; + if($Unmangled=~/\A([\w:]+)/) + { # cut out the parameters + my @Elems = split(/::/, $1); + my ($Class, $Short) = ("", ""); + $Short = $Elems[$#Elems]; + if($#Elems>=1) + { + $Class = $Elems[$#Elems-1]; + pop(@Elems); + } + # the short and class name should be + # matched in one header file + $SymbolGroup{$LRelPath}{$Class} = $Short; + foreach my $Sym (@Elems) + { + if($SysCInfo->{"ignore_symbols"}{$Symbol}) + { # do NOT match this symbol + next; + } + $SysLib_Symbols{$LPath}{$Sym} = 1; + if(my $Prefix = getPrefix_S($Sym)) + { + $PrefixToLib{$Prefix}{$LName} += 1; + $LibPrefix{$LPath}{$Prefix} += 1; + $PrefixSymbols{$LPath}{$Prefix}{$Sym} = 1; + } + $SymbolCounter{$Sym}{$LPath} = 1; + + if(my @Libs = keys(%{$SymbolCounter{$Sym}})) + { + if($#Libs>=1) + { + foreach (@Libs) { + $DupSymbols{$_}{$Sym} = 1; + } + } + } + } + } + } + else + { + if($SysCInfo->{"ignore_symbols"}{$Symbol}) + { # do NOT match this symbol + next; + } + $SysLib_Symbols{$LPath}{$Symbol} = 1; + if(my $Prefix = getPrefix_S($Symbol)) + { + $PrefixToLib{$Prefix}{$LName} += 1; + $LibPrefix{$LPath}{$Prefix} += 1; + $PrefixSymbols{$LPath}{$Prefix}{$Symbol} = 1; + } + $SymbolCounter{$Symbol}{$LPath} = 1; + + if(my @Libs = keys(%{$SymbolCounter{$Symbol}})) + { + if($#Libs>=1) + { + foreach (@Libs) { + $DupSymbols{$_}{$Symbol} = 1; + } + } + } + } + } + } + + %Syms = (); + %{$Translate} = (); + + # remove minor symbols + foreach my $LPath (keys(%SysLib_Symbols)) + { + my $SName = $ShortestNames{$LPath}; + my $Count = keys(%{$SysLib_Symbols{$LPath}}); + my %Prefixes = %{$LibPrefix{$LPath}}; + my @Prefixes = sort {$Prefixes{$b}<=>$Prefixes{$a}} keys(%Prefixes); + + if($#Prefixes>=1) + { + my $MaxPrefix = $Prefixes[0]; + if($MaxPrefix eq "NONE") { + $MaxPrefix = $Prefixes[1]; + } + my $Max = $Prefixes{$MaxPrefix}; + my $None = $Prefixes{"NONE"}; + + next if($None*100/$Count>=50); + next if($None>=$Max); + + foreach my $Prefix (@Prefixes) + { + next if($Prefix eq $MaxPrefix); + my $Num = $Prefixes{$Prefix}; + my $Rm = 0; + + if($Prefix eq "NONE") { + $Rm = 1; + } + else + { + if($Num*100/$Max<5) { + $Rm = 1; + } + } + + if($Rm) + { + next if($Prefix=~/\Q$MaxPrefix\E/i); + next if($MaxPrefix=~/\Q$Prefix\E/i); + next if($Prefix=~/\Q$SName\E/i); + + foreach my $Symbol (keys(%{$PrefixSymbols{$LPath}{$Prefix}})) { + delete($SysLib_Symbols{$LPath}{$Symbol}); + } + } + } + } + } + + %PrefixSymbols = (); # free memory + + if(not $In::Opt{"CheckHeadersOnly"}) { + writeFile($SYS_DUMP_PATH."/debug/symbols.txt", Dumper(\%SysLib_Symbols)); + } + + my (%DupLibs, %VersionedLibs) = (); + foreach my $LPath (sort keys(%DupSymbols)) + { # match duplicated libs + # libmenu contains all symbols from libmenuw + my @Syms = keys(%{$SysLib_Symbols{$LPath}}); + next if($#Syms==-1); + if($#Syms+1==keys(%{$DupSymbols{$LPath}})) { + $DupLibs{$LPath} = 1; + } + } + foreach my $Prefix (keys(%PrefixToLib)) + { + my @Libs = keys(%{$PrefixToLib{$Prefix}}); + @Libs = sort {$PrefixToLib{$Prefix}{$b}<=>$PrefixToLib{$Prefix}{$a}} @Libs; + $PrefixToLib{$Prefix} = $Libs[0]; + } + + my %PackageFile = (); # to improve results + my %FilePackage = (); + my %LibraryFile = (); + + if(0) + { + if($In::Opt{"Debug"}) { + printMsg("INFO", localtime(time)); + } + printMsg("INFO", "Reading info from packages ...\n"); + if(my $Urpmf = getCmdPath("urpmf")) + { # Mandriva, ROSA + my $Out = $TmpDir."/urpmf.out"; + system("urpmf : >\"$Out\""); + open(FILE, $Out); + while() + { + chomp($_); + if(my $M = index($_, ":")) + { + my $Pkg = substr($_, 0, $M); + my $File = substr($_, $M+1); + $PackageFile{$Pkg}{$File} = 1; + $FilePackage{$File} = $Pkg; + } + } + close(FILE); + } + } + + if(keys(%FilePackage)) + { + foreach my $LPath (sort {lc($a) cmp lc($b)} keys(%SysLibs)) + { + my $LName = getFilename($LPath); + my $LDir = getDirname($LPath); + my $LName_Short = libPart($LName, "name+ext"); + + my $Pkg = $FilePackage{$LDir."/".$LName_Short}; + if(not $Pkg) + { + my $RPkg = $FilePackage{$LPath}; + if(defined $PackageFile{$RPkg."-devel"}) { + $Pkg = $RPkg."-devel"; + } + if($RPkg=~s/[\d\.]+\Z//g) + { + if(defined $PackageFile{$RPkg."-devel"}) { + $Pkg = $RPkg."-devel"; + } + } + } + if($Pkg) + { + foreach (keys(%{$PackageFile{$Pkg}})) + { + if(index($_, "/usr/include/")==0) { + $LibraryFile{$LPath}{$_} = 1; + } + } + } + + $LName_Short=~s/\.so\Z/.a/; + if($Pkg = $FilePackage{$LDir."/".$LName_Short}) + { # headers for static library + foreach (keys(%{$PackageFile{$Pkg}})) + { + if(index($_, "/usr/include/")==0) { + $LibraryFile{$LPath}{$_} = 1; + } + } + } + } + } + + my %HeaderFile_Path = (); + + if($In::Opt{"Debug"}) { + printMsg("INFO", localtime(time)); + } + printMsg("INFO", "Reading symbols from headers ...\n"); + foreach my $HPath (@SysHeaders) + { + $HPath = pathFmt($HPath); + if(isElf($HPath)) + { # skip ELF files + next; + } + my $HRelPath = cutPrefix($HPath, $SystemRoot); + my ($HDir, $HName) = sepPath($HRelPath); + if(isNotHeader($HName)) + { # have a wrong extension: .gch, .in + next; + } + if($HName=~/~\Z/) + { # reserved copy + next; + } + if(index($HRelPath, "/_gen")!=-1) + { # telepathy-1.0/telepathy-glib/_gen + # telepathy-1.0/libtelepathy/_gen-tp-constants-deprecated.h + next; + } + if(index($HRelPath, "include/linux/")!=-1) + { # kernel-space headers + next; + } + if(index($HRelPath, "include/asm/")!=-1) + { # asm headers + next; + } + if(index($HRelPath, "/microb-engine/")!=-1) + { # MicroB engine (Maemo 4) + next; + } + if($HRelPath=~/\Wprivate(\W|\Z)/) + { # private directories (include/tcl-private, ...) + next; + } + if(index($HRelPath, "/lib/")!=-1) + { + if(not isHeaderFile($HName)) + { # without or with a wrong extension + # under the /lib directory + next; + } + } + my $Content = readFile($HPath); + $Content=~s/\/\*(.|\n)+?\*\///g; + $Content=~s/\/\/.*?\n//g; # remove comments + $Content=~s/#\s*define[^\n\\]*(\\\n[^\n\\]*)+\n*//g; # remove defines + $Content=~s/#[^\n]*?\n//g; # remove directives + $Content=~s/(\A|\n)class\s+\w+;\n//g; # remove forward declarations + # FIXME: try to add preprocessing stage + foreach my $Symbol (split(/\W+/, $Content)) + { + next if(not $Symbol); + $Symbol_SysHeaders{$Symbol}{$HRelPath} = 1; + $SysHeader_Symbols{$HRelPath}{$Symbol} = 1; + } + $SysHeaderDir_SysHeaders{$HDir}{$HName} = 1; + $HeaderFile_Path{getFilename($HRelPath)}{$HRelPath} = 1; + } + + # writeFile($SYS_DUMP_PATH."/debug/headers.txt", Dumper(\%SysHeader_Symbols)); + + my %SkipDHeaders = ( + # header files, that should be in the section + # but should be matched in the algorithm + # MeeGo 1.2 Harmattan + "libtelepathy-qt4" => ["TelepathyQt4/_gen", "client.h", + "TelepathyQt4/*-*", "debug.h", "global.h", + "properties.h", "Channel", "channel.h", "message.h"], + ); + filterFormat(\%SkipDHeaders); + if(not $GroupByHeaders) + { + if($In::Opt{"Debug"}) { + printMsg("INFO", localtime(time)); + } + printMsg("INFO", "Matching symbols ...\n"); + } + + foreach my $LPath (sort {lc($a) cmp lc($b)} keys(%SysLibs)) + { # matching + my $LName = getFilename($LPath); + } + + foreach my $LPath (sort {lc($a) cmp lc($b)} keys(%SysLibs)) + { # matching + my $LName = getFilename($LPath); + my $LName_Short = libPart($LName, "name"); + my $LRelPath = cutPrefix($LPath, $SystemRoot); + my $LSName = libPart($LName, "short"); + my $SName = $ShortestNames{$LPath}; + + my @TryNames = (); # libX-N.so.M + + if(my $Ver = $SysLibVersion{$LName}) + { # libX-N-M + if($LSName."-".$Ver ne $LName_Short) + { + push(@TryNames, $LName_Short."-".$Ver); + #while($Ver=~s/\.\d+\Z//) { # partial versions + # push(@TryNames, $LName_Short."-".$Ver); + #} + } + } + push(@TryNames, $LName_Short); # libX-N + if($LSName ne $LName_Short) + { # libX + push(@TryNames, $LSName); + } + + if($LRelPath=~/\/debug\//) + { # debug libs + $Skipped{$LRelPath} = 1; + next; + } + $TotalLibs{$LRelPath} = 1; + $SysLib_SysHeaders{$LRelPath} = (); + + my (%SymbolDirs, %SymbolFiles) = (); + + foreach my $Symbol (sort {length($b) cmp length($a)} + sort keys(%{$SysLib_Symbols{$LPath}})) + { + if($SysCInfo->{"ignore_symbols"}{$Symbol}) { + next; + } + if(not $DupLibs{$LPath} + and not $VersionedLibs{$LPath} + and keys(%{$SymbolCounter{$Symbol}})>=2 + and my $Prefix = getPrefix_S($Symbol)) + { # duplicated symbols + if($PrefixToLib{$Prefix} + and $PrefixToLib{$Prefix} ne $LName + and not $Glibc{$LSName}) { + next; + } + } + if(length($Symbol)<=2) { + next; + } + if($Symbol!~/[A-Z_0-9]/ + and length($Symbol)<10 + and keys(%{$Symbol_SysHeaders{$Symbol}})>=3) + { # undistinguished symbols + # FIXME: improve this filter + # signalfd (libc.so) + # regcomp (libpcreposix.so) + next; + } + if($Symbol=~/\A(_M_|_Rb_|_S_)/) + { # _M_insert, _Rb_tree, _S_destroy_c_locale + next; + } + if($Symbol=~/\A[A-Z][a-z]+\Z/) + { # Clone, Initialize, Skip, Unlock, Terminate, Chunk + next; + } + if($Symbol=~/\A[A-Z][A-Z]\Z/) + { # BC, PC, UP, SP + next; + } + if($Symbol=~/_t\Z/) + { # pthread_mutex_t, wchar_t + next; + } + my @SymHeaders = keys(%{$Symbol_SysHeaders{$Symbol}}); + @SymHeaders = sort {lc($a) cmp lc($b)} @SymHeaders; # sort by name + @SymHeaders = sort {length(getDirname($a))<=>length(getDirname($b))} @SymHeaders; # sort by length + if(length($SName)>=3) + { # sort candidate headers by name + @SymHeaders = sort {$b=~/\Q$SName\E/i<=>$a=~/\Q$SName\E/i} @SymHeaders; + } + else + { # libz, libX11 + @SymHeaders = sort {$b=~/lib\Q$SName\E/i<=>$a=~/lib\Q$SName\E/i} @SymHeaders; + @SymHeaders = sort {$b=~/\Q$SName\Elib/i<=>$a=~/\Q$SName\Elib/i} @SymHeaders; + } + @SymHeaders = sort {$b=~/\Q$LSName\E/i<=>$a=~/\Q$LSName\E/i} @SymHeaders; + @SymHeaders = sort {$SymbolDirs{getDirname($b)}<=>$SymbolDirs{getDirname($a)}} @SymHeaders; + @SymHeaders = sort {$SymbolFiles{getFilename($b)}<=>$SymbolFiles{getFilename($a)}} @SymHeaders; + foreach my $HRelPath (@SymHeaders) + { + my $HDir = getDirname($HRelPath); + my $HName = getFilename($HRelPath); + + if(my $Group = $SymbolGroup{$LRelPath}{$Symbol}) + { + if(not $SysHeader_Symbols{$HRelPath}{$Group}) { + next; + } + } + my $Filter = 0; + foreach (@TryNames) + { + if(my $Filt = $SysInfo->{$_}{"headers"}) + { # search for specified headers + if(not checkList($HRelPath, $Filt)) + { + $Filter = 1; + last; + } + } + if(my $Filt = $SysInfo->{$_}{"skip_headers"}) + { # do NOT search for some headers + if(checkList($HRelPath, $Filt)) + { + $Filter = 1; + last; + } + } + if(my $Filt = $SysInfo->{$_}{"skip_including"}) + { # do NOT search for some headers + if(checkList($HRelPath, $Filt)) + { + $SymbolDirs{$HDir}+=1; + $SymbolFiles{$HName}+=1; + $Filter = 1; + last; + } + } + } + if($Filter) { + next; + } + if(my $Filt = $SysCInfo->{"skip_headers"}) + { # do NOT search for some headers + if(checkList($HRelPath, $Filt)) { + next; + } + } + if(my $Filt = $SysCInfo->{"skip_including"}) + { # do NOT search for some headers + if(checkList($HRelPath, $Filt)) { + next; + } + } + + #if(defined $LibraryFile{$LRelPath}) + #{ # skip wrongly matched headers + # if(not defined $LibraryFile{$LRelPath}{$HRelPath}) + # { + # next; + # } + #} + + $SysLib_SysHeaders{$LRelPath}{$HRelPath} = $Symbol; + + $SysHeaderDir_Used{$HDir}{$LName_Short} = 1; + $SysHeaderDir_Used{getDirname($HDir)}{$LName_Short} = 1; + + $SymbolDirs{$HDir} += 1; + $SymbolFiles{$HName} +=1 ; + + # select one header for one symbol + last; + } + } + + if(keys(%{$SysLib_Symbols{$LPath}}) + and not $SysInfo->{$_}{"headers"}) + { # try to match by name of the header + if(length($SName)>=3) + { + my @Paths = (); + foreach my $Path (keys(%{$HeaderFile_Path{$SName.".h"}}), keys(%{$HeaderFile_Path{$LSName.".h"}})) + { + my $Dir = getDirname($Path); + if(defined $SymbolDirs{$Dir} or $Dir eq "/usr/include") { + push(@Paths, $Path); + } + } + if($#Paths==0) + { + my $Path = $Paths[0]; + if(not defined $SysLib_SysHeaders{$LRelPath}{$Path}) { + $SysLib_SysHeaders{$LRelPath}{$Path} = "by name ($LSName)"; + } + } + } + } + + if(not keys(%{$SysLib_SysHeaders{$LRelPath}})) + { + foreach (@TryNames) + { + if(my $List = $SysInfo->{$_}{"headers"}) + { + foreach my $HName (@{$List}) + { + next if($HName=~/[\*\/\\]/); + if(my $HPath = selectSystemHeader($HName, 1)) + { + my $HRelPath = cutPrefix($HPath, $SystemRoot); + $SysLib_SysHeaders{$LRelPath}{$HRelPath} = "by descriptor"; + } + } + } + } + } + + if(not keys(%{$SysLib_SysHeaders{$LRelPath}})) { + $Failed{$LRelPath} = 1; + } + } + + if(not $GroupByHeaders) + { # matching results + writeFile($SYS_DUMP_PATH."/debug/match.txt", Dumper(\%SysLib_SysHeaders)); + writeFile($SYS_DUMP_PATH."/debug/skipped.txt", join("\n", sort keys(%Skipped))); + writeFile($SYS_DUMP_PATH."/debug/failed.txt", join("\n", sort keys(%Failed))); + } + (%SysLib_Symbols, %SymbolGroup, %Symbol_SysHeaders, %SysHeader_Symbols) = (); # free memory + if($GroupByHeaders) + { + if($SysDesc{"Image"} and not $In::Opt{"CheckHeadersOnly"}) { + @SysHeaders = keys(%{$SysLib_SysHeaders{$SysDesc{"Image"}}}); + } + %SysLib_SysHeaders = (); + foreach my $Path (@SysHeaders) + { + if(my $Skip = $SysCInfo->{"skip_headers"}) + { # do NOT search for some headers + if(checkList($Path, $Skip)) { + next; + } + } + if(my $Skip = $SysCInfo->{"skip_including"}) + { # do NOT search for some headers + if(checkList($Path, $Skip)) { + next; + } + } + $SysLib_SysHeaders{$Path}{$Path} = 1; + } + if($In::Opt{"CheckHeadersOnly"}) { + writeFile($SYS_DUMP_PATH."/mode.txt", "headers-only"); + } + else { + writeFile($SYS_DUMP_PATH."/mode.txt", "group-by-headers"); + } + } + @SysHeaders = (); # clear memory + + if($In::Opt{"Debug"}) { + printMsg("INFO", localtime(time)); + } + printMsg("INFO", "Generating XML descriptors ..."); + my %Generated = (); + my %CxxIncompat_L = (); + foreach my $LRelPath (keys(%SysLib_SysHeaders)) + { + my $LName = getFilename($LRelPath); + my $DPath = $SYS_DUMP_PATH."/descriptors/$LName.xml"; + unlink($DPath); + if(my @LibHeaders = keys(%{$SysLib_SysHeaders{$LRelPath}})) + { + my $LSName = libPart($LName, "short"); + my $LName_Short = libPart($LName, "name"); + my $LName_Shortest = libPart($LName, "shortest"); + if($GroupByHeaders) + { # header short name + $LSName = $LName; + $LSName=~s/\.(.+?)\Z//; + } + + my (%DirsHeaders, %Includes, %MainDirs) = (); + foreach my $HRelPath (@LibHeaders) + { + my $Dir = getDirname($HRelPath); + $DirsHeaders{$Dir}{$HRelPath} = 1; + + if($Dir=~/\/\Q$LName_Shortest\E(\/|\Z)/i + or $Dir=~/\/\Q$LName_Short\E(\/|\Z)/i) + { + if(getFilename($Dir) ne "include") + { # except /usr/include + $MainDirs{$Dir} += 1; + } + } + } + + if($#LibHeaders==0) + { # one header at all + $Includes{$LibHeaders[0]} = 1; + } + else + { + foreach my $Dir (keys(%DirsHeaders)) + { + if(keys(%MainDirs) and not defined $MainDirs{$Dir}) + { # search in /X/ dir for libX headers + if(getFilename($Dir) ne "include") + { # except /usr/include + next; + } + } + my $DirPart = 0; + my $TotalHeaders = keys(%{$SysHeaderDir_SysHeaders{$Dir}}); + if($TotalHeaders) { + $DirPart = (keys(%{$DirsHeaders{$Dir}})*100)/$TotalHeaders; + } + my $Neighbourhoods = keys(%{$SysHeaderDir_Used{$Dir}}); + if($Neighbourhoods==1) + { # one lib in this directory + if(getFilename($Dir) ne "include" + and $DirPart>=5) + { # complete directory + $Includes{$Dir} = 1; + } + else + { # list of headers + foreach (keys(%{$DirsHeaders{$Dir}})) { + $Includes{$_} = 1; + } + } + } + elsif((keys(%{$DirsHeaders{$Dir}})*100)/($#LibHeaders+1)>5) + { # remove 5% divergence + if(getFilename($Dir) ne "include" + and $DirPart>=50) + { # complete directory if more than 50% + $Includes{$Dir} = 1; + } + else + { # list of headers + foreach (keys(%{$DirsHeaders{$Dir}})) { + $Includes{$_} = 1; + } + } + } + else + { # noise + foreach (keys(%{$DirsHeaders{$Dir}})) + { # NOTE: /usr/include/libX.h + if(/\Q$LName_Shortest\E/i) { + $Includes{$_} = 1; + } + } + } + } + } + if($GroupByHeaders) + { # one header in one ABI dump + %Includes = ($LibHeaders[0] => 1); + } + my $LVersion = $SysLibVersion{$LName}; + if($LVersion) + { # append by system name + $LVersion .= "-".$SysDesc{"Name"}; + } + else { + $LVersion = $SysDesc{"Name"}; + } + my @Content = ("\n $LVersion\n"); + + my @IncHeaders = sort keys(%Includes); + + # sort files up + @IncHeaders = sort {$b=~/\.h\Z/<=>$a=~/\.h\Z/} @IncHeaders; + + # sort by name + @IncHeaders = sort {sortHeaders($a, $b)} @IncHeaders; + + # sort by library name + sortByWord(\@IncHeaders, libPart($LName, "shortest")); + + if(isAbsPath($IncHeaders[0]) or -f $IncHeaders[0]) { + push(@Content, "\n ".join("\n ", @IncHeaders)."\n"); + } + else { + push(@Content, "\n {RELPATH}/".join("\n {RELPATH}/", @IncHeaders)."\n"); + } + if($GroupByHeaders) + { + if($SysDesc{"Image"}) { + push(@Content, "\n ".$SysDesc{"Image"}."\n"); + } + } + else + { + if(isAbsPath($LRelPath) or -f $LRelPath) { + push(@Content, "\n $LRelPath\n"); + } + else { + push(@Content, "\n {RELPATH}/$LRelPath\n"); + } + } + + # system + if(my @SearchHeaders = sort keys(%{$SysDesc{"SearchHeaders"}})) { + push(@Content, "\n ".join("\n ", @SearchHeaders)."\n"); + } + if(my @SearchLibs = sort keys(%{$SysDesc{"SearchLibs"}})) { + push(@Content, "\n ".join("\n ", @SearchLibs)."\n"); + } + if(my @Tools = sort keys(%{$SysDesc{"Tools"}})) { + push(@Content, "\n ".join("\n ", @Tools)."\n"); + } + if(my $Prefix = $SysDesc{"CrossPrefix"}) { + push(@Content, "\n $Prefix\n"); + } + + # library + my (@Skip, @SkipInc, @AddIncPath, @SkipIncPath, + @SkipTypes, @SkipSymb, @Preamble, @Defines, @CompilerOpts) = (); + + my @TryNames = (); + if(my $Ver = $SysLibVersion{$LName}) + { + if($LSName."-".$Ver ne $LName_Short) { + push(@TryNames, $LName_Short."-".$Ver); + } + } + push(@TryNames, $LName_Short); + if($LSName ne $LName_Short) { + push(@TryNames, $LSName); + } + + # common + if(my $List = $SysCInfo->{"include_preamble"}) { + push(@Preamble, @{$List}); + } + if(my $List = $SysCInfo->{"skip_headers"}) { + @Skip = (@Skip, @{$List}); + } + if(my $List = $SysCInfo->{"skip_including"}) { + @SkipInc = (@SkipInc, @{$List}); + } + if(my $List = $SysCInfo->{"skip_symbols"}) { + push(@SkipSymb, @{$List}); + } + if(my $List = $SysCInfo->{"gcc_options"}) { + push(@CompilerOpts, @{$List}); + } + if($SysCInfo->{"defines"}) { + push(@Defines, $SysCInfo->{"defines"}); + } + + # particular + foreach (@TryNames) + { + if(my $List = $SysInfo->{$_}{"include_preamble"}) { + push(@Preamble, @{$List}); + } + if(my $List = $SysInfo->{$_}{"skip_headers"}) { + @Skip = (@Skip, @{$List}); + } + if(my $List = $SysInfo->{$_}{"skip_including"}) { + @SkipInc = (@SkipInc, @{$List}); + } + if(my $List = $SysInfo->{$_}{"add_include_paths"}) { + @AddIncPath = (@AddIncPath, @{$List}); + } + if(my $List = $SysInfo->{$_}{"skip_include_paths"}) { + @SkipIncPath = (@SkipIncPath, @{$List}); + } + if(my $List = $SysInfo->{$_}{"skip_symbols"}) { + push(@SkipSymb, @{$List}); + } + if(my $List = $SysInfo->{$_}{"skip_types"}) { + @SkipTypes = (@SkipTypes, @{$List}); + } + if(my $List = $SysInfo->{$_}{"gcc_options"}) { + push(@CompilerOpts, @{$List}); + } + if(my $List = $SysInfo->{$_}{"defines"}) { + push(@Defines, $List); + } + if($SysInfo->{$_}{"cxx_incompatible"}) { + $CxxIncompat_L{$LName} = 1; + } + } + + # common other + if($LSName=~/\AlibX\w+\Z/) + { # add Xlib.h for libXt, libXaw, libXext and others + push(@Preamble, "Xlib.h", "X11/Intrinsic.h"); + } + if($SkipDHeaders{$LSName}) { + @SkipInc = (@SkipInc, @{$SkipDHeaders{$LSName}}); + } + if($SysDesc{"Defines"}) { + push(@Defines, $SysDesc{"Defines"}); + } + + # add sections + if(@Preamble) { + push(@Content, "\n ".join("\n ", @Preamble)."\n"); + } + if(@Skip) { + push(@Content, "\n ".join("\n ", @Skip)."\n"); + } + if(@SkipInc) { + push(@Content, "\n ".join("\n ", @SkipInc)."\n"); + } + if(@AddIncPath) { + push(@Content, "\n ".join("\n ", @AddIncPath)."\n"); + } + if(@SkipIncPath) { + push(@Content, "\n ".join("\n ", @SkipIncPath)."\n"); + } + if(@SkipSymb) { + push(@Content, "\n ".join("\n ", @SkipSymb)."\n"); + } + if(@SkipTypes) { + push(@Content, "\n ".join("\n ", @SkipTypes)."\n"); + } + if(@CompilerOpts) { + push(@Content, "\n ".join("\n ", @CompilerOpts)."\n"); + } + if(@Defines) { + push(@Content, "\n ".join("\n ", @Defines)."\n"); + } + + writeFile($DPath, join("\n\n", @Content)); + $Generated{$LRelPath} = 1; + + # save header files to create visual diff later + my $HSDir = $SYS_DUMP_PATH."/headers/".$LName; + rmtree($HSDir); + mkpath($HSDir); + foreach my $H_P (@IncHeaders) + { + if(-f $H_P) { + copy($H_P, $HSDir); + } + } + } + } + printMsg("INFO", "Created descriptors: ".keys(%Generated)." ($SYS_DUMP_PATH/descriptors/)\n"); + + if($In::Opt{"Debug"}) { + printMsg("INFO", localtime(time)); + } + printMsg("INFO", "Dumping ABIs:"); + my %Dumped = (); + my @Descriptors = cmdFind($SYS_DUMP_PATH."/descriptors","f","*.xml","1"); + if(-d $SYS_DUMP_PATH."/descriptors" and $#Descriptors==-1) { + printMsg("ERROR", "internal problem with \'find\' utility"); + } + foreach my $DPath (sort {lc($a) cmp lc($b)} @Descriptors) + { + my $DName = getFilename($DPath); + my $LName = ""; + if($DName=~/\A(.+).xml\Z/) { + $LName = $1; + } + else { + next; + } + if(not isTargetLib($LName) + and not isTargetLib($LibSoname{$LName})) { + next; + } + $DPath = cutPrefix($DPath, $In::Opt{"OrigDir"}); + my $ACC_dump = "perl $0"; + if($GroupByHeaders) + { # header name is going here + $ACC_dump .= " -l $LName"; + } + else { + $ACC_dump .= " -l ".libPart($LName, "name"); + } + $ACC_dump .= " -dump \"$DPath\""; + if($SystemRoot) + { + $ACC_dump .= " -relpath \"$SystemRoot\""; + $ACC_dump .= " -sysroot \"$SystemRoot\""; + } + my $DumpPath = "$SYS_DUMP_PATH/abi_dumps/$LName.abi"; + $ACC_dump .= " -dump-path \"$DumpPath\""; + my $LogPath = "$SYS_DUMP_PATH/logs/$LName.txt"; + unlink($LogPath); + $ACC_dump .= " -log-path \"$LogPath\""; + if($In::Opt{"CrossGcc"}) { + $ACC_dump .= " -cross-gcc \"".$In::Opt{"CrossGcc"}."\""; + } + if($In::Opt{"CheckHeadersOnly"}) { + $ACC_dump .= " -headers-only"; + } + if($In::Opt{"UseStaticLibs"}) { + $ACC_dump .= " -static-libs"; + } + if($GroupByHeaders) { + $ACC_dump .= " -header $LName"; + } + if($In::Opt{"NoStdInc"} + or $In::Opt{"Target"}=~/windows|symbian/) + { # 1. user-defined + # 2. windows/minGW + # 3. symbian/GCC + $ACC_dump .= " -nostdinc"; + } + if($In::Opt{"CxxIncompat"} or $CxxIncompat_L{$LName}) { + $ACC_dump .= " -cxx-incompatible"; + } + if($In::Opt{"SkipUnidentified"}) { + $ACC_dump .= " -skip-unidentified"; + } + if($In::Opt{"Quiet"}) + { # quiet mode + $ACC_dump .= " -quiet"; + } + if($In::Opt{"LogMode"} eq "n") { + $ACC_dump .= " -logging-mode n"; + } + elsif($In::Opt{"Quiet"}) { + $ACC_dump .= " -logging-mode a"; + } + if($In::Opt{"Debug"}) + { # debug mode + $ACC_dump .= " -debug"; + printMsg("INFO", "$ACC_dump"); + } + printMsg("INFO_C", "Dumping $LName: "); + system($ACC_dump." 1>$TmpDir/null 2>$TmpDir/$LName.stderr"); + my $ErrCode = $?; + appendFile("$SYS_DUMP_PATH/logs/$LName.txt", "The ACC parameters:\n $ACC_dump\n"); + my $ErrCont = readFile("$TmpDir/$LName.stderr"); + if($ErrCont) { + appendFile("$SYS_DUMP_PATH/logs/$LName.txt", $ErrCont); + } + + if(filterError($ErrCont)) + { + if(getCodeError($ErrCode>>8) eq "Invalid_Dump") { + printMsg("INFO", "Empty"); + } + else { + printMsg("INFO", "Errors (\'$SYS_DUMP_PATH/logs/$LName.txt\')"); + } + } + elsif(not -f $DumpPath) { + printMsg("INFO", "Failed (\'$SYS_DUMP_PATH/logs/$LName.txt\')"); + } + else + { + $Dumped{$LName}=1; + printMsg("INFO", "Ok"); + } + } + printMsg("INFO", "\n"); + if(not $GroupByHeaders) + { # general mode + printMsg("INFO", "Total libraries: ".keys(%TotalLibs)); + printMsg("INFO", "Skipped libraries: ".keys(%Skipped)." ($SYS_DUMP_PATH/skipped.txt)"); + printMsg("INFO", "Failed to find headers: ".keys(%Failed)." ($SYS_DUMP_PATH/failed.txt)"); + } + printMsg("INFO", "Dumped ABIs: ".keys(%Dumped)." ($SYS_DUMP_PATH/abi_dumps/)"); + printMsg("INFO", "The ".$SysDesc{"Name"}." system ABI has been dumped to:\n $SYS_DUMP_PATH"); +} + +sub filterError($) +{ + my $Error = $_[0]; + + if(not $Error) { + return undef; + } + + my @Err = (); + foreach my $L (split(/\n/, $Error)) + { + if($L!~/warning:/) { + push(@Err, $L); + } + } + + return join("\n", @Err); +} + +sub getBinVer($) +{ + my $Path = $_[0]; + if($In::Opt{"Target"} eq "windows" + and $Path=~/\.dll\Z/) + { # get version of DLL using "sigcheck" + my $Sigcheck = getCmdPath("sigcheck"); + if(not $Sigcheck) { + return undef; + } + my $TmpDir = $In::Opt{"Tmp"}; + my $VInfo = `$Sigcheck -nobanner -n $Path 2>$TmpDir/null`; + $VInfo=~s/\s*\(.*\)\s*//; + chomp($VInfo); + + if($VInfo eq "n/a") { + $VInfo = uc($VInfo); + } + + return $VInfo; + } + return undef; +} + +return 1; diff --git a/abi-compliance-checker-2.4/modules/Internals/SysFiles.pm b/abi-compliance-checker-2.4/modules/Internals/SysFiles.pm new file mode 100644 index 0000000..845a950 --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/SysFiles.pm @@ -0,0 +1,2549 @@ +########################################################################### +# A module to find system files and automatically generate include paths +# +# Copyright (C) 2015-2019 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; + +loadModule("ElfTools"); + +my %Cache; + +my %BinUtils = map {$_=>1} ( + "c++filt", + "objdump", + "readelf" +); + +# Header file extensions as described by gcc +my $HEADER_EXT = "h|hh|hp|hxx|hpp|h\\+\\+|tcc|txx|x|inl|inc|ads|isph"; + +my %GlibcHeader = map {$_=>1} ( + "aliases.h", + "argp.h", + "argz.h", + "assert.h", + "cpio.h", + "ctype.h", + "dirent.h", + "envz.h", + "errno.h", + "error.h", + "execinfo.h", + "fcntl.h", + "fstab.h", + "ftw.h", + "glob.h", + "grp.h", + "iconv.h", + "ifaddrs.h", + "inttypes.h", + "langinfo.h", + "limits.h", + "link.h", + "locale.h", + "malloc.h", + "math.h", + "mntent.h", + "monetary.h", + "nl_types.h", + "obstack.h", + "printf.h", + "pwd.h", + "regex.h", + "sched.h", + "search.h", + "setjmp.h", + "shadow.h", + "signal.h", + "spawn.h", + "stdarg.h", + "stdint.h", + "stdio.h", + "stdlib.h", + "string.h", + "strings.h", + "tar.h", + "termios.h", + "time.h", + "ulimit.h", + "unistd.h", + "utime.h", + "wchar.h", + "wctype.h", + "wordexp.h" ); + +my %GlibcDir = map {$_=>1} ( + "arpa", + "bits", + "gnu", + "netinet", + "net", + "nfs", + "rpc", + "sys", + "linux" ); + +my %WinHeaders = map {$_=>1} ( + "dos.h", + "process.h", + "winsock.h", + "config-win.h", + "mem.h", + "windows.h", + "winsock2.h", + "crtdbg.h", + "ws2tcpip.h" +); + +my %ObsoleteHeaders = map {$_=>1} ( + "iostream.h", + "fstream.h" +); + +my %AlienHeaders = map {$_=>1} ( + # Solaris + "thread.h", + "sys/atomic.h", + # HPUX + "sys/stream.h", + # Symbian + "AknDoc.h", + # Atari ST + "ext.h", + "tos.h", + # MS-DOS + "alloc.h", + # Sparc + "sys/atomic.h" +); + +my %ConfHeaders = map {$_=>1} ( + "atomic", + "conf.h", + "config.h", + "configure.h", + "build.h", + "setup.h" +); + +my %LocalIncludes = map {$_=>1} ( + "/usr/local/include", + "/usr/local" ); + +my %OS_AddPath=( +# These paths are needed if the tool cannot detect them automatically + "macos"=>{ + "include"=>[ + "/Library", + "/Developer/usr/include" + ], + "lib"=>[ + "/Library", + "/Developer/usr/lib" + ], + "bin"=>[ + "/Developer/usr/bin" + ] + }, + "beos"=>{ + # Haiku has GCC 2.95.3 by default + # try to find GCC>=3.0 in /boot/develop/abi + "include"=>[ + "/boot/common", + "/boot/develop" + ], + "lib"=>[ + "/boot/common/lib", + "/boot/system/lib", + "/boot/apps" + ], + "bin"=>[ + "/boot/common/bin", + "/boot/system/bin", + "/boot/develop/abi" + ] + } +); + +my %RegisteredDirs; +my %Header_ErrorRedirect; +my %HeaderName_Paths; +my %Header_Dependency; +my @DefaultCppPaths; +my @DefaultGccPaths; +my @DefaultIncPaths; +my @DefaultBinPaths; +my %SystemHeaders; +my %DefaultCppHeader; +my %DefaultGccHeader; +my @UsersIncPath; +my %Header_Includes; +my %Header_Includes_R; +my %Header_ShouldNotBeUsed; +my %RecursiveIncludes; +my %Header_Include_Prefix; +my @RecurInclude; + +my %Include_Paths = ( + "1"=>[], + "2"=>[] +); + +my %Add_Include_Paths = ( + "1"=>[], + "2"=>[] +); + +sub tryCmd($) +{ + my $Cmd = $_[0]; + + my @Options = ( + "--version", + "-help" + ); + foreach my $Opt (@Options) + { + my $TmpDir = $In::Opt{"Tmp"}; + my $Info = `$Cmd $Opt 2>\"$TmpDir/null\"`; + if($Info) { + return 1; + } + } + return 0; +} + +sub searchTool($) +{ + my $Name = $_[0]; + + if(my @Paths = keys(%{$In::Opt{"TargetTools"}})) + { + foreach my $Path (@Paths) + { + if(-f join_P($Path, $Name)) { + return join_P($Path, $Name); + } + if(my $CrossPrefix = $In::Opt{"CrossPrefix"}) + { # user-defined prefix (arm-none-symbianelf, ...) + my $Candidate = join_P($Path, $CrossPrefix."-".$Name); + if(-f $Candidate) { + return $Candidate; + } + } + } + } + + return undef; +} + +sub syncWithGcc($) +{ + my $Name = $_[0]; + if(my $GccPath = $In::Opt{"GccPath"}) + { + if($GccPath=~s/\bgcc(|\.\w+)\Z/$Name$1/) { + return $GccPath; + } + } + + return undef; +} + +sub getCmdPath($) +{ + my $Name = $_[0]; + + if(defined $Cache{"getCmdPath"}{$Name}) { + return $Cache{"getCmdPath"}{$Name}; + } + + my $Path = searchTool($Name); + if(not $Path and $In::Opt{"OS"} eq "windows") { + $Path = searchTool($Name.".exe"); + } + + if(not $Path and $BinUtils{$Name}) + { + if(my $CrossPrefix = $In::Opt{"CrossPrefix"}) { + $Path = searchCommand($CrossPrefix."-".$Name); + } + } + + if(not $Path and $BinUtils{$Name}) + { + if(my $Cand = syncWithGcc($Name)) + { # sync with GCC + if($Cand=~/[\/\\]/) + { # path + if(-f $Cand) { + $Path = $Cand; + } + } + elsif($Cand = searchCommand($Cand)) + { # name + $Path = $Cand; + } + } + } + if(not $Path) { + $Path = searchCommand($Name); + } + if(not $Path and $In::Opt{"OS"} eq "windows") + { # search for *.exe file + $Path = searchCommand($Name.".exe"); + } + if($Path=~/\s/) { + $Path = "\"".$Path."\""; + } + return ($Cache{"getCmdPath"}{$Name} = $Path); +} + +sub searchCommand($) +{ + my $Name = $_[0]; + + if(defined $Cache{"searchCommand"}{$Name}) { + return $Cache{"searchCommand"}{$Name}; + } + if(my $DefaultPath = getCmdPath_Default($Name)) { + return ($Cache{"searchCommand"}{$Name} = $DefaultPath); + } + foreach my $Path (@{$In::Opt{"SysPaths"}{"bin"}}) + { + my $CmdPath = join_P($Path,$Name); + if(-f $CmdPath) + { + if($Name=~/gcc/) { + next if(not checkGcc("3", $CmdPath)); + } + return ($Cache{"searchCommand"}{$Name} = $CmdPath); + } + } + return ($Cache{"searchCommand"}{$Name} = ""); +} + +sub getCmdPath_Default($) +{ # search in PATH + if(defined $Cache{"getCmdPath_Default"}{$_[0]}) { + return $Cache{"getCmdPath_Default"}{$_[0]}; + } + return ($Cache{"getCmdPath_Default"}{$_[0]} = getCmdPath_Default_I($_[0])); +} + +sub getCmdPath_Default_I($) +{ # search in PATH + my $Name = $_[0]; + + my $TmpDir = $In::Opt{"Tmp"}; + + if($Name=~/find/) + { # special case: search for "find" utility + if(`find \"$TmpDir\" -maxdepth 0 2>\"$TmpDir/null\"`) { + return "find"; + } + } + elsif($Name=~/gcc/) { + return checkGcc("3", $Name); + } + if(tryCmd($Name)) { + return $Name; + } + if($In::Opt{"OS"} eq "windows") + { + if(`$Name /? 2>\"$TmpDir/null\"`) { + return $Name; + } + } + foreach my $Path (@DefaultBinPaths) + { + if(-f $Path."/".$Name) { + return join_P($Path, $Name); + } + } + return ""; +} + +sub checkSystemFiles() +{ + if($Cache{"checkSystemFiles"}) + { # run once + return; + } + $Cache{"checkSystemFiles"} = 1; + + my $LibExt = $In::Opt{"Ext"}; + my @SysHeaders = (); + + foreach my $DevelPath (@{$In::Opt{"SysPaths"}{"lib"}}) + { + if(not -d $DevelPath) { + next; + } + + my @Files = cmdFind($DevelPath,"f"); + foreach my $Link (cmdFind($DevelPath,"l")) + { # add symbolic links + if(-f $Link) { + push(@Files, $Link); + } + } + + # search for headers in /usr/lib + my @Headers = grep { /\.h(pp|xx)?\Z|\/include\// } @Files; + @Headers = grep { not /\/(gcc|jvm|syslinux|kbd|parrot|xemacs|perl|llvm)/ } @Headers; + push(@SysHeaders, @Headers); + + # search for libraries in /usr/lib (including symbolic links) + my @Libs = grep { /\.$LibExt[0-9.]*\Z/ } @Files; + foreach my $Path (@Libs) + { + my $N = getFilename($Path); + $In::Opt{"SystemObjects"}{$N}{$Path} = 1; + $In::Opt{"SystemObjects"}{libPart($N, "name+ext")}{$Path} = 1; + } + } + + foreach my $DevelPath (@{$In::Opt{"SysPaths"}{"include"}}) + { + if(not -d $DevelPath) { + next; + } + # search for all header files in the /usr/include + # with or without extension (ncurses.h, QtCore, ...) + push(@SysHeaders, cmdFind($DevelPath,"f")); + foreach my $Link (cmdFind($DevelPath,"l")) + { # add symbolic links + if(-f $Link) { + push(@SysHeaders, $Link); + } + } + } + getPrefixes_I(\@SysHeaders, \%SystemHeaders); +} + +sub libPart($$) +{ + my ($N, $T) = @_; + if(defined $Cache{"libPart"}{$T}{$N}) { + return $Cache{"libPart"}{$T}{$N}; + } + return ($Cache{"libPart"}{$T}{$N} = libPart_I(@_)); +} + +sub libPart_I($$) +{ + my ($N, $T) = @_; + + my $Ext = $In::Opt{"Ext"}; + my $Target = $In::Opt{"Target"}; + + if($Target eq "symbian") + { + if($N=~/(((.+?)(\{.+\}|))\.$Ext)\Z/) + { # libpthread{00010001}.dso + if($T eq "name") + { # libpthread{00010001} + return $2; + } + elsif($T eq "name+ext") + { # libpthread{00010001}.dso + return $1; + } + elsif($T eq "version") + { # 00010001 + my $V = $4; + $V=~s/\{(.+)\}/$1/; + return $V; + } + elsif($T eq "short") + { # libpthread + return $3; + } + elsif($T eq "shortest") + { # pthread + return shortestName($3); + } + } + } + elsif($Target eq "windows") + { + if($N=~/((.+?)\.$Ext)\Z/) + { # netapi32.dll + if($T eq "name") + { # netapi32 + return $2; + } + elsif($T eq "name+ext") + { # netapi32.dll + return $1; + } + elsif($T eq "version") + { # DLL version embedded + # at binary-level + return ""; + } + elsif($T eq "short") + { # netapi32 + return $2; + } + elsif($T eq "shortest") + { # netapi + return shortestName($2); + } + } + } + else + { # unix + if($N=~/((((lib|).+?)([\-\_][\d\-\.\_]+.*?|))\.$Ext)(\.(.+)|)\Z/) + { # libSDL-1.2.so.0.7.1 + # libwbxml2.so.0.0.18 + # libopcodes-2.21.53-system.20110810.so + if($T eq "name") + { # libSDL-1.2 + # libwbxml2 + return $2; + } + elsif($T eq "name+ext") + { # libSDL-1.2.so + # libwbxml2.so + return $1; + } + elsif($T eq "version") + { + if(defined $7 + and $7 ne "") + { # 0.7.1 + return $7; + } + else + { # libc-2.5.so (=>2.5 version) + my $MV = $5; + $MV=~s/\A[\-\_]+//g; + return $MV; + } + } + elsif($T eq "short") + { # libSDL + # libwbxml2 + return $3; + } + elsif($T eq "shortest") + { # SDL + # wbxml + return shortestName($3); + } + } + } + + # error + return ""; +} + +sub shortestName($) +{ + my $Name = $_[0]; + # remove prefix + $Name=~s/\A(lib|open)//; + # remove suffix + $Name=~s/[\W\d_]+\Z//i; + $Name=~s/([a-z]{2,})(lib)\Z/$1/i; + return $Name; +} + +sub detectDefaultPaths($$$$) +{ + my ($HSearch, $LSearch, $BSearch, $GSearch) = (@_); + + if($Cache{"detectDefaultPaths"}{$HSearch}{$LSearch}{$BSearch}{$GSearch}) + { # enter once + return; + } + $Cache{"detectDefaultPaths"}{$HSearch}{$LSearch}{$BSearch}{$GSearch} = 1; + + if(@{$In::Opt{"SysPaths"}{"include"}}) + { # section of the XML descriptor + # do NOT search for systems headers + $HSearch = undef; + } + if(@{$In::Opt{"SysPaths"}{"lib"}}) + { # section of the XML descriptor + # do NOT search for systems libraries + $LSearch = undef; + } + + foreach my $Type (keys(%{$OS_AddPath{$In::Opt{"OS"}}})) + { # additional search paths + next if($Type eq "include" and not $HSearch); + next if($Type eq "lib" and not $LSearch); + next if($Type eq "bin" and not $BSearch); + + push_U($In::Opt{"SysPaths"}{$Type}, grep { -d $_ } @{$OS_AddPath{$In::Opt{"OS"}}{$Type}}); + } + if($In::Opt{"OS"} ne "windows") + { # unix-like + foreach my $Type ("include", "lib", "bin") + { # automatic detection of system "devel" directories + next if($Type eq "include" and not $HSearch); + next if($Type eq "lib" and not $LSearch); + next if($Type eq "bin" and not $BSearch); + + my ($UsrDir, $RootDir) = ("/usr", "/"); + + if(my $SystemRoot = $In::Opt{"SystemRoot"} + and $Type ne "bin") + { # 1. search for target headers and libraries + # 2. use host commands: ldconfig, readelf, etc. + ($UsrDir, $RootDir) = ("$SystemRoot/usr", $SystemRoot); + } + + push_U($In::Opt{"SysPaths"}{$Type}, cmdFind($RootDir,"d","*$Type*",1)); + + if(-d $RootDir."/".$Type) + { # if "/lib" is symbolic link + if($RootDir eq "/") { + push_U($In::Opt{"SysPaths"}{$Type}, "/".$Type); + } + else { + push_U($In::Opt{"SysPaths"}{$Type}, $RootDir."/".$Type); + } + } + + if(-d $UsrDir) + { + push_U($In::Opt{"SysPaths"}{$Type}, cmdFind($UsrDir,"d","*$Type*",1)); + if(-d $UsrDir."/".$Type) + { # if "/usr/lib" is symbolic link + push_U($In::Opt{"SysPaths"}{$Type}, $UsrDir."/".$Type); + } + } + } + } + if($BSearch) + { + detectBinDefaultPaths(); + push_U($In::Opt{"SysPaths"}{"bin"}, @DefaultBinPaths); + } + + # check environment variables + if($In::Opt{"OS"} eq "beos") + { + foreach (my @Paths = @{$In::Opt{"SysPaths"}{"bin"}}) + { + if($_ eq ".") { + next; + } + # search for /boot/develop/abi/x86/gcc4/tools/gcc-4.4.4-haiku-101111/bin/ + if(my @Dirs = sort cmdFind($_, "d", "bin")) { + push_U($In::Opt{"SysPaths"}{"bin"}, sort {getDepth($a)<=>getDepth($b)} @Dirs); + } + } + + if($HSearch) + { + push_U(\@DefaultIncPaths, grep { isAbsPath($_) } ( + split(/:|;/, $ENV{"BEINCLUDES"}) + )); + } + + if($LSearch) + { + push_U($In::Opt{"DefaultLibPaths"}, grep { isAbsPath($_) } ( + split(/:|;/, $ENV{"BELIBRARIES"}), + split(/:|;/, $ENV{"LIBRARY_PATH"}) + )); + } + } + if($LSearch) + { # using linker to get system paths + if(my $LPaths = detectLibDefaultPaths()) + { # unix-like + my %Dirs = (); + foreach my $Name (keys(%{$LPaths})) + { + if(my $SystemRoot = $In::Opt{"SystemRoot"}) + { + if($LPaths->{$Name}!~/\A\Q$SystemRoot\E\//) + { # wrong ldconfig configuration + # check your /etc/ld.so.conf + next; + } + } + + $In::Opt{"LibDefaultPath"}{$Name} = $LPaths->{$Name}; + if(my $Dir = getDirname($LPaths->{$Name})) { + $Dirs{$Dir} = 1; + } + } + push_U($In::Opt{"DefaultLibPaths"}, sort {getDepth($a)<=>getDepth($b)} sort keys(%Dirs)); + } + push_U($In::Opt{"SysPaths"}{"lib"}, @{$In::Opt{"DefaultLibPaths"}}); + + if(my $EDir = $In::Opt{"ExtraInfo"}) { + writeFile($EDir."/default-libs", join("\n", @{$In::Opt{"DefaultLibPaths"}})); + } + } + + if($BSearch) + { + if($In::Opt{"CrossPrefix"}) + { + if(my $GccPath = getGccPath()) + { + $In::Opt{"GccPath"} = $GccPath; + if(my $D = getDirname($GccPath)) { + $In::Opt{"TargetTools"}{$D}=1; + } + } + } + } + + if($GSearch and my $GccPath = getGccPath()) + { # GCC path and default include dirs + $In::Opt{"GccPath"} = $GccPath; + + my $GccVer = dumpVersion($GccPath); + + if($GccVer=~/\A\d+\.\d+\Z/) + { # on Ubuntu -dumpversion returns 4.8 for gcc 4.8.4 + my $Info = `$GccPath --version`; + + if($Info=~/gcc\s+(|\([^()]+\)\s+)(\d+\.\d+\.\d+)/) + { # gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4 + # gcc (GCC) 4.9.2 20150212 (Red Hat 4.9.2-6) + $GccVer = $2; + } + } + + if($In::Opt{"OS"}=~/macos/) + { + my $Info = `$GccPath --version`; + + if($Info=~/clang/i) { + printMsg("WARNING", "doesn't work with clang, please install GCC instead (and select it by -gcc-path option)"); + } + } + + if($GccVer) + { + my $Target = dumpMachine($GccPath); + + if($Target=~/linux/) { + setTarget("linux"); + } + elsif($Target=~/symbian/) { + setTarget("symbian"); + } + elsif($Target=~/solaris/) { + setTarget("solaris"); + } + + $In::Opt{"GccTarget"} = $Target; + $In::Opt{"GccVer"} = $GccVer; + + printMsg("INFO", "Using GCC $GccVer ($Target, target: ".getArch_GCC(1).")"); + + # check GCC version + if($GccVer=~/\A(4\.8(|\.[012])|[67](\..*)?)\Z/ or cmpVersions($GccVer, "8")>=0) + { # GCC 4.8.[0-2]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57850 + # GCC 6.[1-2].0: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78040 + # GCC 7.1: still the same issue ... + # GCC 8: still the same issue ... + # ABICC 2.3: enable this for all future GCC versions + printMsg("WARNING", "May not work properly with GCC 4.8.[0-2], 6.* and higher due to bug #78040 in GCC. Please try other GCC versions with the help of --gcc-path=PATH option or create ABI dumps by ABI Dumper tool instead to avoid using GCC. Test selected GCC version first by -test and -gcc-path options."); + $In::Opt{"GccMissedMangling"} = 1; + } + } + else { + exitStatus("Error", "something is going wrong with the GCC compiler"); + } + } + + if($HSearch) + { + # GCC standard paths + if($In::Opt{"GccPath"} + and not $In::Opt{"NoStdInc"}) + { + my %DPaths = detectIncDefaultPaths(); + @DefaultCppPaths = @{$DPaths{"Cpp"}}; + @DefaultGccPaths = @{$DPaths{"Gcc"}}; + @DefaultIncPaths = @{$DPaths{"Inc"}}; + push_U($In::Opt{"SysPaths"}{"include"}, @DefaultIncPaths); + } + + # users include paths + my $IncPath = "/usr/include"; + if(my $SystemRoot = $In::Opt{"SystemRoot"}) { + $IncPath = $SystemRoot.$IncPath; + } + if(-d $IncPath) { + push_U(\@UsersIncPath, $IncPath); + } + + if(my $EDir = $In::Opt{"ExtraInfo"}) { + writeFile($EDir."/default-includes", join("\n", (@DefaultCppPaths, @DefaultGccPaths, @DefaultIncPaths))); + } + } + + +} + +sub detectLibDefaultPaths() +{ + my %LPaths = (); + + my $TmpDir = $In::Opt{"Tmp"}; + + if($In::Opt{"OS"} eq "bsd") + { + if(my $LdConfig = getCmdPath("ldconfig")) + { + foreach my $Line (split(/\n/, `$LdConfig -r 2>\"$TmpDir/null\"`)) + { + if($Line=~/\A[ \t]*\d+:\-l(.+) \=\> (.+)\Z/) + { + my $Name = "lib".$1; + if(not defined $LPaths{$Name}) { + $LPaths{$Name} = $2; + } + } + } + } + else { + printMsg("WARNING", "can't find ldconfig"); + } + } + else + { + if(my $LdConfig = getCmdPath("ldconfig")) + { + if(my $SystemRoot = $In::Opt{"SystemRoot"} + and $In::Opt{"OS"} eq "linux") + { # use host (x86) ldconfig with the target (arm) ld.so.conf + if(-e $SystemRoot."/etc/ld.so.conf") { + $LdConfig .= " -f ".$SystemRoot."/etc/ld.so.conf"; + } + } + foreach my $Line (split(/\n/, `$LdConfig -p 2>\"$TmpDir/null\"`)) + { + if($Line=~/\A[ \t]*([^ \t]+) .* \=\> (.+)\Z/) + { + my ($Name, $Path) = ($1, $2); + $Path=~s/[\/]{2,}/\//; + if(not defined $LPaths{$Name}) + { # get first element from the list of available paths + + # libstdc++.so.6 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 + # libstdc++.so.6 (libc6) => /usr/lib/i386-linux-gnu/libstdc++.so.6 + # libstdc++.so.6 (libc6) => /usr/lib32/libstdc++.so.6 + + $LPaths{$Name} = $Path; + } + } + } + } + elsif($In::Opt{"OS"} eq "linux") { + printMsg("WARNING", "can't find ldconfig"); + } + } + return \%LPaths; +} + +sub detectBinDefaultPaths() +{ + my $EnvPaths = $ENV{"PATH"}; + if($In::Opt{"OS"} eq "beos") { + $EnvPaths.=":".$ENV{"BETOOLS"}; + } + my $Sep = ":|;"; + if($In::Opt{"OS"} eq "windows") { + $Sep = ";"; + } + + foreach my $Path (split(/$Sep/, $EnvPaths)) + { + $Path = pathFmt($Path); + if(not $Path) { + next; + } + if(my $SystemRoot = $In::Opt{"SystemRoot"}) + { + if($Path=~/\A\Q$SystemRoot\E\//) + { # do NOT use binaries from target system + next; + } + } + push_U(\@DefaultBinPaths, $Path); + } +} + +sub detectIncDefaultPaths() +{ + my $GccPath = $In::Opt{"GccPath"}; + my %DPaths = ("Cpp"=>[],"Gcc"=>[],"Inc"=>[]); + + my $TmpDir = $In::Opt{"Tmp"}; + writeFile("$TmpDir/empty.h", ""); + + foreach my $Line (split(/\n/, `$GccPath -v -x c++ -E \"$TmpDir/empty.h\" 2>&1`)) + { # detecting GCC default include paths + if(index($Line, "/cc1plus ")!=-1) { + next; + } + + if($Line=~/\A[ \t]*((\/|\w+:\\).+)[ \t]*\Z/) + { + my $Path = realpath_F($1); + if(index($Path, "c++")!=-1 + or index($Path, "/g++/")!=-1) + { + push_U($DPaths{"Cpp"}, $Path); + if(not defined $In::Opt{"MainCppDir"} + or getDepth($In::Opt{"MainCppDir"})>getDepth($Path)) { + $In::Opt{"MainCppDir"} = $Path; + } + } + elsif(index($Path, "gcc")!=-1) { + push_U($DPaths{"Gcc"}, $Path); + } + else + { + if($Path=~/local[\/\\]+include/) + { # local paths + next; + } + if(my $SystemRoot = $In::Opt{"SystemRoot"}) + { + if($Path!~/\A\Q$SystemRoot\E(\/|\Z)/) + { # The GCC include path for user headers is not a part of the system root + # The reason: you are not specified the --cross-gcc option or selected a wrong compiler + # or it is the internal cross-GCC path like arm-linux-gnueabi/include + next; + } + } + push_U($DPaths{"Inc"}, $Path); + } + } + } + unlink("$TmpDir/empty.h"); + return %DPaths; +} + +sub registerGccHeaders() +{ + if($Cache{"registerGccHeaders"}) + { # this function should be called once + return; + } + + foreach my $Path (@DefaultGccPaths) + { + my @Headers = cmdFind($Path,"f"); + @Headers = sort {getDepth($a)<=>getDepth($b)} @Headers; + foreach my $HPath (@Headers) + { + my $FileName = getFilename($HPath); + if(not defined $DefaultGccHeader{$FileName}) + { # skip duplicated + $DefaultGccHeader{$FileName} = $HPath; + } + } + } + $Cache{"registerGccHeaders"} = 1; +} + +sub registerCppHeaders() +{ + if($Cache{"registerCppHeaders"}) + { # this function should be called once + return; + } + + foreach my $CppDir (@DefaultCppPaths) + { + my @Headers = cmdFind($CppDir,"f"); + @Headers = sort {getDepth($a)<=>getDepth($b)} @Headers; + foreach my $Path (@Headers) + { + my $FileName = getFilename($Path); + if(not defined $DefaultCppHeader{$FileName}) + { # skip duplicated + $DefaultCppHeader{$FileName} = $Path; + } + } + } + $Cache{"registerCppHeaders"} = 1; +} + +sub parseRedirect($$$) +{ + my ($Content, $Path, $LVer) = @_; + my @Errors = (); + while($Content=~s/#\s*error\s+([^\n]+?)\s*(\n|\Z)//) { + push(@Errors, $1); + } + my $Redirect = ""; + foreach (@Errors) + { + s/\s{2,}/ /g; + if(/(only|must\ include + |update\ to\ include + |replaced\ with + |replaced\ by|renamed\ to + |\ is\ in|\ use)\ (<[^<>]+>|[\w\-\/\\]+\.($HEADER_EXT))/ix) + { + $Redirect = $2; + last; + } + elsif(/(include|use|is\ in)\ (<[^<>]+>|[\w\-\/\\]+\.($HEADER_EXT))\ instead/i) + { + $Redirect = $2; + last; + } + elsif(/this\ header\ should\ not\ be\ used + |programs\ should\ not\ directly\ include + |you\ should\ not\ (include|be\ (including|using)\ this\ (file|header)) + |is\ not\ supported\ API\ for\ general\ use + |do\ not\ use + |should\ not\ be\ (used|using) + |cannot\ be\ included\ directly/ix and not /\ from\ /i) { + $Header_ShouldNotBeUsed{$LVer}{$Path} = 1; + } + } + if($Redirect) + { + $Redirect=~s/\A\Z//g; + } + return $Redirect; +} + +sub parseIncludes($$) +{ + my ($Content, $Path) = @_; + my %Includes = (); + while($Content=~s/^[ \t]*#[ \t]*(include|include_next|import)[ \t]*([<"].+?[">])[ \t]*//m) + { # C/C++: include, Objective C/C++: import directive + my $Header = $2; + my $Method = substr($Header, 0, 1, ""); + substr($Header, length($Header)-1, 1, ""); + $Header = pathFmt($Header); + if($Method eq "\"" or isAbsPath($Header)) + { + if(-e join_P(getDirname($Path), $Header)) + { # relative path exists + $Includes{$Header} = -1; + } + else + { # include "..." that doesn't exist is equal to include <...> + $Includes{$Header} = 2; + } + } + else { + $Includes{$Header} = 1; + } + } + if($In::Opt{"ExtraInfo"}) + { + while($Content=~s/^[ \t]*#[ \t]*(include|include_next|import)[ \t]+(\w+)[ \t]*//m) + { # FT_FREETYPE_H + $Includes{$2} = 0; + } + } + return \%Includes; +} + +sub sortHeaders($$) +{ + my ($H1, $H2) = @_; + + $H1=~s/\.[a-z]+\Z//ig; + $H2=~s/\.[a-z]+\Z//ig; + + my $Hname1 = getFilename($H1); + my $Hname2 = getFilename($H2); + my $HDir1 = getDirname($H1); + my $HDir2 = getDirname($H2); + my $Dirname1 = getFilename($HDir1); + my $Dirname2 = getFilename($HDir2); + + $HDir1=~s/\A.*[\/\\]+([^\/\\]+[\/\\]+[^\/\\]+)\Z/$1/; + $HDir2=~s/\A.*[\/\\]+([^\/\\]+[\/\\]+[^\/\\]+)\Z/$1/; + + if($_[0] eq $_[1] + or $H1 eq $H2) { + return 0; + } + elsif($H1=~/\A\Q$H2\E/) { + return 1; + } + elsif($H2=~/\A\Q$H1\E/) { + return -1; + } + elsif($HDir1=~/\Q$Hname1\E/i + and $HDir2!~/\Q$Hname2\E/i) + { # include/glib-2.0/glib.h + return -1; + } + elsif($HDir2=~/\Q$Hname2\E/i + and $HDir1!~/\Q$Hname1\E/i) + { # include/glib-2.0/glib.h + return 1; + } + elsif($Hname1=~/\Q$Dirname1\E/i + and $Hname2!~/\Q$Dirname2\E/i) + { # include/hildon-thumbnail/hildon-thumbnail-factory.h + return -1; + } + elsif($Hname2=~/\Q$Dirname2\E/i + and $Hname1!~/\Q$Dirname1\E/i) + { # include/hildon-thumbnail/hildon-thumbnail-factory.h + return 1; + } + elsif($Hname1=~/(config|lib|util)/i + and $Hname2!~/(config|lib|util)/i) + { # include/alsa/asoundlib.h + return -1; + } + elsif($Hname2=~/(config|lib|util)/i + and $Hname1!~/(config|lib|util)/i) + { # include/alsa/asoundlib.h + return 1; + } + else + { + my $R1 = checkRelevance($H1); + my $R2 = checkRelevance($H2); + if($R1 and not $R2) + { # libebook/e-book.h + return -1; + } + elsif($R2 and not $R1) + { # libebook/e-book.h + return 1; + } + else + { + return (lc($H1) cmp lc($H2)); + } + } +} + +sub detectRealIncludes($$) +{ + my ($AbsPath, $LVer) = @_; + + if($Cache{"detectRealIncludes"}{$LVer}{$AbsPath} + or keys(%{$RecursiveIncludes{$LVer}{$AbsPath}})) { + return keys(%{$RecursiveIncludes{$LVer}{$AbsPath}}); + } + $Cache{"detectRealIncludes"}{$LVer}{$AbsPath}=1; + + my $Path = callPreprocessor($AbsPath, "", $LVer); + if(not $Path) { + return (); + } + open(PREPROC, $Path); + while() + { + if(/#\s+\d+\s+"([^"]+)"[\s\d]*\n/) + { + my $Include = pathFmt($1); + if($Include=~/\<(built\-in|internal|command(\-|\s)line)\>|\A\./) { + next; + } + if($Include eq $AbsPath) { + next; + } + $RecursiveIncludes{$LVer}{$AbsPath}{$Include} = 1; + } + } + close(PREPROC); + return keys(%{$RecursiveIncludes{$LVer}{$AbsPath}}); +} + +sub detectHeaderIncludes($$) +{ + my ($Path, $LVer) = @_; + + if(defined $Cache{"detectHeaderIncludes"}{$LVer}{$Path}) { + return; + } + $Cache{"detectHeaderIncludes"}{$LVer}{$Path}=1; + + if(not -e $Path) { + return; + } + + my $Content = readFile($Path); + if(my $Redirect = parseRedirect($Content, $Path, $LVer)) + { # detect error directive in headers + if(my $RedirectPath = identifyHeader($Redirect, $LVer)) + { + if($RedirectPath=~/\/usr\/include\// and $Path!~/\/usr\/include\//) { + $RedirectPath = identifyHeader(getFilename($Redirect), $LVer); + } + if($RedirectPath ne $Path) { + $Header_ErrorRedirect{$LVer}{$Path} = $RedirectPath; + } + } + else + { # can't find + $Header_ShouldNotBeUsed{$LVer}{$Path} = 1; + } + } + if(my $Inc = parseIncludes($Content, $Path)) + { + foreach my $Include (keys(%{$Inc})) + { # detect includes + $Header_Includes{$LVer}{$Path}{$Include} = $Inc->{$Include}; + + if(defined $In::Opt{"Tolerance"} + and $In::Opt{"Tolerance"}=~/4/) + { + if(my $HPath = identifyHeader($Include, $LVer)) + { + $Header_Includes_R{$LVer}{$HPath}{$Path} = 1; + } + } + } + } +} + +sub fromLibc($) +{ # system GLIBC header + my $Path = $_[0]; + my ($Dir, $Name) = sepPath($Path); + if($In::Opt{"Target"} eq "symbian") + { + if(getFilename($Dir) eq "libc" and $GlibcHeader{$Name}) + { # epoc32/include/libc/{stdio, ...}.h + return 1; + } + } + else + { + if($Dir eq "/usr/include" and $GlibcHeader{$Name}) + { # /usr/include/{stdio, ...}.h + return 1; + } + } + return 0; +} + +sub isLibcDir($) +{ # system GLIBC directory + my $Dir = $_[0]; + my ($OutDir, $Name) = sepPath($Dir); + if($In::Opt{"Target"} eq "symbian") + { + if(getFilename($OutDir) eq "libc" + and ($Name=~/\Aasm(|-.+)\Z/ or $GlibcDir{$Name})) + { # epoc32/include/libc/{sys,bits,asm,asm-*}/*.h + return 1; + } + } + else + { # linux + if($OutDir eq "/usr/include" + and ($Name=~/\Aasm(|-.+)\Z/ or $GlibcDir{$Name})) + { # /usr/include/{sys,bits,asm,asm-*}/*.h + return 1; + } + } + return 0; +} + +sub detectRecursiveIncludes($$) +{ + my ($AbsPath, $LVer) = @_; + if(not $AbsPath) { + return (); + } + if(isCyclical(\@RecurInclude, $AbsPath)) { + return keys(%{$RecursiveIncludes{$LVer}{$AbsPath}}); + } + my ($AbsDir, $Name) = sepPath($AbsPath); + if(isLibcDir($AbsDir)) + { # system GLIBC internals + if(not $In::Opt{"ExtraInfo"}) { + return (); + } + } + if(keys(%{$RecursiveIncludes{$LVer}{$AbsPath}})) { + return keys(%{$RecursiveIncludes{$LVer}{$AbsPath}}); + } + if($In::Opt{"OS"} ne "windows" + and $Name=~/windows|win32|win64/i) { + return (); + } + + if($In::Opt{"MainCppDir"} and $AbsPath=~/\A\Q$In::Opt{"MainCppDir"}\E/ and not $In::Opt{"StdcxxTesting"}) + { # skip /usr/include/c++/*/ headers + if(not $In::Opt{"ExtraInfo"}) { + return (); + } + } + + push(@RecurInclude, $AbsPath); + if(grep { $AbsDir eq $_ } @DefaultGccPaths + or (grep { $AbsDir eq $_ } @DefaultIncPaths and fromLibc($AbsPath))) + { # check "real" (non-"model") include paths + my @Paths = detectRealIncludes($AbsPath, $LVer); + pop(@RecurInclude); + return @Paths; + } + if(not keys(%{$Header_Includes{$LVer}{$AbsPath}})) { + detectHeaderIncludes($AbsPath, $LVer); + } + foreach my $Include (keys(%{$Header_Includes{$LVer}{$AbsPath}})) + { + my $IncType = $Header_Includes{$LVer}{$AbsPath}{$Include}; + my $HPath = ""; + if($IncType<0) + { # for #include "..." + my $Candidate = join_P($AbsDir, $Include); + if(-f $Candidate) { + $HPath = realpath_F($Candidate); + } + } + elsif($IncType>0 + and $Include=~/[\/\\]/) # and not findInDefaults($Include) + { # search for the nearest header + # QtCore/qabstractanimation.h includes + my $Candidate = join_P(getDirname($AbsDir), $Include); + if(-f $Candidate) { + $HPath = $Candidate; + } + } + if(not $HPath) { + $HPath = identifyHeader($Include, $LVer); + } + next if(not $HPath); + if($HPath eq $AbsPath) { + next; + } + + #if($In::Opt{"Debug"}) + #{ # boundary headers + # if($HPath=~/vtk/ and $AbsPath!~/vtk/) + # { + # print STDERR "$AbsPath -> $HPath\n"; + # } + #} + + $RecursiveIncludes{$LVer}{$AbsPath}{$HPath} = $IncType; + if($IncType>0) + { # only include <...>, skip include "..." prefixes + $Header_Include_Prefix{$LVer}{$AbsPath}{$HPath}{getDirname($Include)} = 1; + } + foreach my $IncPath (detectRecursiveIncludes($HPath, $LVer)) + { + if($IncPath eq $AbsPath) { + next; + } + my $RIncType = $RecursiveIncludes{$LVer}{$HPath}{$IncPath}; + if($RIncType==-1) + { # include "..." + $RIncType = $IncType; + } + elsif($RIncType==2) + { + if($IncType!=-1) { + $RIncType = $IncType; + } + } + $RecursiveIncludes{$LVer}{$AbsPath}{$IncPath} = $RIncType; + foreach my $Prefix (keys(%{$Header_Include_Prefix{$LVer}{$HPath}{$IncPath}})) { + $Header_Include_Prefix{$LVer}{$AbsPath}{$IncPath}{$Prefix} = 1; + } + } + foreach my $Dep (keys(%{$Header_Include_Prefix{$LVer}{$AbsPath}})) + { + if($GlibcHeader{getFilename($Dep)} and keys(%{$Header_Include_Prefix{$LVer}{$AbsPath}{$Dep}})>=2 + and defined $Header_Include_Prefix{$LVer}{$AbsPath}{$Dep}{""}) + { # distinguish math.h from glibc and math.h from the tested library + delete($Header_Include_Prefix{$LVer}{$AbsPath}{$Dep}{""}); + last; + } + } + } + pop(@RecurInclude); + return keys(%{$RecursiveIncludes{$LVer}{$AbsPath}}); +} + +sub findInFramework($$$) +{ + my ($Header, $Framework, $LVer) = @_; + + if(defined $Cache{"findInFramework"}{$LVer}{$Framework}{$Header}) { + return $Cache{"findInFramework"}{$LVer}{$Framework}{$Header}; + } + foreach my $Dependency (sort {getDepth($a)<=>getDepth($b)} keys(%{$Header_Dependency{$LVer}})) + { + if(getFilename($Dependency) eq $Framework + and -f getDirname($Dependency)."/".$Header) { + return ($Cache{"findInFramework"}{$LVer}{$Framework}{$Header} = getDirname($Dependency)); + } + } + return ($Cache{"findInFramework"}{$LVer}{$Framework}{$Header} = ""); +} + +sub findInDefaults($) +{ + my $Header = $_[0]; + + if(defined $Cache{"findInDefaults"}{$Header}) { + return $Cache{"findInDefaults"}{$Header}; + } + foreach my $Dir (@DefaultIncPaths, + @DefaultGccPaths, + @DefaultCppPaths, + @UsersIncPath) + { + if(not $Dir) { + next; + } + if(-f $Dir."/".$Header) { + return ($Cache{"findInDefaults"}{$Header}=$Dir); + } + } + return ($Cache{"findInDefaults"}{$Header} = ""); +} + +sub cmp_paths($$) +{ + my ($Path1, $Path2) = @_; + my @Parts1 = split(/[\/\\]/, $Path1); + my @Parts2 = split(/[\/\\]/, $Path2); + foreach my $Num (0 .. $#Parts1) + { + my $Part1 = $Parts1[$Num]; + my $Part2 = $Parts2[$Num]; + if($GlibcDir{$Part1} + and not $GlibcDir{$Part2}) { + return 1; + } + elsif($GlibcDir{$Part2} + and not $GlibcDir{$Part1}) { + return -1; + } + elsif($Part1=~/glib/ + and $Part2!~/glib/) { + return 1; + } + elsif($Part1!~/glib/ + and $Part2=~/glib/) { + return -1; + } + elsif(my $CmpRes = ($Part1 cmp $Part2)) { + return $CmpRes; + } + } + return 0; +} + +sub checkRelevance($) +{ + my $Path = $_[0]; + + if(my $SystemRoot = $In::Opt{"SystemRoot"}) { + $Path = cutPrefix($Path, $SystemRoot); + } + + my $Name = lc(getFilename($Path)); + my $Dir = lc(getDirname($Path)); + + $Name=~s/\.\w+\Z//g; # remove extension (.h) + + foreach my $Token (split(/[_\d\W]+/, $Name)) + { + my $Len = length($Token); + next if($Len<=1); + if($Dir=~/(\A|lib|[_\d\W])\Q$Token\E([_\d\W]|lib|\Z)/) + { # include/evolution-data-server-1.4/libebook/e-book.h + return 1; + } + if($Len>=4 and index($Dir, $Token)!=-1) + { # include/gupnp-1.0/libgupnp/gupnp-context.h + return 1; + } + } + return 0; +} + +sub checkFamily(@) +{ + my @Paths = @_; + if($#Paths<=0) { + return 1; + } + my %Prefix = (); + foreach my $Path (@Paths) + { + if(my $SystemRoot = $In::Opt{"SystemRoot"}) { + $Path = cutPrefix($Path, $SystemRoot); + } + if(my $Dir = getDirname($Path)) + { + $Dir=~s/(\/[^\/]+?)[\d\.\-\_]+\Z/$1/g; # remove version suffix + $Prefix{$Dir} += 1; + $Prefix{getDirname($Dir)} += 1; + } + } + foreach (sort keys(%Prefix)) + { + if(getDepth($_)>=3 + and $Prefix{$_}==$#Paths+1) { + return 1; + } + } + return 0; +} + +sub isAcceptable($$$) +{ + my ($Header, $Candidate, $LVer) = @_; + my $HName = getFilename($Header); + if(getDirname($Header)) + { # with prefix + return 1; + } + if($HName=~/config|setup/i and $Candidate=~/[\/\\]lib\d*[\/\\]/) + { # allow to search for glibconfig.h in /usr/lib/glib-2.0/include/ + return 1; + } + if(checkRelevance($Candidate)) + { # allow to search for atk.h in /usr/include/atk-1.0/atk/ + return 1; + } + if(checkFamily(getSystemHeaders($HName, $LVer))) + { # /usr/include/qt4/QtNetwork/qsslconfiguration.h + # /usr/include/qt4/Qt/qsslconfiguration.h + return 1; + } + if($In::Opt{"Target"} eq "symbian") + { + if($Candidate=~/[\/\\]stdapis[\/\\]/) { + return 1; + } + } + return 0; +} + +sub isRelevant($$$) +{ # disallow to search for "abstract" headers in too deep directories + my ($Header, $Candidate, $LVer) = @_; + my $HName = getFilename($Header); + if($In::Opt{"Target"} eq "symbian") + { + if($Candidate=~/[\/\\](tools|stlportv5)[\/\\]/) { + return 0; + } + } + if($In::Opt{"Target"} ne "bsd") + { + if($Candidate=~/[\/\\]include[\/\\]bsd[\/\\]/) + { # openssh: skip /usr/lib/bcc/include/bsd/signal.h + return 0; + } + } + if($In::Opt{"Target"} ne "windows") + { + if($Candidate=~/[\/\\](wine|msvcrt|windows)[\/\\]/) + { # skip /usr/include/wine/msvcrt + return 0; + } + } + if(not getDirname($Header) + and $Candidate=~/[\/\\]wx[\/\\]/) + { # do NOT search in system /wx/ directory + # for headers without a prefix: sstream.h + return 0; + } + if($Candidate=~/c\+\+[\/\\]\d+/ and $In::Opt{"MainCppDir"} + and $Candidate!~/\A\Q$In::Opt{"MainCppDir"}\E/) + { # skip ../c++/3.3.3/ if using ../c++/4.5/ + return 0; + } + if($Candidate=~/[\/\\]asm-/ + and (my $Arch = getArch_GCC($LVer)) ne "unknown") + { # arch-specific header files + if($Candidate!~/[\/\\]asm-\Q$Arch\E/) + {# skip ../asm-arm/ if using x86 architecture + return 0; + } + } + my @Candidates = getSystemHeaders($HName, $LVer); + if($#Candidates==1) + { # unique header + return 1; + } + my @SCandidates = getSystemHeaders($Header, $LVer); + if($#SCandidates==1) + { # unique name + return 1; + } + my $SystemDepth = 0; + + if(my $SystemRoot = $In::Opt{"SystemRoot"}) { + $SystemDepth = getDepth($SystemRoot); + } + + if(getDepth($Candidate)-$SystemDepth>=5) + { # abstract headers in too deep directories + # sstream.h or typeinfo.h in /usr/include/wx-2.9/wx/ + if(not isAcceptable($Header, $Candidate, $LVer)) { + return 0; + } + } + if($Header eq "parser.h" + and $Candidate!~/\/libxml2\//) + { # select parser.h from xml2 library + return 0; + } + if(not getDirname($Header) + and keys(%{$SystemHeaders{$HName}})>=3) + { # many headers with the same name + # like thread.h included without a prefix + if(not checkFamily(@Candidates)) { + return 0; + } + } + return 1; +} + +sub selectSystemHeader($$) +{ # cache function + if(defined $Cache{"selectSystemHeader"}{$_[1]}{$_[0]}) { + return $Cache{"selectSystemHeader"}{$_[1]}{$_[0]}; + } + return ($Cache{"selectSystemHeader"}{$_[1]}{$_[0]} = selectSystemHeader_I(@_)); +} + +sub selectSystemHeader_I($$) +{ + my ($Header, $LVer) = @_; + if(-f $Header) { + return $Header; + } + if(isAbsPath($Header) and not -f $Header) + { # incorrect absolute path + return ""; + } + if(defined $ConfHeaders{lc($Header)}) + { # too abstract configuration headers + return ""; + } + my $HName = getFilename($Header); + if($In::Opt{"OS"} ne "windows") + { + if(defined $WinHeaders{lc($HName)} + or $HName=~/windows|win32|win64/i) + { # windows headers + return ""; + } + } + if($In::Opt{"OS"} ne "macos") + { + if($HName eq "fp.h") + { # pngconf.h includes fp.h in Mac OS + return ""; + } + } + + if(defined $ObsoleteHeaders{$HName}) + { # obsolete headers + return ""; + } + if($In::Opt{"OS"} eq "linux" + or $In::Opt{"OS"} eq "bsd") + { + if(defined $AlienHeaders{$HName} + or defined $AlienHeaders{$Header}) + { # alien headers from other systems + return ""; + } + } + + foreach my $Path (@{$In::Opt{"SysPaths"}{"include"}}) + { # search in default paths + if(-f $Path."/".$Header) { + return join_P($Path,$Header); + } + } + + # register all headers in system include dirs + checkSystemFiles(); + + foreach my $Candidate (sort {getDepth($a)<=>getDepth($b)} + sort {cmp_paths($b, $a)} getSystemHeaders($Header, $LVer)) + { + if(isRelevant($Header, $Candidate, $LVer)) { + return $Candidate; + } + } + # error + return ""; +} + +sub getSystemHeaders($$) +{ + my ($Header, $LVer) = @_; + my @Candidates = (); + foreach my $Candidate (sort keys(%{$SystemHeaders{$Header}})) + { + if(skipHeader($Candidate, $LVer)) { + next; + } + push(@Candidates, $Candidate); + } + return @Candidates; +} + +sub isDefaultIncludeDir($) +{ + my $Dir = $_[0]; + $Dir=~s/[\/\\]+\Z//; + return grep { $Dir eq $_ } (@DefaultGccPaths, @DefaultCppPaths, @DefaultIncPaths); +} + +sub identifyHeader($$) +{ # cache function + my ($Header, $LVer) = @_; + if(not $Header) { + return ""; + } + $Header=~s/\A(\.\.[\\\/])+//g; + if(defined $Cache{"identifyHeader"}{$LVer}{$Header}) { + return $Cache{"identifyHeader"}{$LVer}{$Header}; + } + return ($Cache{"identifyHeader"}{$LVer}{$Header} = identifyHeader_I($Header, $LVer)); +} + +sub identifyHeader_I($$) +{ # search for header by absolute path, relative path or name + my ($Header, $LVer) = @_; + if(-f $Header) + { # it's relative or absolute path + return getAbsPath($Header); + } + elsif($GlibcHeader{$Header} and not $In::Opt{"GlibcTesting"} + and my $HeaderDir = findInDefaults($Header)) + { # search for libc headers in the /usr/include + # for non-libc target library before searching + # in the library paths + return join_P($HeaderDir,$Header); + } + elsif(my $Path = $In::Desc{$LVer}{"IncludeNeighbors"}{$Header}) + { # search in the target library paths + return $Path; + } + elsif(defined $DefaultGccHeader{$Header}) + { # search in the internal GCC include paths + return $DefaultGccHeader{$Header}; + } + elsif(my $DefaultDir = findInDefaults($Header)) + { # search in the default GCC include paths + return join_P($DefaultDir,$Header); + } + elsif(defined $DefaultCppHeader{$Header}) + { # search in the default G++ include paths + return $DefaultCppHeader{$Header}; + } + elsif(my $AnyPath = selectSystemHeader($Header, $LVer)) + { # search everywhere in the system + return $AnyPath; + } + elsif($In::Opt{"OS"} eq "macos") + { # search in frameworks: "OpenGL/gl.h" is "OpenGL.framework/Headers/gl.h" + if(my $Dir = getDirname($Header)) + { + my $RelPath = "Headers\/".getFilename($Header); + if(my $HeaderDir = findInFramework($RelPath, $Dir.".framework", $LVer)) { + return join_P($HeaderDir, $RelPath); + } + } + } + # cannot find anything + return ""; +} + +sub cmdFile($) +{ + my $Path = $_[0]; + + if(my $CmdPath = getCmdPath("file")) { + return `$CmdPath -b \"$Path\"`; + } + return ""; +} + +sub getHeaderDeps($$) +{ + my ($AbsPath, $LVer) = @_; + + if(defined $Cache{"getHeaderDeps"}{$LVer}{$AbsPath}) { + return @{$Cache{"getHeaderDeps"}{$LVer}{$AbsPath}}; + } + my %IncDir = (); + detectRecursiveIncludes($AbsPath, $LVer); + foreach my $HeaderPath (keys(%{$RecursiveIncludes{$LVer}{$AbsPath}})) + { + if(not $HeaderPath) { + next; + } + if($In::Opt{"MainCppDir"} and $HeaderPath=~/\A\Q$In::Opt{"MainCppDir"}\E([\/\\]|\Z)/) { + next; + } + my $Dir = getDirname($HeaderPath); + foreach my $Prefix (keys(%{$Header_Include_Prefix{$LVer}{$AbsPath}{$HeaderPath}})) + { + my $Dep = $Dir; + if($Prefix) + { + if($In::Opt{"OS"} eq "windows") + { # case insensitive search on windows + if(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//ig) { + next; + } + } + elsif($In::Opt{"OS"} eq "macos") + { # search in frameworks + if(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//g) + { + if($HeaderPath=~/(.+\.framework)\/Headers\/([^\/]+)/) + {# frameworks + my ($HFramework, $HName) = ($1, $2); + $Dep = $HFramework; + } + else + {# mismatch + next; + } + } + } + elsif(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//g) + { # Linux, FreeBSD + next; + } + } + if(not $Dep) + { # nothing to include + next; + } + if(isDefaultIncludeDir($Dep)) + { # included by the compiler + next; + } + if(getDepth($Dep)==1) + { # too short + next; + } + if(isLibcDir($Dep)) + { # do NOT include /usr/include/{sys,bits} + next; + } + $IncDir{$Dep} = 1; + } + } + $Cache{"getHeaderDeps"}{$LVer}{$AbsPath} = sortIncPaths([keys(%IncDir)], $LVer); + return @{$Cache{"getHeaderDeps"}{$LVer}{$AbsPath}}; +} + +sub sortIncPaths($$) +{ + my ($ArrRef, $LVer) = @_; + if(not $ArrRef or $#{$ArrRef}<0) { + return $ArrRef; + } + @{$ArrRef} = sort {$b cmp $a} @{$ArrRef}; + @{$ArrRef} = sort {getDepth($a)<=>getDepth($b)} @{$ArrRef}; + @{$ArrRef} = sort {sortDeps($b, $a, $LVer)} @{$ArrRef}; + return $ArrRef; +} + +sub sortDeps($$$) +{ + if($Header_Dependency{$_[2]}{$_[0]} + and not $Header_Dependency{$_[2]}{$_[1]}) { + return 1; + } + elsif(not $Header_Dependency{$_[2]}{$_[0]} + and $Header_Dependency{$_[2]}{$_[1]}) { + return -1; + } + return 0; +} + +sub registerHeader($$) +{ # input: absolute path of header, relative path or name + my ($Header, $LVer) = @_; + if(not $Header) { + return ""; + } + if(isAbsPath($Header) and not -f $Header) + { # incorrect absolute path + exitStatus("Access_Error", "can't access \'$Header\'"); + } + if(skipHeader($Header, $LVer)) + { # skip + return ""; + } + if(my $Header_Path = identifyHeader($Header, $LVer)) + { + detectHeaderIncludes($Header_Path, $LVer); + + if(defined $In::Opt{"Tolerance"} + and $In::Opt{"Tolerance"}=~/3/) + { # 3 - skip headers that include non-Linux headers + if($In::Opt{"OS"} ne "windows") + { + foreach my $Inc (keys(%{$Header_Includes{$LVer}{$Header_Path}})) + { + if(specificHeader($Inc, "windows")) { + return ""; + } + } + } + } + + if(my $RHeader_Path = $Header_ErrorRedirect{$LVer}{$Header_Path}) + { # redirect + if($In::Desc{$LVer}{"RegHeader"}{$RHeader_Path}{"Identity"} + or skipHeader($RHeader_Path, $LVer)) + { # skip + return ""; + } + $Header_Path = $RHeader_Path; + } + elsif($Header_ShouldNotBeUsed{$LVer}{$Header_Path}) + { # skip + return ""; + } + + if(my $HName = getFilename($Header_Path)) + { # register + $In::Desc{$LVer}{"RegHeader"}{$Header_Path}{"Identity"} = $HName; + $HeaderName_Paths{$LVer}{$HName}{$Header_Path} = 1; + } + + if(($Header=~/\.(\w+)\Z/ and $1 ne "h") + or $Header!~/\.(\w+)\Z/) + { # hpp, hh, etc. + $In::ABI{$LVer}{"Language"} = "C++"; + $In::Opt{"CppHeaders"} = 1; + } + + if($Header=~/(\A|\/)c\+\+(\/|\Z)/) + { # /usr/include/c++/4.6.1/... + $In::Opt{"StdcxxTesting"} = 1; + } + + return $Header_Path; + } + return ""; +} + +sub registerDir($$$) +{ + my ($Dir, $WithDeps, $LVer) = @_; + $Dir=~s/[\/\\]+\Z//g; + if(not $Dir) { + return; + } + $Dir = getAbsPath($Dir); + + my $Mode = "All"; + if($WithDeps) + { + if($RegisteredDirs{$LVer}{$Dir}{1}) { + return; + } + elsif($RegisteredDirs{$LVer}{$Dir}{0}) { + $Mode = "DepsOnly"; + } + } + else + { + if($RegisteredDirs{$LVer}{$Dir}{1} + or $RegisteredDirs{$LVer}{$Dir}{0}) { + return; + } + } + $Header_Dependency{$LVer}{$Dir} = 1; + $RegisteredDirs{$LVer}{$Dir}{$WithDeps} = 1; + if($Mode eq "DepsOnly") + { + foreach my $Path (cmdFind($Dir,"d")) { + $Header_Dependency{$LVer}{$Path} = 1; + } + return; + } + foreach my $Path (sort {length($b)<=>length($a)} cmdFind($Dir,"f")) + { + if($WithDeps) + { + my $SubDir = $Path; + while(($SubDir = getDirname($SubDir)) ne $Dir) + { # register all sub directories + $Header_Dependency{$LVer}{$SubDir} = 1; + } + } + if(isNotHeader($Path)) { + next; + } + if(ignorePath($Path)) { + next; + } + # Neighbors + foreach my $Part (getPrefixes($Path)) { + $In::Desc{$LVer}{"IncludeNeighbors"}{$Part} = $Path; + } + } + if(getFilename($Dir) eq "include") + { # search for "lib/include/" directory + my $LibDir = $Dir; + if($LibDir=~s/([\/\\])include\Z/$1lib/g and -d $LibDir) { + registerDir($LibDir, $WithDeps, $LVer); + } + } +} + +sub getIncString($$) +{ + my ($ArrRef, $Style) = @_; + if(not $ArrRef or $#{$ArrRef}<0) { + return ""; + } + + my $Str = ""; + foreach (@{$ArrRef}) { + $Str .= " ".includeOpt($_, $Style); + } + return $Str; +} + +sub getIncPaths($$) +{ + my ($HRef, $LVer) = @_; + + my @IncPaths = @{$Add_Include_Paths{$LVer}}; + if($In::Desc{$LVer}{"AutoIncludePaths"}) + { # auto-detecting dependencies + my %Includes = (); + foreach my $HPath (@{$HRef}) + { + foreach my $Dir (getHeaderDeps($HPath, $LVer)) + { + if($In::Desc{$LVer}{"SkipIncludePaths"}{$Dir}) { + next; + } + if(my $SystemRoot = $In::Opt{"SystemRoot"}) + { + if($In::Desc{$LVer}{"SkipIncludePaths"}{$SystemRoot.$Dir}) { + next; + } + } + $Includes{$Dir} = 1; + } + } + foreach my $Dir (@{sortIncPaths([keys(%Includes)], $LVer)}) { + push_U(\@IncPaths, $Dir); + } + } + else + { # user-defined paths + @IncPaths = @{$Include_Paths{$LVer}}; + } + return \@IncPaths; +} + +sub searchForHeaders($) +{ + my $LVer = $_[0]; + + my $DescRef = $In::Desc{$LVer}; + + # gcc standard include paths + registerGccHeaders(); + + if($In::ABI{$LVer}{"Language"} eq "C++" and not $In::Opt{"StdcxxTesting"}) + { # c++ standard include paths + registerCppHeaders(); + } + + # processing header paths + my @HPaths = (); + + if($DescRef->{"IncludePaths"}) { + @HPaths = @{$DescRef->{"IncludePaths"}}; + } + + if($DescRef->{"AddIncludePaths"}) { + @HPaths = (@HPaths, @{$DescRef->{"AddIncludePaths"}}); + } + + foreach my $Path (@HPaths) + { + my $IPath = $Path; + if(my $SystemRoot = $In::Opt{"SystemRoot"}) + { + if(isAbsPath($Path)) { + $Path = $SystemRoot.$Path; + } + } + if(not -e $Path) { + exitStatus("Access_Error", "can't access \'$Path\'"); + } + elsif(-f $Path) { + exitStatus("Access_Error", "\'$Path\' - not a directory"); + } + elsif(-d $Path) + { + $Path = getAbsPath($Path); + registerDir($Path, 0, $LVer); + + if($DescRef->{"AddIncludePaths"} + and grep {$IPath eq $_} @{$DescRef->{"AddIncludePaths"}}) { + push(@{$Add_Include_Paths{$LVer}}, $Path); + } + else { + push(@{$Include_Paths{$LVer}}, $Path); + } + } + } + + # registering directories + my @Headers = keys(%{$DescRef->{"Headers"}}); + @Headers = sort {$DescRef->{"Headers"}{$a}<=>$DescRef->{"Headers"}{$b}} @Headers; + foreach my $Path (@Headers) + { + if(not -e $Path) { + next; + } + $Path = getAbsPath($Path); + if(-d $Path) { + registerDir($Path, 1, $LVer); + } + elsif(-f $Path) + { + my $Dir = getDirname($Path); + if(not grep { $Dir eq $_ } (@{$In::Opt{"SysPaths"}{"include"}}) + and not $LocalIncludes{$Dir}) { + registerDir($Dir, 1, $LVer); + } + } + } + + # clean memory + %RegisteredDirs = (); + + # registering headers + my $Position = 0; + foreach my $Path (@Headers) + { + if(isAbsPath($Path) and not -e $Path) { + exitStatus("Access_Error", "can't access \'$Path\'"); + } + $Path = pathFmt($Path); + if(isHeader($Path, 1, $LVer)) + { + if(my $HPath = registerHeader($Path, $LVer)) { + $In::Desc{$LVer}{"RegHeader"}{$HPath}{"Pos"} = $Position++; + } + } + elsif(-d $Path) + { + my @Registered = (); + foreach my $P (cmdFind($Path,"f")) + { + if(ignorePath($P)) { + next; + } + if(not isHeader($P, 0, $LVer)) { + next; + } + if(my $HPath = registerHeader($P, $LVer)) { + push(@Registered, $HPath); + } + } + @Registered = sort {sortHeaders($a, $b)} @Registered; + sortByWord(\@Registered, $In::Opt{"TargetLibShort"}); + foreach my $P (@Registered) { + $In::Desc{$LVer}{"RegHeader"}{$P}{"Pos"} = $Position++; + } + } + elsif(not defined $In::Opt{"SkipUnidentified"}) { + exitStatus("Access_Error", "can't identify \'$Path\' as a header file"); + } + } + + if(defined $In::Opt{"Tolerance"} + and $In::Opt{"Tolerance"}=~/4/) + { # 4 - skip headers included by others + foreach my $Path (keys(%{$In::Desc{$LVer}{"RegHeader"}})) + { + if(defined $Header_Includes_R{$LVer}{$Path}) { + delete($In::Desc{$LVer}{"RegHeader"}{$Path}); + } + } + } + + if(not defined $In::Desc{$LVer}{"Include_Preamble"}) { + $In::Desc{$LVer}{"Include_Preamble"} = []; + } + + if(my $HList = $DescRef->{"IncludePreamble"}) + { # preparing preamble headers + foreach my $Header (split(/\s*\n\s*/, $HList)) + { + if(isAbsPath($Header) and not -f $Header) { + exitStatus("Access_Error", "can't access file \'$Header\'"); + } + $Header = pathFmt($Header); + if(my $Header_Path = isHeader($Header, 1, $LVer)) + { + if(skipHeader($Header_Path, $LVer)) { + next; + } + push_U($In::Desc{$LVer}{"Include_Preamble"}, $Header_Path); + } + elsif(not defined $In::Opt{"SkipUnidentified"}) { + exitStatus("Access_Error", "can't identify \'$Header\' as a header file"); + } + } + } + + foreach my $Header_Name (keys(%{$HeaderName_Paths{$LVer}})) + { # set relative paths (for duplicates) + if(keys(%{$HeaderName_Paths{$LVer}{$Header_Name}})>=2) + { # search for duplicates + my $FirstPath = (keys(%{$HeaderName_Paths{$LVer}{$Header_Name}}))[0]; + my $Prefix = getDirname($FirstPath); + while($Prefix=~/\A(.+)[\/\\]+[^\/\\]+\Z/) + { # detect a shortest distinguishing prefix + my $NewPrefix = $1; + my %Identity = (); + foreach my $Path (keys(%{$HeaderName_Paths{$LVer}{$Header_Name}})) + { + if($Path=~/\A\Q$Prefix\E[\/\\]+(.*)\Z/) { + $Identity{$Path} = $1; + } + } + if(keys(%Identity)==keys(%{$HeaderName_Paths{$LVer}{$Header_Name}})) + { # all names are different with current prefix + foreach my $Path (keys(%{$HeaderName_Paths{$LVer}{$Header_Name}})) { + $In::Desc{$LVer}{"RegHeader"}{$Path}{"Identity"} = $Identity{$Path}; + } + last; + } + $Prefix = $NewPrefix; # increase prefix + } + } + } + + # clean memory + %HeaderName_Paths = (); + + foreach my $HName (keys(%{$In::Desc{$LVer}{"IncludeOrder"}})) + { # ordering headers according to the descriptor + my $PairName = $In::Desc{$LVer}{"IncludeOrder"}{$HName}; + my ($Pos, $PairPos, $Path, $PairPath) = (-1, -1, undef, undef); + + my @Paths = keys(%{$In::Desc{$LVer}{"RegHeader"}}); + @Paths = sort {$In::Desc{$LVer}{"RegHeader"}{$a}{"Pos"}<=>$In::Desc{$LVer}{"RegHeader"}{$b}{"Pos"}} @Paths; + + foreach my $HPath (@Paths) + { + if(getFilename($HPath) eq $PairName) + { + $PairPos = $In::Desc{$LVer}{"RegHeader"}{$HPath}{"Pos"}; + $PairPath = $HPath; + } + if(getFilename($HPath) eq $HName) + { + $Pos = $In::Desc{$LVer}{"RegHeader"}{$HPath}{"Pos"}; + $Path = $HPath; + } + } + if($PairPos!=-1 and $Pos!=-1 + and int($PairPos){"Version"}); + } +} + +sub addTargetHeaders($) +{ + my $LVer = $_[0]; + + foreach my $RegHeader (keys(%{$In::Desc{$LVer}{"RegHeader"}})) + { + my $RegDir = getDirname($RegHeader); + $In::Desc{$LVer}{"TargetHeader"}{getFilename($RegHeader)} = 1; + + if(not $In::Desc{$LVer}{"AutoIncludePaths"}) { + detectRecursiveIncludes($RegHeader, $LVer); + } + + foreach my $RecInc (keys(%{$RecursiveIncludes{$LVer}{$RegHeader}})) + { + my $Dir = getDirname($RecInc); + + if(($In::Opt{"DumpSystem"} and familiarDirs($RegDir, $Dir)) + or $RecursiveIncludes{$LVer}{$RegHeader}{$RecInc}<1) + { # in the same directory or included by #include "..." + $In::Desc{$LVer}{"TargetHeader"}{getFilename($RecInc)} = 1; + } + } + } +} + +sub familiarDirs($$) +{ + my ($D1, $D2) = @_; + if($D1 eq $D2) { + return 1; + } + + my $U1 = index($D1, "/usr/"); + my $U2 = index($D2, "/usr/"); + + if($U1==0 and $U2!=0) { + return 0; + } + + if($U2==0 and $U1!=0) { + return 0; + } + + if(index($D2, $D1."/")==0) { + return 1; + } + + # /usr/include/DIR + # /home/user/DIR + + my $DL = getDepth($D1); + + my @Dirs1 = ($D1); + while($DL - getDepth($D1)<=2 + and getDepth($D1)>=4 + and $D1=~s/[\/\\]+[^\/\\]*?\Z//) { + push(@Dirs1, $D1); + } + + my @Dirs2 = ($D2); + while(getDepth($D2)>=4 + and $D2=~s/[\/\\]+[^\/\\]*?\Z//) { + push(@Dirs2, $D2); + } + + foreach my $P1 (@Dirs1) + { + foreach my $P2 (@Dirs2) + { + if($P1 eq $P2) { + return 1; + } + } + } + return 0; +} + +sub isHeaderFile($) +{ + if($_[0]=~/\.($HEADER_EXT)\Z/i) { + return $_[0]; + } + return 0; +} + +sub isNotHeader($) +{ + if($_[0]=~/\.\w+\Z/ + and $_[0]!~/\.($HEADER_EXT)\Z/i) { + return 1; + } + return 0; +} + +sub getGccPath() +{ + if(defined $In::Opt{"GccPath"}) { + return $In::Opt{"GccPath"}; + } + + my $Path = undef; + + if(my $CrossGcc = $In::Opt{"CrossGcc"}) + { # --cross-gcc=arm-linux-gcc + if(-e $CrossGcc) + { # absolute or relative path + $Path = getAbsPath($CrossGcc); + } + elsif($CrossGcc!~/\// and getCmdPath($CrossGcc)) + { # command name + $Path = $CrossGcc; + } + else { + exitStatus("Access_Error", "can't access \'$CrossGcc\'"); + } + + if($Path=~/\s/) { + $Path = "\"".$Path."\""; + } + } + else + { # try default gcc + $Path = getCmdPath("gcc"); + + if(not $Path) + { # try to find gcc-X.Y + foreach my $P (@{$In::Opt{"SysPaths"}{"bin"}}) + { + if(my @GCCs = cmdFind($P, "", '/gcc-[0-9.]*\Z', 1, 1)) + { # select the latest version + @GCCs = sort {$b cmp $a} @GCCs; + if(checkGcc("3", $GCCs[0])) + { + $Path = $GCCs[0]; + last; + } + } + } + } + if(not $Path) { + exitStatus("Not_Found", "can't find GCC>=3.0 in PATH"); + } + } + + return ($In::Opt{"GccPath"} = $Path); +} + +sub clearSysFilesCache($) +{ + my $LVer = $_[0]; + + %Cache = (); + + delete($RecursiveIncludes{$LVer}); + delete($Header_Include_Prefix{$LVer}); + delete($Header_Includes{$LVer}); + delete($Header_ErrorRedirect{$LVer}); +} + +sub dumpFilesInfo($) +{ # extra information for other tools + my $LVer = $_[0]; + my $EInfo = $In::Opt{"ExtraInfo"}; + + writeFile($EInfo."/recursive-includes", Dumper($RecursiveIncludes{$LVer})); + writeFile($EInfo."/direct-includes", Dumper($Header_Includes{$LVer})); + + if(my @Redirects = keys(%{$Header_ErrorRedirect{$LVer}})) + { + my $REDIR = ""; + foreach my $P1 (sort @Redirects) { + $REDIR .= $P1.";".$Header_ErrorRedirect{$LVer}{$P1}."\n"; + } + writeFile($EInfo."/include-redirect", $REDIR); + } +} + +sub callPreprocessor($$$) +{ + my ($Path, $Inc, $LVer) = @_; + + my $IncludeString=$Inc; + if(not $Inc) { + $IncludeString = getIncString(getIncPaths([$Path], $LVer), "GCC"); + } + + my $TmpDir = $In::Opt{"Tmp"}; + my $Cmd = getCompileCmd($Path, "-dD -E", $IncludeString, $LVer); + my $Out = $TmpDir."/preprocessed.h"; + system($Cmd." >\"$Out\" 2>\"$TmpDir/null\""); + + return $Out; +} + +sub isHeader($$$) +{ + my ($Header, $UserDefined, $LVer) = @_; + if(-d $Header) { + return 0; + } + if(-f $Header) { + $Header = getAbsPath($Header); + } + else + { + if(isAbsPath($Header)) + { # incorrect absolute path + return 0; + } + if(my $HPath = identifyHeader($Header, $LVer)) { + $Header = $HPath; + } + else + { # can't find header + return 0; + } + } + if($Header=~/\.\w+\Z/) + { # have an extension + return isHeaderFile($Header); + } + else + { + if($UserDefined==2) + { # specified on the command line + if(cmdFile($Header)!~/HTML|XML/i) { + return $Header; + } + } + elsif($UserDefined) + { # specified in the XML-descriptor + # header file without an extension + return $Header; + } + else + { + if(index($Header, "/include/")!=-1 + or cmdFile($Header)=~/C[\+]*\s+program/i) + { # !~/HTML|XML|shared|dynamic/i + return $Header; + } + } + } + return 0; +} + +return 1; diff --git a/abi-compliance-checker-2.4/modules/Internals/TUDump.pm b/abi-compliance-checker-2.4/modules/Internals/TUDump.pm new file mode 100644 index 0000000..1bc72f3 --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/TUDump.pm @@ -0,0 +1,992 @@ +########################################################################### +# A module to create AST dump +# +# Copyright (C) 2015-2018 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; + +my %C_Structure = map {$_=>1} ( +# FIXME: Can't separate union and struct data types before dumping, +# so it sometimes cause compilation errors for unknown reason +# when trying to declare TYPE* tmp_add_class_N +# This is a list of such structures + list of other C structures + "sigval", + "sigevent", + "sigaction", + "sigvec", + "sigstack", + "timeval", + "timezone", + "rusage", + "rlimit", + "wait", + "flock", + "stat", + "_stat", + "stat32", + "_stat32", + "stat64", + "_stat64", + "_stati64", + "if_nameindex", + "usb_device", + "sigaltstack", + "sysinfo", + "timeLocale", + "tcp_debug", + "rpc_createerr", + "dirent", + "dirent64", + "pthread_attr_t", + "_fpreg", + "_fpstate", + "_fpx_sw_bytes", + "_fpxreg", + "_libc_fpstate", + "_libc_fpxreg", + "_libc_xmmreg", + "_xmmreg", + "_xsave_hdr", + "_xstate", + "_ymmh_state", + "_prop_t", + # Other + "timespec", + "random_data", + "drand48_data", + "_IO_marker", + "_IO_FILE", + "lconv", + "sched_param", + "tm", + "itimerspec", + "_pthread_cleanup_buffer", + "fd_set", + "siginfo", + "mallinfo", + "timex", + "sigcontext", + "ucontext", + # Mac + "_timex", + "_class_t", + "_category_t", + "_class_ro_t", + "_protocol_t", + "_message_ref_t", + "_super_message_ref_t", + "_ivar_t", + "_ivar_list_t" +); + +my %CppKeywords_C = map {$_=>1} ( + # C++ 2003 keywords + "public", + "protected", + "private", + "default", + "template", + "new", + #"asm", + "dynamic_cast", + "auto", + "try", + "namespace", + "typename", + "using", + "reinterpret_cast", + "friend", + "class", + "virtual", + "const_cast", + "mutable", + "static_cast", + "export", + # C++0x keywords + "noexcept", + "nullptr", + "constexpr", + "static_assert", + "explicit", + # cannot be used as a macro name + # as it is an operator in C++ + "and", + #"and_eq", + "not", + #"not_eq", + "or" + #"or_eq", + #"bitand", + #"bitor", + #"xor", + #"xor_eq", + #"compl" +); + +my %CppKeywords_F = map {$_=>1} ( + "delete", + "catch", + "alignof", + "thread_local", + "decltype", + "typeid" +); + +my %CppKeywords_O = map {$_=>1} ( + "bool", + "register", + "inline", + "operator" +); + +my %CppKeywords_A = map {$_=>1} ( + "this", + "throw", + "template" +); + +foreach (keys(%CppKeywords_C), +keys(%CppKeywords_F), +keys(%CppKeywords_O)) { + $CppKeywords_A{$_}=1; +} + +my %IntrinsicKeywords = map {$_=>1} ( + "true", + "false", + "_Bool", + "_Complex", + "const", + "int", + "long", + "void", + "short", + "float", + "volatile", + "restrict", + "unsigned", + "signed", + "char", + "double", + "class", + "struct", + "union", + "enum" +); + +my %PreprocessedHeaders; +my %TUnit_NameSpaces; +my %TUnit_Classes; +my %TUnit_Funcs; +my %TUnit_Vars; + +my %AutoPreambleMode = ( + "1"=>0, + "2"=>0 +); + +my %MinGWMode = ( + "1"=>0, + "2"=>0 +); + +my %Cpp0xMode = ( + "1"=>0, + "2"=>0 +); + +sub createTUDump($) +{ + my $LVer = $_[0]; + + if(not $In::Opt{"GccPath"}) { + exitStatus("Error", "internal error - GCC path is not set"); + } + + searchForHeaders($LVer); + + my @Headers = keys(%{$In::Desc{$LVer}{"RegHeader"}}); + @Headers = sort {$In::Desc{$LVer}{"RegHeader"}{$a}{"Pos"}<=>$In::Desc{$LVer}{"RegHeader"}{$b}{"Pos"}} @Headers; + + my @IncHeaders = (@{$In::Desc{$LVer}{"Include_Preamble"}}, @Headers); + my $IncludeString = getIncString(getIncPaths(\@IncHeaders, $LVer), "GCC"); + + my $TmpDir = $In::Opt{"Tmp"}; + my $TmpHeaderPath = $TmpDir."/dump".$LVer.".h"; + my $HeaderPath = $TmpHeaderPath; + + # write tmp-header + open(TMP_HEADER, ">", $TmpHeaderPath) || die ("can't open file \'$TmpHeaderPath\': $!\n"); + if(my $AddDefines = $In::Desc{$LVer}{"Defines"}) + { + $AddDefines=~s/\n\s+/\n /g; + print TMP_HEADER "\n // add defines\n ".$AddDefines."\n"; + } + print TMP_HEADER "\n // add includes\n"; + foreach my $HPath (@{$In::Desc{$LVer}{"Include_Preamble"}}) { + print TMP_HEADER " #include \"".pathFmt($HPath, "unix")."\"\n"; + } + foreach my $HPath (@Headers) + { + if(not grep {$HPath eq $_} (@{$In::Desc{$LVer}{"Include_Preamble"}})) { + print TMP_HEADER " #include \"".pathFmt($HPath, "unix")."\"\n"; + } + } + close(TMP_HEADER); + + if(my $EInfo = $In::Opt{"ExtraInfo"}) + { + if($IncludeString) { + writeFile($EInfo."/include-string", $IncludeString); + } + dumpFilesInfo($LVer); + } + + if(not defined $In::Desc{$LVer}{"TargetHeader"}) { + addTargetHeaders($LVer); + } + + # preprocessing stage + my $Pre = callPreprocessor($TmpHeaderPath, $IncludeString, $LVer); + checkPreprocessedUnit($Pre, $LVer); + + if(my $EInfo = $In::Opt{"ExtraInfo"}) + { # extra information for other tools + writeFile($EInfo."/header-paths", join("\n", sort keys(%{$PreprocessedHeaders{$LVer}}))); + } + + # clean memory + delete($PreprocessedHeaders{$LVer}); + + if($In::ABI{$LVer}{"Language"} eq "C++") { + checkCTags($Pre, $LVer); + } + + if(my $PrePath = preChange($TmpHeaderPath, $IncludeString, $LVer)) + { # try to correct the preprocessor output + $HeaderPath = $PrePath; + } + + my $GCC_8 = checkGcc("8"); # support for GCC 8 and new options + + if($In::ABI{$LVer}{"Language"} eq "C++") + { # add classes and namespaces to the dump + my $CHdump = "-fdump-class-hierarchy"; + if($GCC_8) + { # -fdump-lang-class instead of -fdump-class-hierarchy + $CHdump = "-fdump-lang-class"; + } + $CHdump .= " -c"; + + if($In::Desc{$LVer}{"CppMode"}==1 + or $MinGWMode{$LVer}==1) { + $CHdump .= " -fpreprocessed"; + } + my $ClassHierarchyCmd = getCompileCmd($HeaderPath, $CHdump, $IncludeString, $LVer); + chdir($TmpDir); + system($ClassHierarchyCmd." >null 2>&1"); + chdir($In::Opt{"OrigDir"}); + if(my $ClassDump = (cmdFind($TmpDir,"f","*.class",1))[0]) + { + my $Content = readFile($ClassDump); + foreach my $ClassInfo (split(/\n\n/, $Content)) + { + if($ClassInfo=~/\AClass\s+(.+)\s*/i) + { + my $CName = $1; + if($CName=~/\A(__|_objc_|_opaque_)/) { + next; + } + $TUnit_NameSpaces{$LVer}{$CName} = -1; + if($CName=~/\A[\w:]+\Z/) + { # classes + $TUnit_Classes{$LVer}{$CName} = 1; + } + if($CName=~/(\w[\w:]*)::/) + { # namespaces + my $NS = $1; + if(not defined $TUnit_NameSpaces{$LVer}{$NS}) { + $TUnit_NameSpaces{$LVer}{$NS} = 1; + } + } + } + elsif($ClassInfo=~/\AVtable\s+for\s+(.+)\n((.|\n)+)\Z/i) + { # read v-tables (advanced approach) + my ($CName, $VTable) = ($1, $2); + $In::ABI{$LVer}{"ClassVTable_Content"}{$CName} = $VTable; + } + } + foreach my $NS (keys(%{$In::Desc{$LVer}{"AddNameSpaces"}})) + { # add user-defined namespaces + $TUnit_NameSpaces{$LVer}{$NS} = 1; + } + if($In::Opt{"Debug"}) + { # debug mode + copy($ClassDump, getDebugDir($LVer)."/class-hierarchy-dump.txt"); + } + unlink($ClassDump); + } + + # add namespaces and classes + if(my $NSAdd = getNSAdditions($LVer, $TUnit_NameSpaces{$LVer})) + { # GCC on all supported platforms does not include namespaces to the dump by default + appendFile($HeaderPath, "\n // add namespaces\n".$NSAdd); + + if($HeaderPath ne $TmpHeaderPath) { + appendFile($TmpHeaderPath, "\n // add namespaces\n".$NSAdd); + } + } + # some GCC versions don't include class methods to the TU dump by default + my ($AddClass, $ClassNum) = ("", 0); + my $GCC_44 = checkGcc("4.4"); # support for old GCC versions + foreach my $CName (sort keys(%{$TUnit_Classes{$LVer}})) + { + next if($C_Structure{$CName}); + next if(not $In::Opt{"StdcxxTesting"} and $CName=~/\Astd::/); + next if($In::Desc{$LVer}{"SkipTypes"}{$CName}); + if(not $In::Opt{"Force"} and $GCC_44 + and $In::Opt{"OS"} eq "linux") + { # optimization for linux with GCC >= 4.4 + # disable this code by -force option + if(index($CName, "::")!=-1) + { # should be added by name space + next; + } + } + else + { + if($CName=~/\A(.+)::[^:]+\Z/ + and $TUnit_Classes{$LVer}{$1}) + { # classes inside other classes + next; + } + } + if(defined $TUnit_Funcs{$LVer}{$CName}) + { # the same name for a function and type + next; + } + if(defined $TUnit_Vars{$LVer}{$CName}) + { # the same name for a variable and type + next; + } + $AddClass .= " $CName* tmp_add_class_".($ClassNum++).";\n"; + } + if($AddClass) + { + appendFile($HeaderPath, "\n // add classes\n".$AddClass); + + if($HeaderPath ne $TmpHeaderPath) { + appendFile($TmpHeaderPath, "\n // add classes\n".$AddClass); + } + } + } + writeLog($LVer, "Temporary header file \'$TmpHeaderPath\' with the following content will be compiled to create GCC translation unit dump:\n".readFile($TmpHeaderPath)."\n"); + + # create TU dump + my $TUdump = "-fdump-translation-unit"; + if ($GCC_8) + { # -fdump-lang-raw instead of -fdump-translation-unit + $TUdump = "-fdump-lang-raw"; + } + $TUdump .= " -fkeep-inline-functions -c"; + if($In::Opt{"UserLang"} eq "C") { + $TUdump .= " -U__cplusplus -D_Bool=\"bool\""; + } + if($In::Desc{$LVer}{"CppMode"}==1 + or $MinGWMode{$LVer}==1) { + $TUdump .= " -fpreprocessed"; + } + my $SyntaxTreeCmd = getCompileCmd($HeaderPath, $TUdump, $IncludeString, $LVer); + writeLog($LVer, "The GCC parameters:\n $SyntaxTreeCmd\n\n"); + chdir($TmpDir); + system($SyntaxTreeCmd." >\"$TmpDir/tu_errors\" 2>&1"); + chdir($In::Opt{"OrigDir"}); + + my $Errors = ""; + if($?) + { # failed to compile, but the TU dump still can be created + if($Errors = readFile($TmpDir."/tu_errors")) + { # try to recompile + # FIXME: handle errors and try to recompile + if($AutoPreambleMode{$LVer}!=-1 + and my $AddHeaders = detectPreamble($Errors, $LVer)) + { # add auto preamble headers and try again + $AutoPreambleMode{$LVer}=-1; + my @Headers = sort {$b cmp $a} keys(%{$AddHeaders}); # sys/types.h should be the first + foreach my $Num (0 .. $#Headers) + { + my $Path = $Headers[$Num]; + if(not grep {$Path eq $_} (@{$In::Desc{$LVer}{"Include_Preamble"}})) + { + push_U($In::Desc{$LVer}{"Include_Preamble"}, $Path); + printMsg("INFO", "Adding \'".$AddHeaders->{$Path}{"Header"}."\' preamble header for \'".$AddHeaders->{$Path}{"Type"}."\'"); + } + } + resetLogging($LVer); + $TmpDir = tempdir(CLEANUP=>1); + return createTUDump($LVer); + } + elsif($Cpp0xMode{$LVer}!=-1 + and ($Errors=~/\Q-std=c++0x\E/ + or $Errors=~/is not a class or namespace/)) + { # c++0x: enum class + if(checkGcc("4.6")) + { + $Cpp0xMode{$LVer}=-1; + printMsg("INFO", "Enabling c++0x mode"); + resetLogging($LVer); + $TmpDir = tempdir(CLEANUP=>1); + $In::Desc{$LVer}{"CompilerOptions"} .= " -std=c++0x"; + return createTUDump($LVer); + } + else { + printMsg("WARNING", "Probably c++0x element detected"); + } + + } + writeLog($LVer, $Errors); + } + else { + writeLog($LVer, "$!: $?\n"); + } + printMsg("ERROR", "some errors occurred when compiling headers"); + printErrorLog($LVer); + $In::Opt{"CompileError"} = 1; + writeLog($LVer, "\n"); # new line + } + + unlink($TmpHeaderPath); + unlink($HeaderPath); + + my $dumpExt; + if ($GCC_8) { + $dumpExt = "*.raw"; + } + else { + $dumpExt = "*.tu"; + } + if(my @TUs = cmdFind($TmpDir,"f",$dumpExt,1)) { + return $TUs[0]; + } + else + { + my $Msg = "can't compile header(s)"; + if($Errors=~/error trying to exec \W+cc1plus\W+/) { + $Msg .= "\nDid you install G++?"; + } + exitStatus("Cannot_Compile", $Msg); + } +} + +sub detectPreamble($$) +{ + my ($Content, $LVer) = @_; + my %HeaderElems = ( + # Types + "stdio.h" => ["FILE", "va_list"], + "stddef.h" => ["NULL", "ptrdiff_t"], + "stdint.h" => ["uint8_t", "uint16_t", "uint32_t", "uint64_t", + "int8_t", "int16_t", "int32_t", "int64_t"], + "time.h" => ["time_t"], + "sys/types.h" => ["ssize_t", "u_int32_t", "u_short", "u_char", + "u_int", "off_t", "u_quad_t", "u_long", "mode_t"], + "unistd.h" => ["gid_t", "uid_t", "socklen_t"], + "stdbool.h" => ["_Bool"], + "rpc/xdr.h" => ["bool_t"], + "in_systm.h" => ["n_long", "n_short"], + # Fields + "arpa/inet.h" => ["fw_src", "ip_src"], + # Functions + "stdlib.h" => ["free", "malloc", "size_t"], + "string.h" => ["memmove", "strcmp"] + ); + my %AutoPreamble = (); + foreach (keys(%HeaderElems)) + { + foreach my $Elem (@{$HeaderElems{$_}}) { + $AutoPreamble{$Elem} = $_; + } + } + my %Types = (); + while($Content=~s/error\:\s*(field\s*|)\W+(.+?)\W+//) + { # error: 'FILE' has not been declared + $Types{$2} = 1; + } + if(keys(%Types)) + { + my %AddHeaders = (); + foreach my $Type (keys(%Types)) + { + if(my $Header = $AutoPreamble{$Type}) + { + if(my $Path = identifyHeader($Header, $LVer)) + { + if(skipHeader($Path, $LVer)) { + next; + } + $Path = pathFmt($Path); + $AddHeaders{$Path}{"Type"} = $Type; + $AddHeaders{$Path}{"Header"} = $Header; + } + } + } + if(keys(%AddHeaders)) { + return \%AddHeaders; + } + } + return undef; +} + +sub checkCTags($$) +{ + my ($Path, $LVer) = @_; + + my $CTags = undef; + + if($In::Opt{"OS"} eq "bsd") + { # use ectags on BSD + $CTags = getCmdPath("ectags"); + if(not $CTags) { + printMsg("WARNING", "can't find \'ectags\' program"); + } + } + if(not $CTags) { + $CTags = getCmdPath("ctags"); + } + if(not $CTags) + { + printMsg("WARNING", "can't find \'ctags\' program"); + return; + } + + my $TmpDir = $In::Opt{"Tmp"}; + + if($In::Opt{"OS"} ne "linux") + { # macos, freebsd, etc. + my $Info = `$CTags --version 2>\"$TmpDir/null\"`; + if($Info!~/universal|exuberant/i) + { + printMsg("WARNING", "incompatible version of \'ctags\' program"); + return; + } + } + + my $Out = $TmpDir."/ctags.txt"; + system("$CTags --c-kinds=pxn -f \"$Out\" \"$Path\" 2>\"$TmpDir/null\""); + if($In::Opt{"Debug"}) { + copy($Out, getDebugDir($LVer)."/ctags.txt"); + } + open(CTAGS, "<", $Out); + while(my $Line = ) + { + chomp($Line); + my ($Name, $Header, $Def, $Type, $Scpe) = split(/\t/, $Line); + if(defined $IntrinsicKeywords{$Name}) + { # noise + next; + } + if($Type eq "n") + { + if(index($Scpe, "class:")==0) { + next; + } + if(index($Scpe, "struct:")==0) { + next; + } + if(index($Scpe, "namespace:")==0) + { + if($Scpe=~s/\Anamespace://) { + $Name = $Scpe."::".$Name; + } + } + $TUnit_NameSpaces{$LVer}{$Name} = 1; + } + elsif($Type eq "p") + { + if(not $Scpe or index($Scpe, "namespace:")==0) { + $TUnit_Funcs{$LVer}{$Name} = 1; + } + } + elsif($Type eq "x") + { + if(not $Scpe or index($Scpe, "namespace:")==0) { + $TUnit_Vars{$LVer}{$Name} = 1; + } + } + } + close(CTAGS); +} + +sub preChange($$$) +{ + my ($HeaderPath, $IncStr, $LVer) = @_; + + my $TmpDir = $In::Opt{"Tmp"}; + my $PreprocessCmd = getCompileCmd($HeaderPath, "-E", $IncStr, $LVer); + my $Content = undef; + + if(not defined $In::Opt{"MinGWCompat"} + and $In::Opt{"Target"} eq "windows" + and $In::Opt{"GccTarget"}=~/mingw/i + and $MinGWMode{$LVer}!=-1) + { # modify headers to compile by MinGW + if(not $Content) + { # preprocessing + $Content = `$PreprocessCmd 2>\"$TmpDir/null\"`; + } + if($Content=~s/__asm\s*(\{[^{}]*?\}|[^{};]*)//g) + { # __asm { ... } + $MinGWMode{$LVer}=1; + } + if($Content=~s/\s+(\/ \/.*?)\n/\n/g) + { # comments after preprocessing + $MinGWMode{$LVer}=1; + } + if($Content=~s/(\W)(0x[a-f]+|\d+)(i|ui)(8|16|32|64)(\W)/$1$2$5/g) + { # 0xffui8 + $MinGWMode{$LVer}=1; + } + + if($MinGWMode{$LVer}) { + printMsg("INFO", "Using MinGW compatibility mode"); + } + } + + if(defined $In::Opt{"CxxIncompat"} + and $In::ABI{$LVer}{"Language"} eq "C" + and $In::Desc{$LVer}{"CppMode"}!=-1 and not $In::Opt{"CppHeaders"}) + { # rename C++ keywords in C code + printMsg("INFO", "Checking the code for C++ keywords"); + if(not $Content) + { # preprocessing + $Content = `$PreprocessCmd 2>\"$TmpDir/null\"`; + } + + my $RegExp_C = join("|", keys(%CppKeywords_C)); + my $RegExp_F = join("|", keys(%CppKeywords_F)); + my $RegExp_O = join("|", keys(%CppKeywords_O)); + + my $Detected = undef; + my $Regex = undef; + + $Regex = qr/(\A|\n[^\#\/\n][^\n]*?|\n)(\*\s*|\s+|\@|\,|\()($RegExp_C|$RegExp_F)(\s*([\,\)\;\.\[]|\-\>|\:\s*\d))/; + while($Content=~/$Regex/) + { # MATCH: + # int foo(int new, int class, int (*new)(int)); + # int foo(char template[], char*); + # unsigned private: 8; + # DO NOT MATCH: + # #pragma GCC visibility push(default) + my $Sentence_O = "$1$2$3$4"; + + if($Sentence_O=~/\s+decltype\(/) + { # C++ + # decltype(nullptr) + last; + } + else + { + $Content=~s/$Regex/$1$2c99_$3$4/g; + $In::Desc{$LVer}{"CppMode"} = 1; + if(not defined $Detected) { + $Detected = $Sentence_O; + } + } + } + if($Content=~s/([^\w\s]|\w\s+)(? $b=~/_/} sort {lc($a) cmp lc($b)} keys(%{$NameSpaces})) + { + next if($In::Desc{$LVer}{"SkipNameSpaces"}{$NS}); + next if(not $NS or $NameSpaces->{$NS}==-1); + next if($NS=~/(\A|::)iterator(::|\Z)/i); + next if($NS=~/\A__/i); + next if(($NS=~/\Astd::/ or $NS=~/\A(std|tr1|rel_ops|fcntl)\Z/) and not $In::Opt{"StdcxxTesting"}); + + $In::ABI{$LVer}{"NameSpaces"}{$NS} = 1; # for future use in reports + + my ($TypeDecl_Prefix, $TypeDecl_Suffix) = (); + my @NS_Parts = split(/::/, $NS); + next if($#NS_Parts==-1); + next if($NS_Parts[0]=~/\A(random|or)\Z/); + foreach my $NS_Part (@NS_Parts) + { + $TypeDecl_Prefix .= "namespace $NS_Part\{"; + $TypeDecl_Suffix .= "}"; + } + my $TypeDecl = $TypeDecl_Prefix."typedef int tmp_add_type_".$AddNameSpaceId.";".$TypeDecl_Suffix; + my $FuncDecl = "$NS\:\:tmp_add_type_$AddNameSpaceId tmp_add_func_$AddNameSpaceId(){return 0;};"; + $Additions .= " $TypeDecl\n $FuncDecl\n"; + $AddNameSpaceId += 1; + } + return $Additions; +} + +sub includeOpt($$) +{ + my ($Path, $Style) = @_; + if($Style eq "GCC") + { # GCC options + if($In::Opt{"OS"} eq "windows") + { # to MinGW GCC + return "-I\"".pathFmt($Path, "unix")."\""; + } + elsif($In::Opt{"OS"} eq "macos" + and $Path=~/\.framework\Z/) + { # to Apple's GCC + return "-F".escapeArg(getDirname($Path)); + } + else { + return "-I".escapeArg($Path); + } + } + elsif($Style eq "CL") { + return "/I \"".$Path."\""; + } + return ""; +} + +sub checkPreprocessedUnit($$) +{ + my ($Path, $LVer) = @_; + + my $TmpDir = $In::Opt{"Tmp"}; + my ($CurHeader, $CurHeaderName) = ("", ""); + my $CurClass = ""; # extra info + + my $CRef = $In::ABI{$LVer}{"Constants"}; + + if(not $CRef) { + $CRef = {}; + } + + open(PREPROC, $Path) || die ("can't open file \'$Path\': $!\n"); + while(my $Line = ) + { # detecting public and private constants + if(substr($Line, 0, 1) eq "#") + { + chomp($Line); + if($Line=~/\A\#\s+\d+\s+\"(.+)\"/) + { + $CurHeader = pathFmt($1); + $CurHeaderName = getFilename($CurHeader); + $CurClass = ""; + + if(index($CurHeader, $TmpDir)==0) { + next; + } + + if(substr($CurHeaderName, 0, 1) eq "<") + { # , , etc. + $CurHeaderName = ""; + $CurHeader = ""; + } + + if($In::Opt{"ExtraInfo"}) + { + if($CurHeaderName) { + $PreprocessedHeaders{$LVer}{$CurHeader} = 1; + } + } + } + if(not $In::Opt{"ExtraDump"}) + { + if($CurHeaderName) + { + if(not $In::Desc{$LVer}{"IncludeNeighbors"}{$CurHeaderName} + and not $In::Desc{$LVer}{"RegHeader"}{$CurHeader}) + { # not a target + next; + } + if(not isTargetHeader($CurHeaderName, 1) + and not isTargetHeader($CurHeaderName, 2)) + { # user-defined header + next; + } + } + } + + if($Line=~/\A\#\s*define\s+(\w+)\s+(.+)\s*\Z/) + { + my ($Name, $Value) = ($1, $2); + if(not $CRef->{$Name}{"Access"}) + { + $CRef->{$Name}{"Access"} = "public"; + $CRef->{$Name}{"Value"} = $Value; + if($CurHeaderName) { + $CRef->{$Name}{"Header"} = $CurHeaderName; + } + } + } + elsif($Line=~/\A\#[ \t]*undef[ \t]+([_A-Z]+)[ \t]*/) { + $CRef->{$1}{"Access"} = "private"; + } + } + else + { + if($In::Opt{"ExtraDump"}) + { + if($Line=~/(\w+)\s*\(/) + { # functions + $In::ABI{$LVer}{"SymbolHeader"}{$CurClass}{$1} = $CurHeader; + } + elsif($Line=~/(\A|\s)class\s+(\w+)/) { + $CurClass = $2; + } + } + } + } + close(PREPROC); + + foreach my $Constant (keys(%{$CRef})) + { + if($CRef->{$Constant}{"Access"} eq "private") + { + delete($CRef->{$Constant}); + next; + } + + if(not $In::Opt{"ExtraDump"} and ($Constant=~/_h\Z/i + or isBuiltIn($CRef->{$Constant}{"Header"}))) + { # skip + delete($CRef->{$Constant}); + } + else { + delete($CRef->{$Constant}{"Access"}); + } + } + + if($In::Opt{"Debug"}) { + copy($Path, getDebugDir($LVer)."/preprocessor.txt"); + } +} + +return 1; diff --git a/abi-compliance-checker-2.4/modules/Internals/TypeAttr.pm b/abi-compliance-checker-2.4/modules/Internals/TypeAttr.pm new file mode 100644 index 0000000..83804ca --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/TypeAttr.pm @@ -0,0 +1,268 @@ +########################################################################### +# A module to handle type attributes +# +# Copyright (C) 2015-2018 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; + +my %Cache; + +my %TypeSpecAttributes = ( + "Const" => 1, + "Volatile" => 1, + "ConstVolatile" => 1, + "Restrict" => 1, + "Typedef" => 1 +); + +my (%TypeInfo, %TName_Tid) = (); + +sub initAliases_TypeAttr($) +{ + my $LVer = $_[0]; + + $TypeInfo{$LVer} = $In::ABI{$LVer}{"TypeInfo"}; + $TName_Tid{$LVer} = $In::ABI{$LVer}{"TName_Tid"}; +} + +sub getTypeIdByName($$) +{ + my ($TypeName, $LVer) = @_; + return $TName_Tid{$LVer}{formatName($TypeName, "T")}; +} + +sub getShortClass($$) +{ + my ($TypeId, $LVer) = @_; + my $TypeName = $TypeInfo{$LVer}{$TypeId}{"Name"}; + if($TypeInfo{$LVer}{$TypeId}{"Type"}!~/Intrinsic|Class|Struct|Union|Enum/) { + $TypeName = uncoverTypedefs($TypeName, $LVer); + } + if(my $NameSpace = $TypeInfo{$LVer}{$TypeId}{"NameSpace"}) { + $TypeName=~s/\A(struct |)\Q$NameSpace\E\:\://g; + } + return $TypeName; +} + +sub goToFirst($$$) +{ + my ($TypeId, $LVer, $Type_Type) = @_; + + if(defined $Cache{"goToFirst"}{$TypeId}{$LVer}{$Type_Type}) { + return %{$Cache{"goToFirst"}{$TypeId}{$LVer}{$Type_Type}}; + } + if(not $TypeInfo{$LVer}{$TypeId}) { + return (); + } + my %Type = %{$TypeInfo{$LVer}{$TypeId}}; + if(not $Type{"Type"}) { + return (); + } + if($Type{"Type"} ne $Type_Type) + { + if(not $Type{"BaseType"}) { + return (); + } + %Type = goToFirst($Type{"BaseType"}, $LVer, $Type_Type); + } + $Cache{"goToFirst"}{$TypeId}{$LVer}{$Type_Type} = \%Type; + return %Type; +} + +sub getPureType($$) +{ + my ($TypeId, $LVer) = @_; + if(not $TypeInfo{$LVer}{$TypeId}) { + return (); + } + if(defined $Cache{"getPureType"}{$TypeId}{$LVer}) { + return %{$Cache{"getPureType"}{$TypeId}{$LVer}}; + } + my %Type = %{$TypeInfo{$LVer}{$TypeId}}; + if(not $Type{"BaseType"}) { + return %Type; + } + if($TypeSpecAttributes{$Type{"Type"}}) { + %Type = getPureType($Type{"BaseType"}, $LVer); + } + $Cache{"getPureType"}{$TypeId}{$LVer} = \%Type; + return %Type; +} + +sub getPLevel($$) +{ + my ($TypeId, $LVer) = @_; + + if(defined $Cache{"getPLevel"}{$TypeId}{$LVer}) { + return $Cache{"getPLevel"}{$TypeId}{$LVer}; + } + if(not $TypeInfo{$LVer}{$TypeId}) { + return 0; + } + my %Type = %{$TypeInfo{$LVer}{$TypeId}}; + if($Type{"Type"}=~/FuncPtr|FieldPtr/) { + return 1; + } + my $PLevel = 0; + if($Type{"Type"} =~/Pointer|Ref|FuncPtr|FieldPtr/) { + $PLevel += 1; + } + if(not $Type{"BaseType"}) { + return $PLevel; + } + $PLevel += getPLevel($Type{"BaseType"}, $LVer); + $Cache{"getPLevel"}{$TypeId}{$LVer} = $PLevel; + return $PLevel; +} + +sub getBaseType($$) +{ + my ($TypeId, $LVer) = @_; + + if(defined $Cache{"getBaseType"}{$TypeId}{$LVer}) { + return %{$Cache{"getBaseType"}{$TypeId}{$LVer}}; + } + if(not $TypeInfo{$LVer}{$TypeId}) { + return (); + } + my %Type = %{$TypeInfo{$LVer}{$TypeId}}; + if(not $Type{"BaseType"}) { + return %Type; + } + %Type = getBaseType($Type{"BaseType"}, $LVer); + $Cache{"getBaseType"}{$TypeId}{$LVer} = \%Type; + return %Type; +} + +sub getOneStepBaseType($$) +{ + my ($TypeId, $LVer) = @_; + + if(not $TypeInfo{$LVer}{$TypeId}) { + return (); + } + + my %Type = %{$TypeInfo{$LVer}{$TypeId}}; + if(not $Type{"BaseType"}) { + return %Type; + } + if(my $BTid = $Type{"BaseType"}) + { + if($TypeInfo{$LVer}{$BTid}) { + return %{$TypeInfo{$LVer}{$BTid}}; + } + + # something is going wrong + return (); + } + + return %Type; +} + +sub getType($$) +{ + my ($TypeId, $LVer) = @_; + + if(not $TypeInfo{$LVer}{$TypeId}) { + return (); + } + return %{$TypeInfo{$LVer}{$TypeId}}; +} + +sub getBaseTypeQual($$) +{ + my ($TypeId, $LVer) = @_; + if(not $TypeInfo{$LVer}{$TypeId}) { + return ""; + } + my %Type = %{$TypeInfo{$LVer}{$TypeId}}; + if(not $Type{"BaseType"}) { + return ""; + } + my $Qual = ""; + if($Type{"Type"} eq "Pointer") { + $Qual .= "*"; + } + elsif($Type{"Type"} eq "Ref") { + $Qual .= "&"; + } + elsif($Type{"Type"} eq "ConstVolatile") { + $Qual .= "const volatile"; + } + elsif($Type{"Type"} eq "Const" + or $Type{"Type"} eq "Volatile" + or $Type{"Type"} eq "Restrict") { + $Qual .= lc($Type{"Type"}); + } + my $BQual = getBaseTypeQual($Type{"BaseType"}, $LVer); + return $BQual.$Qual; +} + +sub isCopyingClass($$) +{ + my ($TypeId, $LVer) = @_; + return $TypeInfo{$LVer}{$TypeId}{"Copied"}; +} + +sub getSubClasses($$$) +{ + my ($ClassId, $LVer, $Recursive) = @_; + if(not defined $In::ABI{$LVer}{"Class_SubClasses"}{$ClassId}) { + return (); + } + + my @Subs = (); + foreach my $SubId (keys(%{$In::ABI{$LVer}{"Class_SubClasses"}{$ClassId}})) + { + if($Recursive) + { + foreach my $SubSubId (getSubClasses($SubId, $LVer, $Recursive)) { + push(@Subs, $SubSubId); + } + } + push(@Subs, $SubId); + } + return @Subs; +} + +sub getBaseClasses($$$) +{ + my ($ClassId, $LVer, $Recursive) = @_; + my %ClassType = getType($ClassId, $LVer); + if(not defined $ClassType{"Base"}) { + return (); + } + + my @Bases = (); + foreach my $BaseId (sort {$ClassType{"Base"}{$a}{"pos"}<=>$ClassType{"Base"}{$b}{"pos"}} + keys(%{$ClassType{"Base"}})) + { + if($Recursive) + { + foreach my $SubBaseId (getBaseClasses($BaseId, $LVer, $Recursive)) { + push(@Bases, $SubBaseId); + } + } + push(@Bases, $BaseId); + } + return @Bases; +} + +return 1; diff --git a/abi-compliance-checker-2.4/modules/Internals/Utils.pm b/abi-compliance-checker-2.4/modules/Internals/Utils.pm new file mode 100644 index 0000000..ffae7a5 --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/Utils.pm @@ -0,0 +1,493 @@ +########################################################################### +# A module with basic functions +# +# Copyright (C) 2015-2018 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; + +my %Cache; + +my %IntrinsicKeywords = map {$_=>1} ( + "true", + "false", + "_Bool", + "_Complex", + "const", + "int", + "long", + "void", + "short", + "float", + "volatile", + "restrict", + "unsigned", + "signed", + "char", + "double", + "class", + "struct", + "union", + "enum" +); + +sub initABI($) +{ + my $V = $_[0]; + foreach my $K ("SymbolInfo", "TypeInfo", "TName_Tid", "Constants") + { + if(not defined $In::ABI{$V}{$K}) { + $In::ABI{$V}{$K} = {}; + } + } +} + +sub cmdFind(@) +{ # native "find" is much faster than File::Find (~6x) + # also the File::Find doesn't support --maxdepth N option + # so using the cross-platform wrapper for the native one + my ($Path, $Type, $Name, $MaxDepth, $UseRegex) = (); + + $Path = shift(@_); + if(@_) { + $Type = shift(@_); + } + if(@_) { + $Name = shift(@_); + } + if(@_) { + $MaxDepth = shift(@_); + } + if(@_) { + $UseRegex = shift(@_); + } + + my $TmpDir = $In::Opt{"Tmp"}; + + if($In::Opt{"OS"} eq "windows") + { + $Path = getAbsPath($Path); + my $Cmd = "cmd /C dir \"$Path\" /B /O"; + if($MaxDepth!=1) { + $Cmd .= " /S"; + } + if($Type eq "d") { + $Cmd .= " /AD"; + } + elsif($Type eq "f") { + $Cmd .= " /A-D"; + } + my @Files = split(/\n/, qx/$Cmd/); + if($Name) + { + if(not $UseRegex) + { # FIXME: how to search file names in MS shell? + # wildcard to regexp + $Name=~s/\*/.*/g; + $Name='\A'.$Name.'\Z'; + } + @Files = grep { /$Name/i } @Files; + } + my @AbsPaths = (); + foreach my $File (@Files) + { + if(not isAbsPath($File)) { + $File = join_P($Path, $File); + } + if($Type eq "f" and not -f $File) + { # skip dirs + next; + } + push(@AbsPaths, pathFmt($File)); + } + if($Type eq "d") { + push(@AbsPaths, $Path); + } + return @AbsPaths; + } + else + { + my $FindCmd = "find"; + if(not checkCmd($FindCmd)) { + exitStatus("Not_Found", "can't find a \"find\" command"); + } + $Path = getAbsPath($Path); + if(-d $Path and -l $Path + and $Path!~/\/\Z/) + { # for directories that are symlinks + $Path.="/"; + } + my $Cmd = $FindCmd." \"$Path\""; + if($MaxDepth) { + $Cmd .= " -maxdepth $MaxDepth"; + } + if($Type) { + $Cmd .= " -type $Type"; + } + if($Name and not $UseRegex) + { # wildcards + $Cmd .= " -name \"$Name\""; + } + my $Res = `$Cmd 2>\"$TmpDir/null\"`; + if($? and $!) { + printMsg("ERROR", "problem with \'find\' utility ($?): $!"); + } + my @Files = split(/\n/, $Res); + if($Name and $UseRegex) + { # regex + @Files = grep { /$Name/ } @Files; + } + return @Files; + } +} + +sub findLibs($$$) +{ # FIXME: correct the search pattern + my ($Path, $Type, $MaxDepth) = @_; + return cmdFind($Path, $Type, '\.'.$In::Opt{"Ext"}.'[0-9.]*\Z', $MaxDepth, 1); +} + +sub getPrefixes($) +{ + my %Prefixes = (); + getPrefixes_I([$_[0]], \%Prefixes); + return keys(%Prefixes); +} + +sub getPrefixes_I($$) +{ + my $S = "/"; + if($In::Opt{"OS"} eq "windows") { + $S = "\\"; + } + + foreach my $P (@{$_[0]}) + { + my @Parts = reverse(split(/[\/\\]+/, $P)); + my $Name = $Parts[0]; + foreach (1 .. $#Parts) + { + $_[1]->{$Name}{$P} = 1; + if($_>4 or $Parts[$_] eq "include") { + last; + } + $Name = $Parts[$_].$S.$Name; + } + } +} + +sub getCompileCmd($$$$) +{ + my ($Path, $Opt, $Inc, $LVer) = @_; + my $GccCall = $In::Opt{"GccPath"}; + if($Opt) { + $GccCall .= " ".$Opt; + } + $GccCall .= " -x "; + if($In::Opt{"OS"} eq "macos") { + $GccCall .= "objective-"; + } + + if($In::Opt{"GccMissedMangling"}) + { # workaround for GCC 4.8 (C only) + $GccCall .= "c++"; + } + elsif(checkGcc("4")) + { # compile as "C++" header + # to obtain complete dump using GCC 4.0 + $GccCall .= "c++-header"; + } + else + { # compile as "C++" source + # GCC 3.3 cannot compile headers + $GccCall .= "c++"; + } + if(my $Opts = platformSpecs($LVer)) + { # platform-specific options + $GccCall .= " ".$Opts; + } + # allow extra qualifications + # and other nonconformant code + $GccCall .= " -fpermissive"; + $GccCall .= " -w"; + if($In::Opt{"NoStdInc"}) + { + $GccCall .= " -nostdinc"; + $GccCall .= " -nostdinc++"; + } + if(my $Opts = getGccOptions($LVer)) + { # user-defined options + $GccCall .= " ".$Opts; + } + $GccCall .= " \"$Path\""; + if($Inc) + { # include paths + $GccCall .= " ".$Inc; + } + return $GccCall; +} + +sub platformSpecs($) +{ + my $LVer = $_[0]; + + if($In::Opt{"Target"} eq "symbian") + { # options for GCCE compiler + my @Symbian_Opts = ( + "-D__GCCE__", + "-DUNICODE", + "-fexceptions", + "-D__SYMBIAN32__", + "-D__MARM_INTERWORK__", + "-D_UNICODE", + "-D__S60_50__", + "-D__S60_3X__", + "-D__SERIES60_3X__", + "-D__EPOC32__", + "-D__MARM__", + "-D__EABI__", + "-D__MARM_ARMV5__", + "-D__SUPPORT_CPP_EXCEPTIONS__", + "-march=armv5t", + "-mapcs", + "-mthumb-interwork", + "-DEKA2", + "-DSYMBIAN_ENABLE_SPLIT_HEADERS" + ); + return join(" ", @Symbian_Opts); + } + elsif($In::Opt{"OS"} eq "windows" + and $In::Opt{"GccTarget"}=~/mingw/i) + { # add options to MinGW compiler + # to simulate the MSVC compiler + my @MinGW_Opts = ( + "-D__unaligned=\" \"", + "-D__nullptr=\"nullptr\"", + "-D_WIN32", + "-D_STDCALL_SUPPORTED", + "-D__int64=\"long long\"", + "-D__int32=int", + "-D__int16=short", + "-D__int8=char", + "-D__possibly_notnullterminated=\" \"", + "-D__nullterminated=\" \"", + "-D__nullnullterminated=\" \"", + "-D__assume=\" \"", + "-D__w64=\" \"", + "-D__ptr32=\" \"", + "-D__ptr64=\" \"", + "-D__forceinline=inline", + "-D__inline=inline", + "-D__uuidof(x)=IID()", + "-D__try=", + "-D__except(x)=", + "-D__declspec(x)=__attribute__((x))", + "-D__pragma(x)=", + "-D_inline=inline", + "-D__forceinline=__inline", + "-D__stdcall=__attribute__((__stdcall__))", + "-D__cdecl=__attribute__((__cdecl__))", + "-D__fastcall=__attribute__((__fastcall__))", + "-D__thiscall=__attribute__((__thiscall__))", + "-D_stdcall=__attribute__((__stdcall__))", + "-D_cdecl=__attribute__((__cdecl__))", + "-D_fastcall=__attribute__((__fastcall__))", + "-D_thiscall=__attribute__((__thiscall__))", + "-DSHSTDAPI_(x)=x", + "-D_MSC_EXTENSIONS", + "-DSECURITY_WIN32", + "-D_MSC_VER=1920", + "-D_USE_DECLSPECS_FOR_SAL", + "-D__noop=\" \"", + "-DDECLSPEC_DEPRECATED=\" \"", + "-D__builtin_alignof(x)=__alignof__(x)", + "-DSORTPP_PASS"); + + if($In::ABI{$LVer}{"Arch"} eq "x86") + { + push(@MinGW_Opts, "-D_X86_=300"); + push(@MinGW_Opts, "-D_M_IX86=300"); + } + elsif($In::ABI{$LVer}{"Arch"} eq "x86_64") + { + push(@MinGW_Opts, "-D_AMD64_=300"); + push(@MinGW_Opts, "-D_M_AMD64=300"); + push(@MinGW_Opts, "-D_M_X64=300"); + } + elsif($In::ABI{$LVer}{"Arch"} eq "ia64") + { + push(@MinGW_Opts, "-D_IA64_=300"); + push(@MinGW_Opts, "-D_M_IA64=300"); + } + + return join(" ", @MinGW_Opts); + } + return undef; +} + +sub uncoverTypedefs($$) +{ + my ($TypeName, $LVer) = @_; + + if(defined $Cache{"uncoverTypedefs"}{$LVer}{$TypeName}) { + return $Cache{"uncoverTypedefs"}{$LVer}{$TypeName}; + } + my ($TypeName_New, $TypeName_Pre) = (formatName($TypeName, "T"), ""); + while($TypeName_New ne $TypeName_Pre) + { + $TypeName_Pre = $TypeName_New; + my $TypeName_Copy = $TypeName_New; + my %Words = (); + while($TypeName_Copy=~s/\b([a-z_]([\w:]*\w|))\b//io) + { + if(not $IntrinsicKeywords{$1}) { + $Words{$1} = 1; + } + } + foreach my $Word (keys(%Words)) + { + my $BaseType_Name = $In::ABI{$LVer}{"TypedefBase"}{$Word}; + + next if(not $BaseType_Name); + next if($BaseType_Name=~/\b$Word\b/); + next if($TypeName_New=~/\b(struct|union|enum)\s\Q$Word\E\b/); + + if($BaseType_Name=~/\([\*]+\)/) + { # FuncPtr + if($TypeName_New=~/\Q$Word\E(.*)\Z/) + { + my $Type_Suffix = $1; + $TypeName_New = $BaseType_Name; + if($TypeName_New=~s/\(([\*]+)\)/($1 $Type_Suffix)/) { + $TypeName_New = formatName($TypeName_New, "T"); + } + } + } + else + { + if($TypeName_New=~s/\b\Q$Word\E\b/$BaseType_Name/g) { + $TypeName_New = formatName($TypeName_New, "T"); + } + } + } + } + return ($Cache{"uncoverTypedefs"}{$LVer}{$TypeName} = $TypeName_New); +} + +sub getGccOptions($) +{ + my $LVer = $_[0]; + + my @Opt = (); + + if(my $COpt = $In::Desc{$LVer}{"CompilerOptions"}) + { # user-defined options + push(@Opt, $COpt); + } + if($In::Opt{"GccOptions"}) + { # additional + push(@Opt, $In::Opt{"GccOptions"}); + } + + if(@Opt) { + return join(" ", @Opt); + } + + return undef; +} + +sub setTarget($) +{ + my $Target = $_[0]; + + if($Target eq "default") + { + $Target = getOSgroup(); + + $In::Opt{"OS"} = $Target; + $In::Opt{"Ar"} = getArExt($Target); + } + + $In::Opt{"Target"} = $Target; + $In::Opt{"Ext"} = getLibExt($Target, $In::Opt{"UseStaticLibs"}); +} + +sub filterFormat($) +{ + my $FiltRef = $_[0]; + foreach my $Entry (keys(%{$FiltRef})) + { + foreach my $Filt (@{$FiltRef->{$Entry}}) + { + if($Filt=~/[\/\\]/) { + $Filt = pathFmt($Filt); + } + } + } +} + +sub checkGcc(@) +{ + my $Req = shift(@_); + my $Gcc = $In::Opt{"GccPath"}; + + if(@_) { + $Gcc = shift(@_); + } + + if(defined $Cache{"checkGcc"}{$Gcc}{$Req}) { + return $Cache{"checkGcc"}{$Gcc}{$Req}; + } + if(my $Ver = dumpVersion($Gcc)) + { + $Ver=~s/(-|_)[a-z_]+.*\Z//; # remove suffix (like "-haiku-100818") + if(cmpVersions($Ver, $Req)>=0) { + return ($Cache{"checkGcc"}{$Gcc}{$Req} = $Gcc); + } + } + return ($Cache{"checkGcc"}{$Gcc}{$Req} = ""); +} + +sub dumpVersion($) +{ + my $Cmd = $_[0]; + + if($Cache{"dumpVersion"}{$Cmd}) { + return $Cache{"dumpVersion"}{$Cmd}; + } + my $TmpDir = $In::Opt{"Tmp"}; + my $V = `$Cmd -dumpversion 2>\"$TmpDir/null\"`; + chomp($V); + return ($Cache{"dumpVersion"}{$Cmd} = $V); +} + +sub dumpMachine($) +{ + my $Cmd = $_[0]; + + if($Cache{"dumpMachine"}{$Cmd}) { + return $Cache{"dumpMachine"}{$Cmd}; + } + my $TmpDir = $In::Opt{"Tmp"}; + my $Machine = `$Cmd -dumpmachine 2>\"$TmpDir/null\"`; + chomp($Machine); + return ($Cache{"dumpMachine"}{$Cmd} = $Machine); +} + +return 1; diff --git a/abi-compliance-checker-2.4/modules/Internals/XmlDump.pm b/abi-compliance-checker-2.4/modules/Internals/XmlDump.pm new file mode 100644 index 0000000..ff0b040 --- /dev/null +++ b/abi-compliance-checker-2.4/modules/Internals/XmlDump.pm @@ -0,0 +1,863 @@ +########################################################################### +# A module to create and read ABI dump in XML format +# +# Copyright (C) 2009-2011 Institute for System Programming, RAS +# Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies) +# Copyright (C) 2012-2018 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use strict; + +my $TAG_ID = 0; +my $INDENT = " "; + +sub createXmlDump($) +{ + my $LVer = $_[0]; + my $ABI_DUMP = "\n"; + + $ABI_DUMP .= "\n"; + + $ABI_DUMP .= addTag("library", $In::ABI{$LVer}{"LibraryName"}); + $ABI_DUMP .= addTag("library_version", $In::ABI{$LVer}{"LibraryVersion"}); + $ABI_DUMP .= addTag("language", $In::ABI{$LVer}{"Language"}); + + $ABI_DUMP .= addTag("gcc", $In::ABI{$LVer}{"GccVersion"}); + $ABI_DUMP .= addTag("clang", $In::ABI{$LVer}{"ClangVersion"}); + $ABI_DUMP .= addTag("architecture", $In::ABI{$LVer}{"Arch"}); + $ABI_DUMP .= addTag("target", $In::ABI{$LVer}{"Target"}); + $ABI_DUMP .= addTag("word_size", $In::ABI{$LVer}{"WordSize"}); + + if($In::ABI{$LVer}{"Mode"}) { + $ABI_DUMP .= addTag("mode", $In::ABI{$LVer}{"Mode"}); + } + if($In::ABI{$LVer}{"SrcBin"}) { + $ABI_DUMP .= addTag("kind", "SrcBin"); + } + elsif($In::ABI{$LVer}{"BinOnly"}) { + $ABI_DUMP .= addTag("kind", "BinOnly"); + } + + if(my @Headers = keys(%{$In::ABI{$LVer}{"Headers"}})) + { + @Headers = sort {$In::ABI{$LVer}{"Headers"}{$a}<=>$In::ABI{$LVer}{"Headers"}{$b}} @Headers; + $ABI_DUMP .= openTag("headers"); + foreach my $Name (@Headers) { + $ABI_DUMP .= addTag("name", $Name); + } + $ABI_DUMP .= closeTag("headers"); + } + + if(my @Sources = keys(%{$In::ABI{$LVer}{"Sources"}})) + { + @Sources = sort {$In::ABI{$LVer}{"Sources"}{$a}<=>$In::ABI{$LVer}{"Sources"}{$b}} @Sources; + $ABI_DUMP .= openTag("sources"); + foreach my $Name (@Sources) { + $ABI_DUMP .= addTag("name", $Name); + } + $ABI_DUMP .= closeTag("sources"); + } + + if(my @Libs = keys(%{$In::ABI{$LVer}{"Needed"}})) + { + $ABI_DUMP .= openTag("needed"); + foreach my $Name (sort {lc($a) cmp lc($b)} @Libs) { + $ABI_DUMP .= addTag("library", $Name); + } + $ABI_DUMP .= closeTag("needed"); + } + + if(my @NameSpaces = keys(%{$In::ABI{$LVer}{"NameSpaces"}})) + { + $ABI_DUMP .= openTag("namespaces"); + foreach my $NameSpace (sort {lc($a) cmp lc($b)} @NameSpaces) { + $ABI_DUMP .= addTag("name", $NameSpace); + } + $ABI_DUMP .= closeTag("namespaces"); + } + + if(my @TypeInfo = keys(%{$In::ABI{$LVer}{"TypeInfo"}})) + { + $ABI_DUMP .= openTag("type_info"); + foreach my $ID (sort {$a<=>$b} @TypeInfo) + { + my %TInfo = %{$In::ABI{$LVer}{"TypeInfo"}{$ID}}; + $ABI_DUMP .= openTag("data_type"); + $ABI_DUMP .= addTag("id", $ID); + foreach my $Attr ("Name", "Type", "Size", + "Header", "Line", "NameSpace", "Class", "Return", "Algn") + { + if(defined $TInfo{$Attr}) { + $ABI_DUMP .= addTag(lc($Attr), $TInfo{$Attr}); + } + } + if($TInfo{"Private"}) { + $ABI_DUMP .= addTag("access", "private"); + } + if($TInfo{"Protected"}) { + $ABI_DUMP .= addTag("access", "protected"); + } + if(my @Positions = keys(%{$TInfo{"Memb"}})) + { + $ABI_DUMP .= openTag("members"); + foreach my $Pos (sort { $a<=>$b } @Positions) + { + $ABI_DUMP .= openTag("field"); + $ABI_DUMP .= addTag("name", $TInfo{"Memb"}{$Pos}{"name"}); + if(my $MTid = $TInfo{"Memb"}{$Pos}{"type"}) { + $ABI_DUMP .= addTag("type", $MTid); + } + if(my $Access = $TInfo{"Memb"}{$Pos}{"access"}) { + $ABI_DUMP .= addTag("access", $Access); + } + my $Val = $TInfo{"Memb"}{$Pos}{"value"}; + if(defined $Val) { + $ABI_DUMP .= addTag("value", $Val); + } + if(my $Align = $TInfo{"Memb"}{$Pos}{"algn"}) { + $ABI_DUMP .= addTag("algn", $Align); + } + if(my $Bitfield = $TInfo{"Memb"}{$Pos}{"bitfield"}) { + $ABI_DUMP .= addTag("bitfield", $Bitfield); + } + if($TInfo{"Memb"}{$Pos}{"mutable"}) { + $ABI_DUMP .= addTag("spec", "mutable"); + } + $ABI_DUMP .= addTag("pos", $Pos); + $ABI_DUMP .= closeTag("field"); + } + $ABI_DUMP .= closeTag("members"); + } + if(my @Positions = keys(%{$TInfo{"Param"}})) + { + $ABI_DUMP .= openTag("parameters"); + foreach my $Pos (sort { $a<=>$b } @Positions) + { + $ABI_DUMP .= openTag("param"); + if(my $PTid = $TInfo{"Param"}{$Pos}{"type"}) { + $ABI_DUMP .= addTag("type", $PTid); + } + $ABI_DUMP .= addTag("pos", $Pos); + $ABI_DUMP .= closeTag("param"); + } + $ABI_DUMP .= closeTag("parameters"); + } + if(my @Positions = keys(%{$TInfo{"TParam"}})) + { + $ABI_DUMP .= openTag("template_parameters"); + foreach my $Pos (sort { $a<=>$b } @Positions) + { + $ABI_DUMP .= openTag("param"); + $ABI_DUMP .= addTag("name", $TInfo{"TParam"}{$Pos}{"name"}); + $ABI_DUMP .= addTag("pos", $Pos); + $ABI_DUMP .= closeTag("param"); + } + $ABI_DUMP .= closeTag("template_parameters"); + } + if(my @Offsets = keys(%{$TInfo{"VTable"}})) + { + $ABI_DUMP .= openTag("vtable"); + foreach my $Offset (sort { $a<=>$b } @Offsets) + { + $ABI_DUMP .= openTag("entry"); + $ABI_DUMP .= addTag("offset", $Offset); + $ABI_DUMP .= addTag("value", $TInfo{"VTable"}{$Offset}); + $ABI_DUMP .= closeTag("entry"); + } + $ABI_DUMP .= closeTag("vtable"); + } + if(my $BTid = $TInfo{"BaseType"}) { + $ABI_DUMP .= addTag("base_type", $BTid); + } + if(my @BaseIDs = keys(%{$TInfo{"Base"}})) + { + @BaseIDs = sort { $TInfo{"Base"}{$a}{"pos"}<=>$TInfo{"Base"}{$b}{"pos"} } @BaseIDs; + $ABI_DUMP .= openTag("base"); + foreach my $BaseID (@BaseIDs) + { + $ABI_DUMP .= openTag("class"); + $ABI_DUMP .= addTag("id", $BaseID); + if(my $Access = $TInfo{"Base"}{$BaseID}{"access"}) { + $ABI_DUMP .= addTag("access", $Access); + } + if(my $Virt = $TInfo{"Base"}{$BaseID}{"virtual"}) { + $ABI_DUMP .= addTag("inherit", "virtual"); + } + $ABI_DUMP .= addTag("pos", $TInfo{"Base"}{$BaseID}{"pos"}); + $ABI_DUMP .= closeTag("class"); + } + $ABI_DUMP .= closeTag("base"); + } + if($TInfo{"Copied"}) { + $ABI_DUMP .= addTag("note", "copied"); + } + if($TInfo{"Spec"}) { + $ABI_DUMP .= addTag("note", "specialization"); + } + if($TInfo{"Forward"}) { + $ABI_DUMP .= addTag("note", "forward"); + } + $ABI_DUMP .= closeTag("data_type"); + } + $ABI_DUMP .= closeTag("type_info"); + } + + if(my @Constants = keys(%{$In::ABI{$LVer}{"Constants"}})) + { + $ABI_DUMP .= openTag("constants"); + foreach my $Constant (@Constants) + { + my %CInfo = %{$In::ABI{$LVer}{"Constants"}{$Constant}}; + $ABI_DUMP .= openTag("constant"); + $ABI_DUMP .= addTag("name", $Constant); + $ABI_DUMP .= addTag("value", $CInfo{"Value"}); + $ABI_DUMP .= addTag("header", $CInfo{"Header"}); + $ABI_DUMP .= closeTag("constant"); + } + $ABI_DUMP .= closeTag("constants"); + } + + if(my @SymbolInfo = keys(%{$In::ABI{$LVer}{"SymbolInfo"}})) + { + my %TR = ( + "MnglName" => "mangled", + "ShortName" => "short" + ); + $ABI_DUMP .= openTag("symbol_info"); + foreach my $ID (sort {$a<=>$b} @SymbolInfo) + { + my %SInfo = %{$In::ABI{$LVer}{"SymbolInfo"}{$ID}}; + $ABI_DUMP .= openTag("symbol"); + $ABI_DUMP .= addTag("id", $ID); + foreach my $Attr ("MnglName", "ShortName", "Class", + "Header", "Line", "Return", "NameSpace", "Value") + { + if(defined $SInfo{$Attr}) + { + my $Tag = $Attr; + if($TR{$Attr}) { + $Tag = $TR{$Attr}; + } + $ABI_DUMP .= addTag(lc($Tag), $SInfo{$Attr}); + } + } + if($SInfo{"Constructor"}) { + $ABI_DUMP .= addTag("kind", "constructor"); + } + if($SInfo{"Destructor"}) { + $ABI_DUMP .= addTag("kind", "destructor"); + } + if($SInfo{"Data"}) { + $ABI_DUMP .= addTag("kind", "data"); + } + if($SInfo{"Virt"}) { + $ABI_DUMP .= addTag("spec", "virtual"); + } + elsif($SInfo{"PureVirt"}) { + $ABI_DUMP .= addTag("spec", "pure virtual"); + } + elsif($SInfo{"Static"}) { + $ABI_DUMP .= addTag("spec", "static"); + } + if($SInfo{"InLine"}) { + $ABI_DUMP .= addTag("spec", "inline"); + } + if($SInfo{"Const"}) { + $ABI_DUMP .= addTag("spec", "const"); + } + if($SInfo{"Volatile"}) { + $ABI_DUMP .= addTag("spec", "volatile"); + } + if($SInfo{"Private"}) { + $ABI_DUMP .= addTag("access", "private"); + } + if($SInfo{"Protected"}) { + $ABI_DUMP .= addTag("access", "protected"); + } + if($SInfo{"Artificial"}) { + $ABI_DUMP .= addTag("note", "artificial"); + } + if(my $Lang = $SInfo{"Lang"}) { + $ABI_DUMP .= addTag("lang", $Lang); + } + if(my @Positions = keys(%{$SInfo{"Param"}})) + { + $ABI_DUMP .= openTag("parameters"); + foreach my $Pos (sort { $a<=>$b } @Positions) + { + $ABI_DUMP .= openTag("param"); + if(my $PName = $SInfo{"Param"}{$Pos}{"name"}) { + $ABI_DUMP .= addTag("name", $PName); + } + if(my $PTid = $SInfo{"Param"}{$Pos}{"type"}) { + $ABI_DUMP .= addTag("type", $PTid); + } + my $Default = $SInfo{"Param"}{$Pos}{"default"}; + if(defined $Default) { + $ABI_DUMP .= addTag("default", $Default); + } + if(my $Align = $SInfo{"Param"}{$Pos}{"algn"}) { + $ABI_DUMP .= addTag("algn", $Align); + } + if(defined $SInfo{"Param"}{$Pos}{"reg"}) { + $ABI_DUMP .= addTag("call", "register"); + } + $ABI_DUMP .= addTag("pos", $Pos); + $ABI_DUMP .= closeTag("param"); + } + $ABI_DUMP .= closeTag("parameters"); + } + if(my @Positions = keys(%{$SInfo{"TParam"}})) + { + $ABI_DUMP .= openTag("template_parameters"); + foreach my $Pos (sort { $a<=>$b } @Positions) + { + $ABI_DUMP .= openTag("param"); + $ABI_DUMP .= addTag("name", $SInfo{"TParam"}{$Pos}{"name"}); + $ABI_DUMP .= closeTag("param"); + } + $ABI_DUMP .= closeTag("template_parameters"); + } + $ABI_DUMP .= closeTag("symbol"); + } + $ABI_DUMP .= closeTag("symbol_info"); + } + + foreach my $K ("Symbols", "UndefinedSymbols") + { + if($In::ABI{$LVer}{$K} and my @Libs = keys(%{$In::ABI{$LVer}{$K}})) + { + my $SymTag = "symbols"; + if($K eq "UndefinedSymbols") { + $SymTag = "undefined_symbols"; + } + + $ABI_DUMP .= openTag($SymTag); + + foreach my $Lib (sort {lc($a) cmp lc($b)} @Libs) + { + $ABI_DUMP .= openTag("library", "name", $Lib); + foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%{$In::ABI{$LVer}{$K}{$Lib}})) + { + if((my $Size = $In::ABI{$LVer}{$K}{$Lib}{$Symbol})<0) + { # data + $ABI_DUMP .= addTag("symbol", $Symbol, "size", -$Size); + } + else + { # functions + $ABI_DUMP .= addTag("symbol", $Symbol); + } + } + $ABI_DUMP .= closeTag("library"); + } + $ABI_DUMP .= closeTag($SymTag); + } + } + + if(my @DepLibs = keys(%{$In::ABI{$LVer}{"DepSymbols"}})) + { + $ABI_DUMP .= openTag("dep_symbols"); + foreach my $Lib (sort {lc($a) cmp lc($b)} @DepLibs) + { + $ABI_DUMP .= openTag("library", "name", $Lib); + foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%{$In::ABI{$LVer}{"DepSymbols"}{$Lib}})) + { + if((my $Size = $In::ABI{$LVer}{"DepSymbols"}{$Lib}{$Symbol})<0) + { # data + $ABI_DUMP .= addTag("symbol", $Symbol, "size", -$Size); + } + else + { # functions + $ABI_DUMP .= addTag("symbol", $Symbol); + } + } + $ABI_DUMP .= closeTag("library"); + } + $ABI_DUMP .= closeTag("dep_symbols"); + } + + if(my @VSymbols = keys(%{$In::ABI{$LVer}{"SymbolVersion"}})) + { + $ABI_DUMP .= openTag("symbol_version"); + foreach my $Symbol (sort {lc($a) cmp lc($b)} @VSymbols) + { + $ABI_DUMP .= openTag("symbol"); + $ABI_DUMP .= addTag("name", $Symbol); + $ABI_DUMP .= addTag("version", $In::ABI{$LVer}{"SymbolVersion"}{$Symbol}); + $ABI_DUMP .= closeTag("symbol"); + } + $ABI_DUMP .= closeTag("symbol_version"); + } + + if(my @TargetHeaders = keys(%{$In::Desc{$LVer}{"TargetHeader"}})) + { + $ABI_DUMP .= openTag("target_headers"); + foreach my $Name (sort {lc($a) cmp lc($b)} @TargetHeaders) { + $ABI_DUMP .= addTag("name", $Name); + } + $ABI_DUMP .= closeTag("target_headers"); + } + + $ABI_DUMP .= "\n"; + + checkTags(); + + return $ABI_DUMP; +} + +sub readXmlDump($) +{ + my $ABI_DUMP = readFile($_[0]); + my %ABI = (); + + $ABI{"LibraryName"} = parseTag(\$ABI_DUMP, "library"); + $ABI{"LibraryVersion"} = parseTag(\$ABI_DUMP, "library_version"); + $ABI{"Language"} = parseTag(\$ABI_DUMP, "language"); + $ABI{"GccVersion"} = parseTag(\$ABI_DUMP, "gcc"); + $ABI{"ClangVersion"} = parseTag(\$ABI_DUMP, "clang"); + $ABI{"Arch"} = parseTag(\$ABI_DUMP, "architecture"); + $ABI{"Target"} = parseTag(\$ABI_DUMP, "target"); + $ABI{"WordSize"} = parseTag(\$ABI_DUMP, "word_size"); + + my $Pos = 0; + + if(my $Headers = parseTag(\$ABI_DUMP, "headers")) + { + while(my $Name = parseTag(\$Headers, "name")) { + $ABI{"Headers"}{$Name} = $Pos++; + } + } + + if(my $Sources = parseTag(\$ABI_DUMP, "sources")) + { + while(my $Name = parseTag(\$Sources, "name")) { + $ABI{"Sources"}{$Name} = $Pos++; + } + } + + if(my $Needed = parseTag(\$ABI_DUMP, "needed")) + { + while(my $Lib = parseTag(\$Needed, "library")) { + $ABI{"Needed"}{$Lib} = 1; + } + } + + if(my $NameSpaces = parseTag(\$ABI_DUMP, "namespaces")) + { + while(my $Name = parseTag(\$NameSpaces, "name")) { + $ABI{"NameSpaces"}{$Name} = 1; + } + } + + if(my $TypeInfo = parseTag(\$ABI_DUMP, "type_info")) + { + while(my $DataType = parseTag(\$TypeInfo, "data_type")) + { + my %TInfo = (); + my $ID = parseTag(\$DataType, "id"); + + if(my $Members = parseTag(\$DataType, "members")) + { + $Pos = 0; + while(my $Field = parseTag(\$Members, "field")) + { + my %MInfo = (); + $MInfo{"name"} = parseTag(\$Field, "name"); + if(my $Tid = parseTag(\$Field, "type")) { + $MInfo{"type"} = $Tid; + } + if(my $Access = parseTag(\$Field, "access")) { + $MInfo{"access"} = $Access; + } + my $Val = parseTag(\$Field, "value"); + if(defined $Val) { + $MInfo{"value"} = $Val; + } + if(my $Align = parseTag(\$Field, "algn")) { + $MInfo{"algn"} = $Align; + } + if(my $Bitfield = parseTag(\$Field, "bitfield")) { + $MInfo{"bitfield"} = $Bitfield; + } + if(my $Spec = parseTag(\$Field, "spec")) { + $MInfo{$Spec} = 1; + } + $TInfo{"Memb"}{$Pos++} = \%MInfo; + } + } + + if(my $Parameters = parseTag(\$DataType, "parameters")) + { + $Pos = 0; + while(my $Parameter = parseTag(\$Parameters, "param")) + { + my %PInfo = (); + if(my $Tid = parseTag(\$Parameter, "type")) { + $PInfo{"type"} = $Tid; + } + $TInfo{"Param"}{$Pos++} = \%PInfo; + } + } + if(my $TParams = parseTag(\$DataType, "template_parameters")) + { + $Pos = 0; + while(my $TParam = parseTag(\$TParams, "param")) { + $TInfo{"TParam"}{$Pos++}{"name"} = parseTag(\$TParam, "name"); + } + } + if(my $VTable = parseTag(\$DataType, "vtable")) + { + $Pos = 0; + while(my $Entry = parseTag(\$VTable, "entry")) { + $TInfo{"VTable"}{parseTag(\$Entry, "offset")} = parseTag(\$Entry, "value"); + } + } + if(my $BTid = parseTag(\$DataType, "base_type")) { + $TInfo{"BaseType"} = $BTid; + } + if(my $Base = parseTag(\$DataType, "base")) + { + $Pos = 0; + while(my $Class = parseTag(\$Base, "class")) + { + my %CInfo = (); + $CInfo{"pos"} = parseTag(\$Class, "pos"); + if(my $Access = parseTag(\$Class, "access")) { + $CInfo{"access"} = $Access; + } + if(my $Inherit = parseTag(\$Class, "inherit")) + { + if($Inherit eq "virtual") { + $CInfo{"virtual"} = 1; + } + } + $TInfo{"Base"}{parseTag(\$Class, "id")} = \%CInfo; + } + } + while(my $Note = parseTag(\$DataType, "note")) + { + if($Note eq "copied") { + $TInfo{"Copied"} = 1; + } + elsif($Note eq "specialization") { + $TInfo{"Spec"} = 1; + } + elsif($Note eq "forward") { + $TInfo{"Forward"} = 1; + } + } + foreach my $Attr ("Name", "Type", "Size", + "Header", "Line", "NameSpace", "Class", "Return", "Algn") + { + my $Val = parseTag(\$DataType, lc($Attr)); + if(defined $Val) { + $TInfo{$Attr} = $Val; + } + } + if(my $Access = parseTag(\$DataType, "access")) { + $TInfo{ucfirst($Access)} = 1; + } + $ABI{"TypeInfo"}{$ID} = \%TInfo; + } + } + + if(my $Constants = parseTag(\$ABI_DUMP, "constants")) + { + while(my $Constant = parseTag(\$Constants, "constant")) + { + if(my $Name = parseTag(\$Constant, "name")) + { + my %CInfo = (); + $CInfo{"Value"} = parseTag(\$Constant, "value"); + $CInfo{"Header"} = parseTag(\$Constant, "header"); + $ABI{"Constants"}{$Name} = \%CInfo; + } + } + } + + if(my $SymbolInfo = parseTag(\$ABI_DUMP, "symbol_info")) + { + my %TR = ( + "MnglName"=>"mangled", + "ShortName"=>"short" + ); + while(my $Symbol = parseTag(\$SymbolInfo, "symbol")) + { + my %SInfo = (); + my $ID = parseTag(\$Symbol, "id"); + + if(my $Parameters = parseTag(\$Symbol, "parameters")) + { + $Pos = 0; + while(my $Parameter = parseTag(\$Parameters, "param")) + { + my %PInfo = (); + if(my $PName = parseTag(\$Parameter, "name")) { + $PInfo{"name"} = $PName; + } + if(my $PTid = parseTag(\$Parameter, "type")) { + $PInfo{"type"} = $PTid; + } + my $Default = parseTag(\$Parameter, "default", "spaces"); + if(defined $Default) { + $PInfo{"default"} = $Default; + } + if(my $Align = parseTag(\$Parameter, "algn")) { + $PInfo{"algn"} = $Align; + } + if(my $Call = parseTag(\$Parameter, "call")) + { + if($Call eq "register") { + $PInfo{"reg"} = 1; + } + } + $SInfo{"Param"}{$Pos++} = \%PInfo; + } + } + if(my $TParams = parseTag(\$Symbol, "template_parameters")) + { + $Pos = 0; + while(my $TParam = parseTag(\$TParams, "param")) { + $SInfo{"TParam"}{$Pos++}{"name"} = parseTag(\$TParam, "name"); + } + } + + foreach my $Attr ("MnglName", "ShortName", "Class", + "Header", "Line", "Return", "NameSpace", "Value") + { + my $Tag = lc($Attr); + if($TR{$Attr}) { + $Tag = $TR{$Attr}; + } + my $Val = parseTag(\$Symbol, $Tag); + if(defined $Val) { + $SInfo{$Attr} = $Val; + } + } + if(my $Kind = parseTag(\$Symbol, "kind")) { + $SInfo{ucfirst($Kind)} = 1; + } + while(my $Spec = parseTag(\$Symbol, "spec")) + { + if($Spec eq "virtual") { + $SInfo{"Virt"} = 1; + } + elsif($Spec eq "pure virtual") { + $SInfo{"PureVirt"} = 1; + } + elsif($Spec eq "inline") { + $SInfo{"InLine"} = 1; + } + else + { # const, volatile, static + $SInfo{ucfirst($Spec)} = 1; + } + } + if(my $Access = parseTag(\$Symbol, "access")) { + $SInfo{ucfirst($Access)} = 1; + } + if(my $Note = parseTag(\$Symbol, "note")) { + $SInfo{ucfirst($Note)} = 1; + } + if(my $Lang = parseTag(\$Symbol, "lang")) { + $SInfo{"Lang"} = $Lang; + } + $ABI{"SymbolInfo"}{$ID} = \%SInfo; + } + } + + foreach my $K ("Symbols", "UndefinedSymbols") + { + my ($SymTag, $SymVal) = ("symbols", 1); + + if($K eq "UndefinedSymbols") { + ($SymTag, $SymVal) = ("undefined_symbols", 0); + } + + if(my $Symbols = parseTag(\$ABI_DUMP, $SymTag)) + { + my %LInfo = (); + while(my $LibSymbols = parseTag_E(\$Symbols, "library", \%LInfo)) + { + my %SInfo = (); + while(my $Symbol = parseTag_E(\$LibSymbols, "symbol", \%SInfo)) + { + if(my $Size = $SInfo{"size"}) { + $ABI{$K}{$LInfo{"name"}}{$Symbol} = -$Size; + } + else { + $ABI{$K}{$LInfo{"name"}}{$Symbol} = $SymVal; + } + %SInfo = (); + } + %LInfo = (); + } + } + } + + if(my $DepSymbols = parseTag(\$ABI_DUMP, "dep_symbols")) + { + my %LInfo = (); + while(my $LibSymbols = parseTag_E(\$DepSymbols, "library", \%LInfo)) + { + my %SInfo = (); + while(my $Symbol = parseTag_E(\$LibSymbols, "symbol", \%SInfo)) + { + if(my $Size = $SInfo{"size"}) { + $ABI{"DepSymbols"}{$LInfo{"name"}}{$Symbol} = -$Size; + } + else { + $ABI{"DepSymbols"}{$LInfo{"name"}}{$Symbol} = 1; + } + %SInfo = (); + } + %LInfo = (); + } + } + + $ABI{"SymbolVersion"} = {}; + + if(my $SymbolVersion = parseTag(\$ABI_DUMP, "symbol_version")) + { + while(my $Symbol = parseTag(\$SymbolVersion, "symbol")) { + $ABI{"SymbolVersion"}{parseTag(\$Symbol, "name")} = parseTag(\$Symbol, "version"); + } + } + + if(my $TargetHeaders = parseTag(\$ABI_DUMP, "target_headers")) + { + while(my $Name = parseTag(\$TargetHeaders, "name")) { + $ABI{"TargetHeaders"}{$Name} = 1; + } + } + + if(my $Mode = parseTag(\$ABI_DUMP, "mode")) { + $ABI{"Mode"} = $Mode; + } + if(my $Kind = parseTag(\$ABI_DUMP, "kind")) + { + if($Kind eq "BinOnly") { + $ABI{"BinOnly"} = 1; + } + elsif($Kind eq "SrcBin") { + $ABI{"SrcBin"} = 1; + } + } + + my %RInfo = (); + parseTag_E(\$ABI_DUMP, "ABI_dump", \%RInfo); + + $ABI{"ABI_DUMP_VERSION"} = $RInfo{"version"}; + $ABI{"XML_ABI_DUMP_VERSION"} = $RInfo{"xml_format"}; + $ABI{"ABI_COMPLIANCE_CHECKER_VERSION"} = $RInfo{"acc"}; + + return \%ABI; +} + +sub parseTag_E($$$) +{ + my ($CodeRef, $Tag, $Info) = @_; + if(not $Tag or not $CodeRef + or not $Info) { + return undef; + } + if(${$CodeRef}=~s/\<\Q$Tag\E(\s+([^<>]+)|)\>((.|\n)*?)\<\/\Q$Tag\E\>//) + { + my ($Ext, $Content) = ($2, $3); + $Content=~s/\A\s+//g; + $Content=~s/\s+\Z//g; + if($Ext) + { + while($Ext=~s/(\w+)\=\"([^\"]*)\"//) + { + my ($K, $V) = ($1, $2); + $Info->{$K} = xmlSpecChars_R($V); + } + } + if(substr($Content, 0, 1) ne "<") { + $Content = xmlSpecChars_R($Content); + } + return $Content; + } + return undef; +} + +sub addTag(@) +{ + my $Tag = shift(@_); + my $Val = shift(@_); + my @Ext = @_; + my $Content = openTag($Tag, @Ext); + chomp($Content); + $Content .= xmlSpecChars($Val); + $Content .= "\n"; + $TAG_ID-=1; + + return $Content; +} + +sub openTag(@) +{ + my $Tag = shift(@_); + my @Ext = @_; + my $Content = ""; + foreach (1 .. $TAG_ID) { + $Content .= $INDENT; + } + $TAG_ID+=1; + if(@Ext) + { + $Content .= "<".$Tag; + my $P = 0; + while($P<=$#Ext-1) + { + $Content .= " ".$Ext[$P]; + $Content .= "=\"".xmlSpecChars($Ext[$P+1])."\""; + $P+=2; + } + $Content .= ">\n"; + } + else { + $Content .= "<".$Tag.">\n"; + } + return $Content; +} + +sub closeTag($) +{ + my $Tag = $_[0]; + my $Content = ""; + $TAG_ID-=1; + foreach (1 .. $TAG_ID) { + $Content .= $INDENT; + } + $Content .= "\n"; + return $Content; +} + +sub checkTags() +{ + if($TAG_ID!=0) { + printMsg("WARNING", "the number of opened tags is not equal to number of closed tags"); + } +} + +return 1; diff --git a/abi-compliance-checker-2.4/modules/RulesBin.xml b/abi-compliance-checker-2.4/modules/RulesBin.xml new file mode 100644 index 0000000..f65acce --- /dev/null +++ b/abi-compliance-checker-2.4/modules/RulesBin.xml @@ -0,0 +1,3563 @@ + + + + + + Added_Virtual_Method + + + High + + + V-table + + + Virtual method @target has been added to this class. + + + The layout of v-table has been changed. Call of any virtual method at higher position in this class or its subclasses may result in crash or incorrect behavior of applications. + + + You should add several padding virtual methods at end of class declaration and use them one by one in the course of interface evolution. + + + + + + Added_Pure_Virtual_Method + + + High + + + V-table + + + Pure virtual method @target has been added to this class. + + + 1) Applications will not provide the implementation for this pure virtual method and therefore cause a crash in the library trying to call this method. + 2) The layout of v-table has been changed. Call of any virtual method at higher position in this class or its subclasses may result in crash or incorrect behavior of applications. + + + + + + Added_Virtual_Method_At_End_Of_Leaf_Copying_Class + + + Medium + + + V-table + + + Virtual method @target has been added to this class. + + + The layout of v-table has been changed. This leaf class has no exported constructors and therefore applications will copy an old v-table of the class that will not contain a pointer to added virtual method. Call of any method in this class may result in crash or incorrect behavior of applications. + NOTE: if new virtual method is called only from other new methods, then binary compatibility should not be affected. + + + + + + Added_Virtual_Method_At_End_Of_Leaf_Allocable_Class + + + Safe + + + V-table + + + Virtual method @target has been added to this class. + + + No effect. You can add virtual functions at end of leaf classes with exported constructors. + + + + + + Added_First_Virtual_Method + + + High + + + V-table + + + First virtual method @target has been added to this class. + + + 1) The layout of type structure has been shifted by @word_size bytes by the added v-table pointer. + 2) Size of class has been increased by @word_size bytes. + + + + + + Removed_Virtual_Method + + + High + + + V-table + + + Virtual method @target has been removed from this class. + + + The layout of v-table has been changed. Call of this virtual method or any virtual method at higher position in this class or its subclasses may result in crash or incorrect behavior of applications. + + + + + + Removed_Pure_Virtual_Method + + + High + + + V-table + + + Pure virtual method @target has been removed from this class. + + + The layout of v-table has been changed. Call of this virtual method or any virtual method at higher position in this class or its subclasses may result in crash or incorrect behavior of applications. + + + + + + Removed_Last_Virtual_Method + + + High + + + V-table + + + Last virtual method @target has been removed from this class. + + + 1) The layout of type structure has been shifted by @word_size bytes by the removed v-table pointer. + 2) Size of class has been decreased by @word_size bytes. + + + + + + Virtual_Replacement + + + Medium + + + V-table + + + Virtual method @target has been added to this class instead of @old_value. + + + Applications will pass parameters of older replaced method to newly added virtual method. This may result in crash or incorrect behavior of applications. + + + + + + Pure_Virtual_Replacement + + + Medium + + + V-table + + + Pure virtual method @target has been added to this class instead of @old_value. + + + Applications will provide an older method to the library instead of expected newely added virtual method. This may result in crash or incorrect behavior of applications. + + + + + + Virtual_Table_Changed_Unknown + + + Medium + + + V-table + + + The layout of v-table has been changed for **unknown** reason. + + + Call of any method in this class may result in crash or incorrect behavior of applications. + + + + + + Virtual_Method_Position + + + High + + + V-table + + + The relative position of virtual method @target has been changed from @old_value to @new_value. + + + The layout of v-table has been changed. Call of this virtual method may result in crash or incorrect behavior of applications. + + + + + + Pure_Virtual_Method_Position + + + High + + + V-table + + + The relative position of pure virtual method @target has been changed from @old_value to @new_value. + + + The layout of v-table has been changed. Call of this pure virtual method implementation may result in crash or incorrect behavior of applications. + + + + + + Overridden_Virtual_Method + + + Low + + + V-table + + + Virtual method @old_value has been overridden by @new_value. + + + Method @new_value will be called instead of @old_value by old applications. + + + + + + Overridden_Virtual_Method_B + + + Low + + + V-table + + + Virtual method @old_value has been overridden by @new_value. + + + Method @new_value will be called instead of @old_value by old applications. + + + + + + Size_Of_Allocable_Class_Increased + + + High + + + Classes + + + Size of this class has been increased from @old_size to @new_size. + + + 1) An object of this class can be allocated by the applications and old size will be hardcoded at the compile time. Call of any exported constructor will break the memory of neighboring objects on the stack or heap. + 2) The memory layout and size of subclasses will be changed. + + + + + + Size_Of_Allocable_Class_Decreased + + + Medium + + + Classes + + + Size of this class has been decreased from @old_size to @new_size. + + + Previous accesses of applications to public fields of this class or its subclasses may be incorrect. + + + + + + Size_Of_Copying_Class + + + High + + + Classes + + + Size of this class has been changed from @old_size to @new_size. + + + 1) The class has only inline or auto-generated constructors which will be copied to applications at compile time and will allocate an older memory layout. Call of any exported method of this class may access a memory outside the allocated objects or inside the older memory structure and result in crash or incorrect behavior of applications. + 2) The memory layout and size of subclasses will be changed. + + + + + + Base_Class_Position + + + Low + + + Classes + + + The relative position of class @target has been changed from @old_value to @new_value in the list of base classes. + + + Possible incorrect access of applications to the memory occupied by the base classes. + + + + + + Base_Class_Became_Virtually_Inherited + + + Medium + + + Classes + + + Base class @target became **virtually** inherited. + + + Size, memory layout and v-table layout of this class and subclasses may change. + + + + + + Base_Class_Became_Non_Virtually_Inherited + + + Medium + + + Classes + + + Base class @target became **non-virtually** inherited. + + + Size, memory layout and v-table layout of this class and subclasses may change. + + + + + + Added_Base_Class_And_Shift + + + High + + + Classes + + + Base class @target has been added. + + + The memory layout in this class has been shifted by @shift bytes. + + + + + + Added_Base_Class_And_Size + + + High + + + Classes + + + Base class @target has been added. + + + 1) Size of the class has been changed from @old_size to @new_size. + 2) The memory layout in this class has been shifted by @shift bytes. + + + + + + Added_Base_Class_And_Shift_And_VTable + + + High + + + Classes + + + Base class @target has been added. + + + 1) The layout of v-table in this class has been changed. + 2) The memory layout in this class has been shifted by @shift bytes. + + + + + + Added_Base_Class_And_Size_And_VTable + + + High + + + Classes + + + Base class @target has been added. + + + 1) The layout of v-table in this class has been changed. + 2) Size of the class has been changed from @old_size to @new_size. + 3) The memory layout in this class has been shifted by @shift bytes. + + + + + + Added_Base_Class_And_VTable + + + High + + + Classes + + + Base class @target has been added. + + + The layout of v-table in this class has been changed. + + + + + + Added_Base_Class + + + Low + + + Classes + + + Base class @target has been added. + + + Possible incorrect access of applications to the memory occupied by the base classes. + + + + + + Removed_Base_Class_And_Size + + + High + + + Classes + + + Base class @target has been removed. + + + 1) Size of the class has been changed from @old_size to @new_size. + 2) The memory layout in this class has been shifted by @shift bytes. + + + + + + Removed_Base_Class_And_Shift + + + High + + + Classes + + + Base class @target has been removed. + + + The memory layout in this class has been shifted by @shift bytes. + + + + + + Removed_Base_Class_And_Shift_And_VTable + + + High + + + Classes + + + Base class @target has been removed. + + + 1) The layout of v-table in this class has been changed. + 2) The memory layout in this class has been shifted by @shift bytes. + + + + + + Removed_Base_Class_And_Size_And_VTable + + + High + + + Classes + + + Base class @target has been removed. + + + 1) The layout of v-table in this class has been changed. + 2) Size of the class has been changed from @old_size to @new_size. + 3) The memory layout in this class has been shifted by @shift bytes. + + + + + + Removed_Base_Class_And_VTable + + + High + + + Classes + + + Base class @target has been removed. + + + The layout of v-table in this class has been changed. + + + + + + Removed_Base_Class + + + Low + + + Classes + + + Base class @target has been removed. + + + Possible incorrect access of applications to the memory occupied by the base classes. + + + + + + DataType_Size + + + Low + + + Types + + + Size of this type has been changed from @old_size to @new_size. + + + The fields or parameters of such data type may be incorrectly initialized or accessed by old client applications. + + + + + + DataType_Type + + + Medium + + + Types + + + Type of this type has been changed from @old_value to @new_value. + + + The fields or parameters of such data type may be incorrectly initialized or accessed by old client applications. + + + + + + DataType_Size_And_Stack + + + High + + + Types + + + Size of this type has been changed from @old_size to @new_size. + + + Layout of parameter's stack of several functions has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications. + + + + + + Moved_Field + + + Medium + + + Fields + + + The relative position of field @target has been changed from @old_value to @new_value. + + + Applications will access incorrect memory when attempting to access this field. + + + + + + Moved_Field_And_Size + + + Medium + + + Fields + + + The relative position of field @target has been changed from @old_value to @new_value. + + + 1) Applications will access incorrect memory when attempting to access this field. + 2) Size of the inclusive type has been changed. + + + + + + Moved_Private_Field_And_Size + + + Low + + + Fields + + + The relative position of private field @target has been changed from @old_value to @new_value. + + + Size of the inclusive type has been changed. + + + + + + Added_Field + + + Low + + + Fields + + + Field @target has been added to this type. + + + This field will not be initialized by old clients. + NOTE: this field should be accessed only from the new library functions, otherwise it may result in crash or incorrect behavior of applications. + + + + + + Added_Field_And_Size + + + Low + + + Fields + + + Field @target has been added to this type. + + + 1) This field will not be initialized by old clients. + 2) Size of the inclusive type has been changed. + NOTE: this field should be accessed only from the new library functions, otherwise it may result in crash or incorrect behavior of applications. + + + + + + Added_Field_And_Layout + + + Medium + + + Fields + + + Field @target has been added at the middle position of this structural type. + + + Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications. + + + + + + Added_Field_And_Layout_And_Size + + + Medium + + + Fields + + + Field @target has been added at the middle position of this structural type. + + + 1) Size of the inclusive type has been changed. + 2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications. + + + + + + Added_Private_Field_And_Size + + + Low + + + Fields + + + Field @target has been added to this type. + + + Size of the inclusive type has been changed. + NOTE: this field should be accessed only from the new library functions, otherwise it may result in crash or incorrect behavior of applications. + + + + + + Added_Private_Field_And_Layout + + + Medium + + + Fields + + + Field @target has been added at the middle position of this structural type. + + + Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications. + + + + + + Added_Private_Field_And_Layout_And_Size + + + Medium + + + Fields + + + Field @target has been added at the middle position of this structural type. + + + 1) Size of the inclusive type has been changed. + 2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications. + + + + + + Added_Union_Field_And_Size + + + Medium + + + Fields + + + Field @target has been added to this type. + + + Size of the union has been changed. + NOTE: this field should be accessed only from the new library functions, otherwise it may result in crash or incorrect behavior of applications. + + + + + + Added_Union_Field + + + Low + + + Fields + + + Field @target has been added to this type. + + + NOTE: this field should be accessed only from the new library functions, otherwise it may result in crash or incorrect behavior of applications. + + + + + + Removed_Field + + + Medium + + + Fields + + + Field @target has been removed from this type. + + + Applications will access incorrect memory when attempting to access this field. + + + + + + Removed_Field_And_Layout + + + Medium + + + Fields + + + Field @target has been removed from the middle position of this structural type. + + + 1) Applications will access incorrect memory when attempting to access this field. + 2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications. + + + + + + Removed_Field_And_Size + + + Medium + + + Fields + + + Field @target has been removed from this type. + + + 1) Applications will access incorrect memory when attempting to access this field. + 2) Size of the inclusive type has been changed. + + + + + + Removed_Field_And_Layout_And_Size + + + High + + + Fields + + + Field @target has been removed from the middle position of this structural type. + + + 1) Previous accesses of applications to the removed field will be incorrect. + 2) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications. + + + + + + Removed_Private_Field_And_Size + + + Low + + + Fields + + + Field @target has been removed from this type. + + + Size of the inclusive type has been changed. + + + + + + Removed_Private_Field_And_Layout + + + Medium + + + Fields + + + Field @target has been removed from the middle position of this structural type. + + + Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications. + + + + + + Removed_Private_Field_And_Layout_And_Size + + + Medium + + + Fields + + + Field @target has been removed from the middle position of this structural type. + + + 1) Layout of structure fields has been changed and therefore fields at higher positions of the structure definition may be incorrectly accessed by applications. + 2) Size of the inclusive type has been changed. + + + + + + Removed_Union_Field_And_Size + + + Medium + + + Fields + + + Field @target has been removed from this union. + + + 1) Applications may access incorrect memory when attempting to access this field. + 2) Size of the union has been changed. + + + + + + Removed_Union_Field + + + Low + + + Fields + + + Field @target has been removed from this union. + + + Applications may access incorrect memory when attempting to access this field. + + + + + + Renamed_Field + + + Low + + + Fields + + + Field @target has been renamed to @new_value. + + + Renaming of a field in data type may indicate a change in the semantic meaning of the field. + + + + + + Used_Reserved_Field + + + Low + + + Fields + + + Reserved field @target has been replaced by @new_value. + + + This field will not be initialized by old clients. + + + + + + Enum_Member_Value + + + Medium + + + Constants + + + Value of member @target has been changed from @old_value to @new_value. + + + Applications may execute a wrong branch of code in the library and therefore change the behavior. + + + + + + Enum_Member_Removed + + + Low + + + Constants + + + The member @target has been removed. + + + This may result in crash or incorrect behavior of applications because the library may not handle removed member anymore. + + + + + + Enum_Last_Member_Value + + + Low + + + Constants + + + Value of member @target has been changed from @old_value to @new_value. + + + Applications may execute a wrong branch of code in the library and therefore change the behavior. + + + + + + Enum_Private_Member_Value + + + Safe + + + Constants + + + Value of private member @target has been changed from @old_value to @new_value. + + + No effect. + + + + + + Enum_Member_Name + + + Low + + + Constants + + + Name of member with value @target has been changed from @old_value to @new_value. + + + Applications may execute a wrong branch of code in the library and therefore change the behavior. + + + + + + Field_Type + + + Low + + + Fields + + + Type of field @target has been changed from @old_value to @new_value. + + + Replacement of the field data type may indicate a change in the semantic meaning of the field. + + + + + + Field_Type_And_Size + + + Medium + + + Fields + + + Type of field @target has been changed from @old_value (@old_size) to @new_value (@new_size). + + + This field may be incorrectly initialized or accessed by applications. + + + + + + Field_Type_And_Size_And_Layout + + + Medium + + + Fields + + + Type of field @target has been changed from @old_value (@old_size) to @new_value (@new_size). + + + Previous accesses of applications and library functions to this field and fields at higher positions of the structure definition may be broken. + + + + + + Field_Type_And_Size_And_Type_Size + + + Medium + + + Fields + + + Type of field @target has been changed from @old_value (@old_size) to @new_value (@new_size). + + + 1) This field may be incorrectly initialized or accessed by applications. + 2) Size of the inclusive type has been changed. + + + + + + Field_Type_And_Size_And_Layout_And_Type_Size + + + Medium + + + Fields + + + Type of field @target has been changed from @old_value (@old_size) to @new_value (@new_size). + + + 1) Size of the inclusive type has been changed. + 2) Previous accesses of applications and library functions to this field and fields at higher positions of the structure definition may be broken. + + + + + + Private_Field_Type_And_Size_And_Layout + + + Medium + + + Fields + + + Type of field @target has been changed from @old_value (@old_size) to @new_value (@new_size). + + + Previous accesses of applications and library functions to the fields at higher positions of the structure definition may be broken. + + + + + + Private_Field_Type_And_Size + + + Safe + + + Fields + + + Type of field @target has been changed from @old_value (@old_size) to @new_value (@new_size). + + + No effect. + + + + + + Private_Field_Type_And_Size_And_Type_Size + + + Low + + + Fields + + + Type of field @target has been changed from @old_value (@old_size) to @new_value (@new_size). + + + Size of the inclusive type has been changed. + + + + + + Private_Field_Type_And_Size_And_Layout_And_Type_Size + + + Medium + + + Fields + + + Type of field @target has been changed from @old_value (@old_size) to @new_value (@new_size). + + + 1) Size of the inclusive type has been changed. + 2) Previous accesses of applications and library functions to the fields at higher positions of the structure definition may be broken. + + + + + + Field_BaseType_And_Size + + + Low + + + Fields + + + Base type of field @target has been changed from @old_value (@old_size) to @new_value (@new_size). + + + Possible access of applications to incorrect memory through the pointer. + + + + + + Field_BaseType + + + Low + + + Fields + + + Base type of field @target has been changed from @old_value to @new_value. + + + Replacement of field base type may indicate a change in the semantic meaning of the field. + + + + + + Field_PointerLevel_Increased + + + Medium + + + Fields + + + The pointer level of field @target has been increased from @old_value to @new_value. + + + The library functions may try to access unallocated memory by the dereferencing of old field value and therefore cause a crash of applications. + + + + + + Field_PointerLevel_Decreased + + + Low + + + Fields + + + The pointer level of field @target has been decreased from @old_value to @new_value. + + + The library functions will treat the value of this field as the lower-dimension array and will not read all elements. This may change the behavior of applications. + + + + + + Field_Size + + + Medium + + + Fields + + + Size of field @target has been changed from @old_size to @new_size. + + + Previous accesses of applications and library functions to this field may be broken. + + + + + + Struct_Field_Size_Increased + + + Low + + + Fields + + + Size of field @target has been changed from @old_size to @new_size. + + + Previous accesses of applications and library functions to this field may be broken. + + + + + + Field_Size_And_Layout + + + Medium + + + Fields + + + Size of field @target has been changed from @old_size to @new_size. + + + Previous accesses of applications and library functions to this field and fields at higher positions of the structure definition may be broken. + + + + + + Field_Size_And_Type_Size + + + Medium + + + Fields + + + Size of field @target has been changed from @old_size to @new_size. + + + 1) Size of the inclusive type has been changed. + 2) Previous accesses of applications and library functions to this field may be broken. + + + + + + Field_Size_And_Layout_And_Type_Size + + + Medium + + + Fields + + + Size of field @target has been changed from @old_size to @new_size. + + + 1) Size of the inclusive type has been changed. + 2) Previous accesses of applications and library functions to this field and fields at higher positions of the structure definition may be broken. + + + + + + Private_Field_Size_And_Layout + + + Medium + + + Fields + + + Size of field @target has been changed from @old_size to @new_size. + + + Previous accesses of applications and library functions to the fields at higher positions of the structure definition may be broken. + + + + + + Private_Field_Size + + + Safe + + + Fields + + + Size of field @target has been changed from @old_size to @new_size. + + + No effect. + + + + + + Private_Field_Size_And_Type_Size + + + Low + + + Fields + + + Size of field @target has been changed from @old_size to @new_size. + + + Size of the inclusive type has been changed. + + + + + + Private_Field_Size_And_Layout_And_Type_Size + + + Medium + + + Fields + + + Size of field @target has been changed from @old_size to @new_size. + + + 1) Size of the inclusive type has been changed. + 2) Previous accesses of applications and library functions to the fields at higher positions of the structure definition may be broken. + + + + + + Typedef_BaseType + + + Low + + + Types + + + Base type has been changed from @old_value to @new_value. + + + Replacement of the base data type may indicate a change in its semantic meaning. + + + + + + Typedef_BaseType_Format + + + Medium + + + Types + + + Base type has been changed from @old_value to @new_value of different format. + + + The fields or parameters of such data type may be incorrectly initialized or accessed by old client applications. + + + + + + Added_Symbol + + + Safe + + + Symbols + + + + + + Removed_Symbol + + + High + + + Symbols + + + + + + Method_Became_Static + + + High + + + Symbols + + + Method became static. + + + Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications. + + + + + + Method_Became_Non_Static + + + High + + + Symbols + + + Method became non-static. + + + Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications. + + + + + + Parameter_Default_Value_Changed + + + Low + + + Parameters + + + The default argument of @param_pos parameter @target has been changed from @old_value to @new_value. + + + Applications will pass an old default (compile-time) argument that may not be properly handled anymore. This may result in crash or incorrect behavior of applications. + + + + + + Parameter_Default_Value_Removed + + + Low + + + Parameters + + + The default argument @old_value of @param_pos parameter @target has been removed. + + + Applications will pass an old default argument (that is not default any more) that may not be properly handled anymore. This may result in crash or incorrect behavior of applications. + + + + + + Parameter_Default_Value_Added + + + Safe + + + Parameters + + + The default argument @new_value of @param_pos parameter @target has been added. + + + No effect. + + + + + + Parameter_Type_And_Register + + + Medium + + + Symbols + + + Type of @param_pos parameter @target has been changed from @old_value (@old_size) to @new_value (@new_size). + + + The parameter became passed in different register. Applications will read the wrong memory block instead of the parameter value. Also, distribution of other parameters on the available registers and stack may be changed. + + + + + + Parameter_Type_And_Stack + + + High + + + Parameters + + + Type of @param_pos parameter @target has been changed from @old_value (@old_size) to @new_value (@new_size). + + + Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications. + + + + + + Parameter_Type_And_Size + + + High + + + Parameters + + + Type of @param_pos parameter @target has been changed from @old_value (@old_size) to @new_value (@new_size). + + + Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications. + + + + + + Parameter_Type_From_Stack_To_Register + + + High + + + Parameters + + + Type of @param_pos parameter @target has been changed from @old_value (@old_size) to @new_value (@new_size). + + + The parameter became passed in the register instead of the stack. This may result in crash or incorrect behavior of applications. + + + + + + Parameter_Type_From_Register_To_Stack + + + High + + + Parameters + + + Type of @param_pos parameter @target has been changed from @old_value (@old_size) to @new_value (@new_size). + + + The parameter became passed through the stack instead of the register. This may result in crash or incorrect behavior of applications. + + + + + + Parameter_Type + + + Low + + + Parameters + + + Type of @param_pos parameter @target has been changed from @old_value to @new_value. + + + Replacement of parameter data type may indicate a change in its semantic meaning. + + + + + + Parameter_Became_Non_Const + + + Medium + + + Parameters + + + Type of @param_pos parameter @target has been changed from @old_value to @new_value (became non-const). + + + This function may change parameter @target, but it will be treated as const by old client applications. This may result in crash or incorrect behavior of applications. + + + + + + Parameter_Removed_Const + + + Medium + + + Parameters + + + Type of @param_pos parameter @target has been changed from @old_value to @new_value (removed const qualifier). + + + This function may change parameter @target, but it will be treated as const by old client applications. This may result in crash or incorrect behavior of applications. + + + + + + Parameter_Became_Restrict + + + Medium + + + Parameters + + + Parameter @target became restrict. + + + Added a new restriction on the parameter: if the memory addressed by the restrict-qualified pointer is modified, no other pointer will access that same memory. The compiler may choose to optimize new library code involving restrict-qualified pointers in a way that might result in incorrect behavior of old applications, that don't meet this restriction. + + + + + + Parameter_Became_Non_Restrict + + + Safe + + + Parameters + + + Parameter @target became non-restrict. + + + No effect. + + + + + + Parameter_Became_Register + + + Medium + + + Parameters + + + Added register modifier to the parameter @target. + + + The parameter may be passed in a register instead of the calling stack. + + + + + + Parameter_Became_Non_Register + + + Medium + + + Parameters + + + Removed register modifier from the parameter @target. + + + The parameter will be passed on the calling stack instead of a register. + + + + + + Parameter_To_Register + + + Medium + + + Parameters + + + The parameter @target became passed in @new_value register instead of stack. + + + Violation of the calling convention. This may result in crash or incorrect behavior of applications. + + + + + + Parameter_From_Register + + + Medium + + + Parameters + + + The parameter @target became passed on stack instead of @old_value register. + + + Violation of the calling convention. This may result in crash or incorrect behavior of applications. + + + + + + Parameter_Changed_Register + + + High + + + Symbols + + + The parameter @target became passed in @new_value register instead of @old_value. + + + Applications will read the wrong memory block instead of the parameter value. + + + + + + Parameter_Changed_Offset + + + High + + + Symbols + + + The parameter @target became passed at the different offset on the stack (@new_value instead of @old_value). + + + Violation of the calling convention. This may result in crash or incorrect behavior of applications. + + + + + + Return_Type_Became_Const + + + Medium + + + Symbols + + + Type of return value became const (has been changed from @old_value to @new_value). + + + The return value will be treated as non-const by old client applications. This may result in crash or incorrect behavior of applications. + + + + + + Return_Type_Added_Const + + + Medium + + + Symbols + + + Added **const** qualifier to return value (has been changed from @old_value to @new_value). + + + The return value will be treated as non-const by old client applications. This may result in crash or incorrect behavior of applications. + + + + + + Parameter_BaseType_And_Size + + + Medium + + + Parameters + + + Base type of @param_pos parameter @target has been changed from @old_value (@old_size) to @new_value (@new_size). + + + This parameter may be incorrectly initialized by applications. + + + + + + Parameter_BaseType + + + Low + + + Parameters + + + Base type of @param_pos parameter @target has been changed from @old_value to @new_value. + + + Replacement of parameter base type may indicate a change in its semantic meaning. + + + + + + Parameter_PointerLevel_Increased + + + High + + + Parameters + + + The pointer level of @param_pos parameter @target has been increased from @old_value to @new_value. + + + The library function may try to access unallocated memory by the dereferencing of old parameter value and therefore cause a crash of applications. + + + + + + Parameter_PointerLevel_Decreased + + + Medium + + + Parameters + + + The pointer level of @param_pos parameter @target has been decreased from @old_value to @new_value. + + + The library function will treat the parameter as the lower-dimension array and will not read all elements. This may change the behavior of applications. + NOTE: if this is out-parameter then this change may cause a crash of applications. + + + + + + Return_Type_And_Size + + + Medium + + + Symbols + + + Type of return value has been changed from @old_value (@old_size) to @new_value (@new_size). + + + Applications will obtain a different return value and execution may change. + + + + + + Global_Data_Type_And_Size + + + Medium + + + Symbols + + + Type of this global data has been changed from @old_value (@old_size) to @new_value (@new_size). + + + Applications will obtain a different value and execution may change. + + + + + + Return_Type + + + Low + + + Symbols + + + Type of return value has been changed from @old_value to @new_value. + + + Replacement of return type may indicate a change in its semantic meaning. + + + + + + Global_Data_Type + + + Low + + + Symbols + + + Type of this global data has been changed from @old_value to @new_value. + + + Replacement of data type may indicate a change in semantic meaning. + + + + + + Global_Data_Type_Format + + + Medium + + + Symbols + + + Type of this global data has been changed from @old_value to @new_value of different format. + + + This global data may be incorrectly accessed by applications. + + + + + + Global_Data_Size + + + Medium + + + Symbols + + + Size of this global data has been changed from @old_size to @new_size. + + + Applications will obtain a different value and execution may change. + + + + + + Return_Type_Became_Void + + + Medium + + + Symbols + + + Type of return value has been changed from @old_value (@old_size) to void. + + + Applications will not obtain a return value and execution may change. + + + + + + Return_Type_Became_Void_And_Stack_Layout + + + High + + + Symbols + + + Type of return value has been changed from @old_value (@old_size) to void. + + + 1) Applications will read the wrong memory block instead of the return value. + 2) Layout of parameter's stack has been shifted by @word_size bytes because the hidden first argument, that is used to pass the return value, has been removed. All the parameters will be incorrectly initialized by applications. + + + + + + Return_Type_Became_Void_And_Register + + + High + + + Symbols + + + Type of return value has been changed from @old_value (@old_size) to void. + + + 1) Applications will read the wrong memory block instead of the return value. + 2) Distribution of parameters on the available registers and stack has been changed because the hidden first argument, that is used to pass the return value, has been removed. All the parameters will be incorrectly initialized by applications. + + + + + + Return_Type_From_Void_And_Stack_Layout + + + High + + + Symbols + + + Type of return value has been changed from void to @new_value (@new_size). + + + Layout of parameter's stack has been shifted by @word_size bytes because the return value became passed in memory as the hidden first argument, that was used to pass the return value. All the parameters will be incorrectly initialized by applications. + + + + + + Return_Type_From_Void_And_Register + + + High + + + Symbols + + + Type of return value has been changed from void to @new_value (@new_size). + + + Distribution of parameters on the available registers and stack has been changed because the return value became passed in memory as the hidden first argument, that is used to pass the return value. All the parameters will be incorrectly initialized by applications. + + + + + + Return_Type_From_Void + + + Low + + + Symbols + + + Type of return value has been changed from void to @new_value (@new_size). + + + Replacement of return type may indicate a change in its semantic meaning. + + + + + + Return_Type_From_Register_To_Stack + + + High + + + Symbols + + + Type of return value has been changed from @old_value (@old_size) to @new_value (@new_size). + + + 1) The return value became passed in memory as the hidden first argument (address of the space on the stack provided by the caller) instead of the register and therefore the layout of parameter's stack has been shifted by @word_size bytes. All the parameters will be incorrectly initialized by applications. + 2) Applications will read the wrong memory block instead of the return value. + + + + + + Return_Type_And_Register_Became_Hidden_Parameter + + + High + + + Symbols + + + Type of return value has been changed from @old_value (@old_size) to @new_value (@new_size). + + + 1) The return value became passed in different register as the hidden first argument (address of the space on the stack provided by the caller) and therefore distribution of parameters on the available registers and stack will be changed. All the parameters will be incorrectly initialized by applications. + 2) Applications will read the wrong memory block instead of the return value. + + + + + + Return_Type_From_Stack_To_Register + + + High + + + Symbols + + + Type of return value has been changed from @old_value (@old_size) to @new_value (@new_size). + + + 1) The return value became passed in register instead of the hidden first argument (address of the space on the stack provided by the caller) and therefore the layout of parameter's stack has been shifted by @word_size bytes. All the parameters will be incorrectly initialized by applications. + 2) Applications will read the wrong memory block instead of the return value. + + + + + + Return_Type_And_Register_Was_Hidden_Parameter + + + High + + + Symbols + + + Type of return value has been changed from @old_value (@old_size) to @new_value (@new_size). + + + 1) The return value became passed in register instead of the hidden first argument, that is passed in different register, and therefore distribution of parameters on the available registers and stack will be changed. All the parameters will be incorrectly initialized by applications. + 2) Applications will read the wrong memory block instead of the return value. + + + + + + Global_Data_Became_Non_Const + + + Medium + + + Symbols + + + This global data became non-const. + + + This data will be copied to applications at compile time. Any attempts to change this global data by library functions may result in crash of applications. + + + + + + Global_Data_Removed_Const + + + Low + + + Symbols + + + Removed **const** qualifier from the type of this global data. + + + This data will be treated as const by old client applications. Any attempts to change this global data by library functions may result in undefined behavior. + + + + + + Global_Data_Became_Const + + + Medium + + + Symbols + + + This global data became const. + + + Any attempts of old applications to change this global data may result in crash. + + + + + + Global_Data_Added_Const + + + Medium + + + Symbols + + + Added **const** qualifier to the type of this global data. + + + Any attempts of old applications to change this global data may result in crash. + + + + + + Return_BaseType_And_Size + + + Medium + + + Symbols + + + Base type of return value has been changed from @old_value (@old_size) to @new_value (@new_size). + + + Applications will obtain a different return value and execution may change. + + + + + + Return_BaseType + + + Low + + + Symbols + + + Base type of return value has been changed from @old_value to @new_value. + + + Replacement of return base type may indicate a change in its semantic meaning. + + + + + + Return_PointerLevel_Increased + + + Low + + + Symbols + + + The pointer level of return value has been increased from @old_value to @new_value. + + + Applications will treat the return value as the lower-dimension array and will not read all elements. This may change the behavior of applications. + + + + + + Return_PointerLevel_Decreased + + + Medium + + + Symbols + + + The pointer level of return value has been decreased from @old_value to @new_value. + + + Applications may try to access unallocated memory by the dereferencing of new return value and therefore cause a crash. + + + + + + Removed_Parameter + + + Medium + + + Parameters + + + @param_pos parameter @target has been removed from the calling stack. + + + This parameter will be ignored by the function. + + + + + + Removed_Unnamed_Parameter + + + Medium + + + Parameters + + + Parameter @target of type @param_type has been removed from the calling stack. + + + This parameter will be ignored by the function. + + + + + + Added_Parameter + + + Medium + + + Parameters + + + Parameter @target of type @param_type has been added to the calling stack. + + + This parameter will not be initialized by old clients. + + + + + + Added_Unnamed_Parameter + + + Medium + + + Parameters + + + @param_pos parameter @target has been added to the calling stack. + + + This parameter will not be initialized by old clients. + + + + + + Removed_Middle_Parameter + + + High + + + Parameters + + + @param_pos middle parameter @target has been removed from the calling stack. + + + Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications. + + + + + + Removed_Middle_Unnamed_Parameter + + + High + + + Parameters + + + Middle parameter @target of type @param_type has been removed from the calling stack. + + + Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications. + + + + + + Added_Middle_Parameter + + + High + + + Parameters + + + Parameter @target of type @param_type has been added to the calling stack at the middle position. + + + Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications. + + + + + + Added_Middle_Unnamed_Parameter + + + High + + + Parameters + + + @param_pos parameter @target has been added to the calling stack at the middle position. + + + Layout of parameter's stack has been changed and therefore parameters at higher positions in the stack may be incorrectly initialized by applications. + + + + + + Renamed_Parameter + + + Low + + + Parameters + + + @param_pos parameter @target has been renamed to @new_value. + + + Renaming of a parameter may indicate a change in its semantic meaning. + + + + + + Symbol_Became_Static + + + High + + + Symbols + + + Method became static. + + + The name of the appropriate symbol for this function on binary level has been changed from @old_value to @new_value. This may cause undefined reference linker error in old client applications. + + + + + + Symbol_Became_Non_Static + + + High + + + Symbols + + + Method became non-static. + + + The name of the appropriate symbol for this function on binary level has been changed from @old_value to @new_value. This may cause undefined reference linker error in old client applications. + + + + + + Symbol_Became_Virtual + + + High + + + Symbols + + + Method became virtual. + + + The name of the appropriate symbol for this function on binary level has been changed from @old_value to @new_value. This may cause undefined reference linker error in old client applications. + + + + + + Symbol_Became_Non_Virtual + + + High + + + Symbols + + + Method became non-virtual. + + + The name of the appropriate symbol for this function on binary level has been changed from @old_value to @new_value. This may cause undefined reference linker error in old client applications. + + + + + + Symbol_Changed_Return + + + High + + + Symbols + + + Type of return value has been changed from @old_type to @new_type. + + + The name of the appropriate symbol for this function on binary level has been changed from @old_value to @new_value. This may cause undefined reference linker error in old client applications. + + + + + + Global_Data_Symbol_Changed_Type + + + High + + + Symbols + + + Type of this global data has been changed from @old_type to @new_type. + + + The name of the appropriate symbol for this data on binary level has been changed from @old_value to @new_value. This may cause undefined reference linker error in old client applications. + + + + + + Symbol_Changed_Parameters + + + High + + + Symbols + + + Parameters list has been changed. + + + The name of the appropriate symbol for this function on binary level has been changed from @old_value to @new_value. This may cause undefined reference linker error in old client applications. + + + + + + Method_Became_Non_Const + + + High + + + Symbols + + + Method became non-const. + + + The name of the appropriate symbol for this function on binary level has been changed from @old_value to @new_value. This may cause undefined reference linker error in old client applications. + + + + + + Method_Became_Const + + + High + + + Symbols + + + Method became const. + + + The name of the appropriate symbol for this function on binary level has been changed from @old_value to @new_value. This may cause undefined reference linker error in old client applications. + + + + + + Method_Became_Volatile + + + High + + + Symbols + + + Method became volatile. + + + The name of the appropriate symbol for this function on binary level has been changed from @old_value to @new_value. This may cause undefined reference linker error in old client applications. + + + + + + Method_Became_Non_Volatile + + + High + + + Symbols + + + Method became non-volatile. + + + The name of the appropriate symbol for this function on binary level has been changed from @old_value to @new_value. This may cause undefined reference linker error in old client applications. + + + + + + Changed_Constant + + + Low + + + Constants + + + The value of constant @target has been changed from @old_value to @new_value. + + + Applications will pass an old value of this constant as the parameter to the new-version library functions, that expect a new one. This may result in crash of incorrect behavior of applications. + + + + + + Added_Constant + + + Safe + + + Constants + + + The constant @target with value @new_value has been added. + + + No effect. + + + + + + Removed_Constant + + + Low + + + Constants + + + The constant @target with value @old_value has been removed. + + + The value of this constant may no longer be properly handled by new-version library functions. + + + + + + Field_Became_Volatile + + + Low + + + Fields + + + Field @target became volatile. + + + The value of this field can begin to change in ways outside the control of old client applications. + + + + + + Field_Became_Non_Volatile + + + Safe + + + Fields + + + Field @target became non-volatile. + + + No effect. + + + + + + Return_Value_Became_Volatile + + + Low + + + Symbols + + + Return value became volatile. + + + Old client applications will get volatile object instead of non-volatile, but may be optimized by the compiler and cannot handle volatile objects. + + + + + + Parameter_Became_Non_Volatile + + + Low + + + Symbols + + + Parameter @target became non-volatile. + + + Old client applications will pass volatile object to the function that may be optimized by the compiler and cannot handle volatile objects. + + + + + + Field_Type_Format + + + Medium + + + Fields + + + Type of field @target has been changed from @old_value to @new_value of different format. + + + This field may be incorrectly initialized or accessed by applications. + + + + + + Field_BaseType_Format + + + Medium + + + Fields + + + Base type of field @target has been changed from @old_value to @new_value of different format. + + + This field may be incorrectly initialized or accessed by applications. + + + + + + Parameter_Type_Format + + + Medium + + + Parameters + + + Type of parameter @target has been changed from @old_value to @new_value of different format. + + + This parameter may be incorrectly initialized by applications. + + + + + + Parameter_BaseType_Format + + + Medium + + + Parameters + + + Base type of parameter @target has been changed from @old_value to @new_value of different format. + + + This parameter may be incorrectly initialized by applications. + + + + + + Return_Type_Format + + + Medium + + + Symbols + + + Type of return value has been changed from @old_value to @new_value of different format. + + + Applications will obtain a different return value and execution may change. + + + + + + Return_Type_And_Register + + + Medium + + + Symbols + + + Type of return value has been changed from @old_value to @new_value. + + + The return value became passed in different register. Applications will read the wrong memory block instead of the return value. Also, distribution of parameters on the available registers and stack may be changed. + + + + + + Return_BaseType_Format + + + Medium + + + Symbols + + + Base type of return value has been changed from @old_value to @new_value of different format. + + + This parameter may be incorrectly initialized by applications. + + + + + + Parameter_Became_Non_VaList + + + Low + + + Parameters + + + Type of @param_pos parameter has been changed from ... (va_list) to @new_value. + + + This parameter may not be initialized by old clients. + + + + + + Parameter_Became_VaList + + + Low + + + Parameters + + + Type of @param_pos parameter @target has been changed from @old_value to ... (va_list). + + + The semantic meaning of the parameter may change. + + + + + + Added_Enum_Member + + + Safe + + + Constants + + + The member @target with value @new_value has been added. + + + No effect. + + + + + + Global_Data_Value_Changed + + + Low + + + Symbols + + + The initial value of this global data has been changed from @old_value to @new_value. + + + Applications will use an old value of this data instead of the new one. This may cause incorrect behavior of applications. + + + + + + Field_Became_Mutable + + + Low + + + Fields + + + Field @target became **mutable**. + + + The value of this field can begin to change in ways outside the control of old client applications. + + + + + + Field_Became_Non_Mutable + + + Low + + + Fields + + + Field @target became **non-mutable**. + + + The value of this field can still be changed by const methods of old client applications, but it's not expected by new-version library. + + + + + + Method_Became_Private + + + Low + + + Symbols + + + This method became **private**. + + + Old applications will continue using this method, but it may require a different initialization of the environment and parameters. + + + + + + Method_Became_Protected + + + Low + + + Symbols + + + This method became **protected**. + + + Old applications will continue using this method, but it may require a different initialization of the environment and parameters. + + + + + + Method_Became_Public + + + Safe + + + Symbols + + + This method became **public**. + + + No effect. + + + + + + Global_Data_Became_Private + + + Low + + + Symbols + + + This global data became **private**. + + + Old applications will continue using this global data, but it may require a different initialization of the environment. + + + + + + Global_Data_Became_Protected + + + Low + + + Symbols + + + This global data became **protected**. + + + Old applications will continue using this global data, but it may require a different initialization of the environment. + + + + + + Global_Data_Became_Public + + + Safe + + + Symbols + + + This global data became **public**. + + + No effect. + + + + + + Field_Became_Const + + + Low + + + Types + + + Field @target became **const**. + + + The value of this field is expected to be **const** in new library version, but can be modified by old applications. + + + + + + Field_Became_Non_Const + + + Safe + + + Types + + + Field @target became **non-const**. + + + No effect. + + + + + + Field_Added_Const + + + Low + + + Types + + + Added **const** qualifier to field @target. + + + The value of this field is expected to be **const** in new library version, but can be modified by old applications. + + + + + + Field_Removed_Const + + + Safe + + + Types + + + Removed **const** qualifier from field @target. + + + No effect. + + + + + + Field_Became_Private + + + Low + + + Types + + + Field @target became **private**. + + + Old applications will continue using this field, but it may require a different initialization of class object. + + + + + + Field_Became_Protected + + + Low + + + Types + + + Field @target became **protected**. + + + Old applications will continue using this field, but it may require a different initialization of class object. + + + + + + Virtual_Method_Became_Pure + + + Medium + + + Types + + + Virtual method @target became **pure**. + + + Old applications will not provide implementation for this pure virtual method. This may result in crash or incorrect behavior of applications. + + + + + + Virtual_Method_Became_Non_Pure + + + Safe + + + Types + + + Virtual method @target became **non-pure**. + + + No effect. + + + + + + Type_Became_Opaque + + + Medium + + + Types + + + This type became **opaque**. + + + The internal structure of this type is hidden in the new library version and may be different. This may result in crash or incorrect behavior of applications. + + + + diff --git a/abi-compliance-checker-2.4/modules/RulesSrc.xml b/abi-compliance-checker-2.4/modules/RulesSrc.xml new file mode 100644 index 0000000..3518e46 --- /dev/null +++ b/abi-compliance-checker-2.4/modules/RulesSrc.xml @@ -0,0 +1,1792 @@ + + + + + + Renamed_Field + + + High + + + Fields + + + Field @target has been renamed to @new_value. + + + Recompilation of a client program may be broken with the error message: @type_name has no member named @target. + + + + + + Used_Reserved_Field + + + Safe + + + Fields + + + Reserved field @target has been replaced by @new_value. + + + No effect. + + + + + + Field_Type + + + Low + + + Fields + + + Type of field @target has been changed from @old_value to @new_value. + + + Recompilation of a client program may be broken. + + + + + + Added_Pure_Virtual_Method + + + High + + + V-table + + + Pure virtual method @target has been added to this class. + + + Recompilation of a client program may be broken with the error message: cannot allocate an object of abstract type @type_name because the following virtual functions are pure within @type_name: virtual @target. + + + + + + Removed_Pure_Virtual_Method + + + High + + + V-table + + + Pure virtual method @target has been removed from this class. + + + Recompilation of a client program may be broken. + + + + + + Overridden_Virtual_Method + + + Low + + + V-table + + + Virtual method @old_value has been overridden by @new_value. + + + Method @new_value will be called after recompilation instead of @old_value. + + + + + + Overridden_Virtual_Method_B + + + Low + + + V-table + + + Virtual method @old_value has been overridden by @new_value. + + + Method @new_value will be called after recompilation instead of @old_value. + + + + + + Added_Base_Class + + + Low + + + Classes + + + Base class @target has been added. + + + Recompilation of a client program may be broken. + + + + + + Removed_Base_Class + + + Low + + + Classes + + + Base class @target has been removed. + + + Recompilation of a client program may be broken. + + + + + + DataType_Type + + + Medium + + + Types + + + Type of this type has been changed from @old_value to @new_value. + + + Recompilation of a client program may be broken. + + + + + + Moved_Field + + + Safe + + + Fields + + + The relative position of field @target has been changed from @old_value to @new_value. + + + No effect. + + + + + + Added_Field + + + Low + + + Fields + + + Field @target has been added to this type. + + + This field will not be initialized or used by old client applications. + + + + + + Added_Union_Field + + + Safe + + + Fields + + + Field @target has been added to this type. + + + No effect. + + + + + + Removed_Field + + + High + + + Fields + + + Field @target has been removed from this type. + + + Recompilation of a client program may be broken with the error message: '@type_name' has no member named '@target'. + + + + + + Removed_Union_Field + + + High + + + Fields + + + Field @target has been removed from this union. + + + Recompilation of a client program may be broken. + + + + + + Enum_Member_Value + + + Safe + + + Constants + + + Value of member @target has been changed from @old_value to @new_value. + + + No effect. + + + + + + Enum_Last_Member_Value + + + Safe + + + Constants + + + Value of member @target has been changed from @old_value to @new_value. + + + No effect. + + + + + + Enum_Private_Member_Value + + + Safe + + + Constants + + + Value of private member @target has been changed from @old_value to @new_value. + + + No effect. + + + + + + Enum_Member_Removed + + + High + + + Constants + + + The member @target has been removed. + + + Recompilation of a client program may be broken. + + + + + + Enum_Member_Name + + + High + + + Constants + + + Name of member with value @target has been changed from @old_value to @new_value. + + + Recompilation of a client program may be broken with the error message: '@old_value' was not declared in this scope. + + + + + + Field_BaseType + + + Low + + + Fields + + + Base type of field @target has been changed from @old_value to @new_value. + + + Recompilation of a client program may be broken. + + + + + + Field_PointerLevel + + + Medium + + + Fields + + + The pointer level of field @target has been increased from @old_value to @new_value. + + + Recompilation of a client program may be broken. + + + + + + Typedef_BaseType + + + Low + + + Types + + + Base type has been changed from @old_value to @new_value. + + + Recompilation of a client program may be broken. + + + + + + Typedef_BaseType_Format + + + Low + + + Types + + + Base type has been changed from @old_value to @new_value of different format. + + + Recompilation of a client program may be broken. + + + + + + Added_Symbol + + + Safe + + + Symbols + + + + + + Removed_Symbol + + + High + + + Symbols + + + + + + Method_Became_Static + + + Safe + + + Symbols + + + Method became static. + + + No effect. + + + + + + Method_Became_Non_Static + + + High + + + Symbols + + + Method became non-static. + + + Recompilation of a client program may be broken with the error message: cannot call member function @target without object. + + + + + + Parameter_Default_Value_Changed + + + Safe + + + Parameters + + + The default argument of @param_pos parameter @target has been changed from @old_value to @new_value. + + + No effect. + + + + + + Parameter_Default_Value_Removed + + + Medium + + + Parameters + + + The default argument @old_value of @param_pos parameter @target has been removed. + + + Recompilation of a client program may be broken. + + + + + + Parameter_Default_Value_Added + + + Safe + + + Parameters + + + The default argument @new_value of @param_pos parameter @target has been added. + + + No effect. + + + + + + Parameter_Type + + + Low + + + Parameters + + + Type of @param_pos parameter @target has been changed from @old_value to @new_value. + + + Recompilation of a client program may be broken. + + + + + + Parameter_Type_Format + + + Medium + + + Parameters + + + Type of parameter @target has been changed from @old_value to @new_value of different format. + + + Recompilation of a client program may be broken. + + + + + + Parameter_BaseType_Format + + + Medium + + + Parameters + + + Base type of parameter @target has been changed from @old_value to @new_value of different format. + + + Recompilation of a client program may be broken. + + + + + + Parameter_BaseType + + + Low + + + Parameters + + + Base type of @param_pos parameter @target has been changed from @old_value to @new_value. + + + Recompilation of a client program may be broken. + + + + + + Parameter_Became_Non_Const + + + Medium + + + Parameters + + + Type of @param_pos parameter @target has been changed from @old_value to @new_value (became non-const). + + + Recompilation of a client program may be broken. + + + + + + Parameter_Removed_Const + + + Medium + + + Parameters + + + Type of @param_pos parameter @target has been changed from @old_value to @new_value (removed const qualifier). + + + Recompilation of a client program may be broken. + + + + + + Return_Type_Became_Const + + + Medium + + + Symbols + + + Type of return value became const (has been changed from @old_value to @new_value). + + + Recompilation of a client program may be broken. + + + + + + Return_Type_Added_Const + + + Medium + + + Symbols + + + Added **const** qualifier to return value (has been changed from @old_value to @new_value). + + + Recompilation of a client program may be broken. + + + + + + Parameter_PointerLevel + + + Medium + + + Parameters + + + The pointer level of @param_pos parameter @target has been increased from @old_value to @new_value. + + + Recompilation of a client program may be broken. + + + + + + Return_Type + + + Low + + + Symbols + + + Type of return value has been changed from @old_value to @new_value. + + + Recompilation of a client program may be broken. + + + + + + Return_Type_From_Void + + + Safe + + + Symbols + + + Type of return value has been changed from void to @new_value. + + + No effect. + + + + + + Global_Data_Type + + + Low + + + Symbols + + + Type of this global data has been changed from @old_value to @new_value. + + + Recompilation of a client program may be broken. + + + + + + Global_Data_Type_Format + + + Medium + + + Symbols + + + Type of this global data has been changed from @old_value to @new_value of different format. + + + Recompilation of a client program may be broken. + + + + + + Return_Type_Became_Void + + + Medium + + + Symbols + + + Type of return value has been changed from @old_value (@old_size) to void. + + + Recompilation of a client program may be broken. + + + + + + Global_Data_Became_Non_Const + + + Safe + + + Symbols + + + This global data became non-const. + + + No effect. + + + + + + Global_Data_Removed_Const + + + Safe + + + Symbols + + + Removed **const** qualifier from the type of this global data. + + + No effect. + + + + + + Global_Data_Became_Const + + + Medium + + + Symbols + + + This global data became const. + + + Recompilation of a client program may be broken. + + + + + + Global_Data_Added_Const + + + Medium + + + Symbols + + + Added **const** qualifier to the type of this global data. + + + Recompilation of a client program may be broken. + + + + + + Return_BaseType + + + Low + + + Symbols + + + Base type of return value has been changed from @old_value to @new_value. + + + Recompilation of a client program may be broken. + + + + + + Return_PointerLevel + + + Medium + + + Symbols + + + The pointer level of return value has been increased from @old_value to @new_value. + + + Recompilation of a client program may be broken. + + + + + + Removed_Parameter + + + Medium + + + Parameters + + + @param_pos parameter @target has been removed from the calling stack. + + + Recompilation of a client program may be broken. + + + + + + Removed_Unnamed_Parameter + + + Medium + + + Parameters + + + Parameter @target of type @param_type has been removed from the calling stack. + + + Recompilation of a client program may be broken. + + + + + + Added_Parameter + + + Medium + + + Parameters + + + Parameter @target of type @param_type has been added to the calling stack. + + + Recompilation of a client program may be broken. + + + + + + Added_Unnamed_Parameter + + + Medium + + + Parameters + + + @param_pos parameter @target has been added to the calling stack. + + + Recompilation of a client program may be broken. + + + + + + Removed_Middle_Parameter + + + High + + + Parameters + + + @param_pos middle parameter @target has been removed from the calling stack. + + + Recompilation of a client program may be broken. + + + + + + Removed_Middle_Unnamed_Parameter + + + High + + + Parameters + + + Middle parameter @target of type @param_type has been removed from the calling stack. + + + Recompilation of a client program may be broken. + + + + + + Added_Middle_Parameter + + + High + + + Parameters + + + Parameter @target of type @param_type has been added to the calling stack at the middle position. + + + Recompilation of a client program may be broken. + + + + + + Added_Middle_Unnamed_Parameter + + + High + + + Parameters + + + @param_pos parameter @target has been added to the calling stack at the middle position. + + + Recompilation of a client program may be broken. + + + + + + Renamed_Parameter + + + Safe + + + Parameters + + + @param_pos parameter @target has been renamed to @new_value. + + + No effect. + + + + + + Changed_Constant + + + Low + + + Constants + + + The value of constant @target has been changed from @old_value to @new_value. + + + Recompilation of a client program may be broken. + + + + + + Added_Constant + + + Safe + + + Constants + + + The constant @target with value @new_value has been added. + + + No effect. + + + + + + Removed_Constant + + + Low + + + Constants + + + The constant @target with value @old_value has been removed. + + + Recompilation of a client program may be broken. + + + + + + Field_Type_Format + + + Medium + + + Fields + + + Type of field @target has been changed from @old_value to @new_value of different format. + + + Recompilation of a client program may be broken. + + + + + + Field_BaseType_Format + + + Medium + + + Fields + + + Base type of field @target has been changed from @old_value to @new_value of different format. + + + Recompilation of a client program may be broken. + + + + + + Return_Type_Format + + + Medium + + + Symbols + + + Type of return value has been changed from @old_value to @new_value of different format. + + + Recompilation of a client program may be broken. + + + + + + Return_BaseType_Format + + + Medium + + + Symbols + + + Base type of return value has been changed from @old_value to @new_value of different format. + + + Recompilation of a client program may be broken. + + + + + + Parameter_Became_Non_VaList + + + Medium + + + Parameters + + + Type of @param_pos parameter has been changed from ... (va_list) to @new_value. + + + Recompilation of a client program may be broken. + + + + + + Parameter_Became_VaList + + + Safe + + + Parameters + + + Type of @param_pos parameter @target has been changed from @old_value to ... (va_list). + + + No effect. + + + + + + Added_Enum_Member + + + Safe + + + Constants + + + The member @target with value @new_value has been added. + + + No effect. + + + + + + Symbol_Changed_Parameters + + + Medium + + + Symbols + + + Parameters list has been changed. + + + Recompilation of a client program may be broken. + + + + + + Method_Became_Non_Const + + + Medium + + + Symbols + + + Method became non-const. + + + Recompilation of a client program may be broken with the error message: passing 'const @type_name' as 'this' argument of '@target' discards qualifiers. + + + + + + Method_Became_Const + + + Safe + + + Symbols + + + Method became const. + + + No effect. + + + + + + Method_Became_Volatile + + + Safe + + + Symbols + + + Method became volatile. + + + No effect. + + + + + + Method_Became_Non_Volatile + + + Safe + + + Symbols + + + Method became non-volatile. + + + No effect. + + + + + + Parameter_Became_Restrict + + + Safe + + + Parameters + + + Parameter @target became restrict. + + + No effect. + + + + + + Parameter_Became_Non_Restrict + + + Safe + + + Parameters + + + Parameter @target became non-restrict. + + + No effect. + + + + + + Field_Became_Volatile + + + Safe + + + Fields + + + Field @target became volatile. + + + No effect. + + + + + + Field_Became_Non_Volatile + + + Safe + + + Fields + + + Field @target became non-volatile. + + + No effect. + + + + + + Return_Value_Became_Volatile + + + Safe + + + Symbols + + + Return value became volatile. + + + No effect. + + + + + + Parameter_Became_Non_Volatile + + + Safe + + + Symbols + + + Parameter @target became non-volatile. + + + No effect. + + + + + + Global_Data_Value_Changed + + + Safe + + + Symbols + + + The initial value of this global data has been changed from @old_value to @new_value. + + + No effect. + + + + + + Field_Became_Mutable + + + Safe + + + Fields + + + Field @target became **mutable**. + + + No effect. + + + + + + Field_Became_Non_Mutable + + + Medium + + + Fields + + + Field @target became **non-mutable**. + + + Recompilation of a client program may be broken with the error message: assignment of member '@type_name::@target' in read-only object. + + + + + + Removed_Const_Overload + + + Medium + + + Types + + + The **const** overload of the method @target has been removed from this class. + + + Recompilation of a client program may be broken with the error message: passing 'const @type_name' as 'this' argument of '@target' discards qualifiers. + + + + + + Method_Became_Private + + + High + + + Symbols + + + This method became **private**. + + + Recompilation of a client program may be broken with the error message: '@target' is private. + + + + + + Method_Became_Protected + + + Medium + + + Symbols + + + This method became **protected**. + + + Recompilation of a client program may be broken with the error message: '@target' is protected. + + + + + + Method_Became_Public + + + Safe + + + Symbols + + + This method became **public**. + + + No effect. + + + + + + Global_Data_Became_Private + + + High + + + Symbols + + + This global data became **private**. + + + Recompilation of a client program may be broken with the error message: '@target' is private. + + + + + + Global_Data_Became_Protected + + + Medium + + + Symbols + + + This global data became **protected**. + + + Recompilation of a client program may be broken with the error message: '@target' is protected. + + + + + + Global_Data_Became_Public + + + Safe + + + Symbols + + + This global data became **public**. + + + No effect. + + + + + + Field_Became_Const + + + Medium + + + Types + + + Field @target became **const**. + + + Recompilation of a client program may be broken with the error message: assignment of read-only member/location '@type_name::@target'. + + + + + + Field_Became_Non_Const + + + Safe + + + Types + + + Field @target became **non-const**. + + + No effect. + + + + + + Field_Added_Const + + + Low + + + Types + + + Added **const** qualifier to field @target. + + + Recompilation of a client program may be broken with the error message: assignment of read-only member/location '@type_name::@target'. + + + + + + Field_Removed_Const + + + Safe + + + Types + + + Removed **const** qualifier from field @target. + + + No effect. + + + + + + Field_Became_Private + + + Medium + + + Types + + + Field @target became **private**. + + + Recompilation of a client program may be broken with the error message: '@type_name::@target' is private. + + + + + + Field_Became_Protected + + + Medium + + + Types + + + Field @target became **protected**. + + + Recompilation of a client program may be broken with the error message: '@type_name::@target' is protected. + + + + + + Virtual_Method_Became_Pure + + + Medium + + + Types + + + Virtual method @target became **pure**. + + + Recompilation of a client program may be broken with the error message: cannot allocate an object of abstract type '@type_name' because the following virtual functions are pure within '@type_name': virtual @target. + + + + + + Virtual_Method_Became_Non_Pure + + + Safe + + + Types + + + Virtual method @target became **non-pure**. + + + No effect. + + + + + + Type_Became_Opaque + + + Medium + + + Types + + + This type became **opaque**. + + + The internal structure of this type is hidden in the new library version and recompilation of a client program may be broken. + + + + diff --git a/abi-dumper-2.1/.github/FUNDING.yml b/abi-dumper-2.1/.github/FUNDING.yml new file mode 100644 index 0000000..5773b1d --- /dev/null +++ b/abi-dumper-2.1/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +patreon: LINUXABI +custom: https://abi-laboratory.pro/?view=donate diff --git a/abi-dumper-2.1/Dockerfile b/abi-dumper-2.1/Dockerfile new file mode 100644 index 0000000..6c349a3 --- /dev/null +++ b/abi-dumper-2.1/Dockerfile @@ -0,0 +1,11 @@ +FROM ubuntu +RUN apt update && \ + apt install -y git perl-base binutils automake autoconf \ + libtool pkg-config elfutils libelf-dev && \ + git clone https://github.com/universal-ctags/ctags && cd ctags && \ + ./autogen.sh && ./configure && make install && cd .. && \ + git clone https://github.com/lvc/vtable-dumper && cd vtable-dumper && \ + make install && cd .. && \ + git clone https://github.com/lvc/abi-dumper && cd abi-dumper && \ + make install && cd .. && \ + rm -rf ctags vtable-dumper abi-dumper diff --git a/abi-dumper-2.1/INSTALL b/abi-dumper-2.1/INSTALL new file mode 100644 index 0000000..6a0bf08 --- /dev/null +++ b/abi-dumper-2.1/INSTALL @@ -0,0 +1,64 @@ + +Copyright (C) 2013-2017 Andrey Ponomarenko's ABI Laboratory +All rights reserved. + + +RELEASE INFORMATION + +Project: ABI Dumper +Version: 1.1 +Date: August 30, 2017 + + +This file explains how to install and setup environment +for the tool in your computer. + + +Content: + + 1. Requirements for Linux and FreeBSD + 2. Configure and Install + 3. Usage + + +1. REQUIREMENTS FOR LINUX AND FREEBSD +===================================== + + 1. Perl 5 (5.8 or newer) + 2. Elfutils (eu-readelf) + 3. Vtable-Dumper (1.1 or newer) + 4. Binutils (objdump) + 5. Universal Ctags + + + +2. CONFIGURE AND INSTALL +======================== + + This command will install an abi-dumper program in the + PREFIX/bin system directory: + + sudo make install prefix=PREFIX [/usr, /usr/local, ...] + +2.1 Remove + + sudo make uninstall prefix=PREFIX + + + +3. USAGE +======== + + Dump ABI of a library: + + abi-dumper libTest.so -o ABI.dump + + Dump ABI of a kernel module: + + abi-dumper Module.ko.debug -o ABI.dump + + For advanced usage, see output of --help option + + + +Enjoy! diff --git a/abi-dumper-2.1/LICENSE b/abi-dumper-2.1/LICENSE new file mode 100644 index 0000000..8000a6f --- /dev/null +++ b/abi-dumper-2.1/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/abi-dumper-2.1/Makefile b/abi-dumper-2.1/Makefile new file mode 100644 index 0000000..55c2793 --- /dev/null +++ b/abi-dumper-2.1/Makefile @@ -0,0 +1,8 @@ +prefix ?= /usr + +.PHONY: install +install: + perl Makefile.pl -install -prefix "$(prefix)" + +uninstall: + perl Makefile.pl -remove -prefix "$(prefix)" diff --git a/abi-dumper-2.1/Makefile.pl b/abi-dumper-2.1/Makefile.pl new file mode 100644 index 0000000..806e8ca --- /dev/null +++ b/abi-dumper-2.1/Makefile.pl @@ -0,0 +1,262 @@ +#!/usr/bin/perl +########################################################################### +# Makefile for ABI Dumper +# Install/remove the tool for GNU/Linux +# +# Copyright (C) 2013-2015 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License or the GNU Lesser +# General Public License as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# and the GNU Lesser General Public License along with this program. +# If not, see . +########################################################################### +use Getopt::Long; +Getopt::Long::Configure ("posix_default", "no_ignore_case"); +use File::Path qw(mkpath rmtree); +use File::Copy qw(copy); +use File::Basename qw(dirname); +use Cwd qw(abs_path); +use File::Find; +use strict; + +my $TOOL_SNAME = "abi-dumper"; +my $ARCHIVE_DIR = abs_path(dirname($0)); + +my $HELP_MSG = " +NAME: + Makefile for ABI Dumper + +DESCRIPTION: + Install $TOOL_SNAME command and private modules. + +USAGE: + sudo perl $0 -install -prefix /usr + sudo perl $0 -remove -prefix /usr + +OPTIONS: + -h|-help + Print this help. + + --prefix=PREFIX + Install files in PREFIX [/usr]. + + -install + Command to install the tool. + + -remove + Command to remove the tool. + +EXTRA OPTIONS: + --destdir=DESTDIR + This option is for maintainers to build + RPM or DEB packages inside the build root. + The environment variable DESTDIR is also + supported. +\n"; + +if(not @ARGV) +{ + print $HELP_MSG; + exit(0); +} + +my ($PREFIX, $DESTDIR, $Help, $Install, $Remove); + +GetOptions( + "h|help!" => \$Help, + "prefix=s" => \$PREFIX, + "destdir=s" => \$DESTDIR, + "install!" => \$Install, + "remove!" => \$Remove +) or exit(1); + +sub scenario() +{ + if($Help) + { + print $HELP_MSG; + exit(0); + } + if(not $Install and not $Remove) + { + print STDERR "ERROR: command is not selected (-install or -remove)\n"; + exit(1); + } + + if($Install) + { # remove old version first + $Remove = 1; + } + + if($PREFIX ne "/") { + $PREFIX=~s/[\/]+\Z//g; + } + if(not $PREFIX) + { # default prefix + $PREFIX = "/usr"; + } + if(my $Var = $ENV{"DESTDIR"}) + { + print "Using DESTDIR environment variable\n"; + $DESTDIR = $Var; + } + if($DESTDIR) + { + if($DESTDIR ne "/") { + $DESTDIR=~s/[\/]+\Z//g; + } + if($DESTDIR!~/\A\//) + { + print STDERR "ERROR: destdir is not absolute path\n"; + exit(1); + } + if(not -d $DESTDIR) + { + print STDERR "ERROR: you should create destdir directory first\n"; + exit(1); + } + $PREFIX = $DESTDIR.$PREFIX; + if(not -d $PREFIX) + { + print STDERR "ERROR: you should create installation directory first (destdir + prefix):\n mkdir -p $PREFIX\n"; + exit(1); + } + } + else + { + if($PREFIX!~/\A\//) + { + print STDERR "ERROR: prefix is not absolute path\n"; + exit(1); + } + if(not -d $PREFIX) + { + print STDERR "ERROR: you should create prefix directory first\n"; + exit(1); + } + } + + print "INSTALL PREFIX: $PREFIX\n"; + + # paths + my $EXE_PATH = "$PREFIX/bin"; + my $MODULES_PATH = "$PREFIX/share/$TOOL_SNAME"; + my $REL_PATH = "../share/$TOOL_SNAME"; + my $TOOL_PATH = "$EXE_PATH/$TOOL_SNAME"; + + if(not -w $PREFIX) + { + print STDERR "ERROR: you should be root\n"; + exit(1); + } + if($Remove) + { + if(-e $EXE_PATH."/".$TOOL_SNAME) + { # remove executable + print "-- Removing $TOOL_PATH\n"; + unlink($EXE_PATH."/".$TOOL_SNAME); + } + elsif(not $Install) { + print "The tool is not installed\n"; + } + + if(-d $ARCHIVE_DIR."/modules") + { + if(-d $MODULES_PATH) + { # remove modules + print "-- Removing $MODULES_PATH\n"; + rmtree($MODULES_PATH); + } + elsif(not $Install) { + print "The modules of the tool are not installed\n"; + } + } + } + if($Install) + { + # configure + my $Content = readFile($ARCHIVE_DIR."/".$TOOL_SNAME.".pl"); + if($DESTDIR) { # relative path + $Content=~s/MODULES_INSTALL_PATH/$REL_PATH/; + } + else { # absolute path + $Content=~s/MODULES_INSTALL_PATH/$MODULES_PATH/; + } + + # copy executable + print "-- Installing $TOOL_PATH\n"; + mkpath($EXE_PATH); + writeFile($EXE_PATH."/".$TOOL_SNAME, $Content); + chmod(0755, $EXE_PATH."/".$TOOL_SNAME); + + # copy modules + if(-d $ARCHIVE_DIR."/modules") + { + print "-- Installing $MODULES_PATH\n"; + mkpath($MODULES_PATH); + copyDir($ARCHIVE_DIR."/modules", $MODULES_PATH); + } + + # check PATH + if($ENV{"PATH"}!~/(\A|:)\Q$EXE_PATH\E[\/]?(\Z|:)/) { + print "WARNING: your PATH variable doesn't include \'$EXE_PATH\'\n"; + } + } + exit(0); +} + +sub copyDir($$) +{ + my ($From, $To) = @_; + my %Files; + find(\&wanted, $From); + sub wanted { + $Files{$File::Find::dir."/$_"} = 1 if($_ ne "."); + } + foreach my $Path (sort keys(%Files)) + { + my $Inst = $Path; + $Inst=~s/\A\Q$ARCHIVE_DIR\E/$To/; + if(-d $Path) + { # directories + mkpath($Inst); + } + else + { # files + mkpath(dirname($Inst)); + copy($Path, $Inst); + } + } +} + +sub readFile($) +{ + my $Path = $_[0]; + return "" if(not $Path or not -f $Path); + open(FILE, $Path) || die ("can't open file \'$Path\': $!\n"); + local $/ = undef; + my $Content = ; + close(FILE); + return $Content; +} + +sub writeFile($$) +{ + my ($Path, $Content) = @_; + return if(not $Path); + open(FILE, ">".$Path) || die ("can't open file \'$Path\': $!\n"); + print FILE $Content; + close(FILE); +} + +scenario(); diff --git a/abi-dumper-2.1/README.md b/abi-dumper-2.1/README.md new file mode 100644 index 0000000..20b1485 --- /dev/null +++ b/abi-dumper-2.1/README.md @@ -0,0 +1,80 @@ +ABI Dumper 1.2 +============== + +ABI Dumper — a tool to dump ABI of an ELF object containing DWARF debug info. + +Contents +-------- + +1. [ About ](#about) +2. [ Install ](#install) +3. [ Usage ](#usage) +4. [ Filter public ABI ](#filter-public-abi) +5. [ Check for ABI changes ](#check-for-abi-changes) + +About +----- + +The tool is intended to be used with ABI Compliance Checker tool for tracking +ABI changes of a C/C++ library or kernel module: https://github.com/lvc/abi-compliance-checker + +The tool is developed by Andrey Ponomarenko. + +Install +------- + + sudo make install prefix=/usr + +###### Requires + +* Perl 5 +* Elfutils (eu-readelf) +* GNU Binutils +* Universal Ctags (https://github.com/universal-ctags/ctags) +* Vtable Dumper >= 1.1 (https://github.com/lvc/vtable-dumper) +* ABI Compliance Checker >= 2.2 (https://github.com/lvc/abi-compliance-checker) +* GCC C++ + +Usage +----- + +Input objects should be compiled with `-g -Og` additional options to contain DWARF debug info. + + abi-dumper libTest.so -o ABI.dump + abi-dumper Module.ko.debug + +###### Examples + + abi-dumper lib/libssh.so.3 + abi-dumper drm/nouveau/nouveau.ko.debug + +###### Docker + +You can try Docker image if the tool is not packaged for your Linux distribution (example for Harfbuzz): + + FROM ebraminio/abi-dumper + RUN apt update && \ + apt install -y ragel cpanminus && \ + git clone https://github.com/harfbuzz/harfbuzz && cd harfbuzz && \ + CFLAGS="-Og -g" CXXFLAGS="-Og -g" ./autogen.sh && make && cd .. && \ + abi-dumper `find . -name 'libharfbuzz.so.0.*'` && \ + cpanm JSON && \ + perl -le 'use JSON; print to_json(do shift, {canonical => 1, pretty => 1});' ./ABI.dump > ABI.json + +###### Adv. usage + + For advanced usage, see output of `--help` option. + +Filter public ABI +----------------- + + abi-dumper libTest.so -public-headers PATH + +PATH — path to the install tree of a library. + +Check for ABI changes +--------------------- + + abi-dumper libTest.so.0 -o ABIv0.dump + abi-dumper libTest.so.1 -o ABIv1.dump + abi-compliance-checker -l libTest -old ABIv0.dump -new ABIv1.dump diff --git a/abi-dumper-2.1/abi-dumper.pl b/abi-dumper-2.1/abi-dumper.pl new file mode 100644 index 0000000..9c07b70 --- /dev/null +++ b/abi-dumper-2.1/abi-dumper.pl @@ -0,0 +1,6802 @@ +#!/usr/bin/perl +########################################################################### +# ABI Dumper 1.2 +# Dump ABI of an ELF object containing DWARF debug info +# +# Copyright (C) 2013-2020 Andrey Ponomarenko's ABI Laboratory +# +# Written by Andrey Ponomarenko +# +# PLATFORMS +# ========= +# Linux +# +# REQUIREMENTS +# ============ +# Perl 5 (5.8 or newer) +# Elfutils (eu-readelf) +# GNU Binutils (objdump) +# Vtable-Dumper (1.1 or newer) +# Universal Ctags +# GCC C++ +# +# COMPATIBILITY +# ============= +# ABI Compliance Checker >= 2.2 +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +########################################################################### +use Getopt::Long; +Getopt::Long::Configure ("posix_default", "no_ignore_case", "permute"); +use File::Path qw(mkpath rmtree); +use File::Temp qw(tempdir); +use Cwd qw(abs_path cwd realpath); +use Storable qw(dclone); +use Data::Dumper; + +my $TOOL_VERSION = "1.2"; +my $ABI_DUMP_VERSION = "3.5"; +my $ORIG_DIR = cwd(); +my $TMP_DIR = tempdir(CLEANUP=>1); + +my $VTABLE_DUMPER = "vtable-dumper"; +my $VTABLE_DUMPER_VERSION = "1.0"; + +my $LOCALE = "LANG=C.UTF-8"; +my $EU_READELF = "eu-readelf"; +my $EU_READELF_L = $LOCALE." ".$EU_READELF; +my $OBJDUMP = "objdump"; +my $CTAGS = "ctags"; +my $EXUBERANT_CTAGS = 0; +my $GPP = "g++"; + +my ($Help, $ShowVersion, $DumpVersion, $OutputDump, $SortDump, $StdOut, +$TargetVersion, $ExtraInfo, $FullDump, $AllTypes, $AllSymbols, $BinOnly, +$SkipCxx, $Loud, $AddrToName, $DumpStatic, $Compare, $AltDebugInfoOpt, +$AddDirs, $VTDumperPath, $SymbolsListPath, $PublicHeadersPath, +$IgnoreTagsPath, @CtagsDef, $KernelExport, $UseTU, $ReimplementStd, +$IncludePreamble, $IncludePaths, $CacheHeaders, $MixedHeaders, $Debug, +$SearchDirDebuginfo, $KeepRegsAndOffsets, $Quiet, $IncludeDefines, +$AllUnits, $LambdaSupport, $LdLibraryPath); + +my $CmdName = getFilename($0); + +my %ERROR_CODE = ( + "Success"=>0, + "Error"=>2, + # System command is not found + "Not_Found"=>3, + # Cannot access input files + "Access_Error"=>4, + # Cannot find a module + "Module_Error"=>9, + # No debug-info + "No_DWARF"=>10, + # Invalid debug-info + "Invalid_DWARF"=>11, + # No exported symbols + "No_Exported"=>12 +); + +my $ShortUsage = "ABI Dumper $TOOL_VERSION +Dump ABI of an ELF object containing DWARF debug info +Copyright (C) 2019 Andrey Ponomarenko's ABI Laboratory +License: GNU LGPL 2.1 + +Usage: $CmdName [options] [object] +Example: + $CmdName libTest.so -o ABI.dump + $CmdName Module.ko.debug -o ABI.dump + +More info: $CmdName --help\n"; + +if($#ARGV==-1) +{ + printMsg("INFO", $ShortUsage); + exit(0); +} + +GetOptions("h|help!" => \$Help, + "v|version!" => \$ShowVersion, + "dumpversion!" => \$DumpVersion, +# general options + "o|output|dump-path=s" => \$OutputDump, + "sort!" => \$SortDump, + "stdout!" => \$StdOut, + "loud!" => \$Loud, + "vnum|lver|lv=s" => \$TargetVersion, + "extra-info=s" => \$ExtraInfo, + "bin-only!" => \$BinOnly, + "all-types!" => \$AllTypes, + "all-symbols!" => \$AllSymbols, + "symbols-list=s" => \$SymbolsListPath, + "skip-cxx!" => \$SkipCxx, + "all!" => \$FullDump, + "dump-static!" => \$DumpStatic, + "compare!" => \$Compare, + "alt=s" => \$AltDebugInfoOpt, + "dir!" => \$AddDirs, + "vt-dumper=s" => \$VTDumperPath, + "public-headers=s" => \$PublicHeadersPath, + "ignore-tags=s" => \$IgnoreTagsPath, + "ctags-def=s" => \@CtagsDef, + "mixed-headers!" => \$MixedHeaders, + "kernel-export!" => \$KernelExport, + "search-debuginfo=s" => \$SearchDirDebuginfo, + "keep-registers-and-offsets!" => \$KeepRegsAndOffsets, + "all-units!" => \$AllUnits, + "quiet!" => \$Quiet, + "debug!" => \$Debug, +# extra options + "use-tu-dump!" => \$UseTU, + "include-preamble=s" => \$IncludePreamble, + "include-paths=s" => \$IncludePaths, + "include-defines=s" => \$IncludeDefines, + "cache-headers=s" => \$CacheHeaders, + "lambda!" => \$LambdaSupport, + "ld-library-path=s" => \$LdLibraryPath, +# internal options + "addr2name!" => \$AddrToName, +# obsolete + "reimplement-std!" => \$ReimplementStd +) or errMsg(); + +sub errMsg() +{ + printMsg("INFO", "\n".$ShortUsage); + exit($ERROR_CODE{"Error"}); +} + +my $HelpMessage=" +NAME: + ABI Dumper ($CmdName) + Dump ABI of an ELF object containing DWARF debug info + +DESCRIPTION: + ABI Dumper is a tool for dumping ABI information of an ELF object + containing DWARF debug info. + + The tool is intended to be used with ABI Compliance Checker tool for + tracking ABI changes of a C/C++ library or kernel module. + + This tool is free software: you can redistribute it and/or modify it + under the terms of the GNU LGPL 2.1. + +USAGE: + $CmdName [options] [object] + +EXAMPLES: + $CmdName libTest.so -o ABI.dump + $CmdName Module.ko.debug -o ABI.dump + +INFORMATION OPTIONS: + -h|-help + Print this help. + + -v|-version + Print version information. + + -dumpversion + Print the tool version ($TOOL_VERSION) and don't do anything else. + +GENERAL OPTIONS: + -o|-output PATH + Path to the output ABI dump file. + Default: ./ABI.dump + + -sort + Sort data in ABI dump. + + -stdout + Print ABI dump to stdout. + + -loud + Print all warnings. + + -vnum NUM + Set version of the library to NUM. + + -extra-info DIR + Dump extra analysis info to DIR. + + -bin-only + Do not dump information about inline functions, + pure virtual functions and non-exported global data. + + -all-types + Dump unused data types. + + -all-symbols + Dump symbols not exported by the object. + + -symbols-list PATH + Specify a file with a list of symbols that should be dumped. + + -skip-cxx + Do not dump stdc++ and gnu c++ symbols. + + -all + Equal to: -all-types -all-symbols. + + -dump-static + Dump static (local) symbols. + + -compare OLD.dump NEW.dump + Show added/removed symbols between two ABI dumps. + + -alt PATH + Path to the alternate debug info (Fedora). It is + detected automatically from gnu_debugaltlink section + of the input object if not specified. + + -dir + Show full paths of source files. + + -vt-dumper PATH + Path to the vtable-dumper executable if it is installed + to non-default location (not in PATH). + + -public-headers PATH + Path to directory with public header files or to file with + the list of header files. This option allows to filter out + private symbols from the ABI dump. + + -ignore-tags PATH + Path to ignore.tags file to help ctags tool to read + symbols in header files. + + -ctags-def DEF + Add -D DEF option to the ctags call. This option may be + specified multiple times. + + -reimplement-std + Do nothing. + + -mixed-headers + This option should be specified if you are using + -public-headers option and the names of public headers + intersect with the internal headers. + + -kernel-export + Dump symbols exported by the Linux kernel and modules, i.e. + symbols declared in the ksymtab section of the object and + system calls. + + -search-debuginfo DIR + Search for debug-info files referenced from gnu_debuglink + section of the object in DIR. + + -keep-registers-and-offsets + Dump used registers and stack offsets even if incompatible + build options detected. + + -all-units + Extract ABI info after reading all compilation units from + the debug info. This may require a lot of extra RAM memory. + By default all compilation units are processed separately. + + -quiet + Do not warn about incompatible build options. + + -debug + Enable debug messages. + +EXTRA OPTIONS: + -use-tu-dump + Use g++ -fdump-translation-unit instead of ctags to + list symbols in headers. This may be useful if all + functions are declared via macros in headers and + ctags can't recognize them. + + -include-preamble PATHS + Specify header files (separated by semicolon) that + should be included before others to compile without + errors. + + -include-paths DIRS + Specify include directories (separated by semicolon) + that should be passed to the compiler by -I option + in order to compile headers without errors. If this + option is not set then the tool will try to generate + include paths automatically. + + -cache-headers DIR + Cache headers analysis results to reuse later. + + -lambda + Enable support for lambda and checking of lexical + blocks. Define it if your C++ library API functions + use lambda expressions. + + -ld-library-path PATHS + Specify paths to add to LD_LIBRARY_PATH variable before + executing vtable-dumper (separated by colon). + + By default lexical blocks are not analyzed to + improve performance. +"; + +sub helpMsg() { + printMsg("INFO", $HelpMessage); +} + +my %Cache; + +# Input +my %DWARF_Info; +my @IDs; + +# Alternate +my @IDs_I; +my $AltDebugInfo = undef; +my $TooBig = 0; + +my $Compressed = undef; +my $Partial = undef; + +# Dump +my %TypeUnit; +my %Post_Change; + +# Output +my %TypeInfo; +my %SymbolInfo; + +# Other +my $TargetName = undef; +my %NestedNameSpaces; +my %HeadersInfo; +my %SourcesInfo; +my %SymVer; +my %LexicalId; + +# Reader (per compile unit) +my %TypeMember; +my %ArrayCount; +my %FuncParam; +my %TmplParam; +my %Inheritance; +my %NameSpace; +my %SpecElem; +my %OrigElem; +my %ClassMethods; + +# Reader +my %TypeSpec; +my %ClassChild; +my %SourceFile; +my %SourceFile_Alt; +my %DebugLoc; +my %TName_Tid; +my %TName_Tids; +my %RegName; + +my $STDCXX_TARGET = 0; +my $GLOBAL_ID = 0; +my %ANON_TYPE_WARN = (); + +my %Mangled_ID; +my %Checked_Spec; +my %SelectedSymbols; + +# Cleaning +my %MergedTypes; +my %LocalType; +my %UsedType; +my %DeletedAnon; +my %CheckedType; +my %DuplBaseType; + +# Language +my %TypeType = ( + "class_type"=>"Class", + "structure_type"=>"Struct", + "union_type"=>"Union", + "enumeration_type"=>"Enum", + "subroutine_type"=>"Func", + "array_type"=>"Array", + "base_type"=>"Intrinsic", + "unspecified_type"=>"Unspecified", + "const_type"=>"Const", + "pointer_type"=>"Pointer", + "reference_type"=>"Ref", + "rvalue_reference_type"=>"RvalueRef", + "volatile_type"=>"Volatile", + "restrict_type"=>"Restrict", + "typedef"=>"Typedef", + "ptr_to_member_type"=>"FieldPtr", + "string_type"=>"String" +); + +my %Qual = ( + "Pointer"=>"*", + "Ref"=>"&", + "RvalueRef"=>"&&", + "Volatile"=>"volatile", + "Restrict"=>"restrict", + "Const"=>"const" +); + +my %ConstSuffix = ( + "unsigned int" => "u", + "unsigned long" => "ul", + "unsigned long long" => "ull", + "long" => "l", + "long long" => "ll" +); + +my $HEADER_EXT = "h|hh|hp|hxx|hpp|h\\+\\+|tcc|txx|x|inl|inc|ads|isph"; +my $SRC_EXT = "c|cc|cp|cpp|cxx|c\\+\\+"; + +# ELF +my %Library_Symbol; +my %Library_UndefSymbol; +my %Library_Needed; +my %SymbolTable; +my %Symbol_Bind; + +# Kernel +my %KSymTab; + +# VTables +my %VirtualTable; + +# Env +my $SYS_ARCH; +my $SYS_WORD; +my $SYS_GCCV; +my $SYS_CLANGV = undef; +my $SYS_COMP; +my $LIB_LANG; +my $OBJ_LANG; + +# Errors +my $InvalidDebugLoc; +my $IncompatibleOpt = undef; +my $FKeepInLine = undef; + +# Public Headers +my %SymbolToHeader; +my %TypeToHeader; +my %PublicHeader; +my $PublicSymbols_Detected; +my $PublicHeadersIsDir = 1; + +# Filter +my %SymbolsList; + +# Dump +my $COMPRESS = "tar.gz"; + +sub printMsg($$) +{ + my ($Type, $Msg) = @_; + if($Type!~/\AINFO/) { + $Msg = $Type.": ".$Msg; + } + if($Type!~/_C\Z/) { + $Msg .= "\n"; + } + if($Type eq "ERROR" + or $Type eq "WARNING") { + print STDERR $Msg; + } + else { + print $Msg; + } +} + +sub exitStatus($$) +{ + my ($Code, $Msg) = @_; + printMsg("ERROR", $Msg); + exit($ERROR_CODE{$Code}); +} + +sub cmpVersions($$) +{ # compare two versions in dotted-numeric format + my ($V1, $V2) = @_; + return 0 if($V1 eq $V2); + return undef if($V1!~/\A\d+[\.\d+]*\Z/); + return undef if($V2!~/\A\d+[\.\d+]*\Z/); + my @V1Parts = split(/\./, $V1); + my @V2Parts = split(/\./, $V2); + for (my $i = 0; $i <= $#V1Parts && $i <= $#V2Parts; $i++) { + return -1 if(int($V1Parts[$i]) < int($V2Parts[$i])); + return 1 if(int($V1Parts[$i]) > int($V2Parts[$i])); + } + return -1 if($#V1Parts < $#V2Parts); + return 1 if($#V1Parts > $#V2Parts); + return 0; +} + +sub writeFile($$) +{ + my ($Path, $Content) = @_; + + if(my $Dir = getDirname($Path)) { + mkpath($Dir); + } + open(FILE, ">", $Path) || die ("can't open file \'$Path\': $!\n"); + print FILE $Content; + close(FILE); +} + +sub readFile($) +{ + my $Path = $_[0]; + + open(FILE, $Path); + local $/ = undef; + my $Content = ; + close(FILE); + return $Content; +} + +sub getFilename($) +{ # much faster than basename() from File::Basename module + if($_[0] and $_[0]=~/([^\/\\]+)[\/\\]*\Z/) { + return $1; + } + return ""; +} + +sub getDirname($) +{ # much faster than dirname() from File::Basename module + if($_[0] and $_[0]=~/\A(.*?)[\/\\]+[^\/\\]*[\/\\]*\Z/) { + return $1; + } + return ""; +} + +sub sepPath($) { + return (getDirname($_[0]), getFilename($_[0])); +} + +sub checkCmd($) +{ + my $Cmd = $_[0]; + + if(defined $Cache{"checkCmd"}{$Cmd}) { + return $Cache{"checkCmd"}{$Cmd}; + } + + if(-x $Cmd) + { # relative or absolute path + return ($Cache{"checkCmd"}{$Cmd} = 1); + } + + foreach my $Path (sort {length($a)<=>length($b)} split(/:/, $ENV{"PATH"})) + { + if(-x $Path."/".$Cmd) { + return ($Cache{"checkCmd"}{$Cmd} = 1); + } + } + return ($Cache{"checkCmd"}{$Cmd} = 0); +} + +my %ELF_BIND = map {$_=>1} ( + "WEAK", + "GLOBAL", + "LOCAL" +); + +my %ELF_TYPE = map {$_=>1} ( + "FUNC", + "IFUNC", + "GNU_IFUNC", + "TLS", + "OBJECT", + "COMMON" +); + +my %ELF_VIS = map {$_=>1} ( + "DEFAULT", + "PROTECTED" +); + +sub readline_ELF($) +{ # read the line of 'eu-readelf' output corresponding to the symbol + my @Info = split(/\s+/, $_[0]); + # Num: Value Size Type Bind Vis Ndx Name + # 3629: 000b09c0 32 FUNC GLOBAL DEFAULT 13 _ZNSt12__basic_fileIcED1Ev@@GLIBCXX_3.4 + # 135: 00000000 0 FUNC GLOBAL DEFAULT UNDEF av_image_fill_pointers@LIBAVUTIL_52 (3) + shift(@Info) if($Info[0] eq ""); # spaces + shift(@Info); # num + + if($#Info==7) + { # UNDEF SYMBOL (N) + if($Info[7]=~/\(\d+\)/) { + pop(@Info); + } + } + + if($#Info!=6) + { # other lines + return (); + } + return () if(not defined $ELF_TYPE{$Info[2]} and $Info[5] ne "UNDEF"); + return () if(not defined $ELF_BIND{$Info[3]}); + return () if(not defined $ELF_VIS{$Info[4]}); + if($Info[5] eq "ABS" and $Info[0]=~/\A0+\Z/) + { # 1272: 00000000 0 OBJECT GLOBAL DEFAULT ABS CXXABI_1.3 + return (); + } + if(index($Info[2], "0x") == 0) + { # size == 0x3d158 + $Info[2] = hex($Info[2]); + } + return @Info; +} + +sub readSymbols($) +{ + my $Lib_Path = $_[0]; + my $Lib_Name = getFilename($Lib_Path); + + my $Dynamic = ($Lib_Name=~/\.so(\.|\Z)/); + my $Dbg = ($Lib_Name=~/\.debug\Z/); + + if(not checkCmd($EU_READELF)) { + exitStatus("Not_Found", "can't find \"eu-readelf\" from Elfutils"); + } + + my %SectionInfo; + my %KSect; + + my $Cmd = $EU_READELF_L." -S \"$Lib_Path\" 2>\"$TMP_DIR/error\""; + foreach (split(/\n/, `$Cmd`)) + { + if(/\[\s*(\d+)\]\s+([\w\.]+)/) + { + my ($Num, $Name) = ($1, $2); + + $SectionInfo{$Num} = $Name; + + if(defined $KernelExport) + { + if($Name=~/\A(__ksymtab|__ksymtab_gpl)\Z/) { + $KSect{$1} = 1; + } + } + } + } + + if(defined $KernelExport) + { + if(not keys(%KSect)) + { + printMsg("ERROR", "can't find __ksymtab or __ksymtab_gpl sections in the object"); + exit(1); + } + + foreach my $Name (sort keys(%KSect)) + { + $Cmd = $OBJDUMP." --section=$Name -d \"$Lib_Path\" 2>\"$TMP_DIR/error\""; + + foreach my $Line (split(/\n/, qx/$Cmd/)) + { + if($Line=~/<__ksymtab_(.+?)>/) + { + $KSymTab{$1} = 1; + } + } + } + } + + if($Dynamic) + { # dynamic library specifics + $Cmd = $EU_READELF_L." -d \"$Lib_Path\" 2>\"$TMP_DIR/error\""; + foreach (split(/\n/, `$Cmd`)) + { + if(/NEEDED.+\[([^\[\]]+)\]/) + { # dependencies: + # 0x00000001 (NEEDED) Shared library: [libc.so.6] + $Library_Needed{$1} = 1; + } + } + } + + my $ExtraPath = undef; + + if($ExtraInfo) + { + mkpath($ExtraInfo); + $ExtraPath = $ExtraInfo."/elf-info"; + } + + $Cmd = $EU_READELF_L." -s \"$Lib_Path\" 2>\"$TMP_DIR/error\""; + + if($ExtraPath) + { # debug mode + # write to file + system($Cmd." >\"$ExtraPath\""); + open(LIB, $ExtraPath); + } + else + { # write to pipe + open(LIB, $Cmd." |"); + } + + my (%Symbol_Value, %Value_Symbol) = (); + + my $symtab = undef; # indicates that we are processing 'symtab' section of 'readelf' output + while() + { + if($Dynamic and not $Dbg) + { # dynamic library specifics + if(defined $symtab) + { + if(index($_, "'.dynsym'")!=-1) + { # dynamic table + $symtab = undef; + } + if(not $AllSymbols) + { # do nothing with symtab + # next; + } + } + elsif(index($_, "'.symtab'")!=-1) + { # symbol table + $symtab = 1; + } + } + if(my ($Value, $Size, $Type, $Bind, $Vis, $Ndx, $Symbol) = readline_ELF($_)) + { # read ELF entry + $Symbol_Bind{$Symbol} = $Bind; + if(index($Symbol, '@')) + { + if($Symbol=~/\A(.+?)\@/) { + $Symbol_Bind{$1} = $Bind; + } + } + + if(not $symtab) + { # dynsym + if(skipSymbol($Symbol)) { + next; + } + + if($Ndx eq "UNDEF") + { # ignore interfaces that are imported from somewhere else + $Library_UndefSymbol{$TargetName}{$Symbol} = 0; + next; + } + + if(defined $KernelExport) + { + if($Bind ne "LOCAL") + { + if(index($Symbol, "sys_")==0 + or index($Symbol, "SyS_")==0) { + $KSymTab{$Symbol} = 1; + } + } + + if(not defined $KSymTab{$Symbol}) { + next; + } + } + + if($Bind ne "LOCAL") { + $Library_Symbol{$TargetName}{$Symbol} = ($Type eq "OBJECT")?-$Size:1; + } + + if(not defined $OBJ_LANG) + { + if(index($Symbol, "_Z")==0) + { + $OBJ_LANG = "C++"; + } + } + } + + if($Ndx ne "UNDEF" and $Value!~/\A0+\Z/) + { + $Symbol_Value{$Symbol} = $Value; + $Value_Symbol{$Value}{$Symbol} = 1; + } + + if(not $symtab) + { + foreach ($SectionInfo{$Ndx}, "") + { + my $Val = $Value; + + $SymbolTable{$_}{$Val}{$Symbol} = 1; + + if($Val=~s/\A[0]+//) + { + if($Val eq "") { + $Val = "0"; + } + $SymbolTable{$_}{$Val}{$Symbol} = 1; + } + } + } + } + } + close(LIB); + + if(not defined $Library_Symbol{$TargetName}) { + return; + } + + my %Found = (); + foreach my $Symbol (sort keys(%Symbol_Value)) + { + next if(index($Symbol, '@')==-1); + if(my $Value = $Symbol_Value{$Symbol}) + { + foreach my $Symbol_SameValue (sort keys(%{$Value_Symbol{$Value}})) + { + if($Symbol_SameValue ne $Symbol + and index($Symbol_SameValue, '@')==-1) + { + $SymVer{$Symbol_SameValue} = $Symbol; + $Found{$Symbol} = 1; + + if(index($Symbol, '@@')==-1) { + last; + } + } + } + } + } + + # default + foreach my $Symbol (sort keys(%Symbol_Value)) + { + next if(defined $Found{$Symbol}); + next if(index($Symbol, '@@')==-1); + + if($Symbol=~/\A([^\@]*)\@\@/ + and not $SymVer{$1}) + { + $SymVer{$1} = $Symbol; + $Found{$Symbol} = 1; + } + } + + # non-default + foreach my $Symbol (sort keys(%Symbol_Value)) + { + next if(defined $Found{$Symbol}); + next if(index($Symbol, '@')==-1); + + if($Symbol=~/\A([^\@]*)\@([^\@]*)/ + and not $SymVer{$1}) + { + $SymVer{$1} = $Symbol; + $Found{$Symbol} = 1; + } + } + + if(not defined $OBJ_LANG) + { + $OBJ_LANG = "C"; + } +} + +sub readAltInfo($) +{ + my $Path = $_[0]; + my $Name = getFilename($Path); + + if(not checkCmd($EU_READELF)) { + exitStatus("Not_Found", "can't find \"$EU_READELF\" command"); + } + + printMsg("INFO", "Reading alternate debug-info"); + + my $ExtraPath = undef; + + # lines info + if($ExtraInfo) + { + $ExtraPath = $ExtraInfo."/alt"; + mkpath($ExtraPath); + $ExtraPath .= "/debug_line"; + } + + if($ExtraPath) + { + system($EU_READELF_L." -N --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); + open(SRC, $ExtraPath); + } + else { + open(SRC, $EU_READELF_L." -N --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" |"); + } + + my $DirTable_Def = undef; + my %DirTable = (); + + while() + { + if(defined $AddDirs) + { + if(/Directory table/i) + { + $DirTable_Def = 1; + next; + } + elsif(/File name table/i) + { + $DirTable_Def = undef; + next; + } + + if(defined $DirTable_Def) + { + if(/\A\s*(.+?)\Z/) { + $DirTable{keys(%DirTable)+1} = $1; + } + } + } + + if(/(\d+)\s+(\d+)\s+\d+\s+\d+\s+([^ ]+)/) + { + my ($Num, $Dir, $File) = ($1, $2, $3); + chomp($File); + + if(defined $AddDirs) + { + if(my $DName = $DirTable{$Dir}) + { + $File = $DName."/".$File; + } + } + + $SourceFile_Alt{0}{$Num} = $File; + } + } + close(SRC); + + # debug info + if($ExtraInfo) + { + $ExtraPath = $ExtraInfo."/alt"; + mkpath($ExtraPath); + $ExtraPath .= "/debug_info"; + } + + my $INFO_fh; + + if($ExtraPath) + { + system($EU_READELF_L." -N --debug-dump=info \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); + open($INFO_fh, $ExtraPath); + } + else { + open($INFO_fh, $EU_READELF_L." -N --debug-dump=info \"$Path\" 2>\"$TMP_DIR/error\" |"); + } + + readDWARFDump($INFO_fh, 0); +} + +sub readDWARFInfo($) +{ + my $Path = $_[0]; + + my $Dir = getDirname($Path); + my $Name = getFilename($Path); + + if(not checkCmd($EU_READELF)) { + exitStatus("Not_Found", "can't find \"$EU_READELF\" command"); + } + + if(-s $Path > 1024*1024*100) { + $TooBig = 1; + } + + my $AddOpt = ""; + if(not defined $AddrToName) + { # disable search of symbol names + $AddOpt .= " -N"; + } + + my $Sect = `$EU_READELF_L -S \"$Path\" 2>\"$TMP_DIR/error\"`; + + if($Sect!~/\.z?debug_info/) + { # No DWARF info + if(my $DebugFile = getDebugFile($Path, "gnu_debuglink")) + { + my $DPath = $DebugFile; + my $DName = getFilename($DPath); + + printMsg("INFO", "Found link to $DName (gnu_debuglink)"); + + if(my $DDir = getDirname($Path)) + { + $DPath = $DDir."/".$DPath; + } + + my $Found = undef; + + if(defined $SearchDirDebuginfo) + { + if(-f $SearchDirDebuginfo."/".$DName) { + $Found = $SearchDirDebuginfo."/".$DName; + } + else + { + my @Files = findFiles($SearchDirDebuginfo, "f"); + + foreach my $F (@Files) + { + if(getFilename($F) eq $DName) + { + $Found = $F; + last; + } + } + } + } + elsif(-f $DPath + and $DPath ne $Path) { + $Found = $DPath; + } + + if($Found and $Found ne $Path) + { + printMsg("INFO", "Reading debug-info file $DName linked from gnu_debuglink"); + return readDWARFInfo($Found); + } + else + { + printMsg("ERROR", "missed debug-info file $DName linked from gnu_debuglink (try --search-debuginfo=DIR option)"); + return 0; + } + } + return 0; + } + elsif(not defined $AltDebugInfoOpt) + { + if($Sect=~/\.gnu_debugaltlink/) + { + if(my $AltObj = getDebugAltLink($Path)) + { + $AltDebugInfo = $AltObj; + readAltInfo($AltObj); + } + else { + exitStatus("Error", "can't read gnu_debugaltlink"); + } + } + } + + if($AltDebugInfo) + { + if($TooBig) { + printMsg("WARNING", "input object is compressed and large, may require a lot of RAM memory to process"); + } + } + + printMsg("INFO", "Reading debug-info"); + + my $ExtraPath = undef; + + # ELF header + if($ExtraInfo) + { + mkpath($ExtraInfo); + $ExtraPath = $ExtraInfo."/elf-header"; + } + + if($ExtraPath) + { + system($EU_READELF_L." -h \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); + open(HEADER, $ExtraPath); + } + else { + open(HEADER, $EU_READELF_L." -h \"$Path\" 2>\"$TMP_DIR/error\" |"); + } + + my %Header = (); + while(
) + { + if(/\A\s*([\w ]+?)\:\s*(.+?)\Z/) { + $Header{$1} = $2; + } + } + close(HEADER); + + $SYS_ARCH = $Header{"Machine"}; + + if($SYS_ARCH=~/80\d86/ + or $SYS_ARCH=~/i\d86/) + { # i386, i586, etc. + $SYS_ARCH = "x86"; + } + + if($SYS_ARCH=~/amd64/i + or $SYS_ARCH=~/x86\-64/i) + { # amd64 + $SYS_ARCH = "x86_64"; + } + + initRegs(); + + # ELF sections + if($ExtraInfo) + { + mkpath($ExtraInfo); + $ExtraPath = $ExtraInfo."/elf-sections"; + } + + if($ExtraPath) + { + system($EU_READELF_L." -S \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); + open(HEADER, $ExtraPath); + } + + # source info + if($ExtraInfo) + { + mkpath($ExtraInfo); + $ExtraPath = $ExtraInfo."/debug_line"; + } + + if($ExtraPath) + { + system($EU_READELF_L." $AddOpt --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); + open(SRC, $ExtraPath); + } + else { + open(SRC, $EU_READELF_L." $AddOpt --debug-dump=line \"$Path\" 2>\"$TMP_DIR/error\" |"); + } + + my $Offset = undef; + my $DirTable_Def = undef; + my %DirTable = (); + + while() + { + if(defined $AddDirs) + { + if(/Directory table/i) + { + $DirTable_Def = 1; + %DirTable = (); + next; + } + elsif(/File name table/i) + { + $DirTable_Def = undef; + next; + } + + if(defined $DirTable_Def) + { + if(/\A\s*(.+?)\Z/) { + $DirTable{keys(%DirTable)+1} = $1; + } + } + } + + if(index($_, "Table")!=-1 + and /Table at offset (\w+)/) { + $Offset = $1; + } + elsif(defined $Offset + and /(\d+)\s+(\d+)\s+\d+\s+\d+\s+([^ ]+)/) + { + my ($Num, $Dir, $File) = ($1, $2, $3); + chomp($File); + + if(defined $AddDirs) + { + if(my $DName = $DirTable{$Dir}) + { + $File = $DName."/".$File; + } + } + + $SourceFile{$Offset}{$Num} = $File; + } + } + close(SRC); + + # debug_loc + if($ExtraInfo) + { + mkpath($ExtraInfo); + $ExtraPath = $ExtraInfo."/debug_loc"; + } + + if($ExtraPath) + { + system($EU_READELF_L." $AddOpt --debug-dump=loc \"$Path\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); + open(LOC, $ExtraPath); + } + else { + open(LOC, $EU_READELF_L." $AddOpt --debug-dump=loc \"$Path\" 2>\"$TMP_DIR/error\" |"); + } + + while() + { + if(/\A \[\s*(\w+)\].*\[\s*\w+\]\s*(.+)\Z/) { + $DebugLoc{$1} = $2; + } + elsif(/\A \[\s*(\w+)\]/) { + $DebugLoc{$1} = ""; + } + } + close(LOC); + + # dwarf + if($ExtraInfo) + { + mkpath($ExtraInfo); + $ExtraPath = $ExtraInfo."/debug_info"; + } + + my $INFO_fh; + + if($Dir) + { # to find ".dwz" directory (Fedora) + chdir($Dir); + } + if($ExtraPath) + { + system($EU_READELF_L." $AddOpt --debug-dump=info \"$Name\" 2>\"$TMP_DIR/error\" >\"$ExtraPath\""); + open($INFO_fh, $ExtraPath); + } + else { + open($INFO_fh, $EU_READELF_L." $AddOpt --debug-dump=info \"$Name\" 2>\"$TMP_DIR/error\" |"); + } + chdir($ORIG_DIR); + + readDWARFDump($INFO_fh, 1); + + if(my $Err = readFile("$TMP_DIR/error")) + { # eu-readelf: cannot get next DIE: invalid DWARF + if($Err=~/invalid DWARF/i) + { + if($Loud) { + printMsg("ERROR", $Err); + } + exitStatus("Invalid_DWARF", "invalid DWARF info"); + } + } + + return 1; +} + +sub getSource($) +{ + my $ID = $_[0]; + + if(defined $DWARF_Info{$ID}{"file"}) + { + my $File = $DWARF_Info{$ID}{"file"}; + my $Unit = $DWARF_Info{$ID}{"unit"}; + + my $Name = undef; + + if($ID>=0) { + $Name = $SourceFile{$Unit}{$File}; + } + else + { # imported + $Name = $SourceFile_Alt{0}{$File}; + } + + return $Name; + } + + return undef; +} + +sub readDWARFDump($$) +{ + my ($FH, $Primary) = @_; + + my $TypeUnit_Sign = undef; + my $TypeUnit_Offset = undef; + my $Type_Offset = undef; + + my $Shift_Enabled = 1; + my $ID_Shift = undef; + + my $CUnit = undef; + + if($AltDebugInfo) { + $Compressed = 1; + } + + my $ID = undef; + my $Kind = undef; + my $NS = undef; + + my $MAX_ID = undef; + + my %Shift = map {$_=>1} ( + "specification", + "spec", + "type", + "sibling", + "object_pointer", + "objptr", + "containing_type", + "container", + "abstract_origin", + "orig", + "import", + "signature" + ); + + my %SkipNode = ( + "imported_declaration" => 1, + "imported_module" => 1 + ); + + my %SkipAttr = ( + "high_pc" => 1, + "frame_base" => 1, + "encoding" => 1, + "Compilation" => 1, + "comp_dir" => 1, + "declaration" => 1, + "prototyped" => 1, + "GNU_vector" => 1, + "GNU_all_call_sites" => 1, + "explicit" => 1 + ); + + my %RenameAttr = ( + "data_member_location" => "mloc", + "decl_file" => "file", + "decl_line" => "line", + "linkage_name" => "linkage", + "object_pointer" => "objptr", + "artificial" => "art", + "external" => "ext", + "specification" => "spec", + "byte_size" => "size", + "accessibility" => "access", + "const_value" => "cval", + "containing_type" => "container", + "abstract_origin" => "orig", + "virtuality" => "virt", + "vtable_elem_location" => "vloc" + ); + + my %RenameKind = ( + "formal_parameter" => "param", + "subprogram" => "prog", + "unspecified_parameters" => "unspec_params", + "template_type_parameter" => "tmpl_param" + ); + + my %MarkByUnit = ( + "member" => 1, + "subprogram" => 1, + "prog" => 1, + "variable" => 1 + ); + + my $Lexical_Block = undef; + my $Inlined_Block = undef; + my $Subprogram_Block = undef; + my $Skip_Block = undef; + + while(my $Line = <$FH>) + { + if(defined $ID and $Line=~/\A\s*(\w+)\s+(.+?)\s*\Z/) + { + if(defined $Skip_Block) { + next; + } + + my $Attr = $1; + my $Val = $2; + + if(defined $RenameAttr{$Attr}) { + $Attr = $RenameAttr{$Attr}; + } + + if(index($Val, "(flag")==0) + { # artificial, external (on Fedora) + # flag_present + $Val = 1; + } + + if(defined $Compressed) + { + if($Kind eq "imported_unit") { + next; + } + } + + if($Kind eq "member") + { + if($Attr eq "mloc") { + delete($DWARF_Info{$ID}{"unit"}); + } + } + + if($Attr eq "sibling") + { + if($Kind ne "structure_type") { + next; + } + } + elsif($Attr eq "Type") + { + if($Line=~/Type\s+signature:\s*0x(\w+)/) { + $TypeUnit_Sign = $1; + } + if($Line=~/Type\s+offset:\s*0x(\w+)/) { + $Type_Offset = hex($1); + } + if($Line=~/Type\s+unit\s+at\s+offset\s+(\d+)/) { + $TypeUnit_Offset = $1; + } + next; + } + elsif(defined $SkipAttr{$Attr}) + { # unused + next; + } + + if($Val=~/\A\s*\(([^()]*)\)\s*\[\s*(\w+)\]\s*\Z/) + { # ref4, ref_udata, ref_addr, etc. + $Val = hex($2); + + if($1 eq "GNU_ref_alt") { + $Val = -$Val; + } + } + elsif($Attr eq "name") + { + $Val=~s/\A\([^()]*\)\s*\"(.*)\"\Z/$1/; + + if(defined $LambdaSupport) + { + if(index($Val, "\Z/}/; + } + } + } + elsif(index($Attr, "linkage_name")!=-1 or $Attr eq "linkage") + { + $Val=~s/\A\([^()]*\)\s*\"(.*)\"\Z/$1/; + $Attr = "linkage"; + } + elsif(index($Attr, "location")!=-1 or $Attr eq "mloc" or $Attr eq "vloc") + { + if($Val=~/\)\s*\Z/) + { # value on the next line + $Val .= <$FH>; + + if(index($Val, "GNU_entry_value")!=-1) + { # value on the next line + $Val .= <$FH>; + } + } + + if($Val=~/\A\(\w+\)\s*(-?)(\w+)\Z/) + { # (data1) 1c + $Val = hex($2); + if($1) { + $Val = -$Val; + } + } + else + { + if($Val=~/ (-?\d+)\Z/) { + $Val = $1; + } + else + { + if($Attr eq "location" + and $Kind eq "param") + { + if($Val=~/location list\s+\[\s*(\w+)\]\Z/) + { + $Attr = "location_list"; + $Val = $1; + } + elsif($Val=~/ reg(\d+)\Z/) + { + $Attr = "register"; + $Val = $1; + } + } + } + } + } + elsif($Attr eq "access") + { + $Val=~s/\A\(.+?\)\s*//; + $Val=~s/\s*\(.+?\)\Z//; + + # NOTE: members: private by default + } + else + { + $Val=~s/\A\(\w+\)\s*//; + + if(substr($Val, 0, 1) eq "{" + and $Val=~/{(.+)}/) + { # {ID} + $Val = $1; + $Post_Change{$ID} = 1; + } + } + + if($Val eq "") + { + if($Attr eq "ext") { + next; + } + } + + if(defined $Shift_Enabled and $ID_Shift) + { + if(defined $Shift{$Attr} + and not $Post_Change{$ID}) { + $Val += $ID_Shift; + } + + # $DWARF_Info{$ID}{"rID"} = $ID-$ID_Shift; + } + + if(not $Primary) + { + if(defined $Shift{$Attr}) { + $Val = -$Val; + } + } + + if($Kind ne "partial_unit" + and $Kind ne "imported_unit") + { + if($Attr ne "stmt_list") { + $DWARF_Info{$ID}{$Attr} = "$Val"; + } + } + + if($Kind eq "compile_unit") + { + if($Attr eq "stmt_list") + { + $CUnit = $Val; + $Partial = undef + } + + if(not defined $LIB_LANG) + { + if($Attr eq "language") + { + if(index($Val, "Assembler")==-1) + { + $Val=~s/\s*\(.+?\)\Z//; + + if($Val=~/C\d/i) { + $LIB_LANG = "C"; + } + elsif($Val=~/C\+\+|C_plus_plus/i) { + $LIB_LANG = "C++"; + } + else { + $LIB_LANG = $Val; + } + } + } + } + + if(not defined $SYS_COMP and not defined $SYS_GCCV) + { + if($Attr eq "producer") + { + if(index($Val, "GNU AS")==-1) + { + $Val=~s/\A\"//; + $Val=~s/\"\Z//; + + if($Val=~/GNU\s+(C\d*|C\+\+|GIMPLE)\s+(.+)\Z/) + { + $SYS_GCCV = $2; + if($SYS_GCCV=~/\A(\d+\.\d+)(\.\d+|)/) + { # 4.6.1 20110627 (Mandriva) + $SYS_GCCV = $1.$2; + } + } + elsif($Val=~/clang\s+version\s+([^\s\(]+)/) { + $SYS_CLANGV = $1; + } + else { + $SYS_COMP = $Val; + } + + if(not defined $KeepRegsAndOffsets) + { + my %Opts = (); + while($Val=~s/(\A| )(\-O([0-3]|g))( |\Z)/ /) { + $Opts{keys(%Opts)} = $2; + } + + if(keys(%Opts)) + { + if($Opts{keys(%Opts)-1} ne "-Og") + { + if(not defined $Quiet) { + printMsg("WARNING", "incompatible build option detected: ".$Opts{keys(%Opts)-1}." (required -Og for better analysis)"); + } + $IncompatibleOpt = 1; + } + } + else + { + if(not defined $Quiet) { + printMsg("WARNING", "the object should be compiled with -Og option for better analysis"); + } + $IncompatibleOpt = 1; + } + } + + if(index($Val, "-fkeep-inline-functions")!=-1) { + $FKeepInLine = 1; + } + } + } + } + } + elsif($Kind eq "type_unit") + { + if($Attr eq "stmt_list") + { + $CUnit = $Val; + $Partial = 1; + } + } + elsif($Kind eq "partial_unit") + { # support for dwz + if($Attr eq "stmt_list") + { + $CUnit = $Val; + $Partial = 1; + } + } + } + elsif($Line=~/\A \[\s*(\w+)\](\s*)(\w+)/) + { + $ID = hex($1); + $NS = length($2); + $Kind = $3; + + if(defined $RenameKind{$Kind}) { + $Kind = $RenameKind{$Kind}; + } + + if(not defined $Compressed) + { + if($Kind eq "partial_unit" or $Kind eq "type_unit") + { # compressed debug_info + $Compressed = 1; + + if($TooBig) { + printMsg("WARNING", "input object is compressed and large, may require a lot of RAM memory to process"); + } + } + } + + if($Kind eq "compile_unit" and $CUnit + and not defined $AllUnits) + { # read the previous compile unit + completeDump($Primary); + + if($Primary) { + readABI(); + } + } + + $Skip_Block = undef; + + if(defined $SkipNode{$Kind}) + { + $Skip_Block = 1; + next; + } + + if($Kind eq "lexical_block") + { + if(defined $Lexical_Block) + { + if(length($NS)<=length($Lexical_Block)) { + $Lexical_Block = $NS; + } + } + else { + $Lexical_Block = $NS; + } + $Skip_Block = 1; + next; + } + else + { + if(defined $Lexical_Block) + { + if($NS>$Lexical_Block) + { + $LexicalId{$ID} = 1; + if(not $LambdaSupport) + { + $Skip_Block = 1; + next; + } + } + else + { # end of lexical block + $Lexical_Block = undef; + } + } + } + + if($Kind eq "inlined_subroutine") + { + $Inlined_Block = $NS; + $Skip_Block = 1; + next; + } + else + { + if(defined $Inlined_Block) + { + if($NS>$Inlined_Block) + { + $Skip_Block = 1; + next; + } + else + { # end of inlined subroutine + $Inlined_Block = undef; + } + } + } + + if($Kind eq "prog") + { + $Subprogram_Block = $NS; + } + else + { + if(defined $Subprogram_Block) + { + if($NS>$Subprogram_Block) + { + if($Kind eq "variable") + { # temp variables + $Skip_Block = 1; + next; + } + } + else + { # end of subprogram block + $Subprogram_Block = undef; + } + } + } + + if(not $Primary) { + $ID = -$ID; + } + + if(defined $Shift_Enabled) + { + if($Kind eq "type_unit") + { + if(not defined $ID_Shift) + { + if($ID_Shift<=$MAX_ID) { + $ID_Shift = $MAX_ID; + } + else { + $ID_Shift = 0; + } + } + } + + if($ID_Shift) { + $ID += $ID_Shift; + } + } + + if(defined $TypeUnit_Sign) + { + if($Kind ne "type_unit" + and $Kind ne "namespace") + { + if($TypeUnit_Offset+$Type_Offset+$ID_Shift==$ID) + { + $TypeUnit{$TypeUnit_Sign} = "$ID"; + $TypeUnit_Sign = undef; + } + } + } + + if($Kind ne "partial_unit" + and $Kind ne "imported_unit") + { + $DWARF_Info{$ID} = {}; + $DWARF_Info{$ID}{"kind"} = $Kind; + $DWARF_Info{$ID}{"ns"} = $NS; + + if(defined $CUnit) + { + if(defined $MarkByUnit{$Kind} + or defined $TypeType{$Kind}) { + $DWARF_Info{$ID}{"unit"} = $CUnit; + } + } + + if($ID>0) { + push(@IDs, $ID); + } + else { + push(@IDs_I, $ID); + } + } + + if(not defined $ID_Shift) { + $MAX_ID = $ID; + } + } + elsif(not defined $SYS_WORD + and $Line=~/Address\s*size:\s*(\d+)/i) + { + $SYS_WORD = $1; + } + } + + close($FH); + + if($Primary and not defined $ID) { + printMsg("ERROR", "the debuginfo looks empty or corrupted"); + } + + # read the last compile unit + completeDump($Primary); + + if($Primary) { + readABI(); + } +} + +sub readVtables($) +{ + my $Path = $_[0]; + + $Path = abs_path($Path); + + my $Dir = getDirname($Path); + + if(index($LIB_LANG, "C++")!=-1 + or $OBJ_LANG eq "C++") + { + printMsg("INFO", "Reading v-tables"); + + if(checkCmd($VTABLE_DUMPER)) + { + if(my $Version = `$VTABLE_DUMPER -dumpversion`) + { + if(cmpVersions($Version, $VTABLE_DUMPER_VERSION)<0) + { + printMsg("ERROR", "the version of Vtable-Dumper should be $VTABLE_DUMPER_VERSION or newer"); + return; + } + } + } + else + { + printMsg("ERROR", "cannot find \'$VTABLE_DUMPER\'"); + return; + } + + my $ExtraPath = $TMP_DIR."/v-tables"; + + if($ExtraInfo) + { + mkpath($ExtraInfo); + $ExtraPath = $ExtraInfo."/v-tables"; + } + + my $LdPaths = $Dir; + + if(defined $LdLibraryPath) { + $LdPaths .= ":".$LdLibraryPath; + } + + system("LD_LIBRARY_PATH=\"$LdPaths\" $VTABLE_DUMPER -mangled -demangled \"$Path\" >\"$ExtraPath\""); + + my $Content = readFile($ExtraPath); + foreach my $ClassInfo (split(/\n\n\n/, $Content)) + { + if($ClassInfo=~/\AVtable\s+for\s+(.+)\n((.|\n)+)\Z/i) + { + my ($CName, $VTable) = ($1, $2); + my @Entries = split(/\n/, $VTable); + + foreach (1 .. $#Entries) + { + my $Entry = $Entries[$_]; + if($Entry=~/\A(\d+)\s+(.+)\Z/) { + $VirtualTable{$CName}{$1} = $2; + } + } + } + } + } + + if(keys(%VirtualTable)) + { + foreach my $Tid (sort keys(%TypeInfo)) + { + if($TypeInfo{$Tid}{"Type"}=~/\A(Struct|Class)\Z/) + { + my $TName = $TypeInfo{$Tid}{"Name"}; + $TName=~s/\bstruct //g; + if(defined $VirtualTable{$TName}) { + $TypeInfo{$Tid}{"VTable"} = $VirtualTable{$TName}; + } + } + } + } +} + +sub createArchive($$) +{ + my ($Path, $To) = @_; + if(not $To) { + $To = "."; + } + + if(not checkCmd("tar")) { + exitStatus("Not_Found", "can't find \"tar\""); + } + if(not checkCmd("gzip")) { + exitStatus("Not_Found", "can't find \"gzip\""); + } + + my ($From, $Name) = sepPath($Path); + my $Pkg = abs_path($To)."/".$Name.".".$COMPRESS; + if(-e $Pkg) { + unlink($Pkg); + } + system("tar", "-C", $From, "-czf", $Pkg, $Name); + if($?) + { # cannot allocate memory (or other problems with "tar") + exitStatus("Error", "can't pack the ABI dump: ".$!); + } + unlink($Path); + return $To."/".$Name.".".$COMPRESS; +} + +sub createABIFile() +{ + printMsg("INFO", "Creating ABI dump"); + + my %ABI = ( + "TypeInfo" => \%TypeInfo, + "SymbolInfo" => \%SymbolInfo, + "Symbols" => \%Library_Symbol, + "UndefinedSymbols" => \%Library_UndefSymbol, + "Needed" => \%Library_Needed, + "SymbolVersion" => \%SymVer, + "LibraryVersion" => $TargetVersion, + "LibraryName" => $TargetName, + "Language" => $LIB_LANG, + "Headers" => \%HeadersInfo, + "Sources" => \%SourcesInfo, + "NameSpaces" => \%NestedNameSpaces, + "Target" => "unix", + "Arch" => $SYS_ARCH, + "WordSize" => $SYS_WORD, + "ABI_DUMP_VERSION" => $ABI_DUMP_VERSION, + "ABI_DUMPER_VERSION" => $TOOL_VERSION, + ); + + if($SYS_GCCV) { + $ABI{"GccVersion"} = $SYS_GCCV; + } + elsif($SYS_CLANGV) { + $ABI{"ClangVersion"} = $SYS_CLANGV; + } + else { + $ABI{"Compiler"} = $SYS_COMP; + } + + if(defined $PublicHeadersPath) { + $ABI{"PublicABI"} = "1"; + } + + if(defined $IncompatibleOpt) + { + $ABI{"MissedOffsets"} = "1"; + $ABI{"MissedRegs"} = "1"; + } + + if($StdOut) + { # --stdout option + print STDOUT Dumper(\%ABI); + } + else + { + my $DumpPath = "ABI.dump"; + if($OutputDump) + { # user defined path + $DumpPath = $OutputDump; + } + my $Archive = ($DumpPath=~s/\Q.$COMPRESS\E\Z//g); + my ($DDir, $DName) = sepPath($DumpPath); + + my $DPath = $TMP_DIR."/".$DName; + if(not $Archive) { + $DPath = $DumpPath; + } + + mkpath($DDir); + + open(DUMP, ">", $DPath) || die ("can't open file \'$DumpPath\': $!\n"); + print DUMP Dumper(\%ABI); + close(DUMP); + + if(not -s $DPath) { + exitStatus("Error", "can't create ABI dump because something is going wrong with the Data::Dumper module"); + } + if($Archive) { + $DumpPath = createArchive($DPath, $DDir); + } + + printMsg("INFO", "\nThe object ABI has been dumped to:\n $DumpPath"); + } +} + +sub unmangleString($) +{ + my $Str = $_[0]; + + $Str=~s/\AN(.+)E\Z/$1/; + while($Str=~s/\A(\d+)//) + { + if(length($Str)==$1) { + last; + } + + $Str = substr($Str, $1, length($Str) - $1); + } + + return $Str; +} + +sub initABI() +{ + # register "void" type + %{$TypeInfo{"1"}} = ( + "Name"=>"void", + "Type"=>"Intrinsic" + ); + $TName_Tid{"Intrinsic"}{"void"} = "1"; + $TName_Tids{"Intrinsic"}{"void"}{"1"} = 1; + $Cache{"getTypeInfo"}{"1"} = 1; + + # register "..." type + %{$TypeInfo{"-1"}} = ( + "Name"=>"...", + "Type"=>"Intrinsic" + ); + $TName_Tid{"Intrinsic"}{"..."} = "-1"; + $TName_Tids{"Intrinsic"}{"..."}{"-1"} = 1; + $Cache{"getTypeInfo"}{"-1"} = 1; +} + +sub completeDump($) +{ + my $Primary = $_[0]; + + foreach my $ID (keys(%Post_Change)) + { + if(my $Type = $DWARF_Info{$ID}{"type"}) + { + if(my $To = $TypeUnit{$Type}) { + $DWARF_Info{$ID}{"type"} = $To; + } + } + if(my $Signature = $DWARF_Info{$ID}{"signature"}) + { + if(my $To = $TypeUnit{$Signature}) { + $DWARF_Info{$ID}{"signature"} = $To; + } + } + } + + %Post_Change = (); + %TypeUnit = (); +} + +my %IsType = map {$_=>1} ( + "struct_type", + "structure_type", + "class_type", + "union_type", + "enumeration_type", + "subroutine_type", + "array_type" +); + +my %MainKind = map {$_=>1} ( + "typedef", + "subprogram", + "prog", + "variable", + "namespace" +); + +sub readABI() +{ + my %CurID = (); + + if(@IDs_I) { + @IDs = (@IDs_I, @IDs); + } + + my $TPack = undef; + my $PPack = undef; + my $NS_Pre = undef; + + foreach my $ID (@IDs) + { + $ID = "$ID"; + + my $Kind = $DWARF_Info{$ID}{"kind"}; + my $NS = $DWARF_Info{$ID}{"ns"}; + my $Scope = $CurID{$NS-2}; + + if(defined $NS_Pre and $NS<=$NS_Pre) + { + foreach (0 .. $NS_Pre-$NS) { + delete($CurID{$NS+$_}); + } + } + + $NS_Pre = $NS; + + if($Kind eq "typedef") + { + if($DWARF_Info{$Scope}{"kind"} eq "prog") + { + $NS = $DWARF_Info{$Scope}{"ns"}; + $Scope = $CurID{$NS-2}; + } + } + + if($Kind ne "prog") { + delete($DWARF_Info{$ID}{"ns"}); + } + + if(defined $IsType{$Kind} + or defined $MainKind{$Kind}) + { + if($Kind ne "variable" + and $Kind ne "typedef") + { + $CurID{$NS} = $ID; + } + + if($Scope) + { + $NameSpace{$ID} = $Scope; + if($Kind eq "prog" + or $Kind eq "variable") + { + if($DWARF_Info{$Scope}{"kind"}=~/class|struct/) + { + $ClassMethods{$Scope} = 1; + if(my $Sp = $DWARF_Info{$Scope}{"spec"}) { + $ClassMethods{$Sp} = 1; + } + } + } + } + + if(my $Spec = $DWARF_Info{$ID}{"spec"}) { + $SpecElem{$Spec} = $ID; + } + + if(my $Orig = $DWARF_Info{$ID}{"orig"}) { + $OrigElem{$Orig} = $ID; + } + + if(defined $IsType{$Kind}) + { + if(not $DWARF_Info{$ID}{"name"} + and $DWARF_Info{$ID}{"linkage"}) + { + $DWARF_Info{$ID}{"name"} = unmangleString($DWARF_Info{$ID}{"linkage"}); + + # free memory + delete($DWARF_Info{$ID}{"linkage"}); + } + } + } + elsif($Kind eq "member") + { + if($Scope) + { + $NameSpace{$ID} = $Scope; + + if(not defined $DWARF_Info{$ID}{"mloc"} + and $DWARF_Info{$Scope}{"kind"}=~/class|struct/) + { # variable (global data) + next; + } + } + + $TypeMember{$Scope}{keys(%{$TypeMember{$Scope}})} = $ID; + } + elsif($Kind eq "enumerator") + { + $TypeMember{$Scope}{keys(%{$TypeMember{$Scope}})} = $ID; + } + elsif($Kind eq "inheritance") + { + my %In = (); + $In{"id"} = $DWARF_Info{$ID}{"type"}; + + if(my $Access = $DWARF_Info{$ID}{"access"}) + { + if($Access ne "public") + { # default inheritance access in ABI dump is "public" + $In{"access"} = $Access; + } + } + + if(defined $DWARF_Info{$ID}{"virt"}) { + $In{"virtual"} = 1; + } + $Inheritance{$Scope}{keys(%{$Inheritance{$Scope}})} = \%In; + + # free memory + delete($DWARF_Info{$ID}); + } + elsif($Kind eq "param") + { + if(defined $PPack) { + $FuncParam{$PPack}{keys(%{$FuncParam{$PPack}})} = $ID; + } + else { + $FuncParam{$Scope}{keys(%{$FuncParam{$Scope}})} = $ID; + } + } + elsif($Kind eq "unspec_params") + { + $FuncParam{$Scope}{keys(%{$FuncParam{$Scope}})} = $ID; + $DWARF_Info{$ID}{"type"} = "-1"; # "..." + } + elsif($Kind eq "subrange_type") + { + if((my $Bound = $DWARF_Info{$ID}{"upper_bound"}) ne "") { + $ArrayCount{$Scope} = $Bound + 1; + } + + # free memory + delete($DWARF_Info{$ID}); + } + elsif($Kind eq "tmpl_param" + or $Kind eq "template_value_parameter") + { + my %Info = ("key"=>$DWARF_Info{$ID}{"name"}); + + if(defined $DWARF_Info{$ID}{"type"}) { + $Info{"type"} = $DWARF_Info{$ID}{"type"}; + } + else { # void + $Info{"type"} = "1"; + } + + if(defined $DWARF_Info{$ID}{"cval"}) { + $Info{"value"} = $DWARF_Info{$ID}{"cval"}; + } + + if(defined $DWARF_Info{$ID}{"default_value"}) { + $Info{"default"} = 1; + } + + if(defined $TPack) { + $TmplParam{$TPack}{keys(%{$TmplParam{$TPack}})} = \%Info; + } + else { + $TmplParam{$Scope}{keys(%{$TmplParam{$Scope}})} = \%Info; + } + } + elsif($Kind eq "GNU_template_parameter_pack") { + $TPack = $Scope; + } + elsif($Kind eq "GNU_formal_parameter_pack") { + $PPack = $Scope; + } + + if($Kind ne "GNU_template_parameter_pack") + { + if(index($Kind, "template_")==-1) { + $TPack = undef; + } + } + + if($Kind ne "GNU_formal_parameter_pack") + { + if($Kind ne "param") { + $PPack = undef; + } + } + } + + # free memory + %CurID = (); + + foreach my $ID (@IDs) + { + if(not defined $DWARF_Info{$ID}) { + next; + } + + if(my $Kind = $DWARF_Info{$ID}{"kind"}) + { + if(defined $TypeType{$Kind} + and not defined $Cache{"getTypeInfo"}{$ID}) + { + getTypeInfo($ID); + } + } + } + + foreach my $Tid (@IDs) + { + if(defined $DWARF_Info{$Tid} + and defined $TypeInfo{$Tid}) + { + my $Type = $TypeInfo{$Tid}{"Type"}; + + if(not defined $TypeInfo{$Tid}{"Memb"}) + { + if($Type=~/Struct|Class|Union|Enum/) + { + if(my $Signature = $DWARF_Info{$Tid}{"signature"}) + { + if(defined $TypeInfo{$Signature}) + { + foreach my $Attr (keys(%{$TypeInfo{$Signature}})) + { + if(not defined $TypeInfo{$Tid}{$Attr}) { + $TypeInfo{$Tid}{$Attr} = $TypeInfo{$Signature}{$Attr}; + } + } + } + } + } + } + } + } + + # delete types info + foreach my $ID (@IDs) + { + if(not defined $DWARF_Info{$ID}) { + next; + } + + if(my $Kind = $DWARF_Info{$ID}{"kind"}) + { + if(defined $TypeType{$Kind}) { + delete($DWARF_Info{$ID}); + } + } + } + + foreach my $ID (@IDs) + { + if(not defined $DWARF_Info{$ID}) { + next; + } + + if($ID<0) + { # imported + next; + } + + if($DWARF_Info{$ID}{"kind"} eq "prog" + or $DWARF_Info{$ID}{"kind"} eq "variable") + { + getSymbolInfo($ID); + } + } + + if(defined $Compressed + and not defined $AllUnits) + { + if(not $Partial) + { + foreach my $ID (@IDs) + { + if($DWARF_Info{$ID}{"kind"} ne "base_type") { + delete($DWARF_Info{$ID}); + } + } + } + + # free memory + %TypeMember = (); + %ArrayCount = (); + %FuncParam = (); + %TmplParam = (); + } + else + { + %DWARF_Info = (); + + # free memory + %TypeMember = (); + %ArrayCount = (); + %FuncParam = (); + %TmplParam = (); + + %Inheritance = (); + %NameSpace = (); + %SpecElem = (); + %OrigElem = (); + %ClassMethods = (); + + %LexicalId = (); + + $Cache{"getTypeInfo"} = {"1"=>1, "-1"=>1}; + } + + @IDs = (); + @IDs_I = (); +} + +sub selectSymbols() +{ + foreach my $ID (sort {$a<=>$b} keys(%SymbolInfo)) + { + my $Symbol = $SymbolInfo{$ID}{"MnglName"}; + + if(not $Symbol) { + $Symbol = $SymbolInfo{$ID}{"ShortName"}; + } + + if(not $Symbol) + { + delete($SymbolInfo{$ID}); + next; + } + + my $S = selectSymbol($SymbolInfo{$ID}); + + if($S==0) + { + if(defined $AllSymbols) + { + if($SymbolInfo{$ID}{"External"}) + { + $S = 1; + } + else + { # local + if(defined $DumpStatic) { + $S = 1; + } + } + } + } + + if($S==0) + { + delete($SymbolInfo{$ID}); + next; + } + elsif(defined $PublicHeadersPath) + { + if(not selectPublic($Symbol, $ID) + and (not defined $SymbolInfo{$ID}{"Alias"} or not selectPublic($SymbolInfo{$ID}{"Alias"}, $ID))) + { + delete($SymbolInfo{$ID}); + next; + } + } + elsif(defined $KernelExport) + { + if(not defined $KSymTab{$Symbol}) + { + delete($SymbolInfo{$ID}); + next; + } + } + + $SelectedSymbols{$ID} = $S; + + delete($SymbolInfo{$ID}{"External"}); + } +} + +sub completeTypes($) +{ + my $Name = $_[0]; + + while($Name=~/T#(\d+)/) + { + my $Tid = $1; + + if(defined $TypeInfo{$Tid} + and my $TName = $TypeInfo{$Tid}{"Name"}) + { + $Name=~s/T#$Tid\b/$TName/g; + } + else + { + last; + } + } + + return formatName($Name, "T"); +} + +sub completeABI() +{ + # types + my %Incomplete = (); + my %Incomplete_TN = (); + + my @TIDs = sort {$a<=>$b} keys(%TypeInfo); + + if($AltDebugInfo) { + @TIDs = sort {$b>0<=>$a>0} sort {abs($a)<=>abs($b)} @TIDs; + } + + if(defined $Compressed + and not defined $AllUnits) + { + foreach my $Tid (@TIDs) + { + my $TName = $TypeInfo{$Tid}{"Name"}; + if(index($TName, "#")!=-1) + { + $TypeInfo{$Tid}{"Name"} = completeTypes($TName); + registerTName($Tid, $TypeInfo{$Tid}{"Name"}, $TypeInfo{$Tid}{"Type"}); + } + } + } + + foreach my $Tid (@TIDs) + { + my $Name = $TypeInfo{$Tid}{"Name"}; + my $Type = $TypeInfo{$Tid}{"Type"}; + + if(not defined $SpecElem{$Tid} + and not defined $Incomplete_TN{$Type}{$Name}) + { + if(not defined $TypeInfo{$Tid}{"Size"}) + { + if($Type=~/Struct|Class|Union|Enum/) + { + $Incomplete{$Tid} = 1; + } + } + } + + $Incomplete_TN{$Type}{$Name} = 1; + } + + # free memory + %Incomplete_TN = (); + + foreach my $Tid (sort {$a<=>$b} keys(%Incomplete)) + { + my $Name = $TypeInfo{$Tid}{"Name"}; + my $Type = $TypeInfo{$Tid}{"Type"}; + + my @Adv_TIDs = sort {$a<=>$b} keys(%{$TName_Tids{$Type}{$Name}}); + + if($AltDebugInfo) { + @Adv_TIDs = sort {$b>0<=>$a>0} sort {abs($a)<=>abs($b)} @Adv_TIDs; + } + + foreach my $Tid_Adv (@Adv_TIDs) + { + if($Tid_Adv!=$Tid) + { + if(defined $SpecElem{$Tid_Adv} + or defined $TypeInfo{$Tid_Adv}{"Size"}) + { + foreach my $Attr (keys(%{$TypeInfo{$Tid_Adv}})) + { + if(not defined $TypeInfo{$Tid}{$Attr}) + { + if(ref($TypeInfo{$Tid_Adv}{$Attr}) eq "HASH") { + $TypeInfo{$Tid}{$Attr} = dclone($TypeInfo{$Tid_Adv}{$Attr}); + } + else { + $TypeInfo{$Tid}{$Attr} = $TypeInfo{$Tid_Adv}{$Attr}; + } + + } + } + last; + } + } + } + } + + # free memory + %Incomplete = (); + + my %ReplacedAnon = (); + + foreach my $Tid (sort {$a<=>$b} keys(%TypeInfo)) + { + if($TypeInfo{$Tid}{"Type"} eq "Typedef") + { + my $TN = $TypeInfo{$Tid}{"Name"}; + my $TL = $TypeInfo{$Tid}{"Line"}; + my $NS = $TypeInfo{$Tid}{"NameSpace"}; + + if(my $BTid = $TypeInfo{$Tid}{"BaseType"}) + { + my $BName = $TypeInfo{$BTid}{"Name"}; + my $BType = $TypeInfo{$BTid}{"Type"}; + + if(defined $TypeInfo{$BTid} + and $BName=~/\Aanon\-(\w+)\-/ + and $BType=~/Enum|Struct|Union/) + { + $TypeInfo{$Tid} = dclone($TypeInfo{$BTid}); + $TypeInfo{$Tid}{"Name"} = lc($TypeInfo{$BTid}{"Type"})." ".$TN; + $TypeInfo{$Tid}{"Line"} = $TL; + + my $Name = $TypeInfo{$Tid}{"Name"}; + my $Type = $TypeInfo{$Tid}{"Type"}; + + registerTName($Tid, $Name, $Type); + + if($NS) { + $TypeInfo{$Tid}{"NameSpace"} = $NS; + } + + $DeletedAnon{$BTid} = $Tid; + foreach my $BTid_S (keys(%{$TName_Tids{$BType}{$BName}})) { + $DeletedAnon{$BTid_S} = $Tid; + } + } + } + } + elsif($TypeInfo{$Tid}{"Type"} eq "Pointer") + { + if(my $BTid = $TypeInfo{$Tid}{"BaseType"}) + { + my $To = undef; + + if(defined $DeletedAnon{$BTid}) { + $To = $DeletedAnon{$BTid}; + } + elsif(defined $ReplacedAnon{$BTid}) { + $To = $BTid; + } + + if($To) + { + $TypeInfo{$Tid}{"BaseType"} = $To; + $TypeInfo{$Tid}{"Name"} = $TypeInfo{$To}{"Name"}."*"; + + my $Name = $TypeInfo{$Tid}{"Name"}; + my $Type = $TypeInfo{$Tid}{"Type"}; + + $TName_Tid{$Type}{$Name} = $Tid; + $TName_Tids{$Type}{$Name}{$Tid} = 1; + + $ReplacedAnon{$Tid} = 1; + } + } + } + elsif($TypeInfo{$Tid}{"Type"} eq "Const") + { + if(my $BTid = $TypeInfo{$Tid}{"BaseType"}) + { + my $To = undef; + + if(defined $DeletedAnon{$BTid}) { + $To = $DeletedAnon{$BTid}; + } + elsif(defined $ReplacedAnon{$BTid}) { + $To = $BTid; + } + + if($To) + { + $TypeInfo{$Tid}{"BaseType"} = $To; + $TypeInfo{$Tid}{"Name"} = formatName($TypeInfo{$To}{"Name"}." const", "T"); + + my $Name = $TypeInfo{$Tid}{"Name"}; + my $Type = $TypeInfo{$Tid}{"Type"}; + + $TName_Tid{$Type}{$Name} = $Tid; + $TName_Tids{$Type}{$Name}{$Tid} = 1; + + $ReplacedAnon{$Tid} = 1; + } + } + } + } + + foreach my $Tid (keys(%DeletedAnon)) + { + my $TN = $TypeInfo{$Tid}{"Name"}; + my $TT = $TypeInfo{$Tid}{"Type"}; + + delete($TName_Tid{$TT}{$TN}); + delete($TName_Tids{$TT}{$TN}{$Tid}); + + if(my @TIDs = sort {$a<=>$b} keys(%{$TName_Tids{$TT}{$TN}})) + { # minimal ID + $TName_Tid{$TT}{$TN} = $TIDs[0]; + } + + delete($TypeInfo{$Tid}); + } + + # symbols + foreach my $ID (sort {$a<=>$b} keys(%SymbolInfo)) + { + if(defined $Compressed + and not defined $AllUnits) + { # replace late template arguments + my $ShortName = $SymbolInfo{$ID}{"ShortName"}; + + if(index($ShortName, "#")!=-1) { + $SymbolInfo{$ID}{"ShortName"} = completeTypes($ShortName); + } + } + + # add missed c-tors + if($SymbolInfo{$ID}{"Constructor"}) + { + if($SymbolInfo{$ID}{"MnglName"}=~/(C[1-2])([EI]).+/) + { + my ($K1, $K2) = ($1, $2); + foreach ("C1", "C2") + { + if($K1 ne $_) + { + my $Name = $SymbolInfo{$ID}{"MnglName"}; + $Name=~s/$K1$K2/$_$K2/; + + if(not defined $Mangled_ID{$Name}) { + $Mangled_ID{$Name} = cloneSymbol($ID, $Name); + } + } + } + } + } + + # add missed d-tors + if($SymbolInfo{$ID}{"Destructor"}) + { + if($SymbolInfo{$ID}{"MnglName"}=~/(D[0-2])([EI]).+/) + { + my ($K1, $K2) = ($1, $2); + foreach ("D0", "D1", "D2") + { + if($K1 ne $_) + { + my $Name = $SymbolInfo{$ID}{"MnglName"}; + $Name=~s/$K1$K2/$_$K2/; + + if(not defined $Mangled_ID{$Name}) { + $Mangled_ID{$Name} = cloneSymbol($ID, $Name); + } + } + } + } + } + } + + foreach my $ID (sort {$a<=>$b} keys(%SymbolInfo)) + { + my $SInfo = $SymbolInfo{$ID}; + my $Symbol = $SInfo->{"MnglName"}; + my $Short = $SInfo->{"ShortName"}; + + if(not $Symbol) { + $Symbol = $Short; + } + + if($LIB_LANG eq "C++") + { + if(not $SInfo->{"MnglName"}) + { + if($SInfo->{"Artificial"} + or index($Short, "~")==0) + { + delete($SymbolInfo{$ID}); + next; + } + } + } + + if($SInfo->{"Class"} + and not $SInfo->{"Data"} + and not $SInfo->{"Constructor"} + and not $SInfo->{"Destructor"} + and not $SInfo->{"Virt"} + and not $SInfo->{"PureVirt"}) + { + if(not defined $SInfo->{"Param"} + or $SInfo->{"Param"}{0}{"name"} ne "this") + { + $SInfo->{"Static"} = 1; + } + } + + if(not $SInfo->{"Return"}) + { # void + if(not $SInfo->{"Constructor"} + and not $SInfo->{"Destructor"}) + { + $SInfo->{"Return"} = "1"; + } + } + + if(not $SInfo->{"Header"}) + { + if($SInfo->{"Class"}) + { # detect missed header by class + if(defined $TypeInfo{$SInfo->{"Class"}}{"Header"}) { + $SInfo->{"Header"} = $TypeInfo{$SInfo->{"Class"}}{"Header"}; + } + } + } + + if(defined $PublicHeadersPath) { + fixHeader($SInfo); + } + + my $Header = $SInfo->{"Header"}; + + if(defined $SInfo->{"Source"} and defined $SInfo->{"SourceLine"}) + { + if(not defined $Header and not defined $SInfo->{"Line"}) + { + $SInfo->{"Line"} = $SInfo->{"SourceLine"}; + delete($SInfo->{"SourceLine"}); + } + } + + if(not $SInfo->{"Constructor"} + and not $SInfo->{"Destructor"}) + { + my $InLineDecl = delete($SInfo->{"DeclaredInlined"}); + + my $Bind = undef; + + if(defined $Symbol_Bind{$Symbol}) { + $Bind = $Symbol_Bind{$Symbol}; + } + elsif(my $SVer = $SymVer{$Symbol}) + { + if(defined $Symbol_Bind{$SVer}) { + $Bind = $Symbol_Bind{$SVer}; + } + } + + if($Bind ne "GLOBAL" and $Bind ne "LOCAL") + { + # Not enough info in the DWARF dump + if($Bind eq "WEAK") + { + if($InLineDecl) { + $SInfo->{"InLine"} = 1; + } + else { + $SInfo->{"InLine"} = 2; + } + } + + #if(not $SInfo->{"InLine"}) + #{ + # if(defined $PublicHeadersPath) + # { + # if($Short and defined $Header + # and defined $PublicHeader{$Header}) + # { + # if(defined $SymbolToHeader{$Short} + # and defined $SymbolToHeader{$Short}{$Header}) + # { + # if($SymbolToHeader{$Short}{$Header} eq "function") { + # $SInfo->{"InLine"} = 2; + # } + # } + # } + # } + #} + } + } + + if(defined $SInfo->{"PureVirt"}) { + delete($SInfo->{"InLine"}); + } + } +} + +sub warnPrivateType($$) +{ + my ($Name, $Note) = @_; + + if($Name=~/Private|Opaque/i) + { # _GstClockPrivate + # _Eo_Opaque + return; + } + + if($Name=~/(\A| )_/i) + { # _GstBufferList + return; + } + + if($Name=~/_\Z/i) + { # FT_RasterRec_ + return; + } + + printMsg("WARNING", "Private data type \'".$Name."\' ($Note)"); +} + +sub warnPrivateSymbol($$) +{ + my ($Name, $Note) = @_; + printMsg("WARNING", "Private symbol \'".$Name."\' ($Note)"); +} + +sub selectPublicType($) +{ + my $Tid = $_[0]; + + if($TypeInfo{$Tid}{"Type"}!~/\A(Struct|Class|Union|Enum|Typedef)\Z/) { + return 1; + } + + my $TName = $TypeInfo{$Tid}{"Name"}; + $TName=~s/\A(struct|class|union|enum) //g; + + my $Header = getFilename($TypeInfo{$Tid}{"Header"}); + + if($OBJ_LANG eq "C++" + or index($TName, "anon-")==0) { + return ($Header and defined $PublicHeader{$Header}); + } + + if($Header) + { + if(not defined $PublicHeader{$Header}) + { + if(not defined $TypeToHeader{$TName}) { + return 0; + } + } + elsif($MixedHeaders) + { + if(not defined $TypeToHeader{$TName}) + { + if(defined $Debug) { + warnPrivateType($TypeInfo{$Tid}{"Name"}, "NOT_FOUND"); + } + return 0; + } + } + } + else + { + if(not defined $TypeToHeader{$TName}) + { + # if(defined $Debug) { + # warnPrivateType($TypeInfo{$Tid}{"Name"}, "NO_HEADER"); + # } + return 0; + } + } + + return 1; +} + +sub selectPublic($$) +{ + my ($Symbol, $ID) = @_; + + my $Header = getFilename($SymbolInfo{$ID}{"Header"}); + + if($OBJ_LANG eq "C++") { + return ($Header and defined $PublicHeader{$Header}); + } + + if($Header) + { + if(not defined $PublicHeader{$Header}) + { + if(not defined $SymbolToHeader{$Symbol}) { + return 0; + } + } + elsif($MixedHeaders) + { + if(not defined $SymbolToHeader{$Symbol}) + { + if(defined $Debug) { + warnPrivateSymbol($Symbol, "NOT_FOUND"); + } + return 0; + } + } + } + else + { + if(not defined $SymbolToHeader{$Symbol}) + { + # if(defined $Debug) { + # warnPrivateSymbol($Symbol, "NO_HEADER"); + # } + return 0; + } + } + + return 1; +} + +sub cloneSymbol($$) +{ + my ($ID, $Symbol) = @_; + + my $nID = undef; + if(not defined $SymbolInfo{$ID + 1}) { + $nID = $ID + 1; + } + else { + $nID = ++$GLOBAL_ID; + } + foreach my $Attr (keys(%{$SymbolInfo{$ID}})) + { + if(ref($SymbolInfo{$ID}{$Attr}) eq "HASH") { + $SymbolInfo{$nID}{$Attr} = dclone($SymbolInfo{$ID}{$Attr}); + } + else { + $SymbolInfo{$nID}{$Attr} = $SymbolInfo{$ID}{$Attr}; + } + } + $SymbolInfo{$nID}{"MnglName"} = $Symbol; + return $nID; +} + +sub selectSymbol($) +{ + my $SInfo = $_[0]; + + my $MnglName = $SInfo->{"MnglName"}; + + if(not $MnglName) { + $MnglName = $SInfo->{"ShortName"}; + } + + if($SymbolsListPath + and not $SymbolsList{$MnglName}) + { + next; + } + + my $Exp = 0; + + if($Library_Symbol{$TargetName}{$MnglName} + or $Library_Symbol{$TargetName}{$SymVer{$MnglName}}) + { + $Exp = 1; + } + + if(my $Alias = $SInfo->{"Alias"}) + { + if($Library_Symbol{$TargetName}{$Alias} + or $Library_Symbol{$TargetName}{$SymVer{$Alias}}) + { + $Exp = 1; + } + } + + if(not $Exp) + { + if(defined $Library_UndefSymbol{$TargetName}{$MnglName} + or defined $Library_UndefSymbol{$TargetName}{$SymVer{$MnglName}}) + { + return 0; + } + + if($SInfo->{"Data"} + or $SInfo->{"InLine"} + or $SInfo->{"PureVirt"}) + { + if(not $SInfo->{"External"}) + { # skip static + return 0; + } + + if(defined $BinOnly) + { # data, inline, pure + return 0; + } + elsif(not defined $SInfo->{"Header"}) + { # defined in source files + return 0; + } + else { + return 2; + } + } + else { + return 0; + } + } + + return 1; +} + +sub formatName($$) +{ # type name correction + if(defined $Cache{"formatName"}{$_[1]}{$_[0]}) { + return $Cache{"formatName"}{$_[1]}{$_[0]}; + } + + my $N = $_[0]; + + if($_[1] ne "S") + { + $N=~s/\A[ ]+//g; + $N=~s/[ ]+\Z//g; + $N=~s/[ ]{2,}/ /g; + } + + $N=~s/[ ]*(\W)[ ]*/$1/g; # std::basic_string const + + $N=~s/\b(const|volatile) ([\w\:]+)([\*&,>]|\Z)/$2 $1$3/g; # "const void" to "void const" + + $N=~s/\bvolatile const\b/const volatile/g; + + $N=~s/\b(long long|short|long) unsigned\b/unsigned $1/g; + $N=~s/\b(short|long) int\b/$1/g; + + $N=~s/([\)\]])(const|volatile)\b/$1 $2/g; + + while($N=~s/>>/> >/g) {}; + + if($_[1] eq "S") + { + if(index($N, "operator")!=-1) { + $N=~s/\b(operator[ ]*)> >/$1>>/; + } + } + + $N=~s/,/, /g; + + if(defined $LambdaSupport) + { # struct {lambda()} + $N=~s/(\w)\{/$1 \{/g; + } + + return ($Cache{"formatName"}{$_[1]}{$_[0]} = $N); +} + +sub sepParams($) +{ + my $Str = $_[0]; + my @Parts = (); + my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 ); + my $Part = 0; + foreach my $Pos (0 .. length($Str) - 1) + { + my $S = substr($Str, $Pos, 1); + if(defined $B{$S}) { + $B{$S} += 1; + } + if($S eq "," and + $B{"("}==$B{")"} and $B{"<"}==$B{">"}) { + $Part += 1; + } + else { + $Parts[$Part] .= $S; + } + } + # remove spaces + foreach (@Parts) + { + s/\A //g; + s/ \Z//g; + } + return @Parts; +} + +sub initFuncType($$$) +{ + my ($TInfo, $FTid, $Type) = @_; + + $TInfo->{"Type"} = $Type; + + if($TInfo->{"Return"} = $DWARF_Info{$FTid}{"type"}) { + getTypeInfo($TInfo->{"Return"}); + } + else + { # void + $TInfo->{"Return"} = "1"; + } + delete($TInfo->{"BaseType"}); + + my @Prms = (); + my $PPos = 0; + foreach my $Pos (sort {$a<=>$b} keys(%{$FuncParam{$FTid}})) + { + my $ParamId = $FuncParam{$FTid}{$Pos}; + my %PInfo = %{$DWARF_Info{$ParamId}}; + + if(defined $PInfo{"art"}) + { # this + next; + } + + if(my $PTypeId = $PInfo{"type"}) + { + $TInfo->{"Param"}{$PPos}{"type"} = $PTypeId; + getTypeInfo($PTypeId); + push(@Prms, $TypeInfo{$PTypeId}{"Name"}); + } + + $PPos += 1; + } + + $TInfo->{"Name"} = $TypeInfo{$TInfo->{"Return"}}{"Name"}; + if($Type eq "FuncPtr") { + $TInfo->{"Name"} .= "(*)"; + } + $TInfo->{"Name"} .= "(".join(",", @Prms).")"; +} + +sub getShortName($) +{ + my $Name = $_[0]; + + if(my $C = findCenter($Name, "<")) + { + return substr($Name, 0, $C); + } + + return $Name; +} + +sub getTKeys($) +{ + my @TParams = @{$_[0]}; + + my @TKeys = (); + + foreach my $Pos (0 .. $#TParams) + { + my $TRef = $TParams[$Pos]; + + if(defined $Compressed + and not defined $AllUnits) + { # not all types are available in the current compile unit + # so handling them later + my $Key = undef; + + if(defined $TRef->{"val"}) { + $Key = computeValue($TRef); + } + elsif(defined $TRef->{"name"}) { + $Key = $TRef->{"name"}; + } + elsif(my $KeyT = $TRef->{"type"}) + { + if(defined $TypeInfo{$KeyT} + and my $TN = $TypeInfo{$KeyT}{"Name"}) + { + if(index($TN, "#")==-1) { + $Key = simpleName($TN); + } + else { + $Key = "T#".$KeyT; + } + } + else { + $Key = "T#".$KeyT; + } + } + + push(@TKeys, $Key); + } + else + { + my $Key = undef; + + if(defined $TRef->{"val"}) { + $Key = computeValue($TRef); + } + elsif(my $KeyT = $TRef->{"type"}) { + $Key = simpleName($TypeInfo{$KeyT}{"Name"}); + } + else { + $Key = $TRef->{"name"}; + } + + push(@TKeys, $Key); + } + } + + return @TKeys; +} + +sub getTParams($$) +{ + my ($ID, $Name) = @_; + + my ($Short, $TParams) = (); + + if(defined $TmplParam{$ID}) + { + $Short = getShortName($Name); + $TParams = getTParams_I($ID); + + my ($AddShort, $AddParam) = (); + + foreach my $Pos (0 .. $#{$TParams}) + { + my $P = $TParams->[$Pos]; + if(not defined $P->{"val"} + and defined $P->{"type"}) + { + my $TTid = $P->{"type"}; + if(not defined $TypeInfo{$TTid} + or not $TypeInfo{$TTid}{"Name"}) + { + if(not $AddParam) { + ($AddShort, $AddParam) = parseTParams($Name); + } + + if($Pos<=$#{$AddParam}) { + $P->{"name"} = $AddParam->[$Pos]{"name"}; + } + } + } + } + } + else { + ($Short, $TParams) = parseTParams($Name); + } + + if(not $TParams) { + return (); + } + + return ($Short, $TParams); +} + +sub getTParams_I($) +{ + my $ID = $_[0]; + + my @TParams = (); + + foreach my $Pos (sort {$a<=>$b} keys(%{$TmplParam{$ID}})) + { + my $TTid = $TmplParam{$ID}{$Pos}{"type"}; + + if($DWARF_Info{$TTid}{"kind"} eq "typedef") { + $TTid = $DWARF_Info{$TTid}{"type"}; + } + + my $Val = undef; + my $Key = undef; + + if(defined $TmplParam{$ID}{$Pos}{"value"}) { + $Val = $TmplParam{$ID}{$Pos}{"value"}; + } + + if(defined $TmplParam{$ID}{$Pos}{"key"}) { + $Key = $TmplParam{$ID}{$Pos}{"key"}; + } + + if($Pos>0) + { + if(defined $TmplParam{$ID}{$Pos}{"default"}) + { + if($Key=~/\A(_Alloc|_Traits|_Compare)\Z/) + { + next; + } + } + } + + getTypeInfo($TTid); + + my %PInfo = ( + "type"=>$TTid, + "key"=>$Key + ); + + if(defined $Val) { + $PInfo{"val"} = $Val; + } + + push(@TParams, \%PInfo); + } + + return \@TParams; +} + +sub parseTParams($) +{ + my $Name = $_[0]; + + if(my $Cent = findCenter($Name, "<")) + { + my $TParams = substr($Name, $Cent); + my $Short = substr($Name, 0, $Cent); + + $TParams=~s/\A<|>\Z//g; + $TParams = simpleName($TParams); + + my @Params = sepParams($TParams); + @Params = shortTParams($Short, \@Params); + + my @TParams = (); + foreach my $Pos (0 .. $#Params) + { + my $Param = $Params[$Pos]; + if($Param=~/\A(.+>)(.*?)\Z/) + { + my ($Tm, $Suf) = ($1, $2); + my ($Sh, $Prm) = parseTParams($Tm); + + if($Prm) + { + my @Keys = (); + foreach my $P (@{$Prm}) { + push(@Keys, $P->{"name"}); + } + + $Param = $Sh."<".join(", ", @Keys).">".$Suf; + } + } + my %PInfo = ( + "name"=>formatName($Param, "T") + ); + push(@TParams, \%PInfo); + } + + return ($Short, \@TParams); + } + + return (); # error +} + +sub shortTParams($$) +{ + my $Short = $_[0]; + my @Params = @{$_[1]}; + + # default arguments + if($Short eq "std::vector") + { + if($#Params==1) + { + if($Params[1] eq "std::allocator<".$Params[0].">") + { # std::vector > + splice(@Params, 1, 1); + } + } + } + elsif($Short eq "std::set") + { + if($#Params==2) + { + if($Params[1] eq "std::less<".$Params[0].">" + and $Params[2] eq "std::allocator<".$Params[0].">") + { # std::set, std::allocator > + splice(@Params, 1, 2); + } + } + } + elsif($Short eq "std::basic_string") + { + if($#Params==2) + { + if($Params[1] eq "std::char_traits<".$Params[0].">" + and $Params[2] eq "std::allocator<".$Params[0].">") + { # std::basic_string, std::allocator > + splice(@Params, 1, 2); + } + } + } + elsif($Short eq "std::basic_ostream") + { + if($#Params==1) + { + if($Params[1] eq "std::char_traits<".$Params[0].">") + { # std::basic_ostream > + splice(@Params, 1, 1); + } + } + } + + return @Params; +} + +sub getTypeInfo($) +{ + my $ID = $_[0]; + + if(not defined $DWARF_Info{$ID}) { + return; + } + + if(not keys(%{$DWARF_Info{$ID}})) + { + delete($DWARF_Info{$ID}); + return; + } + + my $Kind = $DWARF_Info{$ID}{"kind"}; + + if(defined $Cache{"getTypeInfo"}{$ID}) { + return; + } + + if(my $N = $NameSpace{$ID}) + { + if($DWARF_Info{$N}{"kind"} eq "prog") + { # local code + # template instances are declared in the subprogram (constructor) + my $Tmpl = 0; + if(my $ObjP = $DWARF_Info{$N}{"objptr"}) + { + while($DWARF_Info{$ObjP}{"type"}) { + $ObjP = $DWARF_Info{$ObjP}{"type"}; + } + my $CName = $DWARF_Info{$ObjP}{"name"}; + $CName=~s/<.*//g; + if($CName eq $DWARF_Info{$N}{"name"}) { + $Tmpl = 1; + } + } + if(not $Tmpl) + { # local types + $LocalType{$ID} = 1; + } + } + elsif($DWARF_Info{$N}{"kind"} eq "lexical_block") + { # local code + return; + } + } + + $Cache{"getTypeInfo"}{$ID} = 1; + + my %TInfo = (); + + $TInfo{"Type"} = $TypeType{$Kind}; + + if(not $TInfo{"Type"}) + { + if($DWARF_Info{$ID}{"kind"} eq "subroutine_type") { + $TInfo{"Type"} = "Func"; + } + } + + if($DWARF_Info{$ID}{"name"} eq "__unknown__") + { # size of such type may vary + delete($DWARF_Info{$ID}{"size"}); + } + + if(defined $SYS_CLANGV + and $TInfo{"Type"} eq "FieldPtr") + { # support for Clang + if(my $T = $DWARF_Info{$ID}{"type"}) + { + if($DWARF_Info{$T}{"kind"} eq "subroutine_type") + { + $TInfo{"Type"} = "MethodPtr"; + $DWARF_Info{$ID}{"pfn"} = $T; + $DWARF_Info{$T}{"objptr"} = $DWARF_Info{$ID}{"container"}; + } + } + } + + my $RealType = $TInfo{"Type"}; + + if(defined $ClassMethods{$ID}) + { + if($TInfo{"Type"} eq "Struct") { + $RealType = "Class"; + } + } + + if($TInfo{"Type"} ne "Enum" + and my $BaseType = $DWARF_Info{$ID}{"type"}) + { + $TInfo{"BaseType"} = "$BaseType"; + + if(defined $TypeType{$DWARF_Info{$BaseType}{"kind"}}) + { + getTypeInfo($TInfo{"BaseType"}); + + if(not defined $TypeInfo{$TInfo{"BaseType"}} + or not $TypeInfo{$TInfo{"BaseType"}}{"Name"}) + { # local code + delete($TypeInfo{$ID}); + return; + } + } + } + + if($RealType eq "Class") { + $TInfo{"Copied"} = 1; # will be changed in getSymbolInfo() + } + + if(defined $TypeMember{$ID}) + { + my $Unnamed = 0; + foreach my $Pos (sort {$a<=>$b} keys(%{$TypeMember{$ID}})) + { + my $MemId = $TypeMember{$ID}{$Pos}; + my $MInfo = $DWARF_Info{$MemId}; + + if(my $Name = $MInfo->{"name"}) + { + if(index($Name, "_vptr.")==0) + { # v-table pointer + $Name="_vptr"; + } + $TInfo{"Memb"}{$Pos}{"name"} = $Name; + } + else + { + $TInfo{"Memb"}{$Pos}{"name"} = "unnamed".$Unnamed; + $Unnamed += 1; + } + if($TInfo{"Type"} eq "Enum") { + $TInfo{"Memb"}{$Pos}{"value"} = $MInfo->{"cval"}; + } + else + { + $TInfo{"Memb"}{$Pos}{"type"} = $MInfo->{"type"}; + if(my $Access = $MInfo->{"access"}) + { + if($Access ne "public") + { # NOTE: default access of members in the ABI dump is "public" + $TInfo{"Memb"}{$Pos}{"access"} = $Access; + } + } + else + { + if($DWARF_Info{$ID}{"kind"} eq "class_type") + { # NOTE: default access of class members in the debug info is "private" + $TInfo{"Memb"}{$Pos}{"access"} = "private"; + } + else + { + # NOTE: default access of struct members in the debug info is "public" + } + } + if($TInfo{"Type"} eq "Union") { + $TInfo{"Memb"}{$Pos}{"offset"} = "0"; + } + elsif(defined $MInfo->{"mloc"}) { + $TInfo{"Memb"}{$Pos}{"offset"} = $MInfo->{"mloc"}; + } + } + + if((my $BitSize = $MInfo->{"bit_size"}) ne "") { + $TInfo{"Memb"}{$Pos}{"bitfield"} = $BitSize; + } + } + } + + my $NS = $NameSpace{$ID}; + if(not $NS) + { + if(my $Sp = $DWARF_Info{$ID}{"spec"}) { + $NS = $NameSpace{$Sp}; + } + } + + if($NS and $DWARF_Info{$NS}{"kind"}=~/\A(class_type|structure_type)\Z/) + { # member class + if(my $Access = $DWARF_Info{$ID}{"access"}) + { + if($Access ne "public") + { # NOTE: default access of member classes in the ABI dump is "public" + $TInfo{ucfirst($Access)} = 1; + } + } + else + { + if($DWARF_Info{$NS}{"kind"} eq "class_type") + { + # NOTE: default access of member classes in the debug info is "private" + $TInfo{"Private"} = 1; + } + else + { + # NOTE: default access to struct member classes in the debug info is "public" + } + } + } + else + { + if(my $Access = $DWARF_Info{$ID}{"access"}) + { + if($Access ne "public") + { # NOTE: default access of classes in the ABI dump is "public" + $TInfo{ucfirst($Access)} = 1; + } + } + } + + my $Size = $DWARF_Info{$ID}{"size"}; + if($Size ne "") { + $TInfo{"Size"} = $Size; + } + + setSource(\%TInfo, $ID); + + if(not $DWARF_Info{$ID}{"name"} + and my $Spec = $DWARF_Info{$ID}{"spec"}) { + $DWARF_Info{$ID}{"name"} = $DWARF_Info{$Spec}{"name"}; + } + + if($NS) + { + if($DWARF_Info{$NS}{"kind"} eq "namespace") + { + if(my $NS_F = completeNS($ID)) + { + $TInfo{"NameSpace"} = $NS_F; + } + } + elsif($DWARF_Info{$NS}{"kind"} eq "class_type" + or $DWARF_Info{$NS}{"kind"} eq "structure_type") + { # class + getTypeInfo($NS); + + if(my $Sp = $SpecElem{$NS}) { + getTypeInfo($Sp); + } + + if($TypeInfo{$NS}{"Name"}) + { + $TInfo{"NameSpace"} = $TypeInfo{$NS}{"Name"}; + $TInfo{"NameSpace"}=~s/\Astruct //; + } + } + } + + if(my $Name = $DWARF_Info{$ID}{"name"}) + { + $TInfo{"Name"} = $Name; + + if($TInfo{"NameSpace"}) { + $TInfo{"Name"} = $TInfo{"NameSpace"}."::".$TInfo{"Name"}; + } + + if($TInfo{"Type"}=~/\A(Struct|Enum|Union)\Z/) { + $TInfo{"Name"} = lc($TInfo{"Type"})." ".$TInfo{"Name"}; + } + } + + if($TInfo{"Type"} eq "Struct") + { + if(not $TInfo{"Name"}) + { + if(defined $TInfo{"Memb"} + and $TInfo{"Memb"}{0}{"name"} eq "__pfn") + { # __pfn and __delta + my $Pfn = $TInfo{"Memb"}{0}{"type"}; + if(my $Pfn_B = $DWARF_Info{$Pfn}{"type"}) + { + if($DWARF_Info{$Pfn_B}{"kind"} eq "subroutine_type") + { + $TInfo{"Type"} = "MethodPtr"; + } + } + } + } + } + + if($TInfo{"Type"}=~/Pointer|Ptr|Ref/) + { + if(not $TInfo{"Size"}) { + $TInfo{"Size"} = $SYS_WORD; + } + } + + if($TInfo{"Type"} eq "Pointer") + { + if($DWARF_Info{$TInfo{"BaseType"}}{"kind"} eq "subroutine_type") + { + initFuncType(\%TInfo, $TInfo{"BaseType"}, "FuncPtr"); + } + } + elsif($TInfo{"Type"}=~/Typedef|Const|Volatile/) + { + if($DWARF_Info{$TInfo{"BaseType"}}{"kind"} eq "subroutine_type") + { + getTypeInfo($TInfo{"BaseType"}); + } + } + elsif($TInfo{"Type"} eq "Func") + { + initFuncType(\%TInfo, $ID, "Func"); + } + elsif($TInfo{"Type"} eq "MethodPtr") + { + my $Pfn_B = undef; + + if(defined $TInfo{"Memb"} + and $TInfo{"Memb"}{0}{"name"} eq "__pfn") + { + if(my $Pfn = $TInfo{"Memb"}{0}{"type"}) { + $Pfn_B = $DWARF_Info{$Pfn}{"type"}; + } + } + else + { # support for Clang + $Pfn_B = $DWARF_Info{$ID}{"pfn"}; + } + + if($Pfn_B) + { + my @Prms = (); + my $PPos = 0; + foreach my $Pos (sort {$a<=>$b} keys(%{$FuncParam{$Pfn_B}})) + { + my $ParamId = $FuncParam{$Pfn_B}{$Pos}; + my %PInfo = %{$DWARF_Info{$ParamId}}; + + if(defined $PInfo{"art"}) + { # this + next; + } + + if(my $PTypeId = $PInfo{"type"}) + { + $TInfo{"Param"}{$PPos}{"type"} = $PTypeId; + getTypeInfo($PTypeId); + push(@Prms, $TypeInfo{$PTypeId}{"Name"}); + } + + $PPos += 1; + } + + if(my $ClassId = $DWARF_Info{$Pfn_B}{"objptr"}) + { + while($DWARF_Info{$ClassId}{"type"}) { + $ClassId = $DWARF_Info{$ClassId}{"type"}; + } + $TInfo{"Class"} = $ClassId; + getTypeInfo($TInfo{"Class"}); + } + + if($TInfo{"Return"} = $DWARF_Info{$Pfn_B}{"type"}) { + getTypeInfo($TInfo{"Return"}); + } + else + { # void + $TInfo{"Return"} = "1"; + } + + $TInfo{"Name"} = createMethodPtrName(\%TInfo); + + delete($TInfo{"BaseType"}); + } + } + elsif($TInfo{"Type"} eq "FieldPtr") + { + $TInfo{"Return"} = $TInfo{"BaseType"}; + delete($TInfo{"BaseType"}); + + if(my $Class = $DWARF_Info{$ID}{"container"}) + { + $TInfo{"Class"} = $Class; + getTypeInfo($TInfo{"Class"}); + + $TInfo{"Name"} = createFieldPtrName(\%TInfo); + } + + $TInfo{"Size"} = $SYS_WORD; + } + elsif($TInfo{"Type"} eq "String") + { + $TInfo{"Type"} = "Pointer"; + $TInfo{"Name"} = "char*"; + $TInfo{"BaseType"} = $TName_Tid{"Intrinsic"}{"char"}; + } + + if(defined $Inheritance{$ID}) + { + foreach my $Pos (sort {$a<=>$b} keys(%{$Inheritance{$ID}})) + { + if(my $BaseId = $Inheritance{$ID}{$Pos}{"id"}) + { + if(my $E = $SpecElem{$BaseId}) { + $BaseId = $E; + } + + $TInfo{"Base"}{$BaseId}{"pos"} = "$Pos"; + if(my $Access = $Inheritance{$ID}{$Pos}{"access"}) { + $TInfo{"Base"}{$BaseId}{"access"} = $Access; + } + if($Inheritance{$ID}{$Pos}{"virtual"}) { + $TInfo{"Base"}{$BaseId}{"virtual"} = 1; + } + + $ClassChild{$BaseId}{$ID} = 1; + } + } + } + + if(not $TInfo{"BaseType"}) + { + if($TInfo{"Type"} eq "Pointer") + { + $TInfo{"Name"} = "void*"; + $TInfo{"BaseType"} = "1"; + } + elsif($TInfo{"Type"} eq "Const") + { + $TInfo{"Name"} = "const void"; + $TInfo{"BaseType"} = "1"; + } + elsif($TInfo{"Type"} eq "Volatile") + { + $TInfo{"Name"} = "volatile void"; + $TInfo{"BaseType"} = "1"; + } + elsif($TInfo{"Type"} eq "Typedef") + { + $TInfo{"BaseType"} = "1"; + } + } + + if(not $TInfo{"Name"} + and $TInfo{"Type"} ne "Enum") + { + my $ID_ = $ID; + my $BaseID = undef; + my $Name = ""; + + while($BaseID = $DWARF_Info{$ID_}{"type"}) + { + my $Kind = $DWARF_Info{$ID_}{"kind"}; + if(my $Q = $Qual{$TypeType{$Kind}}) + { + $Name = $Q.$Name; + if($Q=~/\A\w/) { + $Name = " ".$Name; + } + } + if(defined $TypeInfo{$BaseID} + and $TypeInfo{$BaseID}{"Name"}) + { + $Name = $TypeInfo{$BaseID}{"Name"}.$Name; + last; + } + elsif(defined $DWARF_Info{$BaseID} + and $DWARF_Info{$BaseID}{"name"}) + { + $Name = $DWARF_Info{$BaseID}{"name"}.$Name; + $ID_ = $BaseID; + } + elsif(defined $Compressed + and not defined $AllUnits) + { + $Name = "T#".$BaseID.$Name; + last; + } + else + { # error + last; + } + } + + if($Name) { + $TInfo{"Name"} = $Name; + } + + if($TInfo{"Type"} eq "Array") + { + if(my $Count = $ArrayCount{$ID}) + { + $TInfo{"Name"} .= "[".$Count."]"; + if(my $BType = $TInfo{"BaseType"}) + { + if(my $BSize = $TypeInfo{$BType}{"Size"}) + { + if(my $Size = $Count*$BSize) + { + $TInfo{"Size"} = "$Size"; + } + } + } + } + else + { + $TInfo{"Name"} .= "[]"; + $TInfo{"Size"} = $SYS_WORD; + } + } + elsif($TInfo{"Type"} eq "Pointer") + { + if(my $BType = $TInfo{"BaseType"}) + { + if($TypeInfo{$BType}{"Type"}=~/MethodPtr|FuncPtr/) + { # void(GTestSuite::**)() + # int(**)(...) + if($TInfo{"Name"}=~s/\*\Z//) { + $TInfo{"Name"}=~s/\*(\))/\*\*$1/; + } + } + } + } + } + + if(my $Bid = $TInfo{"BaseType"}) + { + if(not $TInfo{"Size"} + and $TypeInfo{$Bid}{"Size"}) { + $TInfo{"Size"} = $TypeInfo{$Bid}{"Size"}; + } + } + if($TInfo{"Name"}) { + $TInfo{"Name"} = formatName($TInfo{"Name"}, "T"); + } + + if($TInfo{"Name"}=~/>\Z/) + { + my ($Short, $TParams) = getTParams($ID, $TInfo{"Name"}); + + if($TParams) + { + delete($TInfo{"TParam"}); + + foreach my $Pos (0 .. $#{$TParams}) { + $TInfo{"TParam"}{$Pos} = $TParams->[$Pos]; + } + + my @TKeys = getTKeys($TParams); + @TKeys = shortTParams($Short, \@TKeys); + + $TInfo{"Name"} = formatName($Short."<".join(", ", @TKeys).">", "T"); + } + } + + if(not $TInfo{"Name"}) + { + if($TInfo{"Type"}=~/\A(Class|Struct|Enum|Union)\Z/) + { + if($TInfo{"Header"}) { + $TInfo{"Name"} = "anon-".lc($TInfo{"Type"})."-".$TInfo{"Header"}."-".$TInfo{"Line"}; + } + elsif($TInfo{"Source"}) { + $TInfo{"Name"} = "anon-".lc($TInfo{"Type"})."-".$TInfo{"Source"}."-".$TInfo{"SourceLine"}; + } + else + { + if(not defined $TypeMember{$ID}) + { + if(not defined $ANON_TYPE_WARN{$TInfo{"Type"}}) + { + printMsg("WARNING", "a \"".$TInfo{"Type"}."\" type with no attributes detected in the DWARF dump ($ID)"); + $ANON_TYPE_WARN{$TInfo{"Type"}} = 1; + } + $TInfo{"Name"} = "anon-".lc($TInfo{"Type"}); + } + } + + if($TInfo{"Name"} and $TInfo{"NameSpace"}) { + $TInfo{"Name"} = $TInfo{"NameSpace"}."::".$TInfo{"Name"}; + } + } + } + + if($TInfo{"Name"}) { + registerTName($ID, $TInfo{"Name"}, $TInfo{"Type"}); + } + + if(defined $TInfo{"Source"}) + { + if(not defined $TInfo{"Header"}) + { + $TInfo{"Line"} = $TInfo{"SourceLine"}; + delete($TInfo{"SourceLine"}); + } + } + + foreach my $Attr (keys(%TInfo)) { + $TypeInfo{$ID}{$Attr} = $TInfo{$Attr}; + } + + if(my $BASE_ID = $DWARF_Info{$ID}{"spec"}) + { + foreach my $Attr (keys(%{$TypeInfo{$BASE_ID}})) + { + if($Attr ne "Type") { + $TypeInfo{$ID}{$Attr} = $TypeInfo{$BASE_ID}{$Attr}; + } + } + + foreach my $Attr (keys(%{$TypeInfo{$ID}})) { + $TypeInfo{$BASE_ID}{$Attr} = $TypeInfo{$ID}{$Attr}; + } + + $TypeSpec{$ID} = $BASE_ID; + } + + # remove duplicates + my $DId = undef; + + if(defined $DuplBaseType{$TInfo{"Name"}}) { + $DId = $DuplBaseType{$TInfo{"Name"}}; + } + else + { + my @DIds = sort {$a<=>$b} keys(%{$TName_Tids{$TInfo{"Type"}}{$TInfo{"Name"}}}); + + if($#DIds>0) + { + foreach (@DIds) + { + if($_>0) + { + $DId = $_; + last; + } + } + } + } + + if($DId and $DId ne $ID) + { + my $TInfo_D = $TypeInfo{$DId}; + if(keys(%{$TInfo_D})==keys(%{$TypeInfo{$ID}})) + { + $TypeInfo{$ID} = $TInfo_D; + $DuplBaseType{$TInfo{"Name"}} = $DId; + } + } +} + +sub registerTName($$$) +{ + my ($ID, $Name, $Type) = @_; + + if(not defined $TName_Tid{$Type}{$Name} + or ($ID>0 and $ID<$TName_Tid{$Type}{$Name}) + or ($ID>0 and $TName_Tid{$Type}{$Name}<0)) + { + $TName_Tid{$Type}{$Name} = "$ID"; + } + $TName_Tids{$Type}{$Name}{$ID} = 1; +} + +sub createMethodPtrName($) +{ + my $TInfo = $_[0]; + + my @Prms = (); + + if($TInfo->{"Param"}) + { + foreach my $Pos (sort {$a<=>$b} keys(%{$TInfo->{"Param"}})) { + push(@Prms, $TypeInfo{$TInfo->{"Param"}{$Pos}{"type"}}{"Name"}); + } + } + + my $TName = $TypeInfo{$TInfo->{"Return"}}{"Name"}; + $TName .= "(".$TypeInfo{$TInfo->{"Class"}}{"Name"}."::*)"; + $TName .= "(".join(",", @Prms).")"; + + return $TName; +} + +sub createFieldPtrName($) +{ + my $TInfo = $_[0]; + + return $TypeInfo{$TInfo->{"Return"}}{"Name"}."(".$TypeInfo{$TInfo->{"Class"}}{"Name"}."::*)"; +} + +sub computeValue($) +{ + my $Ref = $_[0]; + + my $TTid = $Ref->{"type"}; + my $TTName = $TypeInfo{$TTid}{"Name"}; + + my $Val = $Ref->{"val"}; + + if($TTName eq "bool") + { + if($Val eq "1") { + $Val = "true"; + } + elsif($Val eq "0") { + $Val = "false"; + } + } + else + { + if($Val=~/\A\d+\Z/) + { + if(my $S = $ConstSuffix{$TTName}) { + $Val .= $S; + } + } + } + + return $Val; +} + +sub setSource(@) +{ + my $R = shift(@_); + my $ID = shift(@_); + my $Target = undef; + + if(@_) { + $Target = shift(@_); + } + + my $File = $DWARF_Info{$ID}{"file"}; + my $Line = $DWARF_Info{$ID}{"line"}; + + if(defined $File) + { + if(index($File, "(")!=-1) + { # Support for new elfutils (Fedora 30) + $File=~s/.+ \((\d+)\)/$1/; + } + + my $Name = undef; + + if($ID>=0) { + $Name = $SourceFile{$DWARF_Info{$ID}{"unit"}}{$File}; + } + else + { # imported + $Name = $SourceFile_Alt{0}{$File}; + } + + if($Name=~/\.($HEADER_EXT)\Z/i + or index($Name, ".")==-1) + { # header + if(not defined $Target or $Target eq "Header") + { + $R->{"Header"} = $Name; + if(defined $Line) { + $R->{"Line"} = $Line; + } + } + elsif($Target eq "Line") + { + if(defined $Line and $R->{"Header"} eq $Name) { + $R->{"Line"} = $Line; + } + } + } + elsif(index($Name, "")==-1) + { # source + if(not defined $Target or $Target eq "Source") + { + $R->{"Source"} = $Name; + if(defined $Line) { + $R->{"SourceLine"} = $Line; + } + } + } + } +} + +sub skipSymbol($) +{ + if($SkipCxx and not $STDCXX_TARGET) + { + if($_[0]=~/\A(_ZS|_ZNS|_ZNKS|_ZN9__gnu_cxx|_ZNK9__gnu_cxx|_ZTIS|_ZTSS|_Zd|_Zn)/) + { # stdc++ symbols + return 1; + } + } + return 0; +} + +sub findCenter($$) +{ + my ($Name, $Target) = @_; + my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 ); + foreach my $Pos (0 .. length($Name)-1) + { + my $S = substr($Name, length($Name)-1-$Pos, 1); + if(defined $B{$S}) { + $B{$S}+=1; + } + if($S eq $Target) + { + if($B{"("}==$B{")"} + and $B{"<"}==$B{">"}) { + return length($Name)-1-$Pos; + } + } + } + return 0; +} + +sub isExternal($) +{ + my $ID = $_[0]; + + if($DWARF_Info{$ID}{"ext"}) { + return 1; + } + elsif(my $Spec = $DWARF_Info{$ID}{"spec"}) + { + if($DWARF_Info{$Spec}{"ext"}) { + return 1; + } + } + + return 0; +} + +sub symByAddr($) +{ + my $Loc = $_[0]; + + my ($Addr, $Sect) = ("", ""); + if($Loc=~/\+(.+)/) + { + $Addr = $1; + if(not $Addr=~s/\A0x//) + { + $Addr=~s/\A00//; + } + } + if($Loc=~/([\w\.]+)\+/) { + $Sect = $1; + } + + if($Addr ne "") + { + foreach ($Sect, "") + { + if(defined $SymbolTable{$_}{$Addr}) + { + if(my @Symbols = sort keys(%{$SymbolTable{$_}{$Addr}})) { + return $Symbols[0]; + } + } + } + } + + return undef; +} + +sub getMangled($) +{ + my $ID = $_[0]; + + if(not defined $AddrToName) + { + if(my $Link = $DWARF_Info{$ID}{"linkage"}) + { + return $Link; + } + } + + if(my $Low_Pc = $DWARF_Info{$ID}{"low_pc"}) + { + if($Low_Pc=~/<([\w\@\.]+)>/) { + return $1; + } + else + { + if(my $Symbol = symByAddr($Low_Pc)) { + return $Symbol; + } + } + } + + if(my $Loc = $DWARF_Info{$ID}{"location"}) + { + if($Loc=~/<([\w\@\.]+)>/) { + return $1; + } + else + { + if(my $Symbol = symByAddr($Loc)) { + return $Symbol; + } + } + } + + if(my $Link = $DWARF_Info{$ID}{"linkage"}) + { + return $Link; + } + + return undef; +} + +sub completeNS($) +{ + my $ID = $_[0]; + + my $NS = undef; + my $ID_ = $ID; + my @NSs = (); + + while($NS = $NameSpace{$ID_} + or $NS = $NameSpace{$DWARF_Info{$ID_}{"spec"}}) + { + if(my $N = $DWARF_Info{$NS}{"name"}) { + push(@NSs, $N); + } + $ID_ = $NS; + } + + if(@NSs) + { + my $N = join("::", reverse(@NSs)); + $NestedNameSpaces{$N} = 1; + return $N; + } + + return undef; +} + +sub getSymbolInfo($) +{ + my $ID = $_[0]; + + if(my $N = $NameSpace{$ID}) + { + if($DWARF_Info{$N}{"kind"} eq "lexical_block" + or $DWARF_Info{$N}{"kind"} eq "prog") + { # local variables + return; + } + } + + my $Orig = $DWARF_Info{$ID}{"orig"}; + my $Container = undef; + + if(defined $DWARF_Info{$ID}{"container"}) { + $Container = $DWARF_Info{$ID}{"container"}; + } + elsif(defined $DWARF_Info{$Orig}{"container"}) { + $Container = $DWARF_Info{$Orig}{"container"}; + } + + if(defined $Container + and defined $LexicalId{$Container}) + { # local functions + return; + } + + if(my $Loc = $DWARF_Info{$ID}{"location"}) + { + if($Loc=~/ reg\d+\Z/) + { # local variables + return; + } + } + + my $ShortName = $DWARF_Info{$ID}{"name"}; + my $MnglName = getMangled($ID); + + if(not $MnglName) + { + if(my $Sp = $SpecElem{$ID}) + { + $MnglName = getMangled($Sp); + + if(not $MnglName) + { + if(my $OrigSp = $OrigElem{$Sp}) + { + $MnglName = getMangled($OrigSp); + } + } + } + } + + if(not $MnglName) + { + if($ShortName!~/\W/) + { # C-func + $MnglName = $ShortName; + } + } + + if(defined $Compressed + and not defined $AllUnits) + { + if(not $MnglName) + { + if(not $Partial) { + return; + } + } + } + else + { + if(not $MnglName) { + return; + } + } + + if(index($MnglName, "\@")!=-1) { + $MnglName=~s/([\@]+.*?)\Z//; + } + + if($MnglName=~/\W/) + { # unmangled operators, etc. + # foo.part.14 + # bar.isra.15 + return; + } + + if(skipSymbol($MnglName)) { + return; + } + + my %SInfo = (); + + if($DWARF_Info{$ID}{"kind"} eq "variable") + { # global data + $SInfo{"Data"} = 1; + } + + if($ShortName) { + $SInfo{"ShortName"} = $ShortName; + } + $SInfo{"MnglName"} = $MnglName; + + if($MnglName and my $OLD_ID = $Mangled_ID{$MnglName}) + { # duplicates + if(not defined $SymbolInfo{$OLD_ID}{"Header"}) { + setSource($SymbolInfo{$OLD_ID}, $ID, "Header"); + } + + if(not defined $SymbolInfo{$OLD_ID}{"Line"}) { + setSource($SymbolInfo{$OLD_ID}, $ID, "Line"); + } + + if(not defined $SymbolInfo{$OLD_ID}{"Source"}) { + setSource($SymbolInfo{$OLD_ID}, $ID, "Source"); + } + + if(not defined $SymbolInfo{$OLD_ID}{"ShortName"} + and $ShortName) { + $SymbolInfo{$OLD_ID}{"ShortName"} = $ShortName; + } + + if(defined $DWARF_Info{$OLD_ID}{"low_pc"} + or not defined $DWARF_Info{$ID}{"low_pc"}) + { + if(defined $Checked_Spec{$MnglName} + or not $DWARF_Info{$ID}{"spec"}) + { + if(defined $SymbolInfo{$OLD_ID}{"Param"} + or not defined $FuncParam{$ID}) + { + if(defined $SymbolInfo{$OLD_ID}{"Return"} + or not defined $DWARF_Info{$ID}{"type"}) + { + if(not defined $SpecElem{$ID} + and not defined $OrigElem{$ID}) { + delete($DWARF_Info{$ID}); + } + return; + } + } + } + } + } + + if($ShortName) + { + if($MnglName eq $ShortName) + { + delete($SInfo{"MnglName"}); + $MnglName = $ShortName; + } + elsif($MnglName + and index($MnglName, "_Z")!=0) + { + if($SInfo{"ShortName"}) + { + if(index($SInfo{"ShortName"}, ".")==-1) { + $SInfo{"Alias"} = $SInfo{"ShortName"}; + } + $SInfo{"ShortName"} = $SInfo{"MnglName"}; + } + + delete($SInfo{"MnglName"}); + $MnglName = $ShortName; + } + } + else + { + if(index($MnglName, "_Z")!=0) + { + $SInfo{"ShortName"} = $SInfo{"MnglName"}; + delete($SInfo{"MnglName"}); + } + } + + if(isExternal($ID)) { + $SInfo{"External"} = 1; + } + + if($Orig) + { + if(isExternal($Orig)) { + $SInfo{"External"} = 1; + } + } + + if(index($MnglName, "_ZNVK")==0) + { + $SInfo{"Const"} = 1; + $SInfo{"Volatile"} = 1; + } + elsif(index($MnglName, "_ZNV")==0) { + $SInfo{"Volatile"} = 1; + } + elsif(index($MnglName, "_ZNK")==0) { + $SInfo{"Const"} = 1; + } + + if($DWARF_Info{$ID}{"art"}) { + $SInfo{"Artificial"} = 1; + } + + my ($C, $D) = (); + + if($MnglName=~/(C[1-4])[EI].+/) + { + $C = $1; + $SInfo{"Constructor"} = 1; + } + + if($MnglName=~/(D[0-4])[EI].+/) + { + $D = $1; + $SInfo{"Destructor"} = 1; + } + + if($C or $D) + { + if($Orig) + { + if(my $InLine = $DWARF_Info{$Orig}{"inline"}) + { + if(index($InLine, "declared_not_inlined")==0) + { + $SInfo{"InLine"} = 1; + $SInfo{"Artificial"} = 1; + } + } + + setSource(\%SInfo, $Orig); + + if(my $Spec = $DWARF_Info{$Orig}{"spec"}) + { + setSource(\%SInfo, $Spec); + + $SInfo{"ShortName"} = $DWARF_Info{$Spec}{"name"}; + if($D) { + $SInfo{"ShortName"}=~s/\A\~//g; + } + + if(my $Class = $NameSpace{$Spec}) { + $SInfo{"Class"} = $Class; + } + + if(my $Virt = $DWARF_Info{$Spec}{"virt"}) + { + if(index($Virt, "virtual")!=-1) { + $SInfo{"Virt"} = 1; + } + } + + if(my $Access = $DWARF_Info{$Spec}{"access"}) + { + if($Access ne "public") + { # default access of methods in the ABI dump is "public" + $SInfo{ucfirst($Access)} = 1; + } + } + else + { # NOTE: default access of class methods in the debug info is "private" + if($TypeInfo{$SInfo{"Class"}}{"Type"} eq "Class") + { + $SInfo{"Private"} = 1; + } + } + + if(not defined $Compressed + or defined $AllUnits) + { + # clean origin + delete($SymbolInfo{$Spec}); + } + } + } + } + else + { + if(my $InLine = $DWARF_Info{$ID}{"inline"}) + { + if(index($InLine, "declared_inlined")==0) { + $SInfo{"DeclaredInlined"} = 1; + } + } + } + + if(defined $AddrToName) + { + if(not $SInfo{"Alias"} + and not $SInfo{"Constructor"} + and not $SInfo{"Destructor"}) + { + if(my $Linkage = $DWARF_Info{$ID}{"linkage"}) + { + if($Linkage ne $MnglName) { + $SInfo{"Alias"} = $Linkage; + } + } + } + } + + if($SInfo{"Data"}) + { + if(my $Spec = $DWARF_Info{$ID}{"spec"}) + { + if($DWARF_Info{$Spec}{"kind"} eq "member") + { + setSource(\%SInfo, $Spec); + $SInfo{"ShortName"} = $DWARF_Info{$Spec}{"name"}; + + if(my $NSp = $NameSpace{$Spec}) + { + if($DWARF_Info{$NSp}{"kind"} eq "namespace") { + $SInfo{"NameSpace"} = completeNS($Spec); + } + else { + $SInfo{"Class"} = $NSp; + } + } + } + } + } + + if(my $Access = $DWARF_Info{$ID}{"access"}) + { + if($Access ne "public") + { # default access of methods in the ABI dump is "public" + $SInfo{ucfirst($Access)} = 1; + } + } + elsif(not $DWARF_Info{$ID}{"spec"} + and not $Orig) + { + if(my $NS = $NameSpace{$ID}) + { + if(defined $TypeInfo{$NS}) + { # NOTE: default access of class methods in the debug info is "private" + if($TypeInfo{$NS}{"Type"} eq "Class") + { + $SInfo{"Private"} = 1; + } + } + } + } + + if(my $Class = $DWARF_Info{$ID}{"container"}) + { + $SInfo{"Class"} = $Class; + } + + if(my $NS = $NameSpace{$ID}) + { + if($DWARF_Info{$NS}{"kind"} eq "namespace") { + $SInfo{"NameSpace"} = completeNS($ID); + } + else { + $SInfo{"Class"} = $NS; + } + } + + if($SInfo{"Class"} and $MnglName + and index($MnglName, "_Z")!=0) { + return; + } + + if(my $Return = $DWARF_Info{$ID}{"type"}) + { + $SInfo{"Return"} = $Return; + } + if(my $Spec = $DWARF_Info{$ID}{"spec"}) + { + if(not $DWARF_Info{$ID}{"type"}) + { + if(my $SpRet = $DWARF_Info{$Spec}{"type"}) { + $SInfo{"Return"} = $SpRet; + } + } + if(my $Value = $DWARF_Info{$Spec}{"cval"}) + { + if($Value=~/ block:\s*(.*?)\Z/) { + $Value = $1; + } + $SInfo{"Value"} = $Value; + } + } + + if($SInfo{"ShortName"}=~/>\Z/) + { # foo + my ($Short, $TParams) = getTParams($ID, $SInfo{"ShortName"}); + + if($TParams) + { + foreach my $Pos (0 .. $#{$TParams}) { + $SInfo{"TParam"}{$Pos} = $TParams->[$Pos]; + } + + my @TKeys = getTKeys($TParams); + + $SInfo{"ShortName"} = $Short.formatName("<".join(", ", @TKeys).">", "T"); + } + } + elsif($SInfo{"ShortName"}=~/\Aoperator (\w.*)\Z/) + { # operator type::name + $SInfo{"ShortName"} = "operator ".simpleName($1); + } + + if(my $Virt = $DWARF_Info{$ID}{"virt"}) + { + if(index($Virt, "virtual")!=-1) + { + if($D or defined $SpecElem{$ID}) { + $SInfo{"Virt"} = 1; + } + else { + $SInfo{"PureVirt"} = 1; + } + } + + if((my $VirtPos = $DWARF_Info{$ID}{"vloc"}) ne "") + { + $SInfo{"VirtPos"} = $VirtPos; + } + } + + setSource(\%SInfo, $ID); + + if(not $SInfo{"Header"}) + { + if($SInfo{"Class"}) + { # detect missed header by class + if(defined $TypeInfo{$SInfo{"Class"}}{"Header"}) { + $SInfo{"Header"} = $TypeInfo{$SInfo{"Class"}}{"Header"}; + } + } + } + + my $PPos = 0; + + foreach my $Pos (sort {$a<=>$b} keys(%{$FuncParam{$ID}})) + { + my $ParamId = $FuncParam{$ID}{$Pos}; + my $Offset = undef; + my $Reg = undef; + + if(my $Sp = $SpecElem{$ID}) + { + if(defined $FuncParam{$Sp}) { + $ParamId = $FuncParam{$Sp}{$Pos}; + } + } + + if((my $Loc = $DWARF_Info{$ParamId}{"location"}) ne "") { + $Offset = $Loc; + } + elsif((my $R = $DWARF_Info{$ParamId}{"register"}) ne "") { + $Reg = $RegName{$R}; + } + elsif((my $LL = $DWARF_Info{$ParamId}{"location_list"}) ne "") + { + if(my $L = $DebugLoc{$LL}) + { + if($L=~/reg(\d+)/) { + $Reg = $RegName{$1}; + } + elsif($L=~/fbreg\s+(-?\w+)\Z/) { + $Offset = $1; + } + } + elsif(not defined $DebugLoc{$LL}) + { # invalid debug_loc + if(not $InvalidDebugLoc) + { + printMsg("ERROR", "invalid debug_loc section of object, please fix your elf utils"); + $InvalidDebugLoc = 1; + } + } + } + + if(my $OrigP = $DWARF_Info{$ParamId}{"orig"}) { + $ParamId = $OrigP; + } + + if(not defined $DWARF_Info{$ParamId}) + { # this is probably a lexical block + printMsg("ERROR", "incomplete info for symbol $ID"); + return; + } + + my %PInfo = %{$DWARF_Info{$ParamId}}; + + if(defined $PInfo{"name"} + and ($PInfo{"name"} eq "__in_chrg" or $PInfo{"name"} eq "__vtt_parm")) { + next; + } + + if(defined $Offset + and not defined $IncompatibleOpt) + { + if($SYS_ARCH eq "x86_64") + { + if($Offset<0) { # debug-info failure + $Offset = undef; + } + } + + if(defined $Offset) { + $SInfo{"Param"}{$Pos}{"offset"} = "$Offset"; + } + } + + if($TypeInfo{$PInfo{"type"}}{"Type"} eq "Const") + { + if(my $BTid = $TypeInfo{$PInfo{"type"}}{"BaseType"}) + { + if($TypeInfo{$BTid}{"Type"} eq "Ref") + { # const&const -> const& + $PInfo{"type"} = $BTid; + } + } + } + + $SInfo{"Param"}{$Pos}{"type"} = $PInfo{"type"}; + + if(defined $PInfo{"name"}) { + $SInfo{"Param"}{$Pos}{"name"} = $PInfo{"name"}; + } + elsif($TypeInfo{$PInfo{"type"}}{"Name"} ne "...") { + $SInfo{"Param"}{$Pos}{"name"} = "p".($PPos+1); + } + + if(defined $Reg + and not defined $IncompatibleOpt) + { + $SInfo{"Reg"}{$Pos} = $Reg; + } + + if($DWARF_Info{$ParamId}{"art"} and $Pos==0) + { + if($SInfo{"Param"}{$Pos}{"name"} eq "p1") { + $SInfo{"Param"}{$Pos}{"name"} = "this"; + } + } + + if($SInfo{"Param"}{$Pos}{"name"} ne "this") + { # this, p1, p2, etc. + $PPos += 1; + } + } + + if($SInfo{"Constructor"} and not $SInfo{"InLine"} + and $SInfo{"Class"}) { + delete($TypeInfo{$SInfo{"Class"}}{"Copied"}); + } + + my $BASE_ID = undef; + + if($MnglName) { + $BASE_ID = $Mangled_ID{$MnglName}; + } + + if(defined $Compressed + and not defined $AllUnits) + { + if($MnglName and not $BASE_ID) + { + my $B_ID = undef; + if(my $Sp = $DWARF_Info{$ID}{"spec"}) { + $B_ID = $Sp; + } + elsif($Orig) + { + if(my $OrigSp = $DWARF_Info{$Orig}{"spec"}) { + $B_ID = $OrigSp; + } + } + + if($B_ID and $B_ID>0) + { # negative ones are not used for symbols + $BASE_ID = $B_ID; + + if($MnglName) { + $Mangled_ID{$MnglName} = $BASE_ID; + } + + # drop old mangled name + delete($Mangled_ID{$SymbolInfo{$BASE_ID}{"MnglName"}}); + } + } + } + + if($BASE_ID) + { + if(defined $SInfo{"Param"}) + { + if($MnglName and index($MnglName, "_Z")!=0) + { + my $DifferentParams = (keys(%{$SInfo{"Param"}})!=keys(%{$SymbolInfo{$BASE_ID}{"Param"}})); + if($DifferentParams or keys(%{$SInfo{"Param"}})==1) + { # different symbols with the same name + if(defined $SInfo{"Param"} + and $SInfo{"Param"}{0}{"type"}=="-1") + { # missed signature (...) + return; + } + } + + if($DifferentParams) + { # take the last one + delete($SymbolInfo{$BASE_ID}); + } + } + } + + $ID = $BASE_ID; + + if(defined $SymbolInfo{$ID}{"PureVirt"}) + { # if the specification of a symbol is located in other compile unit + delete($SymbolInfo{$ID}{"PureVirt"}); + $SymbolInfo{$ID}{"Virt"} = 1; + } + } + + if($MnglName) { + $Mangled_ID{$MnglName} = $ID; + } + + if($DWARF_Info{$ID}{"spec"}) { + $Checked_Spec{$MnglName} = 1; + } + + my $MixedSymbols = 0; + + if(defined $SInfo{"Param"} + and defined $SymbolInfo{$ID} + and defined $SymbolInfo{$ID}{"Param"}) + { + foreach my $K1 (keys(%{$SInfo{"Param"}})) + { + if(defined $SymbolInfo{$ID}{"Param"}{$K1}) + { + if($SInfo{"Param"}{$K1}{"type"} eq "-1" + and $SymbolInfo{$ID}{"Param"}{$K1}{"type"} ne "-1") + { + $MixedSymbols = 1; + last; + } + } + } + } + + if(not $MixedSymbols) + { + foreach my $Attr (keys(%SInfo)) + { + if(ref($SInfo{$Attr}) eq "HASH") + { + my @Prms = keys(%{$SInfo{$Attr}}); + + if($Attr eq "Param" and @Prms + and defined $SymbolInfo{$ID} + and defined $SymbolInfo{$ID}{$Attr}) + { + my $Clear = 0; + + if(keys(%{$SymbolInfo{$ID}{$Attr}})!=$#Prms+1) + { # do not mix parameters of different symbols + $Clear = 1; + } + + if($Clear) { + $SymbolInfo{$ID}{$Attr} = {}; + } + } + + foreach my $K1 (keys(%{$SInfo{$Attr}})) + { + if(ref($SInfo{$Attr}{$K1}) eq "HASH") + { + foreach my $K2 (keys(%{$SInfo{$Attr}{$K1}})) + { + $SymbolInfo{$ID}{$Attr}{$K1}{$K2} = $SInfo{$Attr}{$K1}{$K2}; + } + } + else { + $SymbolInfo{$ID}{$Attr}{$K1} = $SInfo{$Attr}{$K1}; + } + } + } + else + { + $SymbolInfo{$ID}{$Attr} = $SInfo{$Attr}; + } + } + } + + if($ID>$GLOBAL_ID) { + $GLOBAL_ID = $ID; + } +} + +sub fixHeader($) +{ + my $SInfo = $_[0]; + + if(not $SInfo->{"Header"} + or ($SInfo->{"External"} and not defined $PublicHeader{$SInfo->{"Header"}})) + { + if($SInfo->{"MnglName"} and defined $SymbolToHeader{$SInfo->{"MnglName"}}) + { + $SInfo->{"Header"} = chooseHeader($SInfo->{"MnglName"}, $SInfo->{"Source"}); + delete($SInfo->{"Line"}); + } + elsif(not $SInfo->{"Class"} + and defined $SymbolToHeader{$SInfo->{"ShortName"}}) + { + $SInfo->{"Header"} = chooseHeader($SInfo->{"ShortName"}, $SInfo->{"Source"}); + delete($SInfo->{"Line"}); + } + } + + if($SInfo->{"Alias"}) + { + if(defined $SymbolToHeader{$SInfo->{"Alias"}}) { + $SInfo->{"Header"} = chooseHeader($SInfo->{"Alias"}, $SInfo->{"Source"}); + } + } +} + +sub chooseHeader($$) +{ + my ($Symbol, $Source) = @_; + + my @Headers = sort keys(%{$SymbolToHeader{$Symbol}}); + + if($#Headers==0) { + return $Headers[0]; + } + + @Headers = sort {length($a)<=>length($b)} sort {lc($a) cmp lc($b)} @Headers; + + $Source=~s/\.\w+\Z//g; + + foreach my $Header (@Headers) + { + if($Header=~/\A\Q$Source\E(|\.[\w\+]+)\Z/i) { + return $Header; + } + } + + my $SPrefix = undef; + + if(length($Source)>3) { + $SPrefix = substr($Source, 0, 3); + } + + if(defined $SPrefix) + { + foreach my $Header (@Headers) + { + if($Header=~/\A\Q$SPrefix\E/i) { + return $Header; + } + } + } + + return $Headers[0]; +} + +sub getTypeIdByName($$) +{ + my ($Type, $Name) = @_; + return $TName_Tid{$Type}{formatName($Name, "T")}; +} + +sub getFirst($) +{ + my $Tid = $_[0]; + if(not $Tid) { + return $Tid; + } + + if(defined $DeletedAnon{$Tid}) { + $Tid = $DeletedAnon{$Tid}; + } + + if(defined $TypeSpec{$Tid}) { + $Tid = $TypeSpec{$Tid}; + } + + if(my $Name = $TypeInfo{$Tid}{"Name"}) + { + my $Type = $TypeInfo{$Tid}{"Type"}; + + my $FTid = undef; + + if($FTid = $TName_Tid{$Type}{$Name}) { + return "$FTid"; + } + + if($Name=~s/\Astruct //) + { # search for class or derived types (const, *, etc.) + foreach my $Type ("Class", "Const", "Ref", "RvalueRef", "Pointer") + { + if($FTid = $TName_Tid{$Type}{$Name}) + { + if($FTid ne $Tid) + { + $MergedTypes{$Tid} = 1; + } + return "$FTid"; + } + } + + $Name = "struct ".$Name; + } + + if(not $FTid) { + $FTid = $TName_Tid{$Type}{$Name}; + } + + if($FTid) { + return "$FTid"; + } + printMsg("ERROR", "internal error (missed type id $Tid)"); + } + + return $Tid; +} + +sub searchTypeID($) +{ + my $Name = $_[0]; + + my %Pr = map {$_=>1} ( + "Struct", + "Union", + "Enum" + ); + + foreach my $Type ("Class", "Struct", "Union", "Enum", "Typedef", "Const", + "Volatile", "Ref", "RvalueRef", "Pointer", "FuncPtr", "MethodPtr", "FieldPtr") + { + my $Tid = $TName_Tid{$Type}{$Name}; + + if(not $Tid) + { + if(defined $Pr{$Type}) + { + my $NN = lc($Type)." ".$Name; + if(defined $TName_Tid{$Type}{$NN}) { + $Tid = $TName_Tid{$Type}{$NN}; + } + } + } + if($Tid) { + return $Tid; + } + } + return undef; +} + +sub removeUnused() +{ # remove unused data types from the ABI dump + %HeadersInfo = (); + %SourcesInfo = (); + + my (%SelectedHeaders, %SelectedSources) = (); + + foreach my $ID (sort {$a<=>$b} keys(%SymbolInfo)) + { + if($SelectedSymbols{$ID}==2) + { # data, inline, pure + next; + } + + registerSymbolUsage($ID); + + if(my $H = $SymbolInfo{$ID}{"Header"}) { + $SelectedHeaders{$H} = 1; + } + if(my $S = $SymbolInfo{$ID}{"Source"}) { + $SelectedSources{$S} = 1; + } + } + + foreach my $ID (sort {$a<=>$b} keys(%SymbolInfo)) + { + if($SelectedSymbols{$ID}==2) + { # data, inline, pure + my $Save = 0; + if(my $Class = getFirst($SymbolInfo{$ID}{"Class"})) + { + if(defined $UsedType{$Class}) { + $Save = 1; + } + else + { + if(defined $ClassChild{$Class}) + { + foreach (keys(%{$ClassChild{$Class}})) + { + if(defined $UsedType{getFirst($_)}) + { + $Save = 1; + last; + } + } + } + } + } + if(my $Header = $SymbolInfo{$ID}{"Header"}) + { + if(defined $SelectedHeaders{$Header}) { + $Save = 1; + } + } + if(my $Source = $SymbolInfo{$ID}{"Source"}) + { + if(defined $SelectedSources{$Source}) { + $Save = 1; + } + } + if($Save) { + registerSymbolUsage($ID); + } + else { + delete($SymbolInfo{$ID}); + } + } + } + + if(defined $AllTypes) + { + # register all data types (except anon structs and unions) + foreach my $Tid (keys(%TypeInfo)) + { + if(defined $LocalType{$Tid}) + { # except local code + next; + } + if($TypeInfo{$Tid}{"Type"} eq "Enum" + or index($TypeInfo{$Tid}{"Name"}, "anon-")!=0) { + registerTypeUsage($Tid); + } + } + + # remove unused anons (except enums) + foreach my $Tid (keys(%TypeInfo)) + { + if(not $UsedType{$Tid}) + { + if($TypeInfo{$Tid}{"Type"} ne "Enum") + { + if(index($TypeInfo{$Tid}{"Name"}, "anon-")==0) { + delete($TypeInfo{$Tid}); + } + } + } + } + + # remove duplicates + foreach my $Tid (keys(%TypeInfo)) + { + my $Name = $TypeInfo{$Tid}{"Name"}; + my $Type = $TypeInfo{$Tid}{"Type"}; + + if($TName_Tid{$Type}{$Name} ne $Tid) { + delete($TypeInfo{$Tid}); + } + } + } + else + { + foreach my $Tid (keys(%TypeInfo)) + { # remove unused types + if(not $UsedType{$Tid}) { + delete($TypeInfo{$Tid}); + } + } + } + + foreach my $Tid (keys(%MergedTypes)) { + delete($TypeInfo{$Tid}); + } + + foreach my $Tid (keys(%LocalType)) + { + if(not $UsedType{$Tid}) { + delete($TypeInfo{$Tid}); + } + } + + # clean memory + %MergedTypes = (); + %LocalType = (); + + # completeness + foreach my $Tid (sort keys(%TypeInfo)) { + checkCompleteness($TypeInfo{$Tid}); + } + + foreach my $Sid (sort keys(%SymbolInfo)) { + checkCompleteness($SymbolInfo{$Sid}); + } + + # clean memory + %UsedType = (); +} + +sub simpleName($) +{ + my $N = $_[0]; + + $N=~s/\A(struct|class|union|enum) //; # struct, class, union, enum + + if(index($N, "std::basic_string")!=-1) + { + $N=~s/std::basic_string, std::allocator >/std::string /g; + $N=~s/std::basic_string >/std::string /g; + $N=~s/std::basic_string/std::string /g; + + $N=~s/std::basic_string, std::allocator >/std::wstring /g; + } + + if(index($N, "std::basic_ostream")!=-1) { + $N=~s/std::basic_ostream >/std::ostream /g; + } + + return formatName($N, "T"); +} + +sub registerSymbolUsage($) +{ + my $InfoId = $_[0]; + + my %FuncInfo = %{$SymbolInfo{$InfoId}}; + + if(my $S = $FuncInfo{"Source"}) { + $SourcesInfo{$S} = 1; + } + if(my $H = $FuncInfo{"Header"}) { + $HeadersInfo{$H} = 1; + } + if(my $RTid = getFirst($FuncInfo{"Return"})) + { + registerTypeUsage($RTid); + $SymbolInfo{$InfoId}{"Return"} = $RTid; + } + if(my $FCid = getFirst($FuncInfo{"Class"})) + { + registerTypeUsage($FCid); + $SymbolInfo{$InfoId}{"Class"} = $FCid; + + if(my $ThisId = getTypeIdByName("Const", $TypeInfo{$FCid}{"Name"}."*const")) + { # register "this" pointer + registerTypeUsage($ThisId); + } + if(my $ThisId_C = getTypeIdByName("Const", $TypeInfo{$FCid}{"Name"}." const*const")) + { # register "this" pointer (const method) + registerTypeUsage($ThisId_C); + } + } + foreach my $PPos (keys(%{$FuncInfo{"Param"}})) + { + if(my $PTid = getFirst($FuncInfo{"Param"}{$PPos}{"type"})) + { + registerTypeUsage($PTid); + $SymbolInfo{$InfoId}{"Param"}{$PPos}{"type"} = $PTid; + } + } + foreach my $TPos (keys(%{$FuncInfo{"TParam"}})) + { + if(my $TTid = $FuncInfo{"TParam"}{$TPos}{"type"}) + { + if($TTid = getFirst($TTid)) + { + registerTypeUsage($TTid); + $SymbolInfo{$InfoId}{"TParam"}{$TPos}{"type"} = $TTid; + delete($SymbolInfo{$InfoId}{"TParam"}{$TPos}{"name"}); + } + } + elsif(my $TPName = $FuncInfo{"TParam"}{$TPos}{"name"}) + { + if(my $TTid = searchTypeID($TPName)) + { + if(my $FTTid = getFirst($TTid)) + { + registerTypeUsage($FTTid); + $SymbolInfo{$InfoId}{"TParam"}{$TPos}{"type"} = $TTid; + delete($SymbolInfo{$InfoId}{"TParam"}{$TPos}{"name"}); + } + } + } + } +} + +sub registerTypeUsage($) +{ + my $TypeId = $_[0]; + if(not $TypeId) { + return 0; + } + if($UsedType{$TypeId}) + { # already registered + return 1; + } + my %TInfo = %{$TypeInfo{$TypeId}}; + + if(my $S = $TInfo{"Source"}) { + $SourcesInfo{$S} = 1; + } + if(my $H = $TInfo{"Header"}) { + $HeadersInfo{$H} = 1; + } + + if($TInfo{"Type"}) + { + if(my $NS = $TInfo{"NameSpace"}) + { + if(my $NSTid = searchTypeID($NS)) + { + if(my $FNSTid = getFirst($NSTid)) { + registerTypeUsage($FNSTid); + } + } + } + + if($TInfo{"Type"}=~/\A(Struct|Union|Class|FuncPtr|Func|MethodPtr|FieldPtr|Enum)\Z/) + { + $UsedType{$TypeId} = 1; + if($TInfo{"Type"}=~/\A(Struct|Class)\Z/) + { + foreach my $BaseId (keys(%{$TInfo{"Base"}})) + { # register base classes + if(my $FBaseId = getFirst($BaseId)) + { + registerTypeUsage($FBaseId); + if($FBaseId ne $BaseId) + { + %{$TypeInfo{$TypeId}{"Base"}{$FBaseId}} = %{$TypeInfo{$TypeId}{"Base"}{$BaseId}}; + delete($TypeInfo{$TypeId}{"Base"}{$BaseId}); + } + } + } + } + if($TInfo{"Type"}=~/\A(Struct|Class|Union)\Z/) + { + foreach my $TPos (keys(%{$TInfo{"TParam"}})) + { + if(my $TTid = $TInfo{"TParam"}{$TPos}{"type"}) + { + if($TTid = getFirst($TTid)) + { + registerTypeUsage($TTid); + $TypeInfo{$TypeId}{"TParam"}{$TPos}{"type"} = $TTid; + delete($TypeInfo{$TypeId}{"TParam"}{$TPos}{"name"}); + } + } + elsif(my $TPName = $TInfo{"TParam"}{$TPos}{"name"}) + { + if(my $TTid = searchTypeID($TPName)) + { + if(my $TTid = getFirst($TTid)) + { + registerTypeUsage($TTid); + $TypeInfo{$TypeId}{"TParam"}{$TPos}{"type"} = $TTid; + delete($TypeInfo{$TypeId}{"TParam"}{$TPos}{"name"}); + } + } + } + } + } + foreach my $Memb_Pos (keys(%{$TInfo{"Memb"}})) + { + if(my $MTid = getFirst($TInfo{"Memb"}{$Memb_Pos}{"type"})) + { + registerTypeUsage($MTid); + $TypeInfo{$TypeId}{"Memb"}{$Memb_Pos}{"type"} = $MTid; + } + } + if($TInfo{"Type"} eq "FuncPtr" + or $TInfo{"Type"} eq "MethodPtr" + or $TInfo{"Type"} eq "Func") + { + if(my $RTid = getFirst($TInfo{"Return"})) + { + registerTypeUsage($RTid); + $TypeInfo{$TypeId}{"Return"} = $RTid; + } + foreach my $Memb_Pos (keys(%{$TInfo{"Param"}})) + { + if(my $MTid = getFirst($TInfo{"Param"}{$Memb_Pos}{"type"})) + { + registerTypeUsage($MTid); + $TypeInfo{$TypeId}{"Param"}{$Memb_Pos}{"type"} = $MTid; + } + } + } + if($TInfo{"Type"} eq "FieldPtr") + { + if(my $RTid = getFirst($TInfo{"Return"})) + { + registerTypeUsage($RTid); + $TypeInfo{$TypeId}{"Return"} = $RTid; + } + if(my $CTid = getFirst($TInfo{"Class"})) + { + registerTypeUsage($CTid); + $TypeInfo{$TypeId}{"Class"} = $CTid; + } + } + if($TInfo{"Type"} eq "MethodPtr") + { + if(my $CTid = getFirst($TInfo{"Class"})) + { + registerTypeUsage($CTid); + $TypeInfo{$TypeId}{"Class"} = $CTid; + } + } + if($TInfo{"Type"} eq "Enum") + { + if(my $BTid = getFirst($TInfo{"BaseType"})) + { + registerTypeUsage($BTid); + $TypeInfo{$TypeId}{"BaseType"} = $BTid; + } + } + return 1; + } + elsif($TInfo{"Type"}=~/\A(Const|ConstVolatile|Volatile|Pointer|Ref|RvalueRef|Restrict|Array|Typedef)\Z/) + { + $UsedType{$TypeId} = 1; + if(my $BTid = getFirst($TInfo{"BaseType"})) + { + registerTypeUsage($BTid); + $TypeInfo{$TypeId}{"BaseType"} = $BTid; + } + return 1; + } + elsif($TInfo{"Type"}=~/\A(Intrinsic|Unspecified)\Z/) + { + $UsedType{$TypeId} = 1; + return 1; + } + } + return 0; +} + +sub checkCompleteness($) +{ + my $Info = $_[0]; + + # data types + if(defined $Info->{"Memb"}) + { + foreach my $Pos (sort keys(%{$Info->{"Memb"}})) + { + if(defined $Info->{"Memb"}{$Pos}{"type"}) { + checkTypeInfo($Info->{"Memb"}{$Pos}{"type"}); + } + } + } + if(defined $Info->{"Base"}) + { + foreach my $Bid (sort keys(%{$Info->{"Base"}})) { + checkTypeInfo($Bid); + } + } + if(defined $Info->{"BaseType"}) { + checkTypeInfo($Info->{"BaseType"}); + } + if(defined $Info->{"TParam"}) + { + foreach my $Pos (sort keys(%{$Info->{"TParam"}})) + { + my $TRef = $Info->{"TParam"}{$Pos}; + + if(my $Tid = $TRef->{"type"}) { + checkTypeInfo($Tid); + } + else + { + my $TName = $Info->{"TParam"}{$Pos}{"name"}; + if($TName=~/\A(true|false|\d.*)\Z/) { + next; + } + + if(my $Tid = searchTypeID($TName)) { + checkTypeInfo($Tid); + } + else + { + if(defined $Loud) { + printMsg("WARNING", "missed type $TName"); + } + } + } + } + } + + # symbols + if(defined $Info->{"Param"}) + { + foreach my $Pos (sort keys(%{$Info->{"Param"}})) + { + if(defined $Info->{"Param"}{$Pos}{"type"}) { + checkTypeInfo($Info->{"Param"}{$Pos}{"type"}); + } + } + } + if(defined $Info->{"Return"}) { + checkTypeInfo($Info->{"Return"}); + } + if(defined $Info->{"Class"}) { + checkTypeInfo($Info->{"Class"}); + } +} + +sub checkTypeInfo($) +{ + my $Tid = $_[0]; + + if(defined $CheckedType{$Tid}) { + return; + } + $CheckedType{$Tid} = 1; + + if(defined $TypeInfo{$Tid}) + { + if(not $TypeInfo{$Tid}{"Name"}) { + printMsg("ERROR", "missed type name ($Tid)"); + } + checkCompleteness($TypeInfo{$Tid}); + } + else { + printMsg("ERROR", "missed type id $Tid"); + } +} + +sub initRegs() +{ + if($SYS_ARCH eq "x86") + { + %RegName = ( + # integer registers + # 32 bits + "0"=>"eax", + "1"=>"ecx", + "2"=>"edx", + "3"=>"ebx", + "4"=>"esp", + "5"=>"ebp", + "6"=>"esi", + "7"=>"edi", + "8"=>"eip", + "9"=>"eflags", + "10"=>"trapno", + # FPU-control registers + # 16 bits + "37"=>"fctrl", + "38"=>"fstat", + # 32 bits + "39"=>"mxcsr", + # MMX registers + # 64 bits + "29"=>"mm0", + "30"=>"mm1", + "31"=>"mm2", + "32"=>"mm3", + "33"=>"mm4", + "34"=>"mm5", + "35"=>"mm6", + "36"=>"mm7", + # SSE registers + # 128 bits + "21"=>"xmm0", + "22"=>"xmm1", + "23"=>"xmm2", + "24"=>"xmm3", + "25"=>"xmm4", + "26"=>"xmm5", + "27"=>"xmm6", + "28"=>"xmm7", + # segment registers + # 16 bits + "40"=>"es", + "41"=>"cs", + "42"=>"ss", + "43"=>"ds", + "44"=>"fs", + "45"=>"gs", + # x87 registers + # 80 bits + "11"=>"st0", + "12"=>"st1", + "13"=>"st2", + "14"=>"st3", + "15"=>"st4", + "16"=>"st5", + "17"=>"st6", + "18"=>"st7" + ); + } + elsif($SYS_ARCH eq "x86_64") + { + %RegName = ( + # integer registers + # 64 bits + "0"=>"rax", + "1"=>"rdx", + "2"=>"rcx", + "3"=>"rbx", + "4"=>"rsi", + "5"=>"rdi", + "6"=>"rbp", + "7"=>"rsp", + "8"=>"r8", + "9"=>"r9", + "10"=>"r10", + "11"=>"r11", + "12"=>"r12", + "13"=>"r13", + "14"=>"r14", + "15"=>"r15", + "16"=>"rip", + "49"=>"rFLAGS", + # MMX registers + # 64 bits + "41"=>"mm0", + "42"=>"mm1", + "43"=>"mm2", + "44"=>"mm3", + "45"=>"mm4", + "46"=>"mm5", + "47"=>"mm6", + "48"=>"mm7", + # SSE registers + # 128 bits + "17"=>"xmm0", + "18"=>"xmm1", + "19"=>"xmm2", + "20"=>"xmm3", + "21"=>"xmm4", + "22"=>"xmm5", + "23"=>"xmm6", + "24"=>"xmm7", + "25"=>"xmm8", + "26"=>"xmm9", + "27"=>"xmm10", + "28"=>"xmm11", + "29"=>"xmm12", + "30"=>"xmm13", + "31"=>"xmm14", + "32"=>"xmm15", + # control registers + # 64 bits + "62"=>"tr", + "63"=>"ldtr", + "64"=>"mxcsr", + # 16 bits + "65"=>"fcw", + "66"=>"fsw", + # segment registers + # 16 bits + "50"=>"es", + "51"=>"cs", + "52"=>"ss", + "53"=>"ds", + "54"=>"fs", + "55"=>"gs", + # 64 bits + "58"=>"fs.base", + "59"=>"gs.base", + # x87 registers + # 80 bits + "33"=>"st0", + "34"=>"st1", + "35"=>"st2", + "36"=>"st3", + "37"=>"st4", + "38"=>"st5", + "39"=>"st6", + "40"=>"st7" + ); + } + elsif($SYS_ARCH eq "arm") + { + %RegName = ( + # integer registers + # 32-bit + "0"=>"r0", + "1"=>"r1", + "2"=>"r2", + "3"=>"r3", + "4"=>"r4", + "5"=>"r5", + "6"=>"r6", + "7"=>"r7", + "8"=>"r8", + "9"=>"r9", + "10"=>"r10", + "11"=>"r11", + "12"=>"r12", + "13"=>"r13", + "14"=>"r14", + "15"=>"r15" + ); + } +} + +sub dumpSorting($) +{ + my $Hash = $_[0]; + return [] if(not $Hash); + my @Keys = keys(%{$Hash}); + return [] if($#Keys<0); + if($Keys[0]=~/\A\d+\Z/) + { # numbers + return [sort {$a<=>$b} @Keys]; + } + else + { # strings + return [sort {$a cmp $b} @Keys]; + } +} + +sub getDebugFile($$) +{ + my ($Obj, $Header) = @_; + + my $Str = `$EU_READELF_L --strings=.$Header \"$Obj\" 2>\"$TMP_DIR/error\"`; + if($Str=~/(\s|\[)0\]\s*(.+)/) { + return $2; + } + + return undef; +} + +sub findFiles(@) +{ + my ($Path, $Type) = @_; + my $Cmd = "find \"$Path\""; + + if($Type) { + $Cmd .= " -type ".$Type; + } + + my @Res = split(/\n/, `$Cmd`); + return @Res; +} + +sub isHeader($) +{ + my $Path = $_[0]; + + if($Path=~/\.($HEADER_EXT)\Z/i) { + return 1; + } + + if(index(getFilename($Path), ".")==-1 and -T $Path) + { # C++ + return 1; + } + + return 0; +} + +sub detectPublicSymbols($) +{ + my $Path = $_[0]; + + if(not -e $Path) { + exitStatus("Access_Error", "can't access \'$Path\'"); + } + + my $Path_A = abs_path($Path); + + printMsg("INFO", "Detect public symbols"); + + if($UseTU) + { + if(not checkCmd($GPP)) { + exitStatus("Not_Found", "can't find \"$GPP\""); + } + } + else + { + if(not checkCmd($CTAGS)) { + exitStatus("Not_Found", "can't find \"$CTAGS\""); + } + + if(my $CtagsVer = `$CTAGS --version 2>&1`) + { + if($CtagsVer!~/Universal/i) + { + printMsg("ERROR", "requires Universal Ctags to work properly"); + if($CtagsVer=~/Exuberant/i) { + $EXUBERANT_CTAGS = 1; + } + } + } + } + + $PublicSymbols_Detected = 1; + + my @Files = (); + my @Headers = (); + my @DefaultInc = (); + + if($PublicHeadersIsDir) + { # directory + @Files = findFiles($Path, "f"); + + foreach my $File (@Files) + { + if(isHeader($File)) { + push(@Headers, $File); + } + } + + push(@DefaultInc, $Path_A); + + if(-d $Path_A."/include") { + push(@DefaultInc, $Path_A."/include"); + } + } + else + { # list of headers + @Headers = split(/\n/, readFile($Path)); + } + + if(not @Headers) { + exitStatus("Error", "headers not found in \'$Path\'"); + } + + my $PublicHeader_F = $CacheHeaders."/PublicHeader.data"; + my $SymbolToHeader_F = $CacheHeaders."/SymbolToHeader.data"; + my $TypeToHeader_F = $CacheHeaders."/TypeToHeader.data"; + my $Path_F = $CacheHeaders."/PATH"; + + if($CacheHeaders + and -f $PublicHeader_F + and -f $SymbolToHeader_F + and -f $TypeToHeader_F + and -f $Path_F) + { + if(readFile($Path_F) eq $Path_A) + { + %PublicHeader = %{eval(readFile($PublicHeader_F))}; + %SymbolToHeader = %{eval(readFile($SymbolToHeader_F))}; + %TypeToHeader = %{eval(readFile($TypeToHeader_F))}; + + return; + } + } + + foreach my $File (@Headers) + { + $PublicHeader{getFilename($File)} = 1; + } + + my $Is_C = ($OBJ_LANG eq "C"); + + my @Langs = undef; + + if($EXUBERANT_CTAGS) + { + @Langs = ("C++"); + if($Is_C) { + @Langs = ("C"); + } + } + else + { + @Langs = ("C++", "OldC++"); + if($Is_C) { + @Langs = ("C", "OldC"); + } + } + + @Headers = sort {length($b)<=>length($a)} sort {lc($b) cmp lc($a)} @Headers; + + foreach my $File (@Headers) + { + my $HName = getFilename($File); + + if($UseTU) + { + my $TmpDir = $TMP_DIR."/tu"; + if(not -d $TmpDir) { + mkpath($TmpDir); + } + + my $File_A = abs_path($File); + + my $IncDir = getDirname($File_A); + my $IncDir_O = getDirname($IncDir); + + my $TmpInc = $TmpDir."/tmp-inc.h"; + my $TmpContent = ""; + if($IncludeDefines) + { + foreach my $D (split(/;/, $IncludeDefines)) { + $TmpContent = "#define $D\n"; + } + } + if($IncludePreamble) + { + foreach my $P (split(/;/, $IncludePreamble)) + { + if($P=~/\A\//) { + $TmpContent = "#include \"".$P."\"\n"; + } + else { + $TmpContent = "#include <".$P.">\n"; + } + } + } + $TmpContent .= "#include \"$File_A\"\n"; + writeFile($TmpInc, $TmpContent); + + my $Cmd = $GPP." -w -fpermissive -fdump-translation-unit -fkeep-inline-functions -c \"$TmpInc\""; + + if(defined $IncludePaths) + { + foreach my $P (split(/;/, $IncludePaths)) + { + if($P!~/\A\//) { + $P = $Path_A."/".$P; + } + + $Cmd .= " -I\"".$P."\""; + } + } + else + { # automatic + $Cmd .= " -I\"$IncDir\" -I\"$IncDir_O\""; + } + + foreach my $P (@DefaultInc) { + $Cmd .= " -I\"$P\""; + } + + $Cmd .= " -o ./a.out >OUT 2>&1"; + + chdir($TmpDir); + system($Cmd); + chdir($ORIG_DIR); + + my $TuDump = $TmpDir."/tmp-inc.h.001t.tu"; + my $Errors = $TmpDir."/OUT"; + + if(not -e $TuDump) + { + printMsg("ERROR", "failed to list symbols in the header \'$HName\'"); + if($Debug) { + printMsg("ERROR", readFile($Errors)); + } + next; + } + elsif($?) + { + printMsg("ERROR", "some errors occured when compiling header \'$HName\'"); + if($Debug) { + printMsg("ERROR", readFile($Errors)); + } + } + + my (%Fdecl, %Tdecl, %Tname, %Ident, %NotDecl) = (); + my $Content = readFile($TuDump); + $Content=~s/\n[ ]+/ /g; + + my @Lines = split(/\n/, $Content); + foreach my $N (0 .. $#Lines) + { + my $Line = $Lines[$N]; + if(index($Line, "function_decl")!=-1 + or index($Line, "var_decl")!=-1) + { + if($Line=~/name: \@(\d+)/) + { + my $Id = $1; + + if($Line=~/srcp: ([^:]+)\:\d/) + { + if(defined $PublicHeader{$1}) { + $Fdecl{$Id} = $1; + } + } + } + } + elsif($Line=~/\@(\d+)\s+identifier_node\s+strg:\s+(\w+)/) + { + $Ident{$1} = $2; + } + elsif($Is_C) + { + if(index($Line, "type_decl")!=-1) + { + if($Line=~/\A\@(\d+)/) + { + my $Id = $1; + if($Line=~/name: \@(\d+)/) + { + my $NId = $1; + + if($Line=~/srcp: ([^:]+)\:\d/) + { + if(defined $PublicHeader{$1}) + { + $Tdecl{$Id} = $1; + $Tname{$Id} = $NId; + } + } + } + } + } + elsif(index($Line, "record_type")!=-1 + or index($Line, "union_type")!=-1) + { + if($Line!~/ flds:/) + { + if($Line=~/name: \@(\d+)/) + { + $NotDecl{$1} = 1; + } + } + } + elsif(index($Line, "enumeral_type")!=-1) + { + if($Line!~/ csts:/) + { + if($Line=~/name: \@(\d+)/) + { + $NotDecl{$1} = 1; + } + } + } + elsif(index($Line, "integer_type")!=-1) + { + if($Line=~/name: \@(\d+)/) + { + $NotDecl{$1} = 1; + } + } + } + } + + foreach my $Id (keys(%Fdecl)) + { + if(my $Name = $Ident{$Id}) { + $SymbolToHeader{$Name}{$Fdecl{$Id}} = 1; + } + } + + if($Is_C) + { + foreach my $Id (keys(%Tdecl)) + { + if(defined $NotDecl{$Id}) { + next; + } + + if(my $Name = $Ident{$Tname{$Id}}) { + $TypeToHeader{$Name} = $Tdecl{$Id}; + } + } + } + + unlink($TuDump); + } + else + { # using Ctags + my $IgnoreTags = ""; + + if(defined $IgnoreTagsPath) { + $IgnoreTags .= " -I \@".$IgnoreTagsPath; + } + + if(@CtagsDef) + { + foreach my $Def (@CtagsDef) { + $IgnoreTags .= " -D '".$Def."'"; + } + } + + foreach my $Lang (@Langs) + { + my $List_S = `$CTAGS -x --$Lang-kinds=fpvxd --languages=+$Lang --language-force=$Lang $IgnoreTags \"$File\"`; + foreach my $Line (split(/\n/, $List_S)) + { + if($Line=~/\A(\w+)\s+(\w+)/) { + $SymbolToHeader{$1}{$HName} = $2; + } + + if(index($Line, " macro ")!=-1) + { + if($Line=~/#define\s+(\w+)\s+(\w+)\Z/) { + $SymbolToHeader{$2}{$HName} = "prototype"; + } + } + + if(not $Is_C) + { + if(index($Line, "operator ")==0) + { + if($Line=~/\A(operator) (\w.*?)\s+(prototype|function)/) { + $SymbolToHeader{$1." ".$2}{$HName} = $3; + } + elsif($Line=~/\A(operator) (\W.*?)\s+(prototype|function)/) { + $SymbolToHeader{$1.$2}{$HName} = $3; + } + } + } + } + + if($Is_C) + { + my $List_T = `$CTAGS -x --$Lang-kinds=gstu --languages=+$Lang --language-force=$Lang $IgnoreTags \"$File\"`; + foreach my $Line (split(/\n/, $List_T)) + { + if($Line=~/\A(\w+)/) + { + my $N = $1; + + if($Line!~/\b$N\s+$N\b/) { + $TypeToHeader{$N} = $HName; + } + } + } + } + } + } + } + + # We can't fully rely on the output of Ctags because it may + # miss some symbols intentionally (due to branches of code) + # or occasionally (due to complex macros). + if(not $UseTU) + { + foreach my $File (@Headers) + { + my $HName = getFilename($File); + my $Content = readFile($File); + + $Content=~s&/\*.+?\*/&&sg; + $Content=~s&(//|#define).*\n&\n&g; + + # Functions + my @Func = ($Content=~/([a-zA-Z]\w+)\s*\(/g); + foreach (@Func) + { + if(not defined $SymbolToHeader{$_} or not defined $SymbolToHeader{$_}{$HName}) { + $SymbolToHeader{$_}{$HName} = "prototype"; + } + } + + # Data + my @Data = ($Content=~/([a-zA-Z_]\w+)\s*;/gi); + foreach (@Data) + { + if(not defined $SymbolToHeader{$_} or not defined $SymbolToHeader{$_}{$HName}) { + $SymbolToHeader{$_}{$HName} = "prototype"; + } + } + + # Types + if($Is_C) + { + my @Type1 = ($Content=~/}\s*([a-zA-Z]\w+)\s*;/g); + my @Type2 = ($Content=~/([a-zA-Z]\w+)\s*{/g); + foreach (@Type1, @Type2) + { + if(not defined $TypeToHeader{$_} or not defined $TypeToHeader{$_}{$HName}) { + $TypeToHeader{$_}{$HName} = 1; + } + } + } + } + } + + if($CacheHeaders) + { + writeFile($PublicHeader_F, Dumper(\%PublicHeader)); + writeFile($SymbolToHeader_F, Dumper(\%SymbolToHeader)); + writeFile($TypeToHeader_F, Dumper(\%TypeToHeader)); + writeFile($Path_F, $Path_A); + } +} + +sub getDebugAltLink($) +{ + my $Obj = $_[0]; + + my $AltDebugFile = getDebugFile($Obj, "gnu_debugaltlink"); + + if(not $AltDebugFile) { + return undef; + } + + my $Dir = getDirname($Obj); + + my $AltObj_R = $AltDebugFile; + if($Dir and $Dir ne ".") { + $AltObj_R = $Dir."/".$AltObj_R; + } + + if(-e $AltObj_R) + { + printMsg("INFO", "Set alternate debug-info file to \'$AltObj_R\' (use -alt option to change it)"); + return $AltObj_R; + } + + printMsg("WARNING", "can't access \'$AltObj_R\'"); + return undef; +} + +sub scenario() +{ + if($Help) + { + helpMsg(); + exit(0); + } + if($ShowVersion) + { + printMsg("INFO", "ABI Dumper $TOOL_VERSION"); + printMsg("INFO", "Copyright (C) 2019 Andrey Ponomarenko's ABI Laboratory"); + printMsg("INFO", "License: GNU LGPL 2.1 "); + printMsg("INFO", "This program is free software: you can redistribute it and/or modify it.\n"); + printMsg("INFO", "Written by Andrey Ponomarenko."); + exit(0); + } + if($DumpVersion) + { + printMsg("INFO", $TOOL_VERSION); + exit(0); + } + + $Data::Dumper::Sortkeys = 1; + + if($SortDump) { + $Data::Dumper::Sortkeys = \&dumpSorting; + } + + if($SearchDirDebuginfo) + { + if(not -d $SearchDirDebuginfo) { + exitStatus("Access_Error", "can't access directory \'$SearchDirDebuginfo\'"); + } + } + + if($PublicHeadersPath) + { + if(not -e $PublicHeadersPath) { + exitStatus("Access_Error", "can't access \'$PublicHeadersPath\'"); + } + + $PublicHeadersIsDir = (-d $PublicHeadersPath); + + foreach my $P (split(/;/, $IncludePaths)) + { + if($PublicHeadersIsDir and $P!~/\A\//) { + $P = $PublicHeadersPath."/".$P; + } + + if(not -e $P) { + exitStatus("Access_Error", "can't access \'$P\'"); + } + } + } + + if($SymbolsListPath) + { + if(not -f $SymbolsListPath) { + exitStatus("Access_Error", "can't access file \'$SymbolsListPath\'"); + } + foreach my $S (split(/\s*\n\s*/, readFile($SymbolsListPath))) { + $SymbolsList{$S} = 1; + } + } + + if($VTDumperPath) + { + if(not -x $VTDumperPath) { + exitStatus("Access_Error", "can't access \'$VTDumperPath\'"); + } + + $VTABLE_DUMPER = $VTDumperPath; + } + + if(defined $Compare) + { + my $P1 = $ARGV[0]; + my $P2 = $ARGV[1]; + + if(not $P1) { + exitStatus("Error", "arguments are not specified"); + } + elsif(not -e $P1) { + exitStatus("Access_Error", "can't access \'$P1\'"); + } + + if(not $P2) { + exitStatus("Error", "second argument is not specified"); + } + elsif(not -e $P2) { + exitStatus("Access_Error", "can't access \'$P2\'"); + } + + my %ABI = (); + + $ABI{1} = eval(readFile($P1)); + $ABI{2} = eval(readFile($P2)); + + my %SymInfo = (); + + foreach (1, 2) + { + foreach my $ID (keys(%{$ABI{$_}->{"SymbolInfo"}})) + { + my $Info = $ABI{$_}->{"SymbolInfo"}{$ID}; + + if(my $MnglName = $Info->{"MnglName"}) { + $SymInfo{$_}{$MnglName} = $Info; + } + elsif(my $ShortName = $Info->{"ShortName"}) { + $SymInfo{$_}{$ShortName} = $Info; + } + } + } + + foreach my $Symbol (sort keys(%{$SymInfo{1}})) + { + if(not defined $SymInfo{2}{$Symbol}) { + printMsg("INFO", "Removed $Symbol"); + } + } + + foreach my $Symbol (sort keys(%{$SymInfo{2}})) + { + if(not defined $SymInfo{1}{$Symbol}) { + printMsg("INFO", "Added $Symbol"); + } + } + + exit(0); + } + + if($TargetVersion eq "") { + printMsg("WARNING", "module version is not specified (-lver NUM)"); + } + + if($FullDump) + { + $AllTypes = 1; + $AllSymbols = 1; + } + + if(not $OutputDump) { + $OutputDump = "./ABI.dump"; + } + + if(not @ARGV) { + exitStatus("Error", "object path is not specified"); + } + + foreach my $Obj (@ARGV) + { + if(not -e $Obj) { + exitStatus("Access_Error", "can't access \'$Obj\'"); + } + } + + if($AltDebugInfoOpt) + { + if(not -e $AltDebugInfoOpt) { + exitStatus("Access_Error", "can't access \'$AltDebugInfoOpt\'"); + } + $AltDebugInfo = $AltDebugInfoOpt; + readAltInfo($AltDebugInfoOpt); + } + + if($ExtraInfo) + { + mkpath($ExtraInfo); + $ExtraInfo = abs_path($ExtraInfo); + } + + initABI(); + + my $Res = 0; + + foreach my $Obj (@ARGV) + { + if($Obj=~/\.a\Z/) { + exitStatus("Error", "analysis of static libraries is not supported, please dump ABIs of individual objects in the archive or compile a shared library"); + } + + if(not $TargetName) + { + $TargetName = getFilename(realpath($Obj)); + $TargetName=~s/\.debug\Z//; # nouveau.ko.debug + + if(index($TargetName, "libstdc++")==0 + or index($TargetName, "libc++")==0) { + $STDCXX_TARGET = 1; + } + } + + readSymbols($Obj); + + if(not defined $PublicSymbols_Detected) + { + if(defined $PublicHeadersPath) { + detectPublicSymbols($PublicHeadersPath); + } + } + + $Res += readDWARFInfo($Obj); + + %DWARF_Info = (); + + readVtables($Obj); + } + + if(not defined $Library_Symbol{$TargetName}) { + exitStatus("No_Exported", "can't find exported symbols in object(s), please add a shared object on command line"); + } + + if(not $Res) { + exitStatus("No_DWARF", "can't find debug info in object(s)"); + } + + %VirtualTable = (); + + completeABI(); + selectSymbols(); + removeUnused(); + + if(defined $PublicHeadersPath) + { + foreach my $Tid (sort {$a<=>$b} keys(%TypeInfo)) + { + if(not $TypeInfo{$Tid}{"Header"} + or not defined $PublicHeader{$TypeInfo{$Tid}{"Header"}}) + { + if($TypeInfo{$Tid}{"Type"}=~/Struct|Union|Enum|Typedef/) + { + my $TName = $TypeInfo{$Tid}{"Name"}; + $TName=~s/\A(struct|class|union|enum) //g; + + if(defined $TypeToHeader{$TName}) { + $TypeInfo{$Tid}{"Header"} = $TypeToHeader{$TName}; + } + #elsif(index($TName, "::")!=-1) + #{ + # if($TName=~/::(.+?)\Z/) + # { + # if(defined $TypeToHeader{$1}) + # { + # $TypeInfo{$Tid}{"Header"} = $TypeToHeader{$1}; + # } + # } + #} + } + } + + if(not selectPublicType($Tid)) { + $TypeInfo{$Tid}{"PrivateABI"} = 1; + } + } + } + + if(defined $PublicHeadersPath) + { + foreach my $H (keys(%HeadersInfo)) + { + if(not defined $PublicHeader{getFilename($H)}) { + delete($HeadersInfo{$H}); + } + } + } + + # free memory + %Mangled_ID = (); + %Checked_Spec = (); + %SelectedSymbols = (); + %Cache = (); + + %ClassChild = (); + %TypeSpec = (); + + %SourceFile = (); + %SourceFile_Alt = (); + %DebugLoc = (); + %TName_Tid = (); + %TName_Tids = (); + %SymbolTable = (); + + %NameSpace = (); + + %DeletedAnon = (); + %CheckedType = (); + %DuplBaseType = (); + + %KSymTab = (); + %TypeToHeader = (); + %PublicHeader = (); + + createABIFile(); + + exit(0); +} + +scenario(); -- Gitee From 36de0845431169d38b50bdbaa5a8807095b42ce5 Mon Sep 17 00:00:00 2001 From: guoqinglan Date: Tue, 26 Oct 2021 11:09:17 +0800 Subject: [PATCH 2/2] Analyze comparison data with abi-dumper --- src/abi-info-check.py | 240 +++++++++++++++++++++---------- src/abi-info-collect.py | 307 +++++++++++++++++++++++++++++++--------- uos-abi-check.spec | 24 +++- 3 files changed, 429 insertions(+), 142 deletions(-) diff --git a/src/abi-info-check.py b/src/abi-info-check.py index ce1b29c..9497189 100644 --- a/src/abi-info-check.py +++ b/src/abi-info-check.py @@ -3,12 +3,11 @@ # -*- coding: utf-8 -*- import argparse -import io import logging import os import re -import shutil import subprocess +import sys import tarfile import xml.etree.ElementTree as ET @@ -16,6 +15,17 @@ import distro import pandas as pd +TOOL_VERSION = "1.0" + +ABI_CC = "abi-compliance-checker" +ABI_DUMPER = "abi-dumper" + +CMD_NAME = os.path.basename(__file__) +ERROR_CODE = {"Ok": 0, "Error": 1, "Empty": 10, "NoDebug": 11, "NoABI": 12} + +ARGS = {} + + def run_cmd(cmd): ''' run command in subprocess and return exit code, output, error. @@ -41,36 +51,28 @@ def detect_os(): def parse_args(): - args = [] + desc = "Analyze abi infomation about bin file." parser = argparse.ArgumentParser( - description='Analyze infomation about bin file.') - parser.add_argument('-v', '--verbose', action='store_true', - default=False, help='Enable verbose output') + description=desc, epilog=f"example: {CMD_NAME} -tar /path/OLD-abi-info.tar.gz") + parser.add_argument('-v', action='version', + version='Package ABI Info Collector '+TOOL_VERSION) parser.add_argument( - '--info', metavar='info.tar.gz', help='info tarball file', required=True) - args = parser.parse_args() - - if args.verbose: - logging.basicConfig(level=logging.DEBUG) - else: - logging.basicConfig(level=logging.INFO) - - if not os.access(f'{args.info}', os.F_OK): - logging.error( - f"The info tarball file {args.info} doesn't exist, Please check") - exit() - if not os.access(f'{args.info}', os.R_OK): - logging.error( - f"The info tarball file {args.info} can't be read, Please check") - exit() - return args.info + '-debug', help='enable debug messages', action='store_true') + parser.add_argument('-tar', metavar='OLD-abi-info.tar.gz', + help='abi info tarball file',) + parser.add_argument( + '-debuginfo', help=argparse.SUPPRESS, action='store_true') + parser.add_argument( + '-export-dir', help='specify a directory to save and reuse ABI info export (default: ./abi-info-export)', metavar='DIR') + #parser.add_argument('-src', help='collect source abi info', action='store_true') + return parser.parse_args() -def extract_tarball(tarball, target_path): - print(f'Preparing to unpack {tarball} ...') +def extract_tarball(tarball, export_dir): + print(f'Decompressing file {tarball} ...') with tarfile.open(tarball, 'r:gz') as tar: - tar.extractall(path=target_path) - print(f'Unpacking {tarball} ...') + tar.extractall(path=export_dir) + print(f'The file {tarball} has been decompressed.') def get_rpmname_without_libs(x): @@ -78,57 +80,59 @@ def get_rpmname_without_libs(x): return re.sub('-libs$', '', x).strip() -def find_devel_lib_package(target_path): +def find_devel_lib_package(export_dir): print('Checking the dependency ...') - file = f'{target_path}/pkgs.info' + file = f'{export_dir}/OLD-pkgs.info' if not os.access(f'{file}', os.R_OK): - logging.error( - f"The info file {file} can't be read. Please check") - exit() + exit_status( + "Error", f"The info file {file} can't be read. Please check") dev_pkgs_set = set() libs_pkgs_set = set() + dbginfo_pkgs_set = set() with open(file, 'r') as f: for line in f: rpm_name = get_rpmname_without_libs(line) dev_pkgs_set.add(f'{rpm_name}-devel') + dbginfo_pkgs_set.add(f'{rpm_name}-debuginfo') # 包名中包含 lib 关键字的包,本身就是 lib 包 if 'lib' not in rpm_name: libs_pkgs_set.add(f'{rpm_name}-libs') else: libs_pkgs_set.add(f'{rpm_name}') - return dev_pkgs_set, libs_pkgs_set + return dev_pkgs_set, libs_pkgs_set, dbginfo_pkgs_set -def detect_devel_lib_package(dev_pkgs_set, libs_pkgs_set): - lost_pkg_set = set() - dev_pkgs_set = dev_pkgs_set | libs_pkgs_set - for pkg in dev_pkgs_set: +def try_install_packages(dev_pkgs, libs_pkgs=set()): + not_installed_pkgs = set() + + dev_pkgs = set(dev_pkgs) | set(libs_pkgs) + for pkg in dev_pkgs: cmd = f'rpm -q {pkg}' returncode, stdout, stderr = run_cmd(cmd) if returncode != 0: print(f"It seems that the OS doesn't install {pkg}") - lost_pkg_set.add(pkg) + not_installed_pkgs.add(pkg) else: print(f'The packages "{pkg}" have been installed') - lost_yum_pkg_set = set() - for pkg in lost_pkg_set: + install_failed_pkgs = set() + for pkg in not_installed_pkgs: cmd = f'yum install -y {pkg}' print(f'Trying to install package {pkg} with yum') returncode, stdout, stderr = run_cmd(cmd) if returncode != 0: print(f"Can't install {pkg}, with yum") - lost_yum_pkg_set.add(pkg) + install_failed_pkgs.add(pkg) else: print(f'Successfully installed {pkg} with yum') - if lost_yum_pkg_set: - print(f'Please install {lost_yum_pkg_set}, then retry') - exit() + if install_failed_pkgs: + exit_status( + "Error", f'Please install {install_failed_pkgs}, then retry') -def gen_abi_cc_xml(tarball, target_path, dev_pkgs_set, libs_pkgs_set): +def gen_xml_and_dump(abi_name, target_path, dev_pkgs_set, libs_pkgs_set): headers_list = list() for pkg in dev_pkgs_set: cmd = f'rpm -ql {pkg} | grep .*include.*\.h$' @@ -141,7 +145,7 @@ def gen_abi_cc_xml(tarball, target_path, dev_pkgs_set, libs_pkgs_set): returncode, stdout, stderr = run_cmd(cmd) libs_list.append(stdout.decode()) - file = f'{target_path}/dump.xml' + file = f'{target_path}/NEW-dump.xml' with open(file, 'wt+') as f: f.write("\n") f.write('1.0\n') @@ -154,19 +158,18 @@ def gen_abi_cc_xml(tarball, target_path, dev_pkgs_set, libs_pkgs_set): f.write("\n") f.writelines(libs_list) f.write("\n") - lib_name = str(tarball).split('_')[1] - dump_file = f'{target_path}/{lib_name}.dump' - cmd = f'abi-compliance-checker -xml -l {lib_name} -dump {file} -dump-path {dump_file}' + dump_file = f'{target_path}/NEW-abi.dump' + cmd = f'abi-compliance-checker -xml -l {abi_name} -dump {file} -dump-path {dump_file}' print(f'Analyzing the symbols of {libs_pkgs_set} ...') - returncode, stdout, stderr = run_cmd(cmd) + run_cmd(cmd) return dump_file -def compare_syms(target_path, dump_file): +def compare_syms(export_dir, dump_file): print('Checking symbol differences ...') elf_sym_set = set() - elf_file = f'{target_path}/elf.info' + elf_file = f'{export_dir}/OLD-elf.info' with open(f'{elf_file}', 'rt') as f: elf_symbol_fmt = ' *(?P[0-9]*): (?P[0-9abcdef]*) (?P[0-9]*).*(FUNC).*@.*' for line in f: @@ -196,7 +199,7 @@ def compare_syms(target_path, dump_file): return [diff_syms_list, old_syms_list] -def output_result(syms_list, target_path): +def output_result(syms_list, export_dir): df1 = pd.DataFrame(syms_list[0], columns=[u'当前系统缺少符号']) df2 = pd.DataFrame(syms_list[1], columns=[u'二进制依赖符号']) @@ -207,48 +210,141 @@ def output_result(syms_list, target_path): html = result.to_html() csv = result.to_csv() - file = f'{target_path}/result.html' + file = f'{export_dir}/result.html' with open(file, 'wt+') as f: f.writelines(html) html_path = os.path.realpath(file) - file = f'{target_path}/result.csv' + file = f'{export_dir}/result.csv' with open(file, 'wt+') as f: f.writelines(csv) print(f'The check result is {html_path}') +def s_exit(code): + sys.exit(ERROR_CODE[code]) + + +def print_err(msg): + sys.stderr.write(msg+"\n") + + +def exit_status(code, msg): + if code != "Ok": + print_err("ERROR: "+msg) + else: + print(msg) + + s_exit(code) + + +def check_cmd(prog): + for path in os.environ['PATH'].split(os.pathsep): + path = path.strip('"') + candidate = path+"/"+prog + if os.path.isfile(candidate) and os.access(candidate, os.X_OK): + return candidate + return None + + +def get_abi_name(export_dir): + file = f'{export_dir}/OLD-name.info' + if not os.access(f'{file}', os.R_OK): + exit_status( + "Error", f"The info file {file} can't be read, Please check") + with open(file, 'r') as f: + name = f.read() + return name + + +def gen_dump_with_debuginfo(basename, export_dir, debuginfo_pkgs): + # TODO:当前实现有困难 + pass + + +def check_dump_syms(abi_name, export_dir): + new_dump = f'{export_dir}/NEW-abi.dump' + old_dump = f'{export_dir}/OLD-abi.dump' + syms_list = f'{export_dir}/OLD-func-syms.info' + html = f'{export_dir}/export.html' + cmd = f'abi-compliance-checker -l {abi_name} -old {old_dump} -new {new_dump} --symbols-list {syms_list} --report-path {html}' + print(f'Checking the symbols of {abi_name} ...') + run_cmd(cmd) + return html + + def main(): + global ARGS + ARGS = parse_args() + + # 检查参数 + if not ARGS.tar: + exit_status('Error', 'tarball file are not specified (-tar option)') + + tarball = ARGS.tar + if not os.path.isfile(tarball): + exit_status('Error', f'''file "{tarball}" does not exist.''') + if not os.access(tarball, os.R_OK): + exit_status('Error', f'''file "{tarball}" can't be read.''') + if not tarfile.is_tarfile(tarball): + exit_status( + 'Error', f'''file "{tarball}" isn't a standard tarball file.''') + + if ARGS.export_dir: + export_dir = ARGS.export_dir + else: + export_dir = "abi-info-export" - # 判断系统 - # detect_os() + if not os.path.exists(export_dir): + os.makedirs(export_dir) + + # 检查 abi-compliance-checker 是否安装 + global ABI_CC, ABI_DUMPER + if not check_cmd(ABI_CC): + exit_status('Error', 'ABI Compliance Checker is not installed') - # 解析参数 - tarball = parse_args() + # 检查 abi-dumper 是否安装 + if not check_cmd(ABI_DUMPER): + exit_status('Error', 'ABI Dumper is not installed') - # 用 abc/xxx.tar.gz 的 xxx 作为目录名称 - target_path = tarball.split('/')[-1] - target_path = target_path.split('.')[-3] + # 判断系统 + # detect_os() # 解压 tarball - extract_tarball(tarball, target_path) + extract_tarball(tarball, export_dir) - # 解析 dev 包和 lib 包 - dev_pkgs_set, libs_pkgs_set = find_devel_lib_package(target_path) + # 获取OLD-name 值 + abi_name = get_abi_name(export_dir) - # 判断系统中是否安装 dev 包 和 lib 包 - detect_devel_lib_package(dev_pkgs_set, libs_pkgs_set) + # 解析 dev 包 和 lib 包 + dev_pkgs, libs_pkgs, debuginfo_pkgs = find_devel_lib_package(export_dir) - # 生成 abi-compliance-checker 用到的 xml 文件,并生成 dump 文件 - dump_file = gen_abi_cc_xml( - tarball, target_path, dev_pkgs_set, libs_pkgs_set) + if not ARGS.debuginfo: + + # 判断系统中是否安装 dev 包 和 lib 包 + try_install_packages(dev_pkgs, libs_pkgs) + # 生成 abi-compliance-checker 用到的 xml 文件,并生成 dump 文件 + dump_file = gen_xml_and_dump( + abi_name, export_dir, dev_pkgs, libs_pkgs) + else: + # 判断系统中是否安装 debuginfo_pkgs 包 + try_install_packages(debuginfo_pkgs) + # 通过 debuginfo 信息生成 dump 文件 + dump_file = gen_dump_with_debuginfo(abi_name, export_dir, + debuginfo_pkgs) # 读取 dump 文件,分析 symbol - syms_list = compare_syms(target_path, dump_file) + #syms_list = compare_syms(export_dir, dump_file) + # 比较前后两个dump 文件 + html = check_dump_syms(abi_name, export_dir) + if os.path.isfile(html): + print(f'The check result is {html}') + else: + exit_status("Error", "Checking error") # 输出结果 - output_result(syms_list, target_path) + # output_result(export_dir) if __name__ == "__main__": diff --git a/src/abi-info-collect.py b/src/abi-info-collect.py index 587e8c3..15a7315 100644 --- a/src/abi-info-collect.py +++ b/src/abi-info-collect.py @@ -12,6 +12,8 @@ import subprocess import sys import tarfile +TOOL_VERSION = "1.0" + _UNIXCONFDIR = os.environ.get('UNIXCONFDIR', '/etc') _OS_RELEASE_BASENAME = 'os-release' @@ -1178,6 +1180,15 @@ class LinuxDistribution(object): _distro = LinuxDistribution() +ABI_CC = "abi-compliance-checker" +ABI_DUMPER = "abi-dumper" + +CMD_NAME = os.path.basename(__file__) +ERROR_CODE = {"Ok": 0, "Error": 1, "Empty": 10, "NoDebug": 11, "NoABI": 12} + +ARGS = {} + + def run_cmd(cmd): ''' run command in subprocess and return exit code, output, error. @@ -1197,35 +1208,35 @@ def run_cmd(cmd): def parse_args(): - args = [] + desc = "Collect some infomation about bin file." parser = argparse.ArgumentParser( - description='Collect some infomation about bin file.') - parser.add_argument('-v', '--verbose', action='store_true', - default=False, help='Enable verbose output') + description=desc, epilog=f"example: {CMD_NAME} -bin /usr/bin/openssh") + parser.add_argument('-v', action='version', + version='Package ABI Info Collector '+TOOL_VERSION) + parser.add_argument( + '-debug', help='enable debug messages', action='store_true') + parser.add_argument('-bin', metavar='BINFILE', + help='collect binary abi info',) parser.add_argument( - '--bin', metavar='binfile', help='bin files', required=True) - args = parser.parse_args() + '-debuginfo', help=argparse.SUPPRESS, action='store_true') + parser.add_argument( + '-export-dir', help='specify a directory to save and reuse ABI info export (default: ./abi-info-export)', metavar='DIR') + #parser.add_argument('-src', help='collect source abi info', action='store_true') + return parser.parse_args() - if args.verbose: - logging.basicConfig(level=logging.DEBUG) - else: - logging.basicConfig(level=logging.INFO) - if not os.access(f'{args.bin}', os.F_OK): - logging.error( - f"The input bin file {args.bin} doesn't exist. Please check") - exit() - if not os.access(f'{args.bin}', os.R_OK): - logging.error(f"The input bin file {args.bin} can't be read") - exit() - return args.bin +def collect_os_info(export_dir): + shutil.copy('/etc/os-release', f'{export_dir}/OLD-os-release.info') -def collect_os_info(prefix_path): - shutil.copy('/etc/os-release', f'{prefix_path}/os-release.info') +def collect_name_info(name, export_dir): + + outfile = f'{export_dir}/OLD-name.info' + with open(f'{outfile}', 'wb+') as f: + f.write(name.encode()) -def collect_readelf_info(binfile, prefix_path): +def collect_readelf_info(binfile, export_dir): print('Checking information about ELF ...') # eu-readelf is better than readelf @@ -1233,44 +1244,36 @@ def collect_readelf_info(binfile, prefix_path): returncode, stdout, stderr = run_cmd(cmd) if returncode != 0: - logging.error(f"return code: {returncode}, {stderr.decode()}") - exit() - - if not os.path.exists(f"{prefix_path}"): - os.makedirs(f"{prefix_path}") - outfile = outfile = f'{prefix_path}/elf.info' + exit_status('Error', f'read {binfile} elf info failed') + outfile = f'{export_dir}/OLD-elf.info' with open(f'{outfile}', 'wb+') as f: f.write(stdout) return outfile -def collect_ldd_info(binfile, prefix_path): +def collect_ldd_info(binfile, export_dir): print('Checking shared object dependencies ...') cmd = "ldd -v " + binfile returncode, stdout, stderr = run_cmd(cmd) if returncode != 0: - logging.error(f"return code: {returncode.decode()}, {stderr.decode()}") - exit() - - if not os.path.exists(f"{prefix_path}"): - os.makedirs(f"{prefix_path}") - outfile = outfile = f'{prefix_path}/ldd.info' + exit_status('Error', f'read {binfile} ldd info failed') + outfile = f'{export_dir}/OLD-ldd.info' with open(f'{outfile}', 'wb+') as f: f.write(stdout) return outfile -def find_soname_file(f_elf, f_ldd): +def find_soname_file(f_elf, f_ldd, export_dir): print('Checking package dependencies ...') sym_set = set() + func_sym_set = set() with open(f'{f_elf}', 'rt') as f: - elf_symbol_fmt = ' *(?P[0-9]*): (?P[0-9abcdef]*) (?P[0-9]*).*(FUNC).*@.*' for line in f: m = re.match(elf_symbol_fmt, line) @@ -1278,7 +1281,15 @@ def find_soname_file(f_elf, f_ldd): continue elf_line_list = re.split(r'\s+', line.strip()) sym = elf_line_list[7].split('@') + func_sym_set.add(sym[0]) sym_set.add(sym[1]) + if ARGS.debug: + print(f"All elf syms is {func_sym_set}") + + file = f"{export_dir}/OLD-func-syms.info" + with open(f'{file}', 'wb+') as f: + for line in func_sym_set: + f.write((line+'\n').encode()) soname_file_set = set() with open(f'{f_ldd}', 'rt') as f: @@ -1294,7 +1305,7 @@ def find_soname_file(f_elf, f_ldd): return soname_file_set -def find_so_rpm_pkgs_name(soname_file_set, binfile, prefix_path): +def find_so_rpm_pkgs_name(soname_file_set, export_dir): so_pkgs_name_set = set() for file in soname_file_set: @@ -1305,66 +1316,234 @@ def find_so_rpm_pkgs_name(soname_file_set, binfile, prefix_path): continue so_pkgs_name_set.add(stdout) - outfile = f'{prefix_path}/pkgs.info' + outfile = f'{export_dir}/OLD-pkgs.info' with open(f'{outfile}', 'wb+') as f: for rpm_name in so_pkgs_name_set: f.write(rpm_name) -def find_soname_package(soname_file_set, binfile, prefix_path): +def find_soname_package(soname_file_set, export_dir): os_distro = _distro.linux_distribution(full_distribution_name=False)[0] if os_distro == 'debain': pass if os_distro == 'centos' or os_distro == 'fedora': - soname_pkgs_set = find_so_rpm_pkgs_name( - soname_file_set, binfile, prefix_path) + find_so_rpm_pkgs_name(soname_file_set, export_dir) -def compress_outfile(binfile, prefix_path): +def compress_outfile(binfile, export_dir): - tar_name = prefix_path.split('/')[-1] - tar_file = f'{tar_name}.tar.gz' + tar_file = f'{export_dir}/OLD-abi-info.tar.gz' with tarfile.open(tar_file, "w:gz") as tar: - for parent, dirnames, filenames in os.walk(f'{prefix_path}'): - filenames[:] = [f for f in filenames if f.endswith(".info")] + for parent, dirnames, filenames in os.walk(f'{export_dir}'): + filenames[:] = [f for f in filenames if f.endswith( + (".info", '.xml', '.dump'))] for filename in filenames: pathfile = os.path.join(parent, filename) # 去除 tarball 中的父目录 tar.add(pathfile, arcname=filename) os.remove(pathfile) - os.removedirs(prefix_path) tar_path = os.path.realpath(tar_file) print(f'{binfile} information: {tar_path}') +def s_exit(code): + sys.exit(ERROR_CODE[code]) + + +def print_err(msg): + sys.stderr.write(msg+"\n") + + +def exit_status(code, msg): + if code != "Ok": + print_err("ERROR: "+msg) + else: + print(msg) + + s_exit(code) + + +def check_cmd(prog): + for path in os.environ['PATH'].split(os.pathsep): + path = path.strip('"') + candidate = path+"/"+prog + if os.path.isfile(candidate) and os.access(candidate, os.X_OK): + return candidate + return None + + +def get_rpmname_without_libs(x): + # 正则获取rpmname + return re.sub('-libs$', '', x).strip() + + +def find_devel_lib_package(target_path): + print('Checking the dependency ...') + file = f'{target_path}/OLD-pkgs.info' + if not os.access(f'{file}', os.R_OK): + exit_status( + "Error", f"The info file {file} can't be read. Please check") + + dev_pkgs_set = set() + libs_pkgs_set = set() + dbginfo_pkgs_set = set() + with open(file, 'r') as f: + for line in f: + rpm_name = get_rpmname_without_libs(line) + dev_pkgs_set.add(f'{rpm_name}-devel') + dbginfo_pkgs_set.add(f'{rpm_name}-debuginfo') + # 包名中包含 lib 关键字的包,本身就是 lib 包 + if 'lib' not in rpm_name: + libs_pkgs_set.add(f'{rpm_name}-libs') + else: + libs_pkgs_set.add(f'{rpm_name}') + return dev_pkgs_set, libs_pkgs_set, dbginfo_pkgs_set + + +def try_install_packages(dev_pkgs, libs_pkgs=set()): + not_installed_pkgs = set() + + dev_pkgs = set(dev_pkgs) | set(libs_pkgs) + for pkg in dev_pkgs: + cmd = f'rpm -q {pkg}' + returncode, stdout, stderr = run_cmd(cmd) + if returncode != 0: + print(f"It seems that the OS doesn't install {pkg}") + not_installed_pkgs.add(pkg) + else: + print(f'The packages "{pkg}" have been installed') + + install_failed_pkgs = set() + for pkg in not_installed_pkgs: + cmd = f'yum install -y {pkg}' + print(f'Trying to install package {pkg} with yum') + returncode, stdout, stderr = run_cmd(cmd) + if returncode != 0: + print(f"Can't install {pkg}, with yum") + install_failed_pkgs.add(pkg) + else: + print(f'Successfully installed {pkg} with yum') + + if install_failed_pkgs: + exit_status( + "Error", f'Please install {install_failed_pkgs}, then retry') + + +def gen_xml_and_dump(basename, target_path, dev_pkgs_set, libs_files_set): + headers_list = list() + for pkg in dev_pkgs_set: + cmd = f'rpm -ql {pkg} | grep .*include.*\.h$' + returncode, stdout, stderr = run_cmd(cmd) + headers_list.append(stdout.decode()) + + file = f'{target_path}/OLD-dump.xml' + with open(file, 'wt+') as f: + f.write("\n") + f.write('1.0\n') + f.write('\n') + + f.write("\n") + f.writelines(headers_list) + f.write("\n") + + f.write("\n") + for line in libs_files_set: + f.write(line+'\n') + f.write("\n") + dump_file = f'{target_path}/OLD-abi.dump' + cmd = f'abi-compliance-checker -xml -l {basename} -dump {file} -dump-path {dump_file}' + print(f'Analyzing the symbols ...') + returncode, stdout, stderr = run_cmd(cmd) + + return dump_file + + +def gen_dump_with_debuginfo(basename, export_dir, debuginfo_pkgs): + # TODO:当前实现有困难 + pass + + def main(): - binfile = parse_args() + global ARGS + ARGS = parse_args() - os_id = _distro.id() - bin_name = binfile.split('/')[-1] + # 检查参数 + if not ARGS.bin: + exit_status('Error', 'bin file are not specified (-bin option)') - prefix_path = f'./{os_id}_{bin_name}_info' - if not os.path.exists(f"{prefix_path}"): - os.makedirs(f"{prefix_path}") + binfile = ARGS.bin + if not os.path.isfile(binfile): + exit_status('Error', f'''file "{binfile}" does not exist.''') + if not os.access(binfile, os.R_OK): + exit_status('Error', f'''file "{binfile}" can't be read.''') - # 收集 os-release 到 prefix_path 目录下 - collect_os_info(prefix_path) + textchars = bytearray({7, 8, 9, 10, 12, 13, 27} | + set(range(0x20, 0x100)) - {0x7f}) + + def is_binary_string(bytes): return bool(bytes.translate(None, textchars)) + + if not is_binary_string(open(binfile, 'rb').read(1024)): + exit_status( + 'Error', f'''file {binfile} isn't a standard binary file.''') + + if ARGS.export_dir: + export_dir = ARGS.export_dir + else: + export_dir = "abi-info-export" + basename = os.path.basename(binfile) + export_dir += "/" + basename + if not os.path.exists(export_dir): + os.makedirs(export_dir) - # 收集 elf 信息到 prefix_path 目录下 - f_elf_info = collect_readelf_info(binfile, prefix_path) + # 收集 basename 到 export_dir 目录下 + collect_name_info(basename, export_dir) - # 收集 ldd 信息到 prefix_path 目录下 - f_ldd_info = collect_ldd_info(binfile, prefix_path) + # 检查 abi-compliance-checker 是否安装 + global ABI_CC, ABI_DUMPER + if not check_cmd(ABI_CC): + exit_status('Error', 'ABI Compliance Checker is not installed') + + # 检查 abi-dumper 是否安装 + if not check_cmd(ABI_DUMPER): + exit_status('Error', 'ABI Dumper is not installed') + + # 收集 os-release 到 export_dir 目录下 + collect_os_info(export_dir) + + # 收集 elf 信息到 export_dir 目录下 + f_elf = collect_readelf_info(binfile, export_dir) + + # 收集 ldd 信息到 export_dir 目录下 + f_ldd = collect_ldd_info(binfile, export_dir) # 通过 elf 信息和 ldd 信息查找 /lib64/libxxx.so.xx 集合 - soname_file_set = find_soname_file(f_elf=f_elf_info, f_ldd=f_ldd_info) + soname_file_set = find_soname_file(f_elf, f_ldd, export_dir) + if ARGS.debug: + print(f"The dynamic libraries used by the bin are {soname_file_set}") - # 通过 /lib64/libxxx.so.xx 集合, 分析软件包名信息到 prefix_path 目录下 - find_soname_package(soname_file_set, binfile, prefix_path) + # 通过 /lib64/libxxx.so.xx 集合, 分析软件包名信息到 export_dir 目录下 + find_soname_package(soname_file_set, export_dir) - # 输出 tarball 到 prefix_path 目录下 - compress_outfile(binfile, prefix_path) + # 解析 dev 包 和 lib 包 + dev_pkgs, libs_pkgs, debuginfo_pkgs = find_devel_lib_package(export_dir) + + if not ARGS.debuginfo: + + # 判断系统中是否安装 dev 包 和 lib 包 + try_install_packages(dev_pkgs, libs_pkgs) + # 生成 abi-compliance-checker 用到的 xml 文件,并生成 dump 文件 + gen_xml_and_dump( + basename, export_dir, dev_pkgs, soname_file_set) + else: + # 判断系统中是否安装 debuginfo_pkgs 包 + try_install_packages(debuginfo_pkgs) + # 通过 debuginfo 信息生成 dump 文件 + gen_dump_with_debuginfo(basename, export_dir, + debuginfo_pkgs) + + # 输出 tarball 到 export_dir 目录下 + compress_outfile(binfile, export_dir) if __name__ == "__main__": diff --git a/uos-abi-check.spec b/uos-abi-check.spec index 53ac6fe..3151e60 100644 --- a/uos-abi-check.spec +++ b/uos-abi-check.spec @@ -1,7 +1,7 @@ %global debug_package %{nil} Name: uos-abi-check -Version: 0.1 -Release: 1 +Version: 1.0 +Release: 2 Summary: a tool for checking backward binary compatibility of a C/C++ software library License: GPL2 URL: https://github.com/deepinlinux @@ -35,9 +35,14 @@ python3 -O -m compileall -b src %install mkdir -p %{buildroot}/usr/bin/ mkdir -p %{buildroot}/usr/libexec -pushd abi-compliance-checker-2.3 +pushd abi-compliance-checker-2.4 %make_install popd + +pushd abi-dumper-2.1 +%make_install +popd + # collect module install install -m 755 src/abi-info-collect.py %{buildroot}/usr/bin/abi-info-collect install -m 755 src/abi-info-collect.pyc %{buildroot}/usr/libexec/abi-info-collect @@ -48,11 +53,18 @@ install -m 755 src/abi-info-check.pyc %{buildroot}/usr/libexec/abi-info-check install -m 755 uos-abi-check %{buildroot}/usr/bin/uos-abi-check %files -%{_bindir}/uos-abi-check %{_libexecdir}/abi-info-check -%{_datadir}/abi-compliance-checker/ +%{_bindir}/uos-abi-check +%{_bindir}/abi-dumper %{_bindir}/abi-compliance-checker +%{_datadir}/abi-compliance-checker/ + + + %files -n abi-info-collect +%{_libexecdir}/abi-info-collect %{_bindir}/abi-info-collect -%{_libexecdir}/abi-info-collect \ No newline at end of file +%{_bindir}/abi-dumper +%{_bindir}/abi-compliance-checker +%{_datadir}/abi-compliance-checker/ \ No newline at end of file -- Gitee