diff options
Diffstat (limited to '')
133 files changed, 28061 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d2d0a8a --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.o +*.dwo +*~ +dwz +dwz.sum +dwz.log +native.c +testsuite/dwz.tests/execs @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 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. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +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 give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 Program +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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), 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 Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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 + along with this program; 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. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/COPYING.RUNTIME b/COPYING.RUNTIME new file mode 100644 index 0000000..e1b3c69 --- /dev/null +++ b/COPYING.RUNTIME @@ -0,0 +1,73 @@ +GCC RUNTIME LIBRARY EXCEPTION + +Version 3.1, 31 March 2009 + +Copyright (C) 2009 Free Software Foundation, Inc. <http://fsf.org/> + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +This GCC Runtime Library Exception ("Exception") is an additional +permission under section 7 of the GNU General Public License, version +3 ("GPLv3"). It applies to a given file (the "Runtime Library") that +bears a notice placed by the copyright holder of the file stating that +the file is governed by GPLv3 along with this Exception. + +When you use GCC to compile a program, GCC may combine portions of +certain GCC header files and runtime libraries with the compiled +program. The purpose of this Exception is to allow compilation of +non-GPL (including proprietary) programs to use, in this way, the +header files and runtime libraries covered by this Exception. + +0. Definitions. + +A file is an "Independent Module" if it either requires the Runtime +Library for execution after a Compilation Process, or makes use of an +interface provided by the Runtime Library, but is not otherwise based +on the Runtime Library. + +"GCC" means a version of the GNU Compiler Collection, with or without +modifications, governed by version 3 (or a specified later version) of +the GNU General Public License (GPL) with the option of using any +subsequent versions published by the FSF. + +"GPL-compatible Software" is software whose conditions of propagation, +modification and use would permit combination with GCC in accord with +the license of GCC. + +"Target Code" refers to output from any compiler for a real or virtual +target processor architecture, in executable form or suitable for +input to an assembler, loader, linker and/or execution +phase. Notwithstanding that, Target Code does not include data in any +format that is used as a compiler intermediate representation, or used +for producing a compiler intermediate representation. + +The "Compilation Process" transforms code entirely represented in +non-intermediate languages designed for human-written code, and/or in +Java Virtual Machine byte code, into Target Code. Thus, for example, +use of source code generators and preprocessors need not be considered +part of the Compilation Process, since the Compilation Process can be +understood as starting with the output of the generators or +preprocessors. + +A Compilation Process is "Eligible" if it is done using GCC, alone or +with other GPL-compatible software, or if it is done without using any +work based on GCC. For example, using non-GPL-compatible Software to +optimize any GCC intermediate representations would not qualify as an +Eligible Compilation Process. + +1. Grant of Additional Permission. + +You have permission to propagate a work of Target Code formed by +combining the Runtime Library with Independent Modules, even if such +propagation would otherwise violate the terms of GPLv3, provided that +all Target Code was generated by Eligible Compilation Processes. You +may then convey such a combination under terms of your choice, +consistent with the licensing of the Independent Modules. + +2. No Weakening of GCC Copyleft. + +The availability of this Exception does not imply any general +presumption that third-party software is unaffected by the copyleft +requirements of the license of GCC. + diff --git a/COPYING3 b/COPYING3 new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + 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 + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/COPYRIGHT_YEARS b/COPYRIGHT_YEARS new file mode 100644 index 0000000..e4b1511 --- /dev/null +++ b/COPYRIGHT_YEARS @@ -0,0 +1,3 @@ +-DFSF_YEARS='"1992-2021"' +-DRH_YEARS='"2001-2021"' +-DSUSE_YEARS='"2019-2021"' diff --git a/COVER-LETTER b/COVER-LETTER new file mode 100644 index 0000000..9176886 --- /dev/null +++ b/COVER-LETTER @@ -0,0 +1 @@ +One definition rule optimization diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8b7cf76 --- /dev/null +++ b/Makefile @@ -0,0 +1,201 @@ +ifneq ($(srcdir),) +VPATH = $(srcdir) +else +srcdir=$(shell pwd) +endif + +CFLAGS = -O2 -g +DWZ_VERSION := $(shell cat $(srcdir)/VERSION) +CFLAGS_VERSION = -DDWZ_VERSION='"$(DWZ_VERSION)"' +CFLAGS_COPYRIGHT = $(shell cat $(srcdir)/COPYRIGHT_YEARS) +CFLAGS_COMMON = -Wall -W -D_FILE_OFFSET_BITS=64 +XXH_PROG = "\#define XXH_INLINE_ALL 1\n\#include <xxhash.h>\n" +XXH_INLINE_ALL_WORKS = $(shell printf $(XXH_PROG) \ + | $(CC) -xc -c - -o /dev/null 2>/dev/null \ + && echo -n 1) +ifeq "$(XXH_INLINE_ALL_WORKS)" "1" + CFLAGS_COMMON += -DXXH_INLINE_ALL=1 +endif + +override CFLAGS += $(CFLAGS_COMMON) $(CFLAGS_VERSION) $(CFLAGS_COPYRIGHT) + +prefix = /usr +exec_prefix = $(prefix) +bindir = $(exec_prefix)/bin +datarootdir = $(prefix)/share +mandir = $(datarootdir)/man +OBJECTS = args.o dwz.o hashtab.o pool.o sha1.o dwarfnames.o +LIBS=-lelf +ifneq "$(XXH_INLINE_ALL_WORKS)" "1" +LIBS += -lxxhash +endif +dwz: $(OBJECTS) + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) +args.o: native.o +args.o: CFLAGS_FOR_SOURCE = \ + -DNATIVE_ENDIAN_VAL=$(NATIVE_ENDIAN_VAL) \ + -DNATIVE_POINTER_SIZE=$(NATIVE_POINTER_SIZE) +NATIVE_ENDIAN=$(shell readelf -h native.o \ + | grep Data \ + | sed 's/.*, //;s/ endian//') +NATIVE_ENDIAN_LITTLE=$(findstring $(NATIVE_ENDIAN),$(findstring little,$(NATIVE_ENDIAN))) +NATIVE_ENDIAN_BIG=$(findstring $(NATIVE_ENDIAN),$(findstring big,$(NATIVE_ENDIAN))) +NATIVE_ENDIAN_VAL=$(if $(NATIVE_ENDIAN_LITTLE),ELFDATA2LSB,$(if $(NATIVE_ENDIAN_BIG),ELFDATA2MSB,ELFDATANONE)) +NATIVE_POINTER_SIZE=$(shell readelf -wi native.o \ + | grep "Pointer Size:" \ + | sed 's/.*: *//') +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< $(CFLAGS_FOR_SOURCE) +install: dwz + install -D dwz $(DESTDIR)$(bindir)/dwz + install -D -m 644 $(srcdir)/dwz.1 $(DESTDIR)$(mandir)/man1/dwz.1 +clean: + rm -f $(OBJECTS) *~ core* dwz $(TEST_EXECS) $(DWZ_TEST_OBJECTS) \ + dwz.log dwz.sum native.c native.o + rm -Rf testsuite-bin tmp.* +native.c: + echo "int main (void) { return 0; }" > $@ +native.o: native.c + $(CC) -o $@ $< -c -g + +PWD:=$(shell pwd -P) + +TEST_SRC = $(srcdir)/testsuite/dwz.tests +TEST_EXECS_DWARF_ASM = no-multifile-prop invalid-dw-at-stmt-list-encoding \ + unavailable-dwarf-piece +TEST_EXECS_x86_64 = py-section-script dw2-skip-prologue \ + implptr-64bit-d2o4a8r8t0 varval +TEST_EXECS = hello dwz-for-test min two-typedef start hello-gold-gdb-index \ + start-gold hello-gnu-pubnames $(TEST_EXECS_DWARF_ASM) \ + $(TEST_EXECS_$(UNAME)) odr-struct odr-class odr-union odr-struct-ns \ + odr-class-ns odr-union-ns odr-loc def-decl cycle + +UNAME:=$(shell uname -p) + +hello: + $(CC) $(TEST_SRC)/hello.c -o $@ -g + +hello-gnu-pubnames: + $(CC) $(TEST_SRC)/hello.c -o $@ -g -ggnu-pubnames || touch $@ + +dw2-skip-prologue: + $(CC) $(TEST_SRC)/dw2-skip-prologue.S $(TEST_SRC)/dw2-skip-prologue.c \ + -DINLINED -DPTRBITS=64 -o $@ || touch $@ + +py-section-script: + $(CC) $(TEST_SRC)/py-section-script.s -o $@ -g || touch $@ + +DWZ_TEST_OBJECTS := $(patsubst %.o,%-for-test.o,$(OBJECTS)) +dwz-for-test: $(DWZ_TEST_OBJECTS) + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + rm -f $(DWZ_TEST_OBJECTS) +args-for-test.o: CFLAGS_FOR_SOURCE = \ + -DNATIVE_ENDIAN_VAL=$(NATIVE_ENDIAN_VAL) \ + -DNATIVE_POINTER_SIZE=$(NATIVE_POINTER_SIZE) +$(DWZ_TEST_OBJECTS): %-for-test.o : %.c + $(CC) $< -o $@ -c \ + -DUSE_GNUC=0 -DDEVEL \ + -O2 -g \ + $(CFLAGS_COMMON) \ + -DDWZ_VERSION='"for-test"' \ + $(CFLAGS_COPYRIGHT) \ + $(CFLAGS_FOR_SOURCE) + +min: + $(CC) $(TEST_SRC)/min.c $(TEST_SRC)/min-2.c -o $@ -g + +two-typedef: + $(CC) $(TEST_SRC)/two-typedef.c $(TEST_SRC)/two-typedef-2.c \ + -I $(TEST_SRC) -o $@ -g + +start: + $(CC) $(TEST_SRC)/start.c -o $@ -g -nostdlib + +start-gold: + $(CC) $(TEST_SRC)/start.c -fuse-ld=gold -o $@ -g -nostdlib || touch $@ + +implptr-64bit-d2o4a8r8t0: + $(CC) $(TEST_SRC)/implptr-64bit-d2o4a8r8t0.S $(TEST_SRC)/main.c \ + -o $@ -g || touch $@ + +hello-gold-gdb-index: + $(CC) $(TEST_SRC)/hello.c -g -fuse-ld=gold -Wl,--gdb-index -o $@ \ + || touch $@ + +varval: + $(CC) $(TEST_SRC)/varval.c $(TEST_SRC)/varval.S -g -o $@ || touch $@ + +POINTER_SIZE:=$(shell $(CC) $(TEST_SRC)/pointer-size.c -o pointer-size; \ + ./pointer-size; \ + rm -f ./pointer-size) + +TEMP_ASM_FILES=$(addsuffix -dw.S, $(TEST_EXECS_DWARF_ASM)) +.INTERMEDIATE: $(TEMP_ASM_FILES) + +$(TEMP_ASM_FILES): %-dw.S: $(TEST_SRC)/../lib/%.exp + export POINTER_SIZE=$(POINTER_SIZE); \ + export DEJAGNU=$(DEJAGNU); \ + runtest --tool=dwz -srcdir $(srcdir)/testsuite/ lib/$*.exp + +$(filter-out no-multifile-prop unavailable-dwarf-piece, $(TEST_EXECS_DWARF_ASM)): %: %-dw.S + $(CC) $(TEST_SRC)/main.c $< -o $@ + +# Fails to compile on riscv64: Error: non-constant .uleb128 is not supported. +no-multifile-prop unavailable-dwarf-piece: %: %-dw.S + $(CC) $(TEST_SRC)/main.c $< -o $@ || true + +odr-struct: + $(CXX) $(TEST_SRC)/odr.cc $(TEST_SRC)/odr-2.cc -I$(TEST_SRC) -o $@ -g \ + -DKIND=struct + +odr-class: + $(CXX) $(TEST_SRC)/odr.cc $(TEST_SRC)/odr-2.cc -I$(TEST_SRC) -o $@ -g \ + -DKIND=class + +odr-union: + $(CXX) $(TEST_SRC)/odr.cc $(TEST_SRC)/odr-2.cc -I$(TEST_SRC) -o $@ -g \ + -DKIND=union + +odr-struct-ns: + $(CXX) $(TEST_SRC)/odr.cc $(TEST_SRC)/odr-2.cc -I$(TEST_SRC) -o $@ -g \ + -DKIND=struct -DNAMESPACE=1 + +odr-class-ns: + $(CXX) $(TEST_SRC)/odr.cc $(TEST_SRC)/odr-2.cc -I$(TEST_SRC) -o $@ -g \ + -DKIND=class -DNAMESPACE=1 + +odr-union-ns: + $(CXX) $(TEST_SRC)/odr.cc $(TEST_SRC)/odr-2.cc -I$(TEST_SRC) -o $@ -g \ + -DKIND=union -DNAMESPACE=1 + +odr-loc: + $(CXX) $(TEST_SRC)/odr-loc.cc $(TEST_SRC)/odr-loc-2.cc -I$(TEST_SRC) \ + -o $@ -g + + +def-decl: + $(CXX) $(TEST_SRC)/decl.cc $(TEST_SRC)/def.cc $(TEST_SRC)/def2.cc \ + -I$(TEST_SRC) -o $@ -g + +cycle: + $(CC) $(TEST_SRC)/cycle.c -o $@ -g + +# On some systems we need to set and export DEJAGNU to suppress +# WARNING: Couldn't find the global config file. +DEJAGNU ?= /dev/null + +VALGRIND_OPTIONS = -q --error-exitcode=99 + +check check-valgrind: dwz $(TEST_EXECS) + mkdir -p testsuite-bin + cd testsuite-bin; \ + if [ "$@" = "check" ]; then \ + ln -sf $(PWD)/dwz .; \ + else \ + echo "valgrind $(VALGRIND_OPTIONS) $(PWD)/dwz \"\$$@\"" > dwz; \ + chmod +x dwz; \ + fi + export DEJAGNU=$(DEJAGNU); \ + export PATH=$(PWD)/testsuite-bin:$$PATH; export LC_ALL=C; \ + runtest --tool=dwz -srcdir $(srcdir)/testsuite $(RUNTESTFLAGS) + rm -Rf testsuite-bin $(TEST_EXECS) $(DWZ_TEST_OBJECTS) diff --git a/README.release-checklist b/README.release-checklist new file mode 100644 index 0000000..cb6bbad --- /dev/null +++ b/README.release-checklist @@ -0,0 +1,28 @@ +- Verify that copyright notices in source files are up-to-date. +- Update COPYRIGHT_YEARS using contrib/release/gen-copyright-years.sh. + Commit modifications if there are any. +- Run contrib/release/do-release.sh. + Use: + - --minor to do a minor update: $maj.$min -> $maj.$(($min + 1)) + - --major to do a major update: $maj.$min -> $(($maj + 1)).0 + This: + - adds a commit that updates the VERSION file, + - creates a signed annotated release tag for that commit, and + - pushes both the commit and the tag to the remote repository. +- Run contrib/release/upload-release.sh. + This creates and uploads two release tarballs. +- Write draft release announcement. + F.i. using template of + https://sourceware.org/ml/gdb-announce/2019/msg00001.html. +- Sent out draft release announcement to maintainers for review and + further contributions. +- Sent out release announcement. Sent to: + - Maintainers + - dwz@sourceware.org + - dwarf-discuss@lists.dwarfstd.org + - gcc@gcc.gnu.org + - gdb@sourceware.org + - lldb-dev@lists.llvm.org +- Update web page ( https://sourceware.org/dwz/ ): + - Add news item with hlink to release annoucement. + - Add entry in release list. @@ -0,0 +1 @@ +0.15 @@ -0,0 +1,743 @@ +/* Copyright (C) 2001-2021 Red Hat, Inc. + Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2019-2021 SUSE LLC. + Written by Jakub Jelinek <jakub@redhat.com>, 2012. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + 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 + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include <assert.h> +#include <getopt.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <error.h> +#include <gelf.h> +#include <sys/sysinfo.h> + +#include "args.h" + +#include "util.h" + +#if DEVEL +int tracing; +int ignore_size; +int ignore_locus; +int dump_checksum_p; +int dump_dies_p; +int dump_dups_p; +int dump_pus_p; +int verify_dups_p; +int verify_edge_freelist; +int stats_p; +int checksum_cycle_opt = 1; +int skip_producers_p; +#endif + +int unoptimized_multifile; +int save_temps; +int verify_edges_p; +int dump_edges_p; +int partition_dups_opt; +int progress_p; +int progress_mem_p; +int import_opt_p = 1; +int force_p; +int max_forks = -1; + +enum deduplication_mode deduplication_mode = dm_inter_cu; + +int uni_lang_p = 0; +int gen_cu_p = 0; + +enum die_count_methods die_count_method = estimate; + +int odr = 0; +enum odr_mode odr_mode = ODR_LINK; + +/* Filename if inter-file size optimization should be performed. */ +const char *multifile; + +/* Argument of -M option, i.e. preferred name that should be stored + into the .gnu_debugaltlink or .debug_sup section. */ +const char *multifile_name; + +/* True if -r option is present, i.e. .gnu_debugaltlink or .debug_sup section + should contain a filename relative to the directory in which + the particular file is present. */ +bool multifile_relative; + +/* Pointer size of multifile. */ +int multifile_force_ptr_size; +/* Endianity of multifile. */ +int multifile_force_endian; + +/* True if DWARF 5 .debug_sup and DW_FORM_ref_sup4 / DW_FORM_strp_sup + should be used instead of the GNU extensions .gnu_debugaltlink + and DW_FORM_GNU_ref_alt / DW_FORM_GNU_strp_alt etc. */ +bool dwarf_5; + +/* True if -q option has been passed. */ +bool quiet; + +/* Number of DIEs, above which dwz retries processing + in low_mem mode (and give up on multifile optimizing + the file in question). */ +unsigned int low_mem_die_limit = 10000000; + +/* Number of DIEs, above which dwz gives up processing + input altogether. */ +unsigned int max_die_limit = 50000000; + +/* Phase of multifile handling. */ +unsigned char multifile_mode; + +static int die_count_method_parsed; +static int deduplication_mode_parsed; +static int odr_mode_parsed; +static int skip_producer_parsed; + +/* Options for getopt_long. */ +static struct option dwz_options[] = +{ + { "help", no_argument, 0, '?' }, + { "output", required_argument, 0, 'o' }, + { "multifile", required_argument, 0, 'm' }, + { "quiet", no_argument, 0, 'q' }, + { "hardlink", no_argument, 0, 'h' }, + { "low-mem-die-limit", required_argument, 0, 'l' }, + { "max-die-limit", required_argument, 0, 'L' }, + { "multifile-name", required_argument, 0, 'M' }, + { "relative", no_argument, 0, 'r' }, + { "version", no_argument, 0, 'v' }, + { "import-optimize", + no_argument, &import_opt_p, 1 }, + { "no-import-optimize", + no_argument, &import_opt_p, 0 }, + { "dwarf-5", no_argument, 0, '5' }, +#if DEVEL + { "devel-trace", no_argument, &tracing, 1 }, + { "devel-progress", no_argument, &progress_p, 1 }, + { "devel-progress-mem",no_argument, &progress_mem_p, 1 }, + { "devel-ignore-size", no_argument, &ignore_size, 1 }, + { "devel-ignore-locus",no_argument, &ignore_locus, 1 }, + { "devel-force", no_argument, &force_p, 1 }, + { "devel-save-temps", no_argument, &save_temps, 1 }, + { "devel-dump-checksum", + no_argument, &dump_checksum_p, 1 }, + { "devel-dump-dies", no_argument, &dump_dies_p, 1 }, + { "devel-dump-dups", no_argument, &dump_dups_p, 1 }, + { "devel-dump-pus", no_argument, &dump_pus_p, 1 }, + { "devel-unoptimized-multifile", + no_argument, &unoptimized_multifile, 1 }, + { "devel-verify-edges",no_argument, &verify_edges_p, 1 }, + { "devel-verify-dups", no_argument, &verify_dups_p, 1 }, + { "devel-dump-edges", no_argument, &dump_edges_p, 1 }, + { "devel-partition-dups-opt", + no_argument, &partition_dups_opt, 1 }, + { "devel-die-count-method", + required_argument, &die_count_method_parsed, 1 }, + { "devel-stats", no_argument, &stats_p, 1 }, + { "devel-deduplication-mode", + required_argument, &deduplication_mode_parsed, 1 }, + { "devel-uni-lang", + no_argument, &uni_lang_p, 1 }, + { "devel-no-uni-lang", + no_argument, &uni_lang_p, 0 }, + { "devel-gen-cu", + no_argument, &gen_cu_p, 1 }, + { "devel-no-gen-cu", + no_argument, &gen_cu_p, 0 }, + { "devel-checksum-cycle-opt", + no_argument, &checksum_cycle_opt, 1 }, + { "devel-no-checksum-cycle-opt", + no_argument, &checksum_cycle_opt, 0 }, + { "devel-skip-producer", + required_argument, &skip_producer_parsed, 1}, +#endif + { "odr", no_argument, &odr, 1 }, + { "no-odr", no_argument, &odr, 0 }, + { "odr-mode", required_argument, &odr_mode_parsed, 1 }, + { "multifile-pointer-size", + required_argument, 0, 'p' }, + { "multifile-endian", + required_argument, 0, 'e' }, + { "jobs", required_argument, 0, 'j' }, + { NULL, no_argument, 0, 0 } +}; + +/* Struct describing various usage aspects of a command line option. */ +struct option_help +{ + const char *short_name; + const char *long_name; + const char *argument; + const char *default_value; + const char *msg; +}; + +/* Describe common command line options. */ +static struct option_help dwz_common_options_help[] = +{ + { "q", "quiet", NULL, NULL, + "Silence up the most common messages." }, + { "l", "low-mem-die-limit", "<COUNT|none>", "10 million DIEs", + "Handle files larger than this limit using a slower and more memory" + " usage friendly mode and don't optimize those files in multifile mode." }, + { "L", "max-die-limit", "<COUNT|none>", "50 million DIEs", + "Don't optimize files larger than this limit." }, + { NULL, "odr", NULL, NULL, + NULL }, + { NULL, "no-odr", NULL, "Disabled", + "Enable/disable one definition rule optimization." }, + { NULL, "odr-mode", "<basic|link>", "link", + "Set aggressiveness level of one definition rule optimization." }, + { NULL, "import-optimize", NULL, NULL, + NULL }, + { NULL, "no-import-optimize", NULL, "Enabled", + "Enable/disable optimization that reduces the number of" + " DW_TAG_imported_unit DIEs." } +}; + +/* Describe single-file command line options. */ +static struct option_help dwz_single_file_options_help[] = +{ + { "o", "output", "OUTFILE", NULL, + "Place the output in OUTFILE." } +}; + +#if NATIVE_ENDIAN_VAL == ELFDATA2MSB +#define NATIVE_ENDIAN "big" +#elif NATIVE_ENDIAN_VAL == ELFDATA2LSB +#define NATIVE_ENDIAN "little" +#else +#define NATIVE_ENDIAN "not available" +#endif + +/* Describe mult-file command line options. */ +static struct option_help dwz_multi_file_options_help[] = +{ + { "h", "hardlink", NULL, NULL, + "Handle hardlinked files as one file." }, + { "m", "multifile", "COMMONFILE", NULL, + "Enable multifile optimization, placing common DIEs in multifile" + " COMMONFILE." }, + { "M", "multifile-name", "NAME", NULL, + "Set .gnu_debugaltlink or .debug_sup in files to NAME." }, + { "r", "relative", NULL, NULL, + "Set .gnu_debugaltlink in files to relative path from file directory" + " to multifile." }, + { "5", "dwarf-5", NULL, NULL, + "Emit DWARF 5 standardized supplementary object files instead of" + " GNU extension .debug_altlink." }, + { "p", "multifile-pointer-size", "<SIZE|auto|native>", "auto", + "Set pointer size of multifile, in number of bytes." + " Native pointer size is " XSTR (NATIVE_POINTER_SIZE) "." }, + { "e", "multifile-endian", "<l|b|auto|native>", "auto", + "Set endianity of multifile." + " Native endianity is " NATIVE_ENDIAN "." }, + { "j", "jobs", "<n>", "number of processors / 2", + "Process <n> files in parallel." } +}; + +/* Describe misc command line options. */ +static struct option_help dwz_misc_options_help[] = +{ + { "v", "version", NULL, NULL, + "Display dwz version information." }, + { "?", "help", NULL, NULL, + "Display this information." } +}; + +/* Print LEN spaces to STREAM. */ +static void +do_indent (FILE *stream, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; i++) + fprintf (stream, " "); +} + +/* Print MSG to STREAM, indenting to INDENT and wrapping at LIMIT. + Assume starting position is at INDENT. */ +static void +wrap (FILE *stream, unsigned int indent, unsigned int limit, const char *msg) +{ + unsigned int len = indent; + const char *s = msg; + while (true) + { + const char *e = strchr (s, ' '); + unsigned int word_len; + if (e == NULL) + word_len = strlen (s); + else + word_len = e - s; + if (word_len == 0) + return; + + if (len + 1 /* space */ + word_len > limit) + { + fprintf (stream, "\n"); + do_indent (stream ,indent); + len = indent; + } + else if (len > indent) + { + fprintf (stream, " "); + len += 1; + } + + if (e != NULL) + { + const char *i; + for (i = s; i < e; ++i) + fprintf (stream, "%c", *i); + } + else + fprintf (stream, "%s", s); + len += word_len; + + if (e == NULL) + break; + + s = e + 1; + } +} + +/* Print OPTIONS_HELP of length H to STREAM, indenting to help message to + INDENT an wrapping at LIMIT. */ +static void +print_options_help (FILE *stream, struct option_help *options_help, unsigned int n, + unsigned int indent, unsigned int limit) +{ + unsigned len; + const char *s; + unsigned int i; + + for (i = 0; i < n; ++i) + { + len = 0; + + fprintf (stream, " "); + len += 2; + + s = options_help[i].short_name; + if (s) + { + fprintf (stream, "-%s", s); + len += 2; + } + + s = options_help[i].long_name; + if (len == 4) + { + fprintf (stream, ", "); + len += 2; + } + fprintf (stream, "--%s", s); + len += 2 + strlen (s); + + s = options_help[i].argument; + if (s) + { + fprintf (stream, " %s", s); + len += 1 + strlen (s); + } + + s = options_help[i].msg; + if (s) + { + assert (IMPLIES (strlen (s) > 0, s[strlen (s) - 1] == '.')); + if (len > indent) + { + fprintf (stream, "\n"); + do_indent (stream, indent); + } + else + do_indent (stream, indent - len); + len = indent; + + wrap (stream, indent, limit, s); + } + fprintf (stream, "\n"); + + s = options_help[i].default_value; + if (s) + { + do_indent (stream, indent); + fprintf (stream, "Default value: %s.\n", s); + } + } +} + +/* Print usage and exit. */ +static void +usage (int failing) +{ + unsigned int n, i; + unsigned int indent, limit; + FILE *stream = failing ? stderr : stdout; + const char *header_lines[] = { + "dwz [common options] [-h] [-m COMMONFILE] [-M NAME | -r] [-5]", + " [-p <SIZE|auto|native>] [-e <l|b|auto|native>] [-j N] [FILES]", + "dwz [common options] -o OUTFILE FILE", + "dwz [ -v | -? ]" + }; + unsigned int nr_header_lines + = sizeof (header_lines) / sizeof (*header_lines); + + fprintf (stream, "Usage:\n"); + for (i = 0; i < nr_header_lines; ++i) + fprintf (stream, " %s\n", header_lines[i]); + + indent = 30; + limit = 80; + fprintf (stream, "Common options:\n"); + n = (sizeof (dwz_common_options_help) + / sizeof (dwz_common_options_help[0])); + print_options_help (stream, dwz_common_options_help, n, indent, limit); + + fprintf (stream, "Single-file options:\n"); + n = (sizeof (dwz_single_file_options_help) + / sizeof (dwz_single_file_options_help[0])); + print_options_help (stream, dwz_single_file_options_help, n, indent, limit); + + fprintf (stream, "Multi-file options:\n"); + n = (sizeof (dwz_multi_file_options_help) + / sizeof (dwz_multi_file_options_help[0])); + print_options_help (stream, dwz_multi_file_options_help, n, indent, limit); + + fprintf (stream, "Miscellaneous options:\n"); + n = (sizeof (dwz_misc_options_help) + / sizeof (dwz_misc_options_help[0])); + print_options_help (stream, dwz_misc_options_help, n, indent, limit); + +#if DEVEL + fprintf (stream, "Development options:\n"); + fprintf (stream, "%s", + (" --devel-trace\n" + " --devel-progress\n" + " --devel-progress-mem\n" + " --devel-stats\n" + " --devel-ignore-size\n" + " --devel-ignore-locus\n" + " --devel-force\n" + " --devel-save-temps\n" + " --devel-dump-checksum\n" + " --devel-dump-dies\n" + " --devel-dump-dups\n" + " --devel-dump-pus\n" + " --devel-unoptimized-multifile\n" + " --devel-verify-dups\n" + " --devel-verify-edges\n" + " --devel-dump-edges\n" + " --devel-partition-dups-opt\n" + " --devel-die-count-method\n" + " --devel-deduplication-mode={none,intra-cu,inter-cu}\n" + " --devel-uni-lang / --devel-no-uni-lang\n" + " --devel-gen-cu / --devel-no-gen-cu\n" + " --devel-skip-producer <producer>\n")); +#endif + + exit (failing); +} + +/* Print version and exit. */ +static void +version (void) +{ + printf ("dwz version " DWZ_VERSION "\n" + "Copyright (C) " RH_YEARS " Red Hat, Inc.\n" + "Copyright (C) " FSF_YEARS " Free Software Foundation, Inc.\n" + "Copyright (C) " SUSE_YEARS " SUSE LLC.\n" + "This program is free software; you may redistribute it under the terms of\n" + "the GNU General Public License version 3 or (at your option) any later version.\n" + "This program has absolutely no warranty.\n"); + exit (0); +} + +static const char **skip_producers; +static size_t skip_producers_size; +static size_t nr_skip_producers; + +static void +add_skip_producer (const char *producer) +{ + size_t alloc_size; + if (skip_producers == NULL) + { + skip_producers_size = 10; + alloc_size = skip_producers_size * sizeof (const char *); + skip_producers = malloc (alloc_size); + } + else if (nr_skip_producers == skip_producers_size) + { + skip_producers_size += 10; + alloc_size = skip_producers_size * sizeof (const char *); + skip_producers = realloc (skip_producers, alloc_size); + } + + skip_producers[nr_skip_producers] = producer; + nr_skip_producers++; +} + +bool +skip_producer (const char *producer) +{ + size_t i; + + if (producer == NULL) + return false; + + for (i = 0; i < nr_skip_producers; ++i) + { + const char *skip = skip_producers[i]; + if (strncmp (skip, producer, strlen (skip)) == 0) + return true; + } + + return false; +} + +/* Parse command line arguments in ARGV. */ +void +parse_args (int argc, char *argv[], bool *hardlink, const char **outfile) +{ + unsigned long l; + char *end; + + while (1) + { + int option_index = -1; + int c = getopt_long (argc, argv, "m:o:qhl:L:M:r?v5p:e:j:", dwz_options, + &option_index); + if (c == -1) + break; + switch (c) + { + default: + case '?': + usage (option_index == -1); + break; + + case 0: + /* Option handled by getopt_long. */ + if (die_count_method_parsed) + { + die_count_method_parsed = 0; + if (strcmp (optarg, "none") == 0) + { + die_count_method = none; + break; + } + if (strcmp (optarg, "estimate") == 0) + { + die_count_method = estimate; + break; + } + error (1, 0, "invalid argument --devel-die-count-method %s", + optarg); + } + if (deduplication_mode_parsed) + { + deduplication_mode_parsed = 0; + if (strcmp (optarg, "none") == 0) + { + deduplication_mode = dm_none; + break; + } + if (strcmp (optarg, "intra-cu") == 0) + { + deduplication_mode = dm_intra_cu; + break; + } + if (strcmp (optarg, "inter-cu") == 0) + { + deduplication_mode = dm_inter_cu; + break; + } + error (1, 0, "invalid argument --devel-deduplication-mode %s", + optarg); + } + if (odr_mode_parsed) + { + odr_mode_parsed = 0; + if (strcmp (optarg, "basic") == 0) + { + odr_mode = ODR_BASIC; + break; + } + if (strcmp (optarg, "link") == 0) + { + odr_mode = ODR_LINK; + break; + } + error (1, 0, "invalid argument --odr-mode %s", + optarg); + } + if (skip_producer_parsed) + { + skip_producer_parsed = 0; + add_skip_producer (optarg); + +#if DEVEL + skip_producers_p = 1; +#endif + } + break; + + case 'o': + *outfile = optarg; + break; + + case 'm': + multifile = optarg; + break; + + case 'q': + quiet = true; + break; + + case 'h': + *hardlink = true; + break; + + case 'M': + multifile_name = optarg; + break; + + case 'r': + multifile_relative = true; + break; + + case 'l': + if (strcmp (optarg, "none") == 0) + { + low_mem_die_limit = -1U; + break; + } + l = strtoul (optarg, &end, 0); + if (*end != '\0' || optarg == end || (unsigned int) l != l) + error (1, 0, "invalid argument -l %s", optarg); + low_mem_die_limit = l; + break; + + case 'L': + if (strcmp (optarg, "none") == 0) + { + max_die_limit = -1U; + break; + } + l = strtoul (optarg, &end, 0); + if (*end != '\0' || optarg == end || (unsigned int) l != l) + error (1, 0, "invalid argument -L %s", optarg); + max_die_limit = l; + break; + + case '5': + dwarf_5 = true; + break; + + case 'p': + if (strcmp (optarg, "auto") == 0) + { + multifile_force_ptr_size = 0; + break; + } + if (strcmp (optarg, "native") == 0) + { + multifile_force_ptr_size = NATIVE_POINTER_SIZE; + break; + } + l = strtoul (optarg, &end, 0); + if (*end != '\0' || optarg == end || (unsigned int) l != l) + error (1, 0, "invalid argument -l %s", optarg); + multifile_force_ptr_size = l; + break; + + case 'e': + if (strcmp (optarg, "auto") == 0) + { + multifile_force_endian = 0; + break; + } + if (strcmp (optarg, "native") == 0) + { + switch (NATIVE_ENDIAN_VAL) + { + case ELFDATA2MSB: + case ELFDATA2LSB: + multifile_force_endian = NATIVE_ENDIAN_VAL; + break; + default: + error (1, 0, "Cannot determine native endian"); + } + break; + } + if (strlen (optarg) != 1) + error (1, 0, "invalid argument -l %s", optarg); + switch (optarg[0]) + { + case 'l': + case 'L': + multifile_force_endian = ELFDATA2LSB; + break; + case 'b': + case 'B': + multifile_force_endian = ELFDATA2MSB; + break; + default: + error (1, 0, "invalid argument -l %s", optarg); + } + break; + + case 'v': + version (); + break; + + case 'j': + l = strtoul (optarg, &end, 0); + if (*end != '\0' || optarg == end || (unsigned int) l != l) + error (1, 0, "invalid argument -j %s", optarg); + max_forks = l; + break; + } + } + + if (progress_mem_p) + progress_p = 1; + + /* Specifying a low-mem die-limit that is larger than or equal to the + max die-limit has the effect of disabling low-mem mode. Make this + explicit by setting it to the 'none' value. */ + if (low_mem_die_limit != -1U + && low_mem_die_limit >= max_die_limit) + low_mem_die_limit = -1U; + + if (multifile_relative && multifile_name) + error (1, 0, "-M and -r options can't be specified together"); + + if (max_forks == -1) + { + long nprocs = get_nprocs (); + /* Be conservative on max forks: 4 procs may be actually be 4 SMT + threads with only 2 cores. */ + max_forks = nprocs / 2; + } +} @@ -0,0 +1,97 @@ +/* Copyright (C) 2001-2021 Red Hat, Inc. + Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2019-2021 SUSE LLC. + Written by Jakub Jelinek <jakub@redhat.com>, 2012. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + 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 + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#if DEVEL +extern int tracing; +extern int ignore_size; +extern int ignore_locus; +extern int dump_checksum_p; +extern int dump_dies_p; +extern int dump_dups_p; +extern int dump_pus_p; +extern int verify_dups_p; +extern int verify_edge_freelist; +extern int stats_p; +extern int checksum_cycle_opt; +extern int skip_producers_p; +#else +#define tracing 0 +#define ignore_size 0 +#define ignore_locus 0 +#define dump_checksum_p 0 +#define dump_dies_p 0 +#define dump_dups_p 0 +#define dump_pus_p 0 +#define verify_dups_p 0 +#define stats_p 0 +#define checksum_cycle_opt 1 +#define skip_producers_p 0 +#endif + +extern int unoptimized_multifile; +extern int save_temps; +extern int verify_edges_p; +extern int dump_edges_p; +extern int partition_dups_opt; +extern int progress_p; +extern int progress_mem_p; +extern int import_opt_p; +extern int force_p; +extern int max_forks; + +enum deduplication_mode +{ + dm_none, + dm_intra_cu, + dm_inter_cu +}; +extern enum deduplication_mode deduplication_mode; + +extern int uni_lang_p; +extern int gen_cu_p; + +enum die_count_methods +{ + none, + estimate +}; +extern enum die_count_methods die_count_method; + +extern int odr; +enum odr_mode { ODR_BASIC, ODR_LINK }; +extern enum odr_mode odr_mode; + +extern const char *multifile; +extern const char *multifile_name; +extern bool multifile_relative; +extern int multifile_force_ptr_size; +extern int multifile_force_endian; + +extern unsigned char multifile_mode; + +extern bool dwarf_5; + +extern bool quiet; + +extern unsigned int low_mem_die_limit; +extern unsigned int max_die_limit; + +extern void parse_args (int, char *[], bool *, const char **); +extern bool skip_producer (const char *producer); diff --git a/configure b/configure new file mode 100755 index 0000000..894f6be --- /dev/null +++ b/configure @@ -0,0 +1,18 @@ +#!/bin/sh +srcdir=$(cd $(dirname $0); pwd -P) + +if [ "$srcdir" = "$(pwd -P)" ]; then + # We're not in a separate build dir, but in the source dir, we already + # have a Makefile. + exit 0 +fi + +cat > Makefile <<EOF +srcdir:=$srcdir + +default: + \$(MAKE) -f \$(srcdir)/Makefile srcdir=\$(srcdir) + +%: + \$(MAKE) -f \$(srcdir)/Makefile srcdir=\$(srcdir) \$* +EOF diff --git a/contrib/bytes-per-die.sh b/contrib/bytes-per-die.sh new file mode 100755 index 0000000..fda8998 --- /dev/null +++ b/contrib/bytes-per-die.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +f="$1" + +size=$(readelf -WS "$f" \ + | grep -E "[ \t]\.debug_info" \ + | sed 's/.*\.debug_info//' \ + | awk '{print $4}') +size=$((16#$size)) + +nr_dies=$(readelf -wi "$f" \ + | grep -c ': Abbrev Number.*(DW_TAG') + +res=$(echo "scale=2; $size / $nr_dies" \ + | bc) +echo -e "$res\tsize: $size\tnr_dies: $nr_dies" diff --git a/contrib/gen-dwz-debug-all.sh b/contrib/gen-dwz-debug-all.sh new file mode 100755 index 0000000..943062c --- /dev/null +++ b/contrib/gen-dwz-debug-all.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +outputfile=dwz.debug_all + +echo " + int + main (void) + { + return 0; + } +" \ + | gcc -x c - -c -o $outputfile + +sections=" + debug_abbrev + debug_info + debug_line + debug_macro + debug_str +" + +for section in $sections; do + file=dwz.$section + + objcopy \ + --add-section .$section=$file \ + $outputfile +done diff --git a/contrib/release/copyright-lines.awk b/contrib/release/copyright-lines.awk new file mode 100644 index 0000000..b031c50 --- /dev/null +++ b/contrib/release/copyright-lines.awk @@ -0,0 +1,27 @@ +BEGIN { + start=0 +} + +/Copyright \(C\).*[.]/ { + print + next +} + +/Copyright \(C\)/ { + start=1 + printf $0 + next +} + +/[.]/ { + if (start == 0) + next + print + start=0 +} + +// { + if (start == 0) + next + printf $0 +} diff --git a/contrib/release/do-release.sh b/contrib/release/do-release.sh new file mode 100755 index 0000000..1127293 --- /dev/null +++ b/contrib/release/do-release.sh @@ -0,0 +1,67 @@ +#!/bin/sh + +set -e + +do_minor=false +do_major=false + +while [ $# -gt 0 ]; do + case "$1" in + --minor) + do_minor=true + ;; + --major) + do_major=true + ;; + *) + echo "Unknown option: $1" + exit 1 + esac + shift +done + +if $do_minor && $do_major; then + echo "Can only bump minor or major, not both" + exit 1 +fi + +if ! $do_minor && ! $do_major; then + echo "Need to bump minor or major" + exit 1 +fi + +git checkout master + +version=$(cat VERSION) + +minor=$(echo $version \ + | sed 's/.*\.//') +major=$(echo $version \ + | sed 's/\..*//') +echo Current version: major: $major, minor: $minor + +if $do_minor; then + echo "Bumping minor version" + minor=$(($minor + 1)) +elif $do_major; then + echo "Bumping major version" + major=$(($major + 1)) + minor=0 +fi +echo Bumped version: major: $major, minor: $minor + +version=$major.$minor + +set +x + +echo $version > VERSION + +git add VERSION + +git commit -m "Bump version to $version" + +git push origin master:master + +git tag -s -m "dwz $version release" dwz-$version + +git push origin dwz-$version diff --git a/contrib/release/gen-copyright-years.sh b/contrib/release/gen-copyright-years.sh new file mode 100755 index 0000000..f97691a --- /dev/null +++ b/contrib/release/gen-copyright-years.sh @@ -0,0 +1,164 @@ +#!/bin/bash + +this=$(basename $0) + +max () +{ + local a + a=$1 + local b + b=$2 + + if [ "$a" = "" ]; then + echo "$b" + return + elif [ "$b" = "" ]; then + echo "$a" + return + fi + + if [ $a -gt $b ]; then + echo "$a" + else + echo "$b" + fi +} + +min () +{ + local a + a="$1" + local b + b="$2" + + if [ "$a" = "" ]; then + echo "$b" + return + elif [ "$b" = "" ]; then + echo "$a" + return + fi + + if [ $a -lt $b ]; then + echo "$a" + else + echo "$b" + fi +} + +print_range () { + local a + a="$1" + local b + b="$2" + + if [ "$a" = "$b" ]; then + echo "$a" + return + fi + echo "$a-$b" +} + +process_line () +{ + local line + line="$1" + + fsf=false + rh=false + suse=false; + + if echo "$line" \ + | grep -q "Free Software Foundation, Inc\."; then + fsf=true + who=fsf + line=$(echo "$line" \ + | sed 's/Free Software Foundation, Inc\.//') + elif echo "$line" \ + | grep -q "Red Hat, Inc\."; then + rh=true + who=rh + line=$(echo "$line" \ + | sed 's/Red Hat, Inc\.//') + elif echo "$line" \ + | grep -q "SUSE LLC\."; then + suse=true + who=suse + line=$(echo "$line" \ + | sed 's/SUSE LLC\.//') + else + echo "error: unknown copyright: $line" + exit 1 + fi + + line=$(echo "$line" \ + | sed 's/[,-]/ /g') + max_year=$(echo "$line" \ + | sed 's/ /\n/g' \ + | grep -v '^$' \ + | sort -n -r \ + | head -n 1) + min_year=$(echo "$line" \ + | sed 's/ /\n/g' \ + | grep -v '^$' \ + | sort -n \ + | head -n 1) + + if $fsf; then + fsf_max=$(max "$fsf_max" "$max_year") + fsf_min=$(min "$fsf_min" "$min_year") + elif $rh; then + rh_max=$(max "$rh_max" "$max_year") + rh_min=$(min "$rh_min" "$min_year") + elif $suse; then + suse_max=$(max "$suse_max" "$max_year") + suse_min=$(min "$suse_min" "$min_year") + fi +} + +main () +{ + if ! git status --ignored 2>&1 \ + | grep -q "nothing to commit, working tree clean"; then + echo "Git tree not clean" + exit 1 + fi + + local tmp + tmp=$(mktemp) + + for f in *.c *.h *.def; do + if test "$f" = "native.c"; then continue; fi + + if ! grep -q "Copyright (C)" $f; then + echo "error: found file without copyright marker: $f" + exit 1 + fi + + echo processing file: $f + + grep -v '"' $f \ + | awk -f contrib/release/copyright-lines.awk \ + > $tmp + + while read line; do + line=$(echo "$line" \ + | sed 's/ */ /g') + line=$(echo "$line" \ + | sed 's/.*Copyright (C) *//') + echo "Processing line: $line" + process_line "$line" + done < $tmp + done + + rm -f $tmp + + echo "-DFSF_YEARS='\"$(print_range $fsf_min $fsf_max)\"'" \ + > COPYRIGHT_YEARS + echo "-DRH_YEARS='\"$(print_range $rh_min $rh_max)\"'" \ + >> COPYRIGHT_YEARS + echo "-DSUSE_YEARS='\"$(print_range $suse_min $suse_max)\"'" \ + >> COPYRIGHT_YEARS +} + +main "$@" diff --git a/contrib/release/upload-release.sh b/contrib/release/upload-release.sh new file mode 100755 index 0000000..3c2abdd --- /dev/null +++ b/contrib/release/upload-release.sh @@ -0,0 +1,67 @@ +#!/bin/sh -x + +set -e + +pwd=$(pwd -P) + +version=$(cat VERSION) + +tag=dwz-$version +rootdir=dwz +tarfile=dwz-$version.tar +server=sourceware.org +ftpdir=/sourceware/ftp/pub/dwz/releases +# The server itself seems to add a sha512.sum file, so lets not duplicate that +# effort. +#checksums="md5sum sha512sum" +checksums="md5sum" + +repo="$pwd" + +dir=$(mktemp -d) + +cd $dir +git clone \ + $repo \ + $rootdir + +cd $dir/$rootdir +git ch $tag + +rm -Rf .git + +cd $dir +tar cvf \ + $tarfile \ + $rootdir + +xz \ + --best \ + -k \ + $tarfile + +gzip \ + --best \ + -k \ + $tarfile + +files=$(echo $tarfile.*) + +[ "$files" != "" ] + +ssh $server \ + "mkdir -p $ftpdir" + +scp \ + $files \ + "$server:$ftpdir" + +ssh $server \ + "cd $ftpdir && chmod 644 $files" + +for checksum in $checksums; do + ssh $server \ + "cd $ftpdir && touch $checksum && chmod 644 $checksum && ( $checksum $files >> $checksum )" +done + +rm -Rf $dir diff --git a/dwarf2.def b/dwarf2.def new file mode 100644 index 0000000..1ae6e1d --- /dev/null +++ b/dwarf2.def @@ -0,0 +1,818 @@ +/* -*- c -*- + Declarations and definitions of codes relating to the DWARF2 and + DWARF3 symbolic debugging information formats. + Copyright (C) 1992-2021 Free Software Foundation, Inc. + + Written by Gary Funck (gary@intrepid.com) The Ada Joint Program + Office (AJPO), Florida State University and Silicon Graphics Inc. + provided support for this effort -- June 21, 1995. + + Derived from the DWARF 1 implementation written by Ron Guilmette + (rfg@netcom.com), November 1990. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 3, or (at your option) any later + version. + + GCC 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. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +/* This file is derived from the DWARF specification (a public document) + Revision 2.0.0 (July 27, 1993) developed by the UNIX International + Programming Languages Special Interest Group (UI/PLSIG) and distributed + by UNIX International. Copies of this specification are available from + UNIX International, 20 Waterview Boulevard, Parsippany, NJ, 07054. + + This file also now contains definitions from the DWARF 3 specification + published Dec 20, 2005, available from: http://dwarf.freestandards.org. + + This file also now contains definitions from the DWARF 4 + specification, available from: http://dwarfstd.org/ */ + +/* This file declares various DWARF-related constants using a set of + macros which can be redefined by the including file. + + The macros are in sections. Each section corresponds to a single + set of DWARF constants and has a corresponding key. The key is + used in all the macro names. + + The sections are TAG (for DW_TAG_ constants), FORM (DW_FORM_), AT + (DW_AT_), OP (DW_OP_), ATE (DW_ATE_), and CFA (DW_CFA_). + + Using TAG as an example, the following macros may be used for each + key: + + DW_FIRST_TAG(name, value) - Introduce the first DW_TAG constant. + + DW_TAG(name, value) - Define a subsequent constant. + + DW_TAG_DUP(name, value) - Define a subsequent constant whose value + is a duplicate of some other constant. Not all keys use the _DUP + macro form. If more than one name shares a value, then the base + (DW_TAG) form will be the preferred name and DW_TAG_DUP will hold + any alternate names. + + DW_END_TAG - Invoked at the end of the DW_TAG constants. */ + +DW_FIRST_TAG (DW_TAG_padding, 0x00) +DW_TAG (DW_TAG_array_type, 0x01) +DW_TAG (DW_TAG_class_type, 0x02) +DW_TAG (DW_TAG_entry_point, 0x03) +DW_TAG (DW_TAG_enumeration_type, 0x04) +DW_TAG (DW_TAG_formal_parameter, 0x05) +DW_TAG (DW_TAG_imported_declaration, 0x08) +DW_TAG (DW_TAG_label, 0x0a) +DW_TAG (DW_TAG_lexical_block, 0x0b) +DW_TAG (DW_TAG_member, 0x0d) +DW_TAG (DW_TAG_pointer_type, 0x0f) +DW_TAG (DW_TAG_reference_type, 0x10) +DW_TAG (DW_TAG_compile_unit, 0x11) +DW_TAG (DW_TAG_string_type, 0x12) +DW_TAG (DW_TAG_structure_type, 0x13) +DW_TAG (DW_TAG_subroutine_type, 0x15) +DW_TAG (DW_TAG_typedef, 0x16) +DW_TAG (DW_TAG_union_type, 0x17) +DW_TAG (DW_TAG_unspecified_parameters, 0x18) +DW_TAG (DW_TAG_variant, 0x19) +DW_TAG (DW_TAG_common_block, 0x1a) +DW_TAG (DW_TAG_common_inclusion, 0x1b) +DW_TAG (DW_TAG_inheritance, 0x1c) +DW_TAG (DW_TAG_inlined_subroutine, 0x1d) +DW_TAG (DW_TAG_module, 0x1e) +DW_TAG (DW_TAG_ptr_to_member_type, 0x1f) +DW_TAG (DW_TAG_set_type, 0x20) +DW_TAG (DW_TAG_subrange_type, 0x21) +DW_TAG (DW_TAG_with_stmt, 0x22) +DW_TAG (DW_TAG_access_declaration, 0x23) +DW_TAG (DW_TAG_base_type, 0x24) +DW_TAG (DW_TAG_catch_block, 0x25) +DW_TAG (DW_TAG_const_type, 0x26) +DW_TAG (DW_TAG_constant, 0x27) +DW_TAG (DW_TAG_enumerator, 0x28) +DW_TAG (DW_TAG_file_type, 0x29) +DW_TAG (DW_TAG_friend, 0x2a) +DW_TAG (DW_TAG_namelist, 0x2b) +DW_TAG (DW_TAG_namelist_item, 0x2c) +DW_TAG (DW_TAG_packed_type, 0x2d) +DW_TAG (DW_TAG_subprogram, 0x2e) +DW_TAG (DW_TAG_template_type_param, 0x2f) +DW_TAG (DW_TAG_template_value_param, 0x30) +DW_TAG (DW_TAG_thrown_type, 0x31) +DW_TAG (DW_TAG_try_block, 0x32) +DW_TAG (DW_TAG_variant_part, 0x33) +DW_TAG (DW_TAG_variable, 0x34) +DW_TAG (DW_TAG_volatile_type, 0x35) +/* DWARF 3. */ +DW_TAG (DW_TAG_dwarf_procedure, 0x36) +DW_TAG (DW_TAG_restrict_type, 0x37) +DW_TAG (DW_TAG_interface_type, 0x38) +DW_TAG (DW_TAG_namespace, 0x39) +DW_TAG (DW_TAG_imported_module, 0x3a) +DW_TAG (DW_TAG_unspecified_type, 0x3b) +DW_TAG (DW_TAG_partial_unit, 0x3c) +DW_TAG (DW_TAG_imported_unit, 0x3d) +DW_TAG (DW_TAG_condition, 0x3f) +DW_TAG (DW_TAG_shared_type, 0x40) +/* DWARF 4. */ +DW_TAG (DW_TAG_type_unit, 0x41) +DW_TAG (DW_TAG_rvalue_reference_type, 0x42) +DW_TAG (DW_TAG_template_alias, 0x43) +/* DWARF 5. */ +DW_TAG (DW_TAG_coarray_type, 0x44) +DW_TAG (DW_TAG_generic_subrange, 0x45) +DW_TAG (DW_TAG_dynamic_type, 0x46) +DW_TAG (DW_TAG_atomic_type, 0x47) +DW_TAG (DW_TAG_call_site, 0x48) +DW_TAG (DW_TAG_call_site_parameter, 0x49) +DW_TAG (DW_TAG_skeleton_unit, 0x4a) +DW_TAG (DW_TAG_immutable_type, 0x4b) + +DW_TAG_DUP (DW_TAG_lo_user, 0x4080) +DW_TAG_DUP (DW_TAG_hi_user, 0xffff) + +/* SGI/MIPS Extensions. */ +DW_TAG (DW_TAG_MIPS_loop, 0x4081) + +/* HP extensions. See: ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz . */ +DW_TAG (DW_TAG_HP_array_descriptor, 0x4090) +DW_TAG (DW_TAG_HP_Bliss_field, 0x4091) +DW_TAG (DW_TAG_HP_Bliss_field_set, 0x4092) + +/* GNU extensions. */ +DW_TAG (DW_TAG_format_label, 0x4101) /* For FORTRAN 77 and Fortran 90. */ +DW_TAG (DW_TAG_function_template, 0x4102) /* For C++. */ +DW_TAG (DW_TAG_class_template, 0x4103) /* For C++. */ +DW_TAG (DW_TAG_GNU_BINCL, 0x4104) +DW_TAG (DW_TAG_GNU_EINCL, 0x4105) +/* Template template parameter. + See http://gcc.gnu.org/wiki/TemplateParmsDwarf . */ +DW_TAG (DW_TAG_GNU_template_template_param, 0x4106) + +/* Template parameter pack extension, specified at + http://wiki.dwarfstd.org/index.php?title=C%2B%2B0x:_Variadic_templates + The values of these two TAGS are in the DW_TAG_GNU_* space until the tags + are properly part of DWARF 5. */ +DW_TAG (DW_TAG_GNU_template_parameter_pack, 0x4107) +DW_TAG (DW_TAG_GNU_formal_parameter_pack, 0x4108) +/* The GNU call site extension, specified at + http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open . + The values of these two TAGS are in the DW_TAG_GNU_* space until the tags + are properly part of DWARF 5. */ +DW_TAG (DW_TAG_GNU_call_site, 0x4109) +DW_TAG (DW_TAG_GNU_call_site_parameter, 0x410a) +/* Extensions for UPC. See: http://dwarfstd.org/doc/DWARF4.pdf. */ +DW_TAG (DW_TAG_upc_shared_type, 0x8765) +DW_TAG (DW_TAG_upc_strict_type, 0x8766) +DW_TAG (DW_TAG_upc_relaxed_type, 0x8767) +/* PGI (STMicroelectronics) extensions. No documentation available. */ +DW_TAG (DW_TAG_PGI_kanji_type, 0xA000) +DW_TAG (DW_TAG_PGI_interface_block, 0xA020) +DW_END_TAG + +DW_FIRST_FORM (DW_FORM_addr, 0x01) +DW_FORM (DW_FORM_block2, 0x03) +DW_FORM (DW_FORM_block4, 0x04) +DW_FORM (DW_FORM_data2, 0x05) +DW_FORM (DW_FORM_data4, 0x06) +DW_FORM (DW_FORM_data8, 0x07) +DW_FORM (DW_FORM_string, 0x08) +DW_FORM (DW_FORM_block, 0x09) +DW_FORM (DW_FORM_block1, 0x0a) +DW_FORM (DW_FORM_data1, 0x0b) +DW_FORM (DW_FORM_flag, 0x0c) +DW_FORM (DW_FORM_sdata, 0x0d) +DW_FORM (DW_FORM_strp, 0x0e) +DW_FORM (DW_FORM_udata, 0x0f) +DW_FORM (DW_FORM_ref_addr, 0x10) +DW_FORM (DW_FORM_ref1, 0x11) +DW_FORM (DW_FORM_ref2, 0x12) +DW_FORM (DW_FORM_ref4, 0x13) +DW_FORM (DW_FORM_ref8, 0x14) +DW_FORM (DW_FORM_ref_udata, 0x15) +DW_FORM (DW_FORM_indirect, 0x16) +/* DWARF 4. */ +DW_FORM (DW_FORM_sec_offset, 0x17) +DW_FORM (DW_FORM_exprloc, 0x18) +DW_FORM (DW_FORM_flag_present, 0x19) +DW_FORM (DW_FORM_ref_sig8, 0x20) +/* DWARF 5. */ +DW_FORM (DW_FORM_strx, 0x1a) +DW_FORM (DW_FORM_addrx, 0x1b) +DW_FORM (DW_FORM_ref_sup4, 0x1c) +DW_FORM (DW_FORM_strp_sup, 0x1d) +DW_FORM (DW_FORM_data16, 0x1e) +DW_FORM (DW_FORM_line_strp, 0x1f) +DW_FORM (DW_FORM_implicit_const, 0x21) +DW_FORM (DW_FORM_loclistx, 0x22) +DW_FORM (DW_FORM_rnglistx, 0x23) +DW_FORM (DW_FORM_ref_sup8, 0x24) +DW_FORM (DW_FORM_strx1, 0x25) +DW_FORM (DW_FORM_strx2, 0x26) +DW_FORM (DW_FORM_strx3, 0x27) +DW_FORM (DW_FORM_strx4, 0x28) +DW_FORM (DW_FORM_addrx1, 0x29) +DW_FORM (DW_FORM_addrx2, 0x2a) +DW_FORM (DW_FORM_addrx3, 0x2b) +DW_FORM (DW_FORM_addrx4, 0x2c) +/* Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission. */ +DW_FORM (DW_FORM_GNU_addr_index, 0x1f01) +DW_FORM (DW_FORM_GNU_str_index, 0x1f02) +/* Extensions for DWZ multifile. + See http://www.dwarfstd.org/ShowIssue.php?issue=120604.1&type=open . */ +DW_FORM (DW_FORM_GNU_ref_alt, 0x1f20) +DW_FORM (DW_FORM_GNU_strp_alt, 0x1f21) +DW_END_FORM + +DW_FIRST_AT (DW_AT_sibling, 0x01) +DW_AT (DW_AT_location, 0x02) +DW_AT (DW_AT_name, 0x03) +DW_AT (DW_AT_ordering, 0x09) +DW_AT (DW_AT_subscr_data, 0x0a) +DW_AT (DW_AT_byte_size, 0x0b) +DW_AT (DW_AT_bit_offset, 0x0c) +DW_AT (DW_AT_bit_size, 0x0d) +DW_AT (DW_AT_element_list, 0x0f) +DW_AT (DW_AT_stmt_list, 0x10) +DW_AT (DW_AT_low_pc, 0x11) +DW_AT (DW_AT_high_pc, 0x12) +DW_AT (DW_AT_language, 0x13) +DW_AT (DW_AT_member, 0x14) +DW_AT (DW_AT_discr, 0x15) +DW_AT (DW_AT_discr_value, 0x16) +DW_AT (DW_AT_visibility, 0x17) +DW_AT (DW_AT_import, 0x18) +DW_AT (DW_AT_string_length, 0x19) +DW_AT (DW_AT_common_reference, 0x1a) +DW_AT (DW_AT_comp_dir, 0x1b) +DW_AT (DW_AT_const_value, 0x1c) +DW_AT (DW_AT_containing_type, 0x1d) +DW_AT (DW_AT_default_value, 0x1e) +DW_AT (DW_AT_inline, 0x20) +DW_AT (DW_AT_is_optional, 0x21) +DW_AT (DW_AT_lower_bound, 0x22) +DW_AT (DW_AT_producer, 0x25) +DW_AT (DW_AT_prototyped, 0x27) +DW_AT (DW_AT_return_addr, 0x2a) +DW_AT (DW_AT_start_scope, 0x2c) +DW_AT (DW_AT_bit_stride, 0x2e) +DW_AT (DW_AT_upper_bound, 0x2f) +DW_AT (DW_AT_abstract_origin, 0x31) +DW_AT (DW_AT_accessibility, 0x32) +DW_AT (DW_AT_address_class, 0x33) +DW_AT (DW_AT_artificial, 0x34) +DW_AT (DW_AT_base_types, 0x35) +DW_AT (DW_AT_calling_convention, 0x36) +DW_AT (DW_AT_count, 0x37) +DW_AT (DW_AT_data_member_location, 0x38) +DW_AT (DW_AT_decl_column, 0x39) +DW_AT (DW_AT_decl_file, 0x3a) +DW_AT (DW_AT_decl_line, 0x3b) +DW_AT (DW_AT_declaration, 0x3c) +DW_AT (DW_AT_discr_list, 0x3d) +DW_AT (DW_AT_encoding, 0x3e) +DW_AT (DW_AT_external, 0x3f) +DW_AT (DW_AT_frame_base, 0x40) +DW_AT (DW_AT_friend, 0x41) +DW_AT (DW_AT_identifier_case, 0x42) +DW_AT (DW_AT_macro_info, 0x43) +DW_AT (DW_AT_namelist_items, 0x44) +DW_AT (DW_AT_priority, 0x45) +DW_AT (DW_AT_segment, 0x46) +DW_AT (DW_AT_specification, 0x47) +DW_AT (DW_AT_static_link, 0x48) +DW_AT (DW_AT_type, 0x49) +DW_AT (DW_AT_use_location, 0x4a) +DW_AT (DW_AT_variable_parameter, 0x4b) +DW_AT (DW_AT_virtuality, 0x4c) +DW_AT (DW_AT_vtable_elem_location, 0x4d) +/* DWARF 3 values. */ +DW_AT (DW_AT_allocated, 0x4e) +DW_AT (DW_AT_associated, 0x4f) +DW_AT (DW_AT_data_location, 0x50) +DW_AT (DW_AT_byte_stride, 0x51) +DW_AT (DW_AT_entry_pc, 0x52) +DW_AT (DW_AT_use_UTF8, 0x53) +DW_AT (DW_AT_extension, 0x54) +DW_AT (DW_AT_ranges, 0x55) +DW_AT (DW_AT_trampoline, 0x56) +DW_AT (DW_AT_call_column, 0x57) +DW_AT (DW_AT_call_file, 0x58) +DW_AT (DW_AT_call_line, 0x59) +DW_AT (DW_AT_description, 0x5a) +DW_AT (DW_AT_binary_scale, 0x5b) +DW_AT (DW_AT_decimal_scale, 0x5c) +DW_AT (DW_AT_small, 0x5d) +DW_AT (DW_AT_decimal_sign, 0x5e) +DW_AT (DW_AT_digit_count, 0x5f) +DW_AT (DW_AT_picture_string, 0x60) +DW_AT (DW_AT_mutable, 0x61) +DW_AT (DW_AT_threads_scaled, 0x62) +DW_AT (DW_AT_explicit, 0x63) +DW_AT (DW_AT_object_pointer, 0x64) +DW_AT (DW_AT_endianity, 0x65) +DW_AT (DW_AT_elemental, 0x66) +DW_AT (DW_AT_pure, 0x67) +DW_AT (DW_AT_recursive, 0x68) +/* DWARF 4. */ +DW_AT (DW_AT_signature, 0x69) +DW_AT (DW_AT_main_subprogram, 0x6a) +DW_AT (DW_AT_data_bit_offset, 0x6b) +DW_AT (DW_AT_const_expr, 0x6c) +DW_AT (DW_AT_enum_class, 0x6d) +DW_AT (DW_AT_linkage_name, 0x6e) +/* DWARF 5. */ +DW_AT (DW_AT_string_length_bit_size, 0x6f) +DW_AT (DW_AT_string_length_byte_size, 0x70) +DW_AT (DW_AT_rank, 0x71) +DW_AT (DW_AT_str_offsets_base, 0x72) +DW_AT (DW_AT_addr_base, 0x73) +DW_AT (DW_AT_rnglists_base, 0x74) +DW_AT (DW_AT_dwo_name, 0x76) +DW_AT (DW_AT_reference, 0x77) +DW_AT (DW_AT_rvalue_reference, 0x78) +DW_AT (DW_AT_macros, 0x79) +DW_AT (DW_AT_call_all_calls, 0x7a) +DW_AT (DW_AT_call_all_source_calls, 0x7b) +DW_AT (DW_AT_call_all_tail_calls, 0x7c) +DW_AT (DW_AT_call_return_pc, 0x7d) +DW_AT (DW_AT_call_value, 0x7e) +DW_AT (DW_AT_call_origin, 0x7f) +DW_AT (DW_AT_call_parameter, 0x80) +DW_AT (DW_AT_call_pc, 0x81) +DW_AT (DW_AT_call_tail_call, 0x82) +DW_AT (DW_AT_call_target, 0x83) +DW_AT (DW_AT_call_target_clobbered, 0x84) +DW_AT (DW_AT_call_data_location, 0x85) +DW_AT (DW_AT_call_data_value, 0x86) +DW_AT (DW_AT_noreturn, 0x87) +DW_AT (DW_AT_alignment, 0x88) +DW_AT (DW_AT_export_symbols, 0x89) +DW_AT (DW_AT_deleted, 0x8a) +DW_AT (DW_AT_defaulted, 0x8b) +DW_AT (DW_AT_loclists_base, 0x8c) + +DW_AT_DUP (DW_AT_lo_user, 0x2000) /* Implementation-defined range start. */ +DW_AT_DUP (DW_AT_hi_user, 0x3fff) /* Implementation-defined range end. */ + +/* SGI/MIPS extensions. */ +DW_AT (DW_AT_MIPS_fde, 0x2001) +DW_AT (DW_AT_MIPS_loop_begin, 0x2002) +DW_AT (DW_AT_MIPS_tail_loop_begin, 0x2003) +DW_AT (DW_AT_MIPS_epilog_begin, 0x2004) +DW_AT (DW_AT_MIPS_loop_unroll_factor, 0x2005) +DW_AT (DW_AT_MIPS_software_pipeline_depth, 0x2006) +DW_AT (DW_AT_MIPS_linkage_name, 0x2007) +DW_AT (DW_AT_MIPS_stride, 0x2008) +DW_AT (DW_AT_MIPS_abstract_name, 0x2009) +DW_AT (DW_AT_MIPS_clone_origin, 0x200a) +DW_AT (DW_AT_MIPS_has_inlines, 0x200b) +/* HP extensions. */ +DW_AT (DW_AT_HP_block_index, 0x2000) +DW_AT_DUP (DW_AT_HP_unmodifiable, 0x2001) /* Same as DW_AT_MIPS_fde. */ +DW_AT_DUP (DW_AT_HP_prologue, 0x2005) /* Same as DW_AT_MIPS_loop_unroll. */ +DW_AT_DUP (DW_AT_HP_epilogue, 0x2008) /* Same as DW_AT_MIPS_stride. */ +DW_AT (DW_AT_HP_actuals_stmt_list, 0x2010) +DW_AT (DW_AT_HP_proc_per_section, 0x2011) +DW_AT (DW_AT_HP_raw_data_ptr, 0x2012) +DW_AT (DW_AT_HP_pass_by_reference, 0x2013) +DW_AT (DW_AT_HP_opt_level, 0x2014) +DW_AT (DW_AT_HP_prof_version_id, 0x2015) +DW_AT (DW_AT_HP_opt_flags, 0x2016) +DW_AT (DW_AT_HP_cold_region_low_pc, 0x2017) +DW_AT (DW_AT_HP_cold_region_high_pc, 0x2018) +DW_AT (DW_AT_HP_all_variables_modifiable, 0x2019) +DW_AT (DW_AT_HP_linkage_name, 0x201a) +DW_AT (DW_AT_HP_prof_flags, 0x201b) /* In comp unit of procs_info for -g. */ +DW_AT (DW_AT_HP_unit_name, 0x201f) +DW_AT (DW_AT_HP_unit_size, 0x2020) +DW_AT (DW_AT_HP_widened_byte_size, 0x2021) +DW_AT (DW_AT_HP_definition_points, 0x2022) +DW_AT (DW_AT_HP_default_location, 0x2023) +DW_AT (DW_AT_HP_is_result_param, 0x2029) + +/* GNU extensions. */ +DW_AT (DW_AT_sf_names, 0x2101) +DW_AT (DW_AT_src_info, 0x2102) +DW_AT (DW_AT_mac_info, 0x2103) +DW_AT (DW_AT_src_coords, 0x2104) +DW_AT (DW_AT_body_begin, 0x2105) +DW_AT (DW_AT_body_end, 0x2106) +DW_AT (DW_AT_GNU_vector, 0x2107) +/* Thread-safety annotations. + See http://gcc.gnu.org/wiki/ThreadSafetyAnnotation . */ +DW_AT (DW_AT_GNU_guarded_by, 0x2108) +DW_AT (DW_AT_GNU_pt_guarded_by, 0x2109) +DW_AT (DW_AT_GNU_guarded, 0x210a) +DW_AT (DW_AT_GNU_pt_guarded, 0x210b) +DW_AT (DW_AT_GNU_locks_excluded, 0x210c) +DW_AT (DW_AT_GNU_exclusive_locks_required, 0x210d) +DW_AT (DW_AT_GNU_shared_locks_required, 0x210e) +/* One-definition rule violation detection. + See http://gcc.gnu.org/wiki/DwarfSeparateTypeInfo . */ +DW_AT (DW_AT_GNU_odr_signature, 0x210f) +/* Template template argument name. + See http://gcc.gnu.org/wiki/TemplateParmsDwarf . */ +DW_AT (DW_AT_GNU_template_name, 0x2110) +/* The GNU call site extension. + See http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open . */ +DW_AT (DW_AT_GNU_call_site_value, 0x2111) +DW_AT (DW_AT_GNU_call_site_data_value, 0x2112) +DW_AT (DW_AT_GNU_call_site_target, 0x2113) +DW_AT (DW_AT_GNU_call_site_target_clobbered, 0x2114) +DW_AT (DW_AT_GNU_tail_call, 0x2115) +DW_AT (DW_AT_GNU_all_tail_call_sites, 0x2116) +DW_AT (DW_AT_GNU_all_call_sites, 0x2117) +DW_AT (DW_AT_GNU_all_source_call_sites, 0x2118) +/* Section offset into .debug_macro section. */ +DW_AT (DW_AT_GNU_macros, 0x2119) +/* Attribute for C++ deleted special member functions (= delete;). */ +DW_AT (DW_AT_GNU_deleted, 0x211a) +/* Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission. */ +DW_AT (DW_AT_GNU_dwo_name, 0x2130) +DW_AT (DW_AT_GNU_dwo_id, 0x2131) +DW_AT (DW_AT_GNU_ranges_base, 0x2132) +DW_AT (DW_AT_GNU_addr_base, 0x2133) +DW_AT (DW_AT_GNU_pubnames, 0x2134) +DW_AT (DW_AT_GNU_pubtypes, 0x2135) +/* Attribute for discriminator. + See http://gcc.gnu.org/wiki/Discriminator */ +DW_AT (DW_AT_GNU_discriminator, 0x2136) +DW_AT (DW_AT_GNU_locviews, 0x2137) +DW_AT (DW_AT_GNU_entry_view, 0x2138) +/* VMS extensions. */ +DW_AT (DW_AT_VMS_rtnbeg_pd_address, 0x2201) +/* GNAT extensions. */ +/* GNAT descriptive type. + See http://gcc.gnu.org/wiki/DW_AT_GNAT_descriptive_type . */ +DW_AT (DW_AT_use_GNAT_descriptive_type, 0x2301) +DW_AT (DW_AT_GNAT_descriptive_type, 0x2302) +/* Rational constant extension. + See https://gcc.gnu.org/wiki/DW_AT_GNU_numerator_denominator . */ +DW_TAG (DW_AT_GNU_numerator, 0x2303) +DW_TAG (DW_AT_GNU_denominator, 0x2304) +/* Biased integer extension. + See https://gcc.gnu.org/wiki/DW_AT_GNU_bias . */ +DW_TAG (DW_AT_GNU_bias, 0x2305) +/* UPC extension. */ +DW_AT (DW_AT_upc_threads_scaled, 0x3210) +/* PGI (STMicroelectronics) extensions. */ +DW_AT (DW_AT_PGI_lbase, 0x3a00) +DW_AT (DW_AT_PGI_soffset, 0x3a01) +DW_AT (DW_AT_PGI_lstride, 0x3a02) +/* Apple extensions. */ +DW_AT (DW_AT_APPLE_optimized, 0x3fe1) +DW_AT (DW_AT_APPLE_flags, 0x3fe2) +DW_AT (DW_AT_APPLE_isa, 0x3fe3) +DW_AT (DW_AT_APPLE_block, 0x3fe4) +DW_AT (DW_AT_APPLE_major_runtime_vers, 0x3fe5) +DW_AT (DW_AT_APPLE_runtime_class, 0x3fe6) +DW_AT (DW_AT_APPLE_omit_frame_ptr, 0x3fe7) +DW_AT (DW_AT_APPLE_property_name, 0x3fe8) +DW_AT (DW_AT_APPLE_property_getter, 0x3fe9) +DW_AT (DW_AT_APPLE_property_setter, 0x3fea) +DW_AT (DW_AT_APPLE_property_attribute, 0x3feb) +DW_AT (DW_AT_APPLE_objc_complete_type, 0x3fec) +DW_AT (DW_AT_APPLE_property, 0x3fed) +DW_END_AT + +DW_FIRST_OP (DW_OP_addr, 0x03) +DW_OP (DW_OP_deref, 0x06) +DW_OP (DW_OP_const1u, 0x08) +DW_OP (DW_OP_const1s, 0x09) +DW_OP (DW_OP_const2u, 0x0a) +DW_OP (DW_OP_const2s, 0x0b) +DW_OP (DW_OP_const4u, 0x0c) +DW_OP (DW_OP_const4s, 0x0d) +DW_OP (DW_OP_const8u, 0x0e) +DW_OP (DW_OP_const8s, 0x0f) +DW_OP (DW_OP_constu, 0x10) +DW_OP (DW_OP_consts, 0x11) +DW_OP (DW_OP_dup, 0x12) +DW_OP (DW_OP_drop, 0x13) +DW_OP (DW_OP_over, 0x14) +DW_OP (DW_OP_pick, 0x15) +DW_OP (DW_OP_swap, 0x16) +DW_OP (DW_OP_rot, 0x17) +DW_OP (DW_OP_xderef, 0x18) +DW_OP (DW_OP_abs, 0x19) +DW_OP (DW_OP_and, 0x1a) +DW_OP (DW_OP_div, 0x1b) +DW_OP (DW_OP_minus, 0x1c) +DW_OP (DW_OP_mod, 0x1d) +DW_OP (DW_OP_mul, 0x1e) +DW_OP (DW_OP_neg, 0x1f) +DW_OP (DW_OP_not, 0x20) +DW_OP (DW_OP_or, 0x21) +DW_OP (DW_OP_plus, 0x22) +DW_OP (DW_OP_plus_uconst, 0x23) +DW_OP (DW_OP_shl, 0x24) +DW_OP (DW_OP_shr, 0x25) +DW_OP (DW_OP_shra, 0x26) +DW_OP (DW_OP_xor, 0x27) +DW_OP (DW_OP_bra, 0x28) +DW_OP (DW_OP_eq, 0x29) +DW_OP (DW_OP_ge, 0x2a) +DW_OP (DW_OP_gt, 0x2b) +DW_OP (DW_OP_le, 0x2c) +DW_OP (DW_OP_lt, 0x2d) +DW_OP (DW_OP_ne, 0x2e) +DW_OP (DW_OP_skip, 0x2f) +DW_OP (DW_OP_lit0, 0x30) +DW_OP (DW_OP_lit1, 0x31) +DW_OP (DW_OP_lit2, 0x32) +DW_OP (DW_OP_lit3, 0x33) +DW_OP (DW_OP_lit4, 0x34) +DW_OP (DW_OP_lit5, 0x35) +DW_OP (DW_OP_lit6, 0x36) +DW_OP (DW_OP_lit7, 0x37) +DW_OP (DW_OP_lit8, 0x38) +DW_OP (DW_OP_lit9, 0x39) +DW_OP (DW_OP_lit10, 0x3a) +DW_OP (DW_OP_lit11, 0x3b) +DW_OP (DW_OP_lit12, 0x3c) +DW_OP (DW_OP_lit13, 0x3d) +DW_OP (DW_OP_lit14, 0x3e) +DW_OP (DW_OP_lit15, 0x3f) +DW_OP (DW_OP_lit16, 0x40) +DW_OP (DW_OP_lit17, 0x41) +DW_OP (DW_OP_lit18, 0x42) +DW_OP (DW_OP_lit19, 0x43) +DW_OP (DW_OP_lit20, 0x44) +DW_OP (DW_OP_lit21, 0x45) +DW_OP (DW_OP_lit22, 0x46) +DW_OP (DW_OP_lit23, 0x47) +DW_OP (DW_OP_lit24, 0x48) +DW_OP (DW_OP_lit25, 0x49) +DW_OP (DW_OP_lit26, 0x4a) +DW_OP (DW_OP_lit27, 0x4b) +DW_OP (DW_OP_lit28, 0x4c) +DW_OP (DW_OP_lit29, 0x4d) +DW_OP (DW_OP_lit30, 0x4e) +DW_OP (DW_OP_lit31, 0x4f) +DW_OP (DW_OP_reg0, 0x50) +DW_OP (DW_OP_reg1, 0x51) +DW_OP (DW_OP_reg2, 0x52) +DW_OP (DW_OP_reg3, 0x53) +DW_OP (DW_OP_reg4, 0x54) +DW_OP (DW_OP_reg5, 0x55) +DW_OP (DW_OP_reg6, 0x56) +DW_OP (DW_OP_reg7, 0x57) +DW_OP (DW_OP_reg8, 0x58) +DW_OP (DW_OP_reg9, 0x59) +DW_OP (DW_OP_reg10, 0x5a) +DW_OP (DW_OP_reg11, 0x5b) +DW_OP (DW_OP_reg12, 0x5c) +DW_OP (DW_OP_reg13, 0x5d) +DW_OP (DW_OP_reg14, 0x5e) +DW_OP (DW_OP_reg15, 0x5f) +DW_OP (DW_OP_reg16, 0x60) +DW_OP (DW_OP_reg17, 0x61) +DW_OP (DW_OP_reg18, 0x62) +DW_OP (DW_OP_reg19, 0x63) +DW_OP (DW_OP_reg20, 0x64) +DW_OP (DW_OP_reg21, 0x65) +DW_OP (DW_OP_reg22, 0x66) +DW_OP (DW_OP_reg23, 0x67) +DW_OP (DW_OP_reg24, 0x68) +DW_OP (DW_OP_reg25, 0x69) +DW_OP (DW_OP_reg26, 0x6a) +DW_OP (DW_OP_reg27, 0x6b) +DW_OP (DW_OP_reg28, 0x6c) +DW_OP (DW_OP_reg29, 0x6d) +DW_OP (DW_OP_reg30, 0x6e) +DW_OP (DW_OP_reg31, 0x6f) +DW_OP (DW_OP_breg0, 0x70) +DW_OP (DW_OP_breg1, 0x71) +DW_OP (DW_OP_breg2, 0x72) +DW_OP (DW_OP_breg3, 0x73) +DW_OP (DW_OP_breg4, 0x74) +DW_OP (DW_OP_breg5, 0x75) +DW_OP (DW_OP_breg6, 0x76) +DW_OP (DW_OP_breg7, 0x77) +DW_OP (DW_OP_breg8, 0x78) +DW_OP (DW_OP_breg9, 0x79) +DW_OP (DW_OP_breg10, 0x7a) +DW_OP (DW_OP_breg11, 0x7b) +DW_OP (DW_OP_breg12, 0x7c) +DW_OP (DW_OP_breg13, 0x7d) +DW_OP (DW_OP_breg14, 0x7e) +DW_OP (DW_OP_breg15, 0x7f) +DW_OP (DW_OP_breg16, 0x80) +DW_OP (DW_OP_breg17, 0x81) +DW_OP (DW_OP_breg18, 0x82) +DW_OP (DW_OP_breg19, 0x83) +DW_OP (DW_OP_breg20, 0x84) +DW_OP (DW_OP_breg21, 0x85) +DW_OP (DW_OP_breg22, 0x86) +DW_OP (DW_OP_breg23, 0x87) +DW_OP (DW_OP_breg24, 0x88) +DW_OP (DW_OP_breg25, 0x89) +DW_OP (DW_OP_breg26, 0x8a) +DW_OP (DW_OP_breg27, 0x8b) +DW_OP (DW_OP_breg28, 0x8c) +DW_OP (DW_OP_breg29, 0x8d) +DW_OP (DW_OP_breg30, 0x8e) +DW_OP (DW_OP_breg31, 0x8f) +DW_OP (DW_OP_regx, 0x90) +DW_OP (DW_OP_fbreg, 0x91) +DW_OP (DW_OP_bregx, 0x92) +DW_OP (DW_OP_piece, 0x93) +DW_OP (DW_OP_deref_size, 0x94) +DW_OP (DW_OP_xderef_size, 0x95) +DW_OP (DW_OP_nop, 0x96) +/* DWARF 3 extensions. */ +DW_OP (DW_OP_push_object_address, 0x97) +DW_OP (DW_OP_call2, 0x98) +DW_OP (DW_OP_call4, 0x99) +DW_OP (DW_OP_call_ref, 0x9a) +DW_OP (DW_OP_form_tls_address, 0x9b) +DW_OP (DW_OP_call_frame_cfa, 0x9c) +DW_OP (DW_OP_bit_piece, 0x9d) + +/* DWARF 4 extensions. */ +DW_OP (DW_OP_implicit_value, 0x9e) +DW_OP (DW_OP_stack_value, 0x9f) + +/* DWARF 5 extensions. */ +DW_OP (DW_OP_implicit_pointer, 0xa0) +DW_OP (DW_OP_addrx, 0xa1) +DW_OP (DW_OP_constx, 0xa2) +DW_OP (DW_OP_entry_value, 0xa3) +DW_OP (DW_OP_const_type, 0xa4) +DW_OP (DW_OP_regval_type, 0xa5) +DW_OP (DW_OP_deref_type, 0xa6) +DW_OP (DW_OP_xderef_type, 0xa7) +DW_OP (DW_OP_convert, 0xa8) +DW_OP (DW_OP_reinterpret, 0xa9) + +DW_OP_DUP (DW_OP_lo_user, 0xe0) /* Implementation-defined range start. */ +DW_OP_DUP (DW_OP_hi_user, 0xff) /* Implementation-defined range end. */ + +/* GNU extensions. */ +DW_OP (DW_OP_GNU_push_tls_address, 0xe0) +/* The following is for marking variables that are uninitialized. */ +DW_OP (DW_OP_GNU_uninit, 0xf0) +DW_OP (DW_OP_GNU_encoded_addr, 0xf1) +/* The GNU implicit pointer extension. + See http://www.dwarfstd.org/ShowIssue.php?issue=100831.1&type=open . */ +DW_OP (DW_OP_GNU_implicit_pointer, 0xf2) +/* The GNU entry value extension. + See http://www.dwarfstd.org/ShowIssue.php?issue=100909.1&type=open . */ +DW_OP (DW_OP_GNU_entry_value, 0xf3) +/* The GNU typed stack extension. + See http://www.dwarfstd.org/doc/040408.1.html . */ +DW_OP (DW_OP_GNU_const_type, 0xf4) +DW_OP (DW_OP_GNU_regval_type, 0xf5) +DW_OP (DW_OP_GNU_deref_type, 0xf6) +DW_OP (DW_OP_GNU_convert, 0xf7) +DW_OP (DW_OP_GNU_reinterpret, 0xf9) +/* The GNU parameter ref extension. */ +DW_OP (DW_OP_GNU_parameter_ref, 0xfa) +/* Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission. */ +DW_OP (DW_OP_GNU_addr_index, 0xfb) +DW_OP (DW_OP_GNU_const_index, 0xfc) +/* The GNU variable value extension. + See http://dwarfstd.org/ShowIssue.php?issue=161109.2 . */ +DW_OP (DW_OP_GNU_variable_value, 0xfd) +/* HP extensions. */ +DW_OP_DUP (DW_OP_HP_unknown, 0xe0) /* Ouch, the same as GNU_push_tls_address. */ +DW_OP (DW_OP_HP_is_value, 0xe1) +DW_OP (DW_OP_HP_fltconst4, 0xe2) +DW_OP (DW_OP_HP_fltconst8, 0xe3) +DW_OP (DW_OP_HP_mod_range, 0xe4) +DW_OP (DW_OP_HP_unmod_range, 0xe5) +DW_OP (DW_OP_HP_tls, 0xe6) +/* PGI (STMicroelectronics) extensions. */ +DW_OP (DW_OP_PGI_omp_thread_num, 0xf8) +/* AARCH64 extensions. + DW_OP_AARCH64_operation takes one mandatory unsigned LEB128 operand. + Bits[6:0] of this operand is the action code, all others bits are initialized + to 0 except explicitly documented for one action. Please refer AArch64 DWARF + ABI documentation for details. */ +DW_OP (DW_OP_AARCH64_operation, 0xea) +DW_END_OP + +DW_FIRST_ATE (DW_ATE_void, 0x0) +DW_ATE (DW_ATE_address, 0x1) +DW_ATE (DW_ATE_boolean, 0x2) +DW_ATE (DW_ATE_complex_float, 0x3) +DW_ATE (DW_ATE_float, 0x4) +DW_ATE (DW_ATE_signed, 0x5) +DW_ATE (DW_ATE_signed_char, 0x6) +DW_ATE (DW_ATE_unsigned, 0x7) +DW_ATE (DW_ATE_unsigned_char, 0x8) +/* DWARF 3. */ +DW_ATE (DW_ATE_imaginary_float, 0x9) +DW_ATE (DW_ATE_packed_decimal, 0xa) +DW_ATE (DW_ATE_numeric_string, 0xb) +DW_ATE (DW_ATE_edited, 0xc) +DW_ATE (DW_ATE_signed_fixed, 0xd) +DW_ATE (DW_ATE_unsigned_fixed, 0xe) +DW_ATE (DW_ATE_decimal_float, 0xf) +/* DWARF 4. */ +DW_ATE (DW_ATE_UTF, 0x10) +/* DWARF 5. */ +DW_ATE (DW_ATE_UCS, 0x11) +DW_ATE (DW_ATE_ASCII, 0x12) + +DW_ATE_DUP (DW_ATE_lo_user, 0x80) +DW_ATE_DUP (DW_ATE_hi_user, 0xff) + +/* HP extensions. */ +DW_ATE (DW_ATE_HP_float80, 0x80) /* Floating-point (80 bit). */ +DW_ATE (DW_ATE_HP_complex_float80, 0x81) /* Complex floating-point (80 bit). */ +DW_ATE (DW_ATE_HP_float128, 0x82) /* Floating-point (128 bit). */ +DW_ATE (DW_ATE_HP_complex_float128, 0x83) /* Complex fp (128 bit). */ +DW_ATE (DW_ATE_HP_floathpintel, 0x84) /* Floating-point (82 bit IA64). */ +DW_ATE (DW_ATE_HP_imaginary_float80, 0x85) +DW_ATE (DW_ATE_HP_imaginary_float128, 0x86) +DW_ATE (DW_ATE_HP_VAX_float, 0x88) /* F or G floating. */ +DW_ATE (DW_ATE_HP_VAX_float_d, 0x89) /* D floating. */ +DW_ATE (DW_ATE_HP_packed_decimal, 0x8a) /* Cobol. */ +DW_ATE (DW_ATE_HP_zoned_decimal, 0x8b) /* Cobol. */ +DW_ATE (DW_ATE_HP_edited, 0x8c) /* Cobol. */ +DW_ATE (DW_ATE_HP_signed_fixed, 0x8d) /* Cobol. */ +DW_ATE (DW_ATE_HP_unsigned_fixed, 0x8e) /* Cobol. */ +DW_ATE (DW_ATE_HP_VAX_complex_float, 0x8f) /* F or G floating complex. */ +DW_ATE (DW_ATE_HP_VAX_complex_float_d, 0x90) /* D floating complex. */ + +DW_END_ATE + +DW_FIRST_CFA (DW_CFA_advance_loc, 0x40) +DW_CFA (DW_CFA_offset, 0x80) +DW_CFA (DW_CFA_restore, 0xc0) +DW_CFA (DW_CFA_nop, 0x00) +DW_CFA (DW_CFA_set_loc, 0x01) +DW_CFA (DW_CFA_advance_loc1, 0x02) +DW_CFA (DW_CFA_advance_loc2, 0x03) +DW_CFA (DW_CFA_advance_loc4, 0x04) +DW_CFA (DW_CFA_offset_extended, 0x05) +DW_CFA (DW_CFA_restore_extended, 0x06) +DW_CFA (DW_CFA_undefined, 0x07) +DW_CFA (DW_CFA_same_value, 0x08) +DW_CFA (DW_CFA_register, 0x09) +DW_CFA (DW_CFA_remember_state, 0x0a) +DW_CFA (DW_CFA_restore_state, 0x0b) +DW_CFA (DW_CFA_def_cfa, 0x0c) +DW_CFA (DW_CFA_def_cfa_register, 0x0d) +DW_CFA (DW_CFA_def_cfa_offset, 0x0e) +/* DWARF 3. */ +DW_CFA (DW_CFA_def_cfa_expression, 0x0f) +DW_CFA (DW_CFA_expression, 0x10) +DW_CFA (DW_CFA_offset_extended_sf, 0x11) +DW_CFA (DW_CFA_def_cfa_sf, 0x12) +DW_CFA (DW_CFA_def_cfa_offset_sf, 0x13) +DW_CFA (DW_CFA_val_offset, 0x14) +DW_CFA (DW_CFA_val_offset_sf, 0x15) +DW_CFA (DW_CFA_val_expression, 0x16) + +DW_CFA (DW_CFA_lo_user, 0x1c) +DW_CFA (DW_CFA_hi_user, 0x3f) + +/* SGI/MIPS specific. */ +DW_CFA (DW_CFA_MIPS_advance_loc8, 0x1d) +/* GNU extensions. + NOTE: DW_CFA_GNU_window_save is multiplexed on Sparc and AArch64. */ +DW_CFA (DW_CFA_GNU_window_save, 0x2d) +DW_CFA_DUP (DW_CFA_AARCH64_negate_ra_state, 0x2d) +DW_CFA (DW_CFA_GNU_args_size, 0x2e) +DW_CFA (DW_CFA_GNU_negative_offset_extended, 0x2f) + +DW_END_CFA + +/* Index attributes in the Abbreviations Table. */ +DW_FIRST_IDX (DW_IDX_compile_unit, 1) +DW_IDX (DW_IDX_type_unit, 2) +DW_IDX (DW_IDX_die_offset, 3) +DW_IDX (DW_IDX_parent, 4) +DW_IDX (DW_IDX_type_hash, 5) +DW_IDX_DUP (DW_IDX_lo_user, 0x2000) +DW_IDX (DW_IDX_hi_user, 0x3fff) +DW_IDX (DW_IDX_GNU_internal, 0x2000) +DW_IDX (DW_IDX_GNU_external, 0x2001) +DW_END_IDX + +/* DWARF5 Unit type header encodings */ +DW_FIRST_UT (DW_UT_compile, 0x01) +DW_UT (DW_UT_type, 0x02) +DW_UT (DW_UT_partial, 0x03) +DW_UT (DW_UT_skeleton, 0x04) +DW_UT (DW_UT_split_compile, 0x05) +DW_UT (DW_UT_split_type, 0x06) +DW_UT (DW_UT_lo_user, 0x80) +DW_UT (DW_UT_hi_user, 0xff) +DW_END_UT diff --git a/dwarf2.h b/dwarf2.h new file mode 100644 index 0000000..50b6ddb --- /dev/null +++ b/dwarf2.h @@ -0,0 +1,556 @@ +/* Declarations and definitions of codes relating to the DWARF2 and + DWARF3 symbolic debugging information formats. + Copyright (C) 1992-2021 Free Software Foundation, Inc. + + Written by Gary Funck (gary@intrepid.com) The Ada Joint Program + Office (AJPO), Florida State University and Silicon Graphics Inc. + provided support for this effort -- June 21, 1995. + + Derived from the DWARF 1 implementation written by Ron Guilmette + (rfg@netcom.com), November 1990. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 3, or (at your option) any later + version. + + GCC 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. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +/* This file is derived from the DWARF specification (a public document) + Revision 2.0.0 (July 27, 1993) developed by the UNIX International + Programming Languages Special Interest Group (UI/PLSIG) and distributed + by UNIX International. Copies of this specification are available from + UNIX International, 20 Waterview Boulevard, Parsippany, NJ, 07054. + + This file also now contains definitions from the DWARF 3 specification + published Dec 20, 2005, available from: http://dwarf.freestandards.org. */ + +#ifndef _DWARF2_H +#define _DWARF2_H + +#define DW_TAG(name, value) , name = value +#define DW_TAG_DUP(name, value) , name = value +#define DW_FORM(name, value) , name = value +#define DW_AT(name, value) , name = value +#define DW_AT_DUP(name, value) , name = value +#define DW_OP(name, value) , name = value +#define DW_OP_DUP(name, value) , name = value +#define DW_ATE(name, value) , name = value +#define DW_ATE_DUP(name, value) , name = value +#define DW_CFA(name, value) , name = value +#define DW_CFA_DUP(name, value) , name = value +#define DW_IDX(name, value) , name = value +#define DW_IDX_DUP(name, value) , name = value +#define DW_UT(name, value) , name = value + +#define DW_FIRST_TAG(name, value) enum dwarf_tag { \ + name = value +#define DW_END_TAG }; +#define DW_FIRST_FORM(name, value) enum dwarf_form { \ + name = value +#define DW_END_FORM }; +#define DW_FIRST_AT(name, value) enum dwarf_attribute { \ + name = value +#define DW_END_AT }; +#define DW_FIRST_OP(name, value) enum dwarf_location_atom { \ + name = value +#define DW_END_OP }; +#define DW_FIRST_ATE(name, value) enum dwarf_type { \ + name = value +#define DW_END_ATE }; +#define DW_FIRST_CFA(name, value) enum dwarf_call_frame_info { \ + name = value +#define DW_END_CFA }; +#define DW_FIRST_IDX(name, value) enum dwarf_name_index_attribute { \ + name = value +#define DW_END_IDX }; +#define DW_FIRST_UT(name, value) enum dwarf_unit_type { \ + name = value +#define DW_END_UT }; + +#include "dwarf2.def" + +#undef DW_FIRST_TAG +#undef DW_END_TAG +#undef DW_FIRST_FORM +#undef DW_END_FORM +#undef DW_FIRST_AT +#undef DW_END_AT +#undef DW_FIRST_OP +#undef DW_END_OP +#undef DW_FIRST_ATE +#undef DW_END_ATE +#undef DW_FIRST_CFA +#undef DW_END_CFA +#undef DW_FIRST_IDX +#undef DW_END_IDX +#undef DW_FIRST_UT +#undef DW_END_UT + +#undef DW_TAG +#undef DW_TAG_DUP +#undef DW_FORM +#undef DW_AT +#undef DW_AT_DUP +#undef DW_OP +#undef DW_OP_DUP +#undef DW_ATE +#undef DW_ATE_DUP +#undef DW_CFA +#undef DW_CFA_DUP +#undef DW_IDX +#undef DW_IDX_DUP +#undef DW_UT + +/* Flag that tells whether entry has a child or not. */ +#define DW_children_no 0 +#define DW_children_yes 1 + +#define DW_AT_stride_size DW_AT_bit_stride /* Note: The use of DW_AT_stride_size is deprecated. */ +#define DW_AT_stride DW_AT_byte_stride /* Note: The use of DW_AT_stride is deprecated. */ + +/* Decimal sign encodings. */ +enum dwarf_decimal_sign_encoding + { + /* DWARF 3. */ + DW_DS_unsigned = 0x01, + DW_DS_leading_overpunch = 0x02, + DW_DS_trailing_overpunch = 0x03, + DW_DS_leading_separate = 0x04, + DW_DS_trailing_separate = 0x05 + }; + +/* Endianity encodings. */ +enum dwarf_endianity_encoding + { + /* DWARF 3. */ + DW_END_default = 0x00, + DW_END_big = 0x01, + DW_END_little = 0x02, + + DW_END_lo_user = 0x40, + DW_END_hi_user = 0xff + }; + +/* Array ordering names and codes. */ +enum dwarf_array_dim_ordering + { + DW_ORD_row_major = 0, + DW_ORD_col_major = 1 + }; + +/* Access attribute. */ +enum dwarf_access_attribute + { + DW_ACCESS_public = 1, + DW_ACCESS_protected = 2, + DW_ACCESS_private = 3 + }; + +/* Visibility. */ +enum dwarf_visibility_attribute + { + DW_VIS_local = 1, + DW_VIS_exported = 2, + DW_VIS_qualified = 3 + }; + +/* Virtuality. */ +enum dwarf_virtuality_attribute + { + DW_VIRTUALITY_none = 0, + DW_VIRTUALITY_virtual = 1, + DW_VIRTUALITY_pure_virtual = 2 + }; + +/* Case sensitivity. */ +enum dwarf_id_case + { + DW_ID_case_sensitive = 0, + DW_ID_up_case = 1, + DW_ID_down_case = 2, + DW_ID_case_insensitive = 3 + }; + +/* Calling convention. */ +enum dwarf_calling_convention + { + DW_CC_normal = 0x1, + DW_CC_program = 0x2, + DW_CC_nocall = 0x3, + + /* DWARF 5. */ + DW_CC_pass_by_reference = 0x4, + DW_CC_pass_by_value = 0x5, + + DW_CC_lo_user = 0x40, + DW_CC_hi_user = 0xff, + + DW_CC_GNU_renesas_sh = 0x40, + DW_CC_GNU_borland_fastcall_i386 = 0x41, + + /* This DW_CC_ value is not currently generated by any toolchain. It is + used internally to GDB to indicate OpenCL C functions that have been + compiled with the IBM XL C for OpenCL compiler and use a non-platform + calling convention for passing OpenCL C vector types. This value may + be changed freely as long as it does not conflict with any other DW_CC_ + value defined here. */ + DW_CC_GDB_IBM_OpenCL = 0xff + }; + +/* Inline attribute. */ +enum dwarf_inline_attribute + { + DW_INL_not_inlined = 0, + DW_INL_inlined = 1, + DW_INL_declared_not_inlined = 2, + DW_INL_declared_inlined = 3 + }; + +/* Discriminant lists. */ +enum dwarf_discrim_list + { + DW_DSC_label = 0, + DW_DSC_range = 1 + }; + +/* Line number opcodes. */ +enum dwarf_line_number_ops + { + DW_LNS_extended_op = 0, + DW_LNS_copy = 1, + DW_LNS_advance_pc = 2, + DW_LNS_advance_line = 3, + DW_LNS_set_file = 4, + DW_LNS_set_column = 5, + DW_LNS_negate_stmt = 6, + DW_LNS_set_basic_block = 7, + DW_LNS_const_add_pc = 8, + DW_LNS_fixed_advance_pc = 9, + /* DWARF 3. */ + DW_LNS_set_prologue_end = 10, + DW_LNS_set_epilogue_begin = 11, + DW_LNS_set_isa = 12 + }; + +/* Line number extended opcodes. */ +enum dwarf_line_number_x_ops + { + DW_LNE_end_sequence = 1, + DW_LNE_set_address = 2, + DW_LNE_define_file = 3, + DW_LNE_set_discriminator = 4, + /* HP extensions. */ + DW_LNE_HP_negate_is_UV_update = 0x11, + DW_LNE_HP_push_context = 0x12, + DW_LNE_HP_pop_context = 0x13, + DW_LNE_HP_set_file_line_column = 0x14, + DW_LNE_HP_set_routine_name = 0x15, + DW_LNE_HP_set_sequence = 0x16, + DW_LNE_HP_negate_post_semantics = 0x17, + DW_LNE_HP_negate_function_exit = 0x18, + DW_LNE_HP_negate_front_end_logical = 0x19, + DW_LNE_HP_define_proc = 0x20, + DW_LNE_HP_source_file_correlation = 0x80, + + DW_LNE_lo_user = 0x80, + DW_LNE_hi_user = 0xff + }; + +/* Sub-opcodes for DW_LNE_HP_source_file_correlation. */ +enum dwarf_line_number_hp_sfc_ops + { + DW_LNE_HP_SFC_formfeed = 1, + DW_LNE_HP_SFC_set_listing_line = 2, + DW_LNE_HP_SFC_associate = 3 + }; + +/* Content type codes in line table directory_entry_format + and file_name_entry_format sequences. */ +enum dwarf_line_number_content_type + { + DW_LNCT_path = 0x1, + DW_LNCT_directory_index = 0x2, + DW_LNCT_timestamp = 0x3, + DW_LNCT_size = 0x4, + DW_LNCT_MD5 = 0x5, + DW_LNCT_lo_user = 0x2000, + DW_LNCT_hi_user = 0x3fff + }; + +/* Type codes for location list entries. */ +enum dwarf_location_list_entry_type + { + DW_LLE_end_of_list = 0x00, + DW_LLE_base_addressx = 0x01, + DW_LLE_startx_endx = 0x02, + DW_LLE_startx_length = 0x03, + DW_LLE_offset_pair = 0x04, + DW_LLE_default_location = 0x05, + DW_LLE_base_address = 0x06, + DW_LLE_start_end = 0x07, + DW_LLE_start_length = 0x08, + + /* <http://lists.dwarfstd.org/private.cgi/dwarf-discuss-dwarfstd.org/2017-April/004347.html> + has the proposal for now; only available to list members. + + A (possibly updated) copy of the proposal is available at + <http://people.redhat.com/aoliva/papers/sfn/dwarf6-sfn-lvu.txt>. */ + DW_LLE_GNU_view_pair = 0x09, +#define DW_LLE_view_pair DW_LLE_GNU_view_pair + + /* Former extension for Fission. + See http://gcc.gnu.org/wiki/DebugFission. */ + DW_LLE_GNU_end_of_list_entry = 0x00, + DW_LLE_GNU_base_address_selection_entry = 0x01, + DW_LLE_GNU_start_end_entry = 0x02, + DW_LLE_GNU_start_length_entry = 0x03 + }; + +#define DW_CIE_ID 0xffffffff +#define DW64_CIE_ID 0xffffffffffffffffULL + +#define DW_CFA_extended 0 + +#define DW_CHILDREN_no 0x00 +#define DW_CHILDREN_yes 0x01 + +#define DW_ADDR_none 0 + +/* Source language names and codes. */ +enum dwarf_source_language + { + DW_LANG_C89 = 0x0001, + DW_LANG_C = 0x0002, + DW_LANG_Ada83 = 0x0003, + DW_LANG_C_plus_plus = 0x0004, + DW_LANG_Cobol74 = 0x0005, + DW_LANG_Cobol85 = 0x0006, + DW_LANG_Fortran77 = 0x0007, + DW_LANG_Fortran90 = 0x0008, + DW_LANG_Pascal83 = 0x0009, + DW_LANG_Modula2 = 0x000a, + /* DWARF 3. */ + DW_LANG_Java = 0x000b, + DW_LANG_C99 = 0x000c, + DW_LANG_Ada95 = 0x000d, + DW_LANG_Fortran95 = 0x000e, + DW_LANG_PLI = 0x000f, + DW_LANG_ObjC = 0x0010, + DW_LANG_ObjC_plus_plus = 0x0011, + DW_LANG_UPC = 0x0012, + DW_LANG_D = 0x0013, + /* DWARF 4. */ + DW_LANG_Python = 0x0014, + /* DWARF 5. */ + DW_LANG_OpenCL = 0x0015, + DW_LANG_Go = 0x0016, + DW_LANG_Modula3 = 0x0017, + DW_LANG_Haskell = 0x0018, + DW_LANG_C_plus_plus_03 = 0x0019, + DW_LANG_C_plus_plus_11 = 0x001a, + DW_LANG_OCaml = 0x001b, + DW_LANG_Rust = 0x001c, + DW_LANG_C11 = 0x001d, + DW_LANG_Swift = 0x001e, + DW_LANG_Julia = 0x001f, + DW_LANG_Dylan = 0x0020, + DW_LANG_C_plus_plus_14 = 0x0021, + DW_LANG_Fortran03 = 0x0022, + DW_LANG_Fortran08 = 0x0023, + DW_LANG_RenderScript = 0x0024, + + DW_LANG_lo_user = 0x8000, /* Implementation-defined range start. */ + DW_LANG_hi_user = 0xffff, /* Implementation-defined range start. */ + + /* MIPS. */ + DW_LANG_Mips_Assembler = 0x8001, + /* UPC. */ + DW_LANG_Upc = 0x8765, + /* HP extensions. */ + DW_LANG_HP_Bliss = 0x8003, + DW_LANG_HP_Basic91 = 0x8004, + DW_LANG_HP_Pascal91 = 0x8005, + DW_LANG_HP_IMacro = 0x8006, + DW_LANG_HP_Assembler = 0x8007, + + /* Rust extension, but replaced in DWARF 5. */ + DW_LANG_Rust_old = 0x9000 + }; + +/* Names and codes for macro information. */ +enum dwarf_macinfo_record_type + { + DW_MACINFO_define = 1, + DW_MACINFO_undef = 2, + DW_MACINFO_start_file = 3, + DW_MACINFO_end_file = 4, + DW_MACINFO_vendor_ext = 255 + }; + +/* DW_TAG_defaulted/DW_TAG_GNU_defaulted attributes. */ +enum dwarf_defaulted_attribute + { + DW_DEFAULTED_no = 0x00, + DW_DEFAULTED_in_class = 0x01, + DW_DEFAULTED_out_of_class = 0x02 + }; + +/* Names and codes for new style macro information. */ +enum dwarf_macro_record_type + { + DW_MACRO_define = 0x01, + DW_MACRO_undef = 0x02, + DW_MACRO_start_file = 0x03, + DW_MACRO_end_file = 0x04, + DW_MACRO_define_strp = 0x05, + DW_MACRO_undef_strp = 0x06, + DW_MACRO_import = 0x07, + DW_MACRO_define_sup = 0x08, + DW_MACRO_undef_sup = 0x09, + DW_MACRO_import_sup = 0x0a, + DW_MACRO_define_strx = 0x0b, + DW_MACRO_undef_strx = 0x0c, + DW_MACRO_lo_user = 0xe0, + DW_MACRO_hi_user = 0xff, + + /* Compatibility macros for the GNU .debug_macro extension. */ + DW_MACRO_GNU_define = 0x01, + DW_MACRO_GNU_undef = 0x02, + DW_MACRO_GNU_start_file = 0x03, + DW_MACRO_GNU_end_file = 0x04, + DW_MACRO_GNU_define_indirect = 0x05, + DW_MACRO_GNU_undef_indirect = 0x06, + DW_MACRO_GNU_transparent_include = 0x07, + /* Extensions for DWZ multifile. + See http://www.dwarfstd.org/ShowIssue.php?issue=120604.1&type=open . */ + DW_MACRO_GNU_define_indirect_alt = 0x08, + DW_MACRO_GNU_undef_indirect_alt = 0x09, + DW_MACRO_GNU_transparent_include_alt = 0x0a, + DW_MACRO_GNU_lo_user = 0xe0, + DW_MACRO_GNU_hi_user = 0xff + }; + +/* Range list entry kinds in .debug_rnglists* section. */ +enum dwarf_range_list_entry + { + DW_RLE_end_of_list = 0x00, + DW_RLE_base_addressx = 0x01, + DW_RLE_startx_endx = 0x02, + DW_RLE_startx_length = 0x03, + DW_RLE_offset_pair = 0x04, + DW_RLE_base_address = 0x05, + DW_RLE_start_end = 0x06, + DW_RLE_start_length = 0x07 + }; + +/* @@@ For use with GNU frame unwind information. */ + +#define DW_EH_PE_absptr 0x00 +#define DW_EH_PE_omit 0xff + +#define DW_EH_PE_uleb128 0x01 +#define DW_EH_PE_udata2 0x02 +#define DW_EH_PE_udata4 0x03 +#define DW_EH_PE_udata8 0x04 +#define DW_EH_PE_sleb128 0x09 +#define DW_EH_PE_sdata2 0x0A +#define DW_EH_PE_sdata4 0x0B +#define DW_EH_PE_sdata8 0x0C +#define DW_EH_PE_signed 0x08 + +#define DW_EH_PE_pcrel 0x10 +#define DW_EH_PE_textrel 0x20 +#define DW_EH_PE_datarel 0x30 +#define DW_EH_PE_funcrel 0x40 +#define DW_EH_PE_aligned 0x50 + +#define DW_EH_PE_indirect 0x80 + +/* Codes for the debug sections in a dwarf package (.dwp) file. + (From the pre-standard formats Extensions for Fission. + See http://gcc.gnu.org/wiki/DebugFissionDWP). */ +enum dwarf_sect +{ + DW_SECT_INFO = 1, + DW_SECT_TYPES = 2, + DW_SECT_ABBREV = 3, + DW_SECT_LINE = 4, + DW_SECT_LOC = 5, + DW_SECT_STR_OFFSETS = 6, + DW_SECT_MACINFO = 7, + DW_SECT_MACRO = 8, + DW_SECT_MAX = 8 +}; + +/* Codes for the debug sections in a dwarf package (.dwp) file. + (From the official DWARF v5 spec. + See http://dwarfstd.org/doc/DWARF5.pdf, section 7.3.5). */ +enum dwarf_sect_v5 +{ + DW_SECT_INFO_V5 = 1, + DW_SECT_RESERVED_V5 = 2, + DW_SECT_ABBREV_V5 = 3, + DW_SECT_LINE_V5 = 4, + DW_SECT_LOCLISTS_V5 = 5, + DW_SECT_STR_OFFSETS_V5 = 6, + DW_SECT_MACRO_V5 = 7, + DW_SECT_RNGLISTS_V5 = 8, + DW_SECT_MAX_V5 = 8 +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Return the name of a DW_TAG_ constant, or NULL if the value is not + recognized. */ +extern const char *get_DW_TAG_name (unsigned int tag); + +/* Return the name of a DW_AT_ constant, or NULL if the value is not + recognized. */ +extern const char *get_DW_AT_name (unsigned int attr); + +/* Return the name of a DW_FORM_ constant, or NULL if the value is not + recognized. */ +extern const char *get_DW_FORM_name (unsigned int form); + +/* Return the name of a DW_OP_ constant, or NULL if the value is not + recognized. */ +extern const char *get_DW_OP_name (unsigned int op); + +/* Return the name of a DW_ATE_ constant, or NULL if the value is not + recognized. */ +extern const char *get_DW_ATE_name (unsigned int enc); + +/* Return the name of a DW_CFA_ constant, or NULL if the value is not + recognized. */ +extern const char *get_DW_CFA_name (unsigned int opc); + +/* Return the name of a DW_IDX_ constant, or NULL if the value is not + recognized. */ +extern const char *get_DW_IDX_name (unsigned int idx); + +/* Return the name of a DW_UT_ constant, or NULL if the value is not + recognized. */ +extern const char *get_DW_UT_name (unsigned int ut); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _DWARF2_H */ diff --git a/dwarfnames.c b/dwarfnames.c new file mode 100644 index 0000000..630d841 --- /dev/null +++ b/dwarfnames.c @@ -0,0 +1,118 @@ +/* Names of various DWARF tags. + Copyright (C) 2012-2021 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combined +executable.) + +GNU CC 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 +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street - Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#include "dwarf2.h" + +#define DW_FIRST_TAG(name, value) \ + const char *get_DW_TAG_name (unsigned int tag) \ + { switch (tag) { \ + DW_TAG (name, value) +#define DW_END_TAG } return 0; } +#define DW_FIRST_FORM(name, value) \ + const char *get_DW_FORM_name (unsigned int form) \ + { switch (form) { \ + DW_FORM (name, value) +#define DW_END_FORM } return 0; } +#define DW_FIRST_AT(name, value) \ + const char *get_DW_AT_name (unsigned int attr) { \ + switch (attr) { \ + DW_AT (name, value) +#define DW_END_AT } return 0; } +#define DW_FIRST_OP(name, value) \ + const char *get_DW_OP_name (unsigned int op) { \ + switch (op) { \ + DW_OP (name, value) +#define DW_END_OP } return 0; } +#define DW_FIRST_ATE(name, value) \ + const char *get_DW_ATE_name (unsigned int enc) { \ + switch (enc) { \ + DW_ATE (name, value) +#define DW_END_ATE } return 0; } +#define DW_FIRST_CFA(name, value) \ + const char *get_DW_CFA_name (unsigned int opc) { \ + switch (opc) { \ + DW_CFA (name, value) +#define DW_END_CFA } return 0; } +#define DW_FIRST_IDX(name, value) \ + const char *get_DW_IDX_name (unsigned int idx) { \ + switch (idx) { \ + DW_IDX (name, value) +#define DW_END_IDX } return 0; } +#define DW_FIRST_UT(name, value) \ + const char *get_DW_UT_name (unsigned int ut) { \ + switch (ut) { \ + DW_UT (name, value) +#define DW_END_UT } return 0; } + +#define DW_TAG(name, value) case name: return # name ; +#define DW_TAG_DUP(name, value) +#define DW_FORM(name, value) case name: return # name ; +#define DW_AT(name, value) case name: return # name ; +#define DW_AT_DUP(name, value) +#define DW_OP(name, value) case name: return # name ; +#define DW_OP_DUP(name, value) +#define DW_ATE(name, value) case name: return # name ; +#define DW_ATE_DUP(name, value) +#define DW_CFA(name, value) case name: return # name ; +#define DW_CFA_DUP(name, value) +#define DW_IDX(name, value) case name: return # name ; +#define DW_IDX_DUP(name, value) +#define DW_UT(name, value) case name: return # name ; + +#include "dwarf2.def" + +#undef DW_FIRST_TAG +#undef DW_END_TAG +#undef DW_FIRST_FORM +#undef DW_END_FORM +#undef DW_FIRST_AT +#undef DW_END_AT +#undef DW_FIRST_OP +#undef DW_END_OP +#undef DW_FIRST_ATE +#undef DW_END_ATE +#undef DW_FIRST_CFA +#undef DW_END_CFA +#undef DW_FIRST_IDX +#undef DW_END_IDX +#undef DW_END_UT + +#undef DW_TAG +#undef DW_TAG_DUP +#undef DW_FORM +#undef DW_AT +#undef DW_AT_DUP +#undef DW_OP +#undef DW_OP_DUP +#undef DW_ATE +#undef DW_ATE_DUP +#undef DW_CFA +#undef DW_CFA_DUP +#undef DW_IDX +#undef DW_IDX_DUP @@ -0,0 +1,207 @@ +.TH dwz 1 "15 Feb 2021" +.SH NAME +dwz \- DWARF optimization and duplicate removal tool +.SH SYNOPSIS +dwz +.RB [OPTION...]\ [FILES] +.SH DESCRIPTION +\fBdwz\fR is a program that attempts to optimize DWARF debugging information +contained in ELF shared libraries and ELF executables for size, by +replacing DWARF information representation with equivalent smaller +representation where possible and by reducing the amount of duplication +using techniques from DWARF standard appendix E - creating +\fIDW_TAG_partial_unit\fR +compilation units (CUs) for duplicated information and using +\fIDW_TAG_imported_unit\fR +to import it into each CU that needs it. + +The tool handles DWARF 32-bit format debugging sections of versions 2, +3, 4, most of version 5 and GNU extensions on top of those. It is +strongly recommended to use at least DWARF 3, but using DWARF 4 or +higher will work much better. + +While most of DWARF 5 is supported dwz doesn't yet generate spec +compliant DWARF Supplementary Object Files (DWARF 5, section +7.3.6) unless the \fI--dwarf-5\fR option is used. Instead of a +\fI.debug_sup\fR section it will generate by default a \fI.gnu_debugaltlink\fR +section. And it will use the \fIDW_FORM_GNU_strp_alt\fR and +\fIDW_FORM_GNU_reg_alt\fR, instead of \fIDW_FORM_strp_sup\fR +and \fIDW_FORM_ref_sup\fR to keep compatibility with existing DWARF +consumers. + +DWARF 4 \fI.debug_types\fR are supported, but DWARF 5 \fIDW_UT_type\fR +units are not. Likewise \fI.gdb_index\fR is supported, but the DWARF 5 +\fI.debug_names\fR is not. Also some forms and sections that are only +emitted by GCC when generating Split DWARF, \fIDW_FORM_strx\fR and +\fI.debug_str_offsets\fR, \fIDW_FORM_addrx\fR and \fI.debug_addr\fR, +\fIDW_FORM_rnglistx\fR and \fIDW_FORM_loclistsx\fR, are not supported +yet. + +The tool has two main modes of operation, without the +\fI-m\fR option it attempts to optimize DWARF debugging information in each +given object (executable or shared library) individually, with the +\fI-m\fR option it afterwards attempts to optimize even more by moving +DWARF debugging information entries (DIEs), strings and macro descriptions +duplicated in more than one object into a newly created ELF ET_REL +object whose filename is given as +\fI-m\fR +option argument. The debug sections in the executables and shared libraries +specified on the command line are then modified again, referring to +the entities in the newly created object. +.SH OPTIONS +.TP +.B \-m\ FILE \-\-multifile FILE +Multifile mode. +After processing all named executables and shared libraries, attempt to +create ELF object +\fIFILE\fR +and put debugging information duplicated in more than one object there, +afterwards optimize each named executable or shared library even further +if possible. +.TP +.B \-h\ \-\-hardlink +Look for executables or shared libraries hardlinked together, instead +of rewriting them individually rewrite just one of them and hardlink the +rest to the first one again. +.TP +.B \-M NAME \-\-multifile-name NAME +Specify the name of the common file that should be put into the +\fI.gnu_debugaltlink\fR section alongside with its build ID. By default +\fBdwz\fR puts there the argument of the \fB-m\fR option. +.TP +.B \-r \-\-relative +Specify that the name of the common file to be put into the +\fI.gnu_debugaltlink\fR +section is supposed to be relative path from the directory containing +the executable or shared library to the file named in the argument +of the \fB-m\fR option. Either \fB-M\fR or \fB-r\fR +option can be specified, but not both. +.TP +.B \-p N \-\-multifile-pointer-size <N|auto|native> +Specify the pointer size of the multifile, in bytes. If auto, use the +pointer size of the files, provided they match. If native, use native pointer +size, as specified in the help message. +.TP +.B \-p <l|b|auto> \-\-multifile-endian <l|b|auto|native> +Specify the endianity of the multifile. If auto, use the endianity of +the files, provided they match. If native, use native endianity, as specified +in the help message. +.TP +.B \-q \-\-quiet +Silence up some of the most common messages. +.TP +.B \-o FILE \-\-output FILE +This option instructs +\fBdwz\fR not to overwrite the specified file, but instead store the new content +into \fBFILE\fR. Nothing is written if \fBdwz\fR +exits with non-zero exit code. Can be used only with a single executable +or shared library (if there are no arguments at all, +\fIa.out\fR +is assumed). +.TP +.B \-l <COUNT|none> \-\-low\-mem\-die\-limit <COUNT|none> +Handle executables or shared libraries containing more than +\fICOUNT\fR debugging information entries in their \fI.debug_info\fR +section using a slower and more memory usage friendly mode and don't +attempt to optimize that object in multifile mode. +The default is 10 million DIEs. There is a risk that for very large +amounts of debugging information in a single shared library or executable +there might not be enough memory (especially when \fBdwz\fR +tool is 32-bit binary, it might run out of available virtual address +space even sooner). Specifying none as argument disables the limit. +.TP +.B \-L <COUNT|none> \-\-max\-die\-limit <COUNT|none> +Don't attempt to optimize executables or shared libraries +containing more than +\fICOUNT\fR DIEs at all. The default is 50 million DIEs. Specifying none as +argument disables the limit. +.TP +.B \-5 \-\-dwarf\-5 +Emit standard DWARF 5 Supplementary Object Files with \fI.debug_sup\fR and +corresponding forms, instead of the GNU extension \fI.gnu_debugaltlink\fR +and corresponding forms. +.TP +.B \-j <N> \-\-jobs <N> +Process \fIN\fR files in parallel. The default is processors / 2. Disabled +when multifile is used. +.TP +.B \-\-odr / \-\-no-odr +.B Experimental. +Enable/disable One-Definition-Rule optimization for C++ compilation units. +This optimization causes struct/union/class DIEs with the same name to be +considered equal. This has the effect that DIEs referring to distinct DIEs +representing the same type (like f.i. pointer type DIEs) are considered equal, +and may be deduplicated. The status of this optimization is experimental. +It's disabled in low-mem mode. +Disabled by default. +.TP +.B \-\-odr-mode=<basic|link> +Set the One-Definition-Rule optimization aggressiveness: basic or link. +When using the link setting, the optimization will attempt to replace +declarations of a struct/union/class with a corresponding definition. When +using the basic setting, that part of the optimization is disabled. +In normal operation, the link setting should be used. The basic setting is +provided only as fallback in case of problems with the link setting. Set to +link by default. +.TP +.B \-\-import-optimize / \-\-no-import-optimize +Enable/disable optimization that reduces the number of +\fIDW_TAG_imported_unit\fR DIEs generated to import the partial units created +by \fBdwz\fR. Disabling the optimization can be used to work around problems +in the optimization, or to make it easier to observe which CU imports which +PU. +Enabled by default. +.TP +.B \-? \-\-help +Print short help and exit. +.TP +.B \-v \-\-version +Print version number and short licensing notice and exit. +.SH ARGUMENTS +Command-line arguments should be the executables, shared libraries +or their stripped to file separate debug information objects. +.SH EXAMPLES +.RS +$ dwz -m .dwz/foobar-1.2.debug -rh \\ + bin/foo.debug bin/foo2.debug foo/lib/libbar.so.debug +.RE +will attempt to optimize debugging information in +\fIbin/foo.debug\fR, \fIbin/foo2.debug\fR and +\fIlib/libbar.so.debug\fR (by modifying the files in place) and +when beneficial also will create \fI.dwz/foobar-1.2.debug\fR file. +\fI.gnu_debugaltlink\fR section in the first two files will refer to +\fI../.dwz/foobar-1.2.debug\fR and in the last file to +\fI../../.dwz/foobar-1.2.debug\fR. If e.g. +\fIbin/foo.debug\fR and \fIbin/foo2.debug\fR were hardlinked +together initially, they will be hardlinked again and for multifile +optimizations considered just as a single file rather than two. +.RS +$ dwz -o foo.dwz foo +.RE +will not modify \fIfoo\fR +but instead store the ELF object with optimized debugging information +if successful into \fIfoo.dwz\fR +file it creates. +.RS +$ dwz *.debug foo/*.debug +.RE +will attempt to optimize debugging information in *.debug and foo/*.debug +files, optimizing each file individually in place. +.RS +$ dwz +.RE +is equivalent to \fIdwz a.out\fR command. +.SH SEE ALSO +.BR http://dwarfstd.org/doc/DWARF4.pdf +, +.BR http://dwarfstd.org/doc/DWARF5.pdf +, +.BR gdb (1). +.SH AUTHORS +Jakub Jelinek <jakub@redhat.com>, +Tom de Vries <tdevries@suse.de>, +Mark Wielaard <mark@klomp.org> +.SH BUGS +Use the Bugzilla link of the project web page or our mailing list. +.nh +.BR https://sourceware.org/dwz/ ", " <dwz@sourceware.org> . @@ -0,0 +1,16872 @@ +/* Copyright (C) 2001-2021 Red Hat, Inc. + Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2019-2021 SUSE LLC. + Written by Jakub Jelinek <jakub@redhat.com>, 2012. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + 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 + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <setjmp.h> +#include <string.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <inttypes.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/times.h> +#include <sys/wait.h> + +#include <obstack.h> + +#include <gelf.h> +#include <xxhash.h> + +#include "dwarf2.h" +#include "hashtab.h" +#include "sha1.h" +#include "args.h" +#include "util.h" +#include "pool.h" + +#ifndef SHF_COMPRESSED + /* Glibc elf.h contains SHF_COMPRESSED starting v2.22. Libelf libelf.h has + a fallback definition starting v0.166. Define a fallback definition here + for the case of both pre-v2.22 glibc and pre-v0.166 libelf. */ +# define SHF_COMPRESSED (1 << 11) /* Section with compressed data. */ +#endif + +/* Theory of operation: + The DWZ tool can either optimize debug sections of a single + executable or shared library at a time, or, when -m option + is used, optimize debug sections even in between different + executables or shared libraries by constructing a new ET_REL + ELF object containing debug sections to which the individual + debug sections in the various executables or shared libraries + can refer. As debug info can be pretty large, the multifile + optimization is done in several phases in order to decrease + memory usage of the tool, which can still be quite high. + + The dwz () function optimizes a single file, and at the end, + after processing that single file, it frees all allocated memory. + Without -m, the dwz () function is called once on each argument. + + When -m has been passed, the dwz () function is first called on + each argument, and during it in addition to performing the same + optimizations as dwz tool does without -m it also may append + data to several temporary files (one for each .debug_* section + that is needed for the multifile optimization). During + preparation of the additions to the temporary files (write_multifile + function), wr_multifile flag is true. + + Next phase (optimize_multifile) is that all these temporary files + are mmapped, it is determined what DIEs, strings, .debug_macro + sequences etc. might be beneficial to have in the common debug + sections and finally a new ET_REL ELF object is written. During + this phase the op_multifile flag is true. This is often very + memory consuming phase, so extra hacks are used to decrease + the memory usage during it. + + Next phase (read_multifile) is where the previously written ET_REL + ELF object is parsed again and needed hash tables and other data + structures filled in. The rd_multifile flag is set during this + phase. The reason why this phase is separate from the previous one, + as opposed to just populating the hash tables right away in + optimize_multifile, is that the memory consumption during that phase + can be very big and keeping malloced data around would mean the address + space would be unnecessarily fragmented. read_multifile usually needs + to allocate only small fragment of the memory optimize_multifile + needs, on the other side that memory needs to be kept around until + the end of the last phase. + + During the last phase, the dwz () function is called second + time on each argument, with fi_multifile flag set. During this + phase duplicates in the common debug sections are referenced + from the local debug sections where possible. + + If some executable or shared library has too large debug information + (number of DIEs in .debug_info section) that there would be + risk of too high memory consumption, that file isn't multifile + optimized, instead it is processed by dwz () in a low-memory mode + with low_mem flag set. This can decrease memory consumption to + half in some very large cases. */ + +#ifndef NT_GNU_BUILD_ID +# define NT_GNU_BUILD_ID 3 +#endif + +/* xxHash state object. Init in main. */ +static XXH64_state_t *state; + +/* Clear xxHash state to zero. */ +#define hash_init_state() XXH64_reset(state, 0) + +/* Update hash STATE with VALUE. */ +#define hash_update_state_object(value) XXH64_update(state, &value, sizeof value) + +/* Update hash STATE with OBJECT that has a provided SIZE. */ +#define hash_update_state(object, size) XXH64_update(state, object, size) + +/* Get digest once we are done with a state. */ +#define hash_digest() XXH64_digest(state) + +/* Shorthand for hashing something with an intrinsic size. */ +#define hash(IN,LEN) XXH64(IN, LEN, 0) +#define iterative_hash(IN,LEN,INIT) XXH64(IN, LEN, INIT) +#define iterative_hash_object(OB,INIT) iterative_hash (&OB, sizeof (OB), INIT) + +/* Print memory amount M (in kb) in both exact and human readable, like so: + 1382508 (1.3G). */ +static void +print_mem (long m) +{ + float h = m; + int level = 0; + const char *unit[] = { "K", "M", "G"}; + while (h > 1024 && level <= 2) + { + h = h / 1024; + level++; + } + fprintf (stderr, "%ld (%.1f%s)\n", m, h, unit[level]); +} + +static void +report_progress (void) +{ + static struct tms current; + static struct tms prev; + static bool first = true; + static long ticks_per_second = 0; + + if (!first) + prev = current; + + times (¤t); + + if (first) + { + ticks_per_second = sysconf (_SC_CLK_TCK); + first = false; + return; + } + + clock_t user = current.tms_utime - prev.tms_utime; + clock_t sys = current.tms_stime - prev.tms_stime; + fprintf (stderr, "user: %.2f\n", (float)user / (float)ticks_per_second); + fprintf (stderr, "sys : %.2f\n", (float)sys / (float)ticks_per_second); + + if (progress_mem_p) + { + FILE *s = fopen ("/proc/self/status", "r"); + char *p; + bool print_next = false; + for (p = NULL; fscanf (s, "%ms", &p) && p != NULL; free (p)) + { + if (print_next) + { + long mem = strtol (p, NULL, 10); + print_mem (mem); + print_next = false; + continue; + } + + if (!(p[0] == 'V' && p[1] == 'm')) + continue; + + if (strcmp (&p[2], "Peak:") == 0) + fprintf (stderr, "VM Peak: "); + else if (strcmp (&p[2], "Size:") == 0) + fprintf (stderr, "VM Current: "); + else if (strcmp (&p[2], "HWM:") == 0) + fprintf (stderr, "RSS Peak: "); + else if (strcmp (&p[2], "RSS:") == 0) + fprintf (stderr, "RSS Current: "); + else + continue; + + print_next = true; + } + fclose (s); + } +} + +#define obstack_chunk_alloc malloc +#define obstack_chunk_free free + +/* Where to longjmp on OOM. */ +static jmp_buf oom_buf; + +/* Handle OOM situation. If handling more than one file, we might + just fail to handle some large file due to OOM, but could very well + handle other smaller files after it. */ +void +dwz_oom (void) +{ + longjmp (oom_buf, 1); +} + +/* General obstack for struct dw_cu, dw_die, also used for temporary + vectors. */ +static struct obstack ob; +/* Short lived obstack, global only to free it on allocation failures. */ +static struct obstack ob2; + +/* After read_multifile ob and ob2 are moved over to these variables + and restored during final cleanup. */ +static struct obstack alt_ob, alt_ob2; + +static bool odr_active_p = false; + +/* Struct to gather statistics. */ +struct stats +{ + const char *file; + unsigned int root_cnt; + unsigned int namespace_cnt; + unsigned int lower_toplevel; + unsigned int die_count; + unsigned int lower_toplevel_with_checksum; + unsigned int dup_cnt; + unsigned int dup_chain_cnt; + unsigned int dup_chain_max_length; + unsigned int part_cnt; + unsigned int pu_ph1_cnt; + unsigned int pu_ph2_cnt; + unsigned int pu_toplevel_die_cnt; +}; +static struct stats *stats; + +/* Initialize stats struct. */ +static void +init_stats (const char *file) +{ + if (stats == NULL) + stats = (struct stats *)malloc (sizeof (*stats)); + memset (stats, 0, sizeof (*stats)); + stats->file = file; +} + +/* Print stats struct, parsing statistics. */ +static void +print_parse_stats (void) +{ + if (stats == NULL || stats->file == NULL) + return; + + fprintf (stderr, "Parse statistics for %s\n", stats->file); + + fprintf (stderr, "root_count : %10u\n", + stats->root_cnt); + fprintf (stderr, "namespace_count : %10u\n", + stats->namespace_cnt); + unsigned int upper_toplevel = stats->root_cnt + stats->namespace_cnt; + fprintf (stderr, "upper_toplevel : %10u\n", + upper_toplevel); + unsigned lower_toplevel + = stats->lower_toplevel + stats->lower_toplevel_with_checksum; + fprintf (stderr, "lower_toplevel : %10u\n", + lower_toplevel); + unsigned int toplevel = upper_toplevel + lower_toplevel; + fprintf (stderr, "toplevel : %10u\n", + toplevel); + unsigned non_toplevel = stats->die_count - toplevel; + fprintf (stderr, "non_toplevel : %10u\n", + non_toplevel); + fprintf (stderr, "die_count : %10u\n", + stats->die_count); +} + +/* Print stats struct, dups statistics. */ +static void +print_dups_stats (void) +{ + if (stats == NULL || stats->file == NULL) + return; + + fprintf (stderr, "Duplicate statistics for %s\n", stats->file); + + fprintf (stderr, "lower_toplevel with checksum : %10u\n", + stats->lower_toplevel_with_checksum); + fprintf (stderr, "dup_cnt : %10u\n", + stats->dup_cnt); + fprintf (stderr, "dup_chain_cnt : %10u\n", + stats->dup_chain_cnt); + fprintf (stderr, "average dup_chain length : %10.2f\n", + (double)stats->dup_cnt / (double)stats->dup_chain_cnt); + fprintf (stderr, "max dup_chain length : %10u\n", + stats->dup_chain_max_length); +} + +static void +print_part_stats (void) +{ + if (stats == NULL || stats->file == NULL) + return; + + fprintf (stderr, "Partition statistics for %s\n", stats->file); + + fprintf (stderr, "part_cnt : %10u\n", stats->part_cnt); + fprintf (stderr, "pu_ph1_cnt : %10u\n", + stats->pu_ph1_cnt); + fprintf (stderr, "pu_ph2_cnt : %10u\n", + stats->pu_ph2_cnt); + fprintf (stderr, "pu_cnt : %10u\n", + stats->pu_ph1_cnt + stats->pu_ph2_cnt); + fprintf (stderr, "pu_toplevel_die_cnt : %10u\n", + stats->pu_toplevel_die_cnt); +} + +typedef struct +{ + Elf *elf; + GElf_Ehdr ehdr; + Elf_Scn **scn; + const char *filename; + int lastscn; + GElf_Shdr shdr[0]; +} DSO; + +/* Macro to parse an uleb128 number, return it and + update ptr to the end of the uleb128 at the same time. */ +#define read_uleb128(ptr) ({ \ + uint64_t ret = 0; \ + uint64_t c; \ + int shift = 0; \ + do \ + { \ + c = *ptr++; \ + ret |= (c & 0x7f) << shift; \ + shift += 7; \ + } while (c & 0x80); \ + \ + if (shift >= 70) \ + ret = ~(uint64_t) 0; \ + ret; \ +}) + +/* Macro to parse a sleb128 number, return it and + update ptr to the end of the sleb128 at the same time. */ +#define read_sleb128(ptr) ({ \ + uint64_t ret = 0; \ + uint64_t c; \ + int shift = 0; \ + do \ + { \ + c = *ptr++; \ + ret |= (c & 0x7f) << shift; \ + shift += 7; \ + } while (c & 0x80); \ + \ + if (shift >= 70) \ + ret = ~(uint64_t) 0; \ + else if (c & 0x40) \ + ret |= (-(uint64_t) 1) << shift; \ + ret; \ +}) + +/* Macro to store an uleb128 number to ptr and update + ptr to point after it. */ +#define write_uleb128(ptr, val) \ + do \ + { \ + uint64_t valv = (val); \ + do \ + { \ + unsigned char c = valv & 0x7f;\ + valv >>= 7; \ + if (valv) \ + c |= 0x80; \ + *ptr++ = c; \ + } \ + while (valv); \ + } \ + while (0) + +#define write_sleb128(ptr, val) \ + do \ + { \ + int64_t valv = (val); \ + bool more; \ + do \ + { \ + unsigned char c = valv & 0x7f; \ + valv >>= 7; \ + more = ((valv != 0 || (c & 0x40) != 0) \ + && (valv != -1 || (c & 0x40) == 0)); \ + if (more) \ + c |= 0x80; \ + *ptr++ = c; \ + } \ + while (more); \ + } \ + while (0) + +/* Macro to skip a uleb128 or sleb128 number and update ptr to the end of the + number. */ +#define skip_leb128(ptr) \ + do {} while ((*ptr++) & 0x80) + +/* Macro to parse a uint16_t value represented using form, return it and + update ptr to the end of the value at the same time. If the value doesn't + fit, assign true to error_p. */ +#define read_u16(ptr, form, error_p) \ + ({ \ + uint16_t ret = 0; \ + switch (form) \ + { \ + case DW_FORM_data1: \ + ret = read_8 (ptr); \ + break; \ + case DW_FORM_data2: \ + ret = read_16 (ptr); \ + break; \ + case DW_FORM_data4: \ + { \ + uint32_t res = read_32 (ptr); \ + ret = (uint16_t)res; \ + if ((uint32_t)ret != res) \ + error_p = true; \ + break; \ + } \ + case DW_FORM_data8: \ + { \ + uint64_t res = read_64 (ptr); \ + ret = (uint16_t)res; \ + if ((uint64_t)ret != res) \ + error_p = true; \ + break; \ + } \ + case DW_FORM_udata: \ + { \ + uint64_t res = read_uleb128 (ptr); \ + ret = (uint16_t)res; \ + if ((uint64_t)ret != res) \ + error_p = true; \ + break; \ + } \ + case DW_FORM_sdata: \ + { \ + union { \ + uint64_t u; \ + int64_t i; \ + } res; \ + res.u = read_sleb128 (ptr); \ + ret = (uint16_t)res.u; \ + if (res.i < 0 || (uint64_t)ret != res.u) \ + error_p = true; \ + break; \ + } \ + default: \ + error_p = true; \ + break; \ + } \ + ret; \ + }) + +/* Pointer size in the debug info, in bytes. Only debug info + with a single pointer size are handled. */ +static int ptr_size; + +/* Lowest debug_line version we have seen. When writing out the multi + file .debug_line we'll only use a DWARF5 version when there is no + lower line table seen (since the debug_line dir and file table is + shared between all CUs). */ +static unsigned int lowest_line_version = 5; + +/* Utility functions and macros for reading/writing values in + given ELF endianity, which might be different from host endianity. + No specific alignment is expected. */ +static uint16_t (*do_read_16) (unsigned char *ptr); +static uint32_t (*do_read_32) (unsigned char *ptr); +static uint64_t (*do_read_64) (unsigned char *ptr); +static void (*do_write_16) (unsigned char *ptr, unsigned short val); +static void (*do_write_32) (unsigned char *ptr, unsigned int val); +static void (*do_write_64) (unsigned char *ptr, uint64_t val); + +static inline uint16_t +buf_read_ule16 (unsigned char *data) +{ + return data[0] | (data[1] << 8); +} + +static inline uint16_t +buf_read_ube16 (unsigned char *data) +{ + return data[1] | (data[0] << 8); +} + +static inline uint32_t +buf_read_ule32 (unsigned char *data) +{ + return (data[0] | (data[1] << 8) | (data[2] << 16) + | ((unsigned int)data[3] << 24)); +} + +static inline uint32_t +buf_read_ube32 (unsigned char *data) +{ + return (data[3] | (data[2] << 8) | (data[1] << 16) + | ((unsigned int)data[0] << 24)); +} + +static inline uint64_t +buf_read_ule64 (unsigned char *data) +{ + return buf_read_ule32 (data) + | (((uint64_t) buf_read_ule32 (data + 4)) << 32); +} + +static inline uint64_t +buf_read_ube64 (unsigned char *data) +{ + return (((uint64_t) buf_read_ube32 (data)) << 32) + | buf_read_ube32 (data + 4); +} + +#define read_8(ptr) *ptr++ + +#define read_16(ptr) ({ \ + uint16_t ret = do_read_16 (ptr); \ + ptr += 2; \ + ret; \ +}) + +#define read_32(ptr) ({ \ + uint32_t ret = do_read_32 (ptr); \ + ptr += 4; \ + ret; \ +}) + +#define read_64(ptr) ({ \ + uint64_t ret = do_read_64 (ptr); \ + ptr += 8; \ + ret; \ +}) + +#define write_8(ptr, val) \ + do \ + *ptr++ = (val); \ + while (0) + +#define write_16(ptr, val) \ + do \ + { \ + do_write_16 (ptr, val); \ + ptr += 2; \ + } \ + while (0) + +#define write_32(ptr, val) \ + do \ + { \ + do_write_32 (ptr, val); \ + ptr += 4; \ + } \ + while (0) + +#define write_64(ptr, val) \ + do \ + { \ + do_write_64 (ptr, val); \ + ptr += 8; \ + } \ + while (0) + +static uint64_t +read_size (unsigned char *p, int size) +{ + switch (size) + { + case 1: return read_8 (p); + case 2: return read_16 (p); + case 4: return read_32 (p); + case 8: return read_64 (p); + default: abort (); + } +} + +static void +write_size (unsigned char *p, int size, uint64_t val) +{ + switch (size) + { + case 1: write_8 (p, val); break; + case 2: write_16 (p, val); break; + case 4: write_32 (p, val); break; + case 8: write_64 (p, val); break; + default: abort (); + } +} + +static void +buf_write_le16 (unsigned char *p, unsigned short v) +{ + p[0] = v; + p[1] = v >> 8; +} + +static void +buf_write_be16 (unsigned char *p, unsigned short v) +{ + p[1] = v; + p[0] = v >> 8; +} + +static void +buf_write_le32 (unsigned char *p, unsigned int v) +{ + p[0] = v; + p[1] = v >> 8; + p[2] = v >> 16; + p[3] = v >> 24; +} + +static void +buf_write_be32 (unsigned char *p, unsigned int v) +{ + p[3] = v; + p[2] = v >> 8; + p[1] = v >> 16; + p[0] = v >> 24; +} + +static void +buf_write_le64 (unsigned char *data, uint64_t v) +{ + buf_write_le32 (data, v); + buf_write_le32 (data + 4, v >> 32); +} + +static void +buf_write_be64 (unsigned char *data, uint64_t v) +{ + buf_write_be32 (data, v >> 32); + buf_write_be32 (data + 4, v); +} + +/* Return a DW_FORM_* name. */ +static const char * +get_DW_FORM_str (unsigned int form) +{ + const char *name = get_DW_FORM_name (form); + static char buf[9 + 3 * sizeof (int)]; + if (name) + return name; + sprintf (buf, "DW_FORM_%u", form); + return buf; +} + +/* Return a DW_OP_* name. */ +static const char * +get_DW_OP_str (unsigned int op) +{ + const char *name = get_DW_OP_name (op); + static char buf[7 + 3 * sizeof (int)]; + if (name) + return name; + sprintf (buf, "DW_OP_%u", op); + return buf; +} + +/* Return a DW_AT_* name. */ +static const char * +get_DW_AT_str (unsigned int at) +{ + const char *name = get_DW_AT_name (at); + static char buf[7 + 3 * sizeof (int)]; + if (name) + return name; + sprintf (buf, "DW_AT_%u", at); + return buf; +} + +/* Return a DW_UT_* name. */ +static const char * +get_DW_UT_str (unsigned int ut) +{ + const char *name = get_DW_UT_name (ut); + static char buf[7 + 3 * sizeof (int)]; + if (name) + return name; + sprintf (buf, "DW_UT_%u", ut); + return buf; +} + +/* Retrun a DW_LNCT_* name. */ +static const char * +get_DW_LNCT_str (unsigned int lnct) +{ + const char *name; + static char buf[9 + 3 * sizeof (int)]; + switch (lnct) + { + case DW_LNCT_path: name = "DW_LNCT_path"; break; + case DW_LNCT_directory_index: name = "DW_LNCT_directory_index"; break; + case DW_LNCT_timestamp: name = "DW_LNCT_timestamp"; break; + case DW_LNCT_size: name = "DW_LNCT_size"; break; + case DW_LNCT_MD5: name = "DW_LNCT_MD5"; break; + + default: name = 0; break; + } + if (name) + return name; + sprintf (buf, "DW_LNCT_%u", lnct); + return buf; +} + +/* This must match the debug_sections array content + below. */ +enum debug_section_kind +{ + DEBUG_INFO, + DEBUG_ABBREV, + DEBUG_LINE, + DEBUG_STR, + DEBUG_MACRO, + DEBUG_TYPES, + DEBUG_ARANGES, + DEBUG_PUBNAMES, + DEBUG_PUBTYPES, + DEBUG_GNU_PUBNAMES, + DEBUG_GNU_PUBTYPES, + DEBUG_MACINFO, + DEBUG_LOC, + DEBUG_LOCLISTS, + DEBUG_FRAME, + DEBUG_RANGES, + DEBUG_RNGLISTS, + DEBUG_LINE_STR, + DEBUG_SUP, + DEBUG_GDB_SCRIPTS, + GDB_INDEX, + GNU_DEBUGALTLINK, + SECTION_COUNT, + SAVED_SECTIONS = DEBUG_TYPES + 1 +}; + +/* Details about standard DWARF sections. */ +static struct +{ + const char *name; + unsigned char *data; + unsigned char *new_data; + size_t size; + size_t new_size; + int sec; +} debug_sections[] = + { + { ".debug_info", NULL, NULL, 0, 0, 0 }, + { ".debug_abbrev", NULL, NULL, 0, 0, 0 }, + { ".debug_line", NULL, NULL, 0, 0, 0 }, + { ".debug_str", NULL, NULL, 0, 0, 0 }, + { ".debug_macro", NULL, NULL, 0, 0, 0 }, + { ".debug_types", NULL, NULL, 0, 0, 0 }, + { ".debug_aranges", NULL, NULL, 0, 0, 0 }, + { ".debug_pubnames", NULL, NULL, 0, 0, 0 }, + { ".debug_pubtypes", NULL, NULL, 0, 0, 0 }, + { ".debug_gnu_pubnames", NULL, NULL, 0, 0, 0 }, + { ".debug_gnu_pubtypes", NULL, NULL, 0, 0, 0 }, + { ".debug_macinfo", NULL, NULL, 0, 0, 0 }, + { ".debug_loc", NULL, NULL, 0, 0, 0 }, + { ".debug_loclists", NULL, NULL, 0, 0, 0 }, + { ".debug_frame", NULL, NULL, 0, 0, 0 }, + { ".debug_ranges", NULL, NULL, 0, 0, 0 }, + { ".debug_rnglists", NULL, NULL, 0, 0, 0 }, + { ".debug_line_str", NULL, NULL, 0, 0, 0 }, + { ".debug_sup", NULL, NULL, 0, 0, 0 }, + { ".debug_gdb_scripts", NULL, NULL, 0, 0, 0 }, + { ".gdb_index", NULL, NULL, 0, 0, 0 }, + { ".gnu_debugaltlink", NULL, NULL, 0, 0, 0 }, + { NULL, NULL, NULL, 0, 0, 0 } + }; + +/* Copies of .new_data fields during write_multifile. */ +static unsigned char *saved_new_data[SAVED_SECTIONS]; +/* Copies of .new_size fields during write_multifile. */ +static size_t saved_new_size[SAVED_SECTIONS]; + +/* Copies of .data fields after read_multifile. */ +static unsigned char *alt_data[SAVED_SECTIONS]; +/* Copies of .size fields after read_multifile. */ +static size_t alt_size[SAVED_SECTIONS]; + +/* How many bytes of each of /tmp/dwz.debug_*.XXXXXX have we written + already. */ +static unsigned int multi_info_off, multi_abbrev_off; +static unsigned int multi_line_off, multi_str_off; +static unsigned int multi_macro_off; +/* And corresponding file descriptors. */ +static int multi_info_fd = -1, multi_abbrev_fd = -1; +static int multi_line_fd = -1, multi_str_fd = -1; +static int multi_macro_fd = -1; + +/* Copy of one of the input file's ehdr. */ +static GElf_Ehdr multi_ehdr; + +/* Pointer size of all debug info sources accumulated during + write_multifile. */ +static int multi_ptr_size; +/* And their endianity. */ +static int multi_endian; +/* Highest .gdb_index version seen. */ +static unsigned int multi_gdb_index_ver; + +enum multifile_mode_kind +{ + MULTIFILE_MODE_WR = 1, + MULTIFILE_MODE_OP = 2, + MULTIFILE_MODE_RD = 4, + MULTIFILE_MODE_FI = 8, + MULTIFILE_MODE_LOW_MEM = 16 +}; + +/* True while in write_multifile. */ +#define wr_multifile (multifile_mode & MULTIFILE_MODE_WR) + +/* True while in optimize_multifile. */ +#define op_multifile (multifile_mode & MULTIFILE_MODE_OP) + +/* True while in read_multifile. */ +#define rd_multifile (multifile_mode & MULTIFILE_MODE_RD) + +/* True while in finalize_multifile. */ +#define fi_multifile (multifile_mode & MULTIFILE_MODE_FI) + +/* True if running in low_mem mode. */ +#define low_mem (multifile_mode & MULTIFILE_MODE_LOW_MEM) + +/* SHA1 checksum (build-id) of the common file. */ +static unsigned char multifile_sha1[0x14]; + +/* A single attribute in abbreviations. */ +struct abbrev_attr +{ + /* DW_AT_* attribute code. */ + unsigned int attr; + /* DW_FORM_* form code. */ + unsigned int form; +}; + +/* Internal structure for .debug_abbrev entries. */ +struct abbrev_tag +{ + /* Abbreviation number. */ + unsigned int entry; + /* Hash value, in first abbrev hash tables it is + the same as entry, in cu->cu_new_abbrev hash tables + iterative hash of all the relevant values. */ + hashval_t hash; + /* DW_TAG_* code. */ + unsigned int tag; + /* Number of attributes. */ + unsigned int nattr; + /* How many DIEs refer to this abbreviation (unused + in first abbrev hash tables). */ + unsigned int nusers; + /* True if DIEs with this abbreviation have children. */ + bool children; + /* True if any typed DWARF opcodes refer to this. */ + bool op_type_referenced; + /* The values of DW_FORM_implicit_const attribute forms. */ + int64_t *values; + /* Attribute/form pairs. */ + struct abbrev_attr attr[0]; +}; + +typedef struct dw_die *dw_die_ref; +typedef struct dw_cu *dw_cu_ref; +struct import_cu; + +/* An entry from .debug_line file table. */ +struct dw_file +{ + char *dir; + char *file; + uint64_t time, size; + unsigned int file_angle_brackets_encapsulated_no_slash : 1; +}; + +/* Internal representation of a compilation (or partial) + unit. */ +struct dw_cu +{ + /* Cached entries from .debug_line file table. */ + struct dw_file *cu_files; + unsigned int cu_nfiles; + /* Kind of CU, normal (present in .debug_info), newly created + partial unit, .debug_types unit or .debug_info partial unit + from the common file. */ + enum { CU_NORMAL, CU_PU, CU_TYPES, CU_ALT } cu_kind; + /* CUs linked from first_cu through this chain. */ + dw_cu_ref cu_next; + /* Offset in original .debug_info if CU_NORMAL or .debug_types + if CU_TYPES, otherwise a unique index of the newly created + partial CU. */ + unsigned int cu_offset; + /* DWARF version of the CU. */ + unsigned int cu_version; + /* Cached DW_AT_comp_dir value from DW_TAG_*_unit cu_die, + or NULL if that attribute is not present. */ + char *cu_comp_dir; + /* Pointer to the DW_TAG_*_unit inside of the .debug_info + chunk. */ + dw_die_ref cu_die; + /* The original abbreviation hash table. */ + htab_t cu_abbrev; + /* New abbreviation hash table. */ + htab_t cu_new_abbrev; + union dw_cu_u1 + { + /* Pointer to another struct dw_cu that owns + cu_new_abbrev for this CU. */ + dw_cu_ref cu_new_abbrev_owner; + /* Pointer used during create_import_tree. */ + struct import_cu *cu_icu; + } u1; + union dw_cu_u2 + { + /* Offset into the new .debug_abbrev section. */ + unsigned int cu_new_abbrev_offset; + /* Highest ->entry value in the new abbrev table + For abbrevs computed by this tool it is always + equal to the number of abbreviations, but if + abbrevs are read for .debug_types section which + is not rewritten, there might be holes. */ + unsigned int cu_largest_entry; + } u2; + /* Offset into the new .debug_info section. */ + unsigned int cu_new_offset; + /* When op_multifile, record which object this came from here, + otherwise it is the index of the CU. */ + unsigned int cu_chunk; + /* Form chosen for intra-cu references. */ + enum dwarf_form cu_intracu_form; + /* Intracusize argument to init_new_die_offsets. Set in compute_abbrevs, + used in recompute_abbrevs. */ + unsigned int initial_intracusize; + enum dwarf_source_language lang; +}; + +/* Internal representation of a debugging information entry (DIE). + This structure should be kept as small as possible, + there are .debug_info sections with tens of millions of DIEs + in them and this structure is allocated for each of them. */ +struct dw_die +{ + /* Offset in old .debug_info from the start of the .debug_info section, + -1U for newly created DIEs. */ + unsigned int die_offset; + /* Cached copy of die_abbrev->tag. */ + enum dwarf_tag die_tag : 16; + /* State of checksum computation. Not computed yet, computed and + suitable for moving into partial units, currently being computed + and finally determined unsuitable for moving into partial units. */ + enum { CK_UNKNOWN, CK_KNOWN, CK_BEING_COMPUTED, CK_BAD } die_ck_state : 2; + /* Set if any DW_OP_call2 opcode refers to this DIE. */ + unsigned int die_op_call2_referenced : 1; + /* Set if any DW_OP_GNU_{{regval,deref,const}_type,convert,reinterpret} + opcode refers to this DIE. Only DW_TAG_base_type DIEs should be + referenced. As those opcodes refer to them using uleb128, we need to try + hard to have those DIEs with low enough offsets that the uleb128 will + fit. */ + unsigned int die_op_type_referenced : 1; + /* Set in DW_TAG_namespace or DW_TAG_module with DW_AT_name that is + either a child of DW_TAG_*_unit, or a child of another + die_named_namespace DIE. */ + unsigned int die_named_namespace : 1; + /* Set if u.p1.die_ref_hash is valid. */ + unsigned int die_ref_hash_computed : 1; + /* Set if die_dup and die_nextdup fields are after this structure. + True for DW_TAG_*_unit DIEs, die_named_namespace DIEs and their + immediate children. */ + unsigned int die_toplevel : 1; + /* Set if we want to remove this DIE from its containing CU. */ + unsigned int die_remove : 1; + /* Set if DIE is unsuitable for moving into alternate .debug_info. */ + unsigned int die_no_multifile : 1; + /* Set if DIE is referenced using DW_FORM_ref*. So far only used during + optimize_multifile and low_mem. */ + unsigned int die_referenced : 1; + /* Set if DIE is referenced using DW_FORM_ref_addr. So far used only + during low_mem. */ + unsigned int die_intercu_referenced : 1; + /* Set if DIE has its children collapsed. Only used during + optimize_multifile. */ + unsigned int die_collapsed_children : 1; + /* Set on collapsed child DIE that is referenced. In that case, die_tag + is reused for die_enter difference from parent and no fields after + die_parent are allocated. */ + unsigned int die_collapsed_child : 1; + /* Set if die_parent field is reused for struct dw_cu pointer. */ + unsigned int die_root : 1; + /* State for ODR optimization. */ + enum { ODR_UNKNOWN, ODR_NONE, ODR_DEF, ODR_DECL } die_odr_state : 2; + /* Tree pointer to parent. */ + dw_die_ref die_parent; + + /* The remaining fields are present only if die_collapsed_child is + 0. */ + + /* Tree pointers, to first child and pointer to next sibling. */ + dw_die_ref die_child, die_sib; + /* Pointer to the old .debug_abbrev entry's internal representation. */ + struct abbrev_tag *die_abbrev; + /* Size of the DIE (abbrev number + attributes), not including children. + In later phases this holds the new size as opposed to the old one. */ + unsigned int die_size; + /* Index into the dw_die_ref vector used in checksum_ref_die function. + While this is only phase 1 field, we get better packing by having it + here instead of u.p1. */ + unsigned int die_ref_seen; + union dw_die_phase + { + /* Fields used in the first phase (read_debug_info and partition_dups + and functions they call). */ + struct dw_die_p1 + { + /* Iterative hash value of the tag, attributes other than + references or DW_FORM_ref_addr references or references + within the subtree of ultimate parent's die_toplevel DIE's + children. Computed by checksum_die function. */ + hashval_t die_hash; + /* Iterative hash of other references. Computed by + checksum_ref_die function. */ + hashval_t die_ref_hash; + /* For ODR phase 1, we change die_hash for ODR_DEF and ODR_DECL DIEs + to only hash in the tag and the name, to be able to construct + maximal duplicate chains. But during ODR phase 2, we want to + compare ODR_DEF DIEs in the normal way, for which we need the + unchanged die_hash, which we store here in die_hash2. */ + hashval_t die_hash2; + /* Tick count of entering and leaving a DIE during depth first + traversal of the CU, used to quickly find if a subtree is + referenced. */ + unsigned int die_enter, die_exit; + } p1; + /* Fields used only after the first phase (from compute_abbrevs + till the end). */ + struct dw_die_p2 + { + /* Pointer to internal representation of new .debug_abbrev + entry for this DIE. */ + struct abbrev_tag *die_new_abbrev; + /* Offset within the new .debug_info CU. Unlike die_offset + this one is CU relative, so die_cu (die)->cu_new_offset needs + to be added to it to get .debug_info offset. */ + unsigned int die_new_offset; + /* Used during compute_abbrevs DW_FORM_ref_udata optimization. */ + unsigned int die_intracu_udata_size; + } p2; + } u; + + /* The remaining fields are present only if die_toplevel is + 1. */ + + /* Pointer to a duplicate DIE. */ + dw_die_ref die_dup; + /* Chain of duplicate DIEs. If die_dup is NULL, but die_nextdup + is non-NULL, this is the reference DIE of the duplicates. + All DIEs in the die->nextdup linked list have die_dup pointing + to this node. The reference DIE is initially just a DIE in the + lowest CU that has the matching DIE, later on it is a DIE in + the newly created partial unit CU. */ + dw_die_ref die_nextdup; +}; + +#include "iterators.h" + +/* Return CU structure pointer for a DIE. In order to save memory, + individual DIEs don't have a dw_cu_ref field, and the pointer can + be only found by overriding the die_parent pointer in a + DW_TAG_{compile,partial}_unit descriptor, which has no parent. */ +static inline dw_cu_ref +die_cu (dw_die_ref die) +{ + while (!die->die_root) + die = die->die_parent; + return (dw_cu_ref) die->die_parent; +} + +/* Given a toplevel die DIE, return the first (that is, the reference die) in + the duplicate chain. */ +#define first_dup(die) \ + (die->die_dup \ + ? die->die_dup \ + : (die->die_nextdup \ + ? die \ + : NULL)) + +/* Safe variant that check die_toplevel. Can't be used on LHS. */ +#define die_safe_dup(die) \ + ((die)->die_toplevel ? (die)->die_dup : (dw_die_ref) NULL) +#define die_safe_nextdup(die) \ + ((die)->die_toplevel ? (die)->die_nextdup : (dw_die_ref) NULL) + +ALIGN_STRUCT (abbrev_tag) +ALIGN_STRUCT (dw_file) +ALIGN_STRUCT (dw_cu) +ALIGN_STRUCT (dw_die) + + +/* After read_multifile, pool variable is moved over to this variable + as the pool from read_multifile needs to be around for subsequent dwz + calls. Freed only during the final cleanup at the very end. */ +static unsigned char *alt_pool; + +static struct abbrev_tag * +pool_clone_abbrev (struct abbrev_tag *t) +{ + struct abbrev_tag *newt; + size_t newt_size; + unsigned nvalue = 0; + if (t->values != NULL) + { + unsigned i; + for (i = 0; i < t->nattr; i++) + if (t->attr[i].form == DW_FORM_implicit_const) + nvalue = i + 1; + } + newt_size = (sizeof (*newt) + + t->nattr * sizeof (struct abbrev_attr) + + nvalue * sizeof (int64_t)); + newt = pool_alloc (abbrev_tag, newt_size); + memcpy (newt, t, newt_size - (nvalue * sizeof (int64_t))); + if (nvalue == 0) + newt->values = NULL; + else + { + newt->values = (int64_t *) &newt->attr[newt->nattr]; + memcpy (newt->values, t->values, nvalue * sizeof (int64_t)); + } + return newt; +} + +/* Hash function in first abbrev hash table as well as cu->cu_new_abbrev + htab. */ +static hashval_t +abbrev_hash (const void *p) +{ + struct abbrev_tag *t = (struct abbrev_tag *)p; + + return t->hash; +} + +/* Equality function in first abbrev htab. */ +static int +abbrev_eq (const void *p, const void *q) +{ + struct abbrev_tag *t1 = (struct abbrev_tag *)p; + struct abbrev_tag *t2 = (struct abbrev_tag *)q; + + return t1->entry == t2->entry; +} + +/* Equality function in cu->cu_new_abbrev htab. */ +static int +abbrev_eq2 (const void *p, const void *q) +{ + struct abbrev_tag *t1 = (struct abbrev_tag *)p; + struct abbrev_tag *t2 = (struct abbrev_tag *)q; + unsigned int i; + + if (t1->hash != t2->hash + || t1->tag != t2->tag + || t1->nattr != t2->nattr + || t1->children != t2->children) + return 0; + for (i = 0; i < t1->nattr; i++) + if (t1->attr[i].attr != t2->attr[i].attr + || t1->attr[i].form != t2->attr[i].form + || (t1->attr[i].form == DW_FORM_implicit_const + && t1->values[i] != t2->values[i])) + return 0; + return 1; +} + +/* Helper function to compute abbrev entry iterative hash value. */ +static void +compute_abbrev_hash (struct abbrev_tag *t) +{ + unsigned int i; + + hash_init_state (); + hash_update_state_object (t->tag); + hash_update_state_object (t->nattr); + hash_update_state_object (t->children); + for (i = 0; i < t->nattr; i++) + { + hash_update_state_object (t->attr[i].attr); + hash_update_state_object (t->attr[i].form); + if (t->attr[i].form == DW_FORM_implicit_const) + hash_update_state_object (t->values[i]); + } + t->hash = hash_digest (); +} + +/* Maximum number of attributes in a DIE. */ +static unsigned int max_nattr; + +/* Parse a .debug_abbrev entry at PTR. */ +static htab_t +read_abbrev (DSO *dso, unsigned char *ptr) +{ + htab_t h; + unsigned int attr, form; + struct abbrev_tag *t; + void **slot; + + h = htab_try_create (50, abbrev_hash, abbrev_eq, NULL); + if (h == NULL) + dwz_oom (); + + while ((attr = read_uleb128 (ptr)) != 0) + { + int highest_implicit_value_ndx = -1; + unsigned int nattr = 0; + unsigned char *p = ptr; + + skip_leb128 (p); + p++; + while (read_uleb128 (p) != 0) + { + nattr++; + form = read_uleb128 (p); + if (form == DW_FORM_implicit_const) + { + skip_leb128 (p); + highest_implicit_value_ndx = nattr - 1; + } + else if (form == 2 + || (form > DW_FORM_flag_present + && !(form == DW_FORM_ref_sig8 + || form == DW_FORM_data16 + || form == DW_FORM_line_strp))) + { + error (0, 0, "%s: Unknown DWARF %s at .debug_abbrev [%zd]", + dso->filename, get_DW_FORM_str (form), + p - debug_sections[DEBUG_ABBREV].data); + htab_delete (h); + return NULL; + } + } + if (read_uleb128 (p) != 0) + { + error (0, 0, "%s: DWARF abbreviation does not end with 2 zeros", + dso->filename); + htab_delete (h); + return NULL; + } + + t = pool_alloc (abbrev_tag, + sizeof (*t) + nattr * sizeof (struct abbrev_attr) + + sizeof (int64_t) * (highest_implicit_value_ndx + 1)); + t->entry = attr; + t->hash = attr; + t->nattr = nattr; + t->nusers = 0; + t->tag = read_uleb128 (ptr); + t->children = *ptr++ == DW_CHILDREN_yes; + t->op_type_referenced = false; + t->values = (highest_implicit_value_ndx >= 0 + ? (int64_t *) &t->attr[nattr] : NULL); + nattr = 0; + while ((attr = read_uleb128 (ptr)) != 0) + { + form = read_uleb128 (ptr); + if (form == DW_FORM_implicit_const) + t->values[nattr] = read_sleb128 (ptr); + t->attr[nattr].attr = attr; + t->attr[nattr++].form = form; + } + skip_leb128 (ptr); + if (t->nattr > max_nattr) + max_nattr = t->nattr; + slot = htab_find_slot_with_hash (h, t, t->hash, INSERT); + if (slot == NULL) + { + htab_delete (h); + dwz_oom (); + } + if (*slot != NULL) + { + error (0, 0, "%s: Duplicate DWARF abbreviation %d", dso->filename, + t->entry); + htab_delete (h); + return NULL; + } + *slot = t; + } + + return h; +} + +/* For a die attribute with form FORM starting at PTR, with the die in CU, + return the pointer after the attribute, assuming FORM is not + dw_form_indirect. */ +static inline unsigned char * FORCE_INLINE +skip_attr_no_dw_form_indirect (unsigned int cu_version, uint32_t form, + unsigned char *ptr) +{ + size_t len = 0; + + switch (form) + { + case DW_FORM_ref_addr: + ptr += cu_version == 2 ? ptr_size : 4; + break; + case DW_FORM_addr: + ptr += ptr_size; + break; + case DW_FORM_flag_present: + case DW_FORM_implicit_const: + break; + case DW_FORM_ref1: + case DW_FORM_flag: + case DW_FORM_data1: + ++ptr; + break; + case DW_FORM_ref2: + case DW_FORM_data2: + ptr += 2; + break; + case DW_FORM_ref4: + case DW_FORM_data4: + case DW_FORM_sec_offset: + case DW_FORM_strp: + case DW_FORM_line_strp: + ptr += 4; + break; + case DW_FORM_ref8: + case DW_FORM_data8: + case DW_FORM_ref_sig8: + ptr += 8; + break; + case DW_FORM_data16: + ptr += 16; + break; + case DW_FORM_sdata: + case DW_FORM_ref_udata: + case DW_FORM_udata: + skip_leb128 (ptr); + break; + case DW_FORM_string: + ptr = (unsigned char *) strchr ((char *)ptr, '\0') + 1; + break; + case DW_FORM_indirect: + abort (); + case DW_FORM_block1: + len = *ptr++; + break; + case DW_FORM_block2: + len = read_16 (ptr); + form = DW_FORM_block1; + break; + case DW_FORM_block4: + len = read_32 (ptr); + form = DW_FORM_block1; + break; + case DW_FORM_block: + case DW_FORM_exprloc: + len = read_uleb128 (ptr); + form = DW_FORM_block1; + break; + default: + abort (); + } + + if (form == DW_FORM_block1) + ptr += len; + + return ptr; +} + +/* Read the directory and file table from .debug_line offset OFF, + record it in CU. */ +static int +read_debug_line (DSO *dso, dw_cu_ref cu, uint32_t off) +{ + unsigned char *ptr = debug_sections[DEBUG_LINE].data, *dir, *file; + unsigned char **dirt; + unsigned char *endsec = ptr + debug_sections[DEBUG_LINE].size; + unsigned char *endcu, *endprol; + unsigned char opcode_base; + unsigned int culen; + uint32_t value, version, ndirs, nfiles, dirt_cnt, file_cnt; + /* DWARF5 has a dynamic table of elements in possible different + forms. But we are only interested in the known elements (path, + dir index, time, size and possibly later md5). */ + unsigned char n, nelems = 0; + int path_ndx = -1; + int dir_ndx = -1; + int time_ndx = -1; + int size_ndx = -1; + uint16_t elems[256]; + + if (off >= debug_sections[DEBUG_LINE].size - 4) + { + error (0, 0, "%s: .debug_line reference above end of section", + dso->filename); + return 1; + } + + ptr += off; + + endcu = ptr + 4; + culen = read_32 (ptr); + if (culen >= 0xfffffff0) + { + error (0, 0, "%s: 64-bit DWARF not supported", dso->filename); + return 1; + } + endcu += culen; + + if (endcu > endsec) + { + error (0, 0, "%s: .debug_line CU does not fit into section", + dso->filename); + return 1; + } + + value = read_16 (ptr); + if (value < 2 || value > 5) + { + error (0, 0, "%s: DWARF version %d in .debug_line unhandled", + dso->filename, value); + return 1; + } + version = value; + + if (version < lowest_line_version) + lowest_line_version = version; + + if (version >= 5) + { + int addr_size, seg_size; + if (ptr + 2 > endcu) + { + error (0, 0, "%s: .debug_line header too short", dso->filename); + return 1; + } + addr_size = *ptr++; + seg_size = *ptr++; + if (addr_size != ptr_size) + { + error (0, 0, "%s: .debug_line address size differs from CU ptr size", + dso->filename); + return 1; + } + if (seg_size != 0) + { + error (0, 0, "%s: .debug_line non-zero segment selector size", + dso->filename); + return 1; + } + } + + endprol = ptr + 4; + endprol += read_32 (ptr); + if (endprol > endcu) + { + error (0, 0, "%s: .debug_line CU prologue does not fit into CU", + dso->filename); + return 1; + } + + opcode_base = ptr[4 + (version >= 4)]; + ptr = dir = ptr + 4 + (version >= 4) + opcode_base; + + /* dir table: */ + if (version < 5) + { + value = 1; + while (*ptr != 0) + { + ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1; + ++value; + } + ndirs = value; + } + else + { + nelems = *ptr++; + for (n = 0; n < nelems; n++) + { + uint16_t lnct = read_uleb128 (ptr); + uint16_t form = read_uleb128 (ptr); + if (lnct == DW_LNCT_path) + { + if (path_ndx != -1) + { + error (0, 0, "%s: .debug_line duplicate dir path elements", + dso->filename); + return 1; + } + path_ndx = n; + } + else + { + error (0, 0, "%s: .debug_line unhandled dir element %s", + dso->filename, get_DW_LNCT_str (lnct)); + return 1; + } + + if (form != DW_FORM_string + && form != DW_FORM_strp + && form != DW_FORM_line_strp) + { + error (0, 0, "%s: .debug_line unhandled form %s for dir path", + dso->filename, get_DW_FORM_str (form)); + return 1; + } + + elems[n] = form; + } + + ndirs = read_uleb128 (ptr); + } + + dirt = (unsigned char **) alloca (ndirs * sizeof (unsigned char *)); + if (version < 5) + { + dirt[0] = NULL; + dirt_cnt = 1; + ptr = dir; + while (*ptr != 0) + { + dirt[dirt_cnt++] = ptr; + ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1; + } + ptr++; + } + else + { + for (dirt_cnt = 0; dirt_cnt < ndirs; dirt_cnt++) + { + for (n = 0; n < nelems; n++) + { + uint32_t form = elems[n]; + if (n == path_ndx) + { + unsigned char *d; + switch (form) + { + case DW_FORM_string: + d = (unsigned char *) ptr; + break; + case DW_FORM_strp: + { + unsigned int strp = do_read_32 (ptr); + if (strp >= debug_sections[DEBUG_STR].size) + d = NULL; + else + d = ((unsigned char *) + debug_sections[DEBUG_STR].data + + strp); + } + break; + case DW_FORM_line_strp: + { + unsigned int line_strp = do_read_32 (ptr); + if (line_strp >= debug_sections[DEBUG_LINE_STR].size) + d = NULL; + else + d = ((unsigned char *) + debug_sections[DEBUG_LINE_STR].data + + line_strp); + } + break; + default: + d = NULL; + break; + } + + if (d == NULL) + { + error (0, 0, "%s: .debug_line bad dir path", + dso->filename); + return 1; + } + + /* Note we do this even for the zero entry, which is + marked as NULL for pre-DWARF5 line tables. This + is important for when we merge file entries + together for a multifile because the zero dir + entry could differ. It is should be equivalent + to the CU DIE comp_dir attribute, but we don't + track that all CUs referring to the (same) line + table share identical an DW_AT_comp_dir value. */ + dirt[dirt_cnt] = d; + } + ptr = skip_attr_no_dw_form_indirect (cu->cu_version, form, ptr); + } + } + } + + /* file table: */ + file = ptr; + if (version < 5) + { + file_cnt = 0; + while (*ptr != 0) + { + ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1; + value = read_uleb128 (ptr); + + if (value >= dirt_cnt) + { + error (0, 0, "%s: Wrong directory table index %u", + dso->filename, value); + return 1; + } + + skip_leb128 (ptr); + skip_leb128 (ptr); + file_cnt++; + } + nfiles = file_cnt; + } + else + { + nelems = *ptr++; + path_ndx = -1; + for (n = 0; n < nelems; n++) + { + uint16_t lnct = read_uleb128 (ptr); + uint16_t form = read_uleb128 (ptr); + switch (lnct) + { + case DW_LNCT_path: + if (path_ndx != -1) + { + error (0, 0, + "%s: .debug_line duplicate file path elements", + dso->filename); + return 1; + } + path_ndx = n; + + /* Currently we only handle two string form which always + stay... */ + if (form != DW_FORM_string && form != DW_FORM_line_strp) + { + error (0, 0, + "%s: .debug_line unhandled form %s for file path", + dso->filename, get_DW_FORM_str (form)); + return 1; + } + break; + + case DW_LNCT_directory_index: + if (dir_ndx != -1) + { + error (0, 0, + "%s: .debug_line duplicate file dir elements", + dso->filename); + return 1; + } + dir_ndx = n; + + if (form != DW_FORM_data1 + && form != DW_FORM_data2 + && form != DW_FORM_udata) + { + error (0, 0, + "%s: .debug_line unhandled form %s for dir index", + dso->filename, get_DW_FORM_str (form)); + return 1; + } + break; + + case DW_LNCT_timestamp: + if (time_ndx != -1) + { + error (0, 0, + "%s: .debug_line duplicate file time elements", + dso->filename); + return 1; + } + time_ndx = n; + + if (form != DW_FORM_udata + && form != DW_FORM_data4 + && form != DW_FORM_data8) + { + error (0, 0, + "%s: .debug_line unhandled form %s for file time", + dso->filename, get_DW_FORM_str (form)); + return 1; + } + break; + + case DW_LNCT_size: + if (size_ndx != -1) + { + error (0, 0, + "%s: .debug_line duplicate file size elements", + dso->filename); + return 1; + } + size_ndx = n; + + if (form != DW_FORM_udata + && form != DW_FORM_data1 + && form != DW_FORM_data2 + && form != DW_FORM_data4 + && form != DW_FORM_data8) + { + error (0, 0, + "%s: .debug_line unhandled form %s for file size", + dso->filename, get_DW_FORM_str (form)); + return 1; + } + break; + + default: + error (0, 0, "%s: .debug_line unhandled file element %s", + dso->filename, get_DW_LNCT_str (lnct)); + return 1; + } + elems[n] = form; + } + + nfiles = read_uleb128 (ptr); + if (nfiles > 0) + nfiles--; /* We will skip the first (zero) entry. */ + } + + cu->cu_nfiles = nfiles; + cu->cu_files = pool_alloc (dw_file, nfiles * sizeof (struct dw_file)); + memset (cu->cu_files, 0, nfiles * sizeof (struct dw_file)); + + if (version < 5) + ptr = file; + + for (file_cnt = 0; file_cnt < nfiles; file_cnt++) + { + char *f = NULL; + char *end = NULL; + uint32_t d = 0; + uint64_t time = 0; + uint64_t size = 0; + if (version < 5) + { + f = (char *) ptr; + ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1; + end = (char *) ptr; + d = read_uleb128 (ptr); + time = read_uleb128 (ptr); + size = read_uleb128 (ptr); + } + else + { + /* Skip zero entry. */ + if (file_cnt == 0) + for (n = 0; n < nelems; n++) + ptr = skip_attr_no_dw_form_indirect (cu->cu_version, + elems[n], ptr); + + for (n = 0; n < nelems; n++) + { + uint32_t form = elems[n]; + if (n == path_ndx) + { + switch (form) + { + case DW_FORM_string: + f = (char *) ptr; + end = strchr ((char *)ptr, 0) + 1; + break; + case DW_FORM_strp: + { + unsigned int strp = do_read_32 (ptr); + if (strp >= debug_sections[DEBUG_STR].size) + f = NULL; + else + { + f = ((char *) debug_sections[DEBUG_STR].data + + strp); + end = f + strlen (f) + 1; + } + } + break; + case DW_FORM_line_strp: + { + unsigned int line_strp = do_read_32 (ptr); + if (line_strp >= debug_sections[DEBUG_LINE_STR].size) + f = NULL; + else + { + f = ((char *) debug_sections[DEBUG_LINE_STR].data + + line_strp); + end = f + strlen (f) + 1; + } + } + break; + default: + f = NULL; + break; + } + + if (f == NULL) + { + error (0, 0, "%s: .debug_line bad file path", + dso->filename); + return 1; + } + } + else if (n == dir_ndx) + { + switch (form) + { + case DW_FORM_data1: + d = *ptr; + break; + case DW_FORM_data2: + d = do_read_16 (ptr); + break; + case DW_FORM_udata: + { + unsigned char *p = ptr; + d = read_uleb128 (p); + } + break; + } + } + ptr = skip_attr_no_dw_form_indirect (cu->cu_version, form, ptr); + } + } + + cu->cu_files[file_cnt].file = f; + if (d >= dirt_cnt) + { + error (0, 0, "%s: Wrong directory table index %u", + dso->filename, value); + return 1; + } + + cu->cu_files[file_cnt].dir = (char *) dirt[d]; + cu->cu_files[file_cnt].time = time; + cu->cu_files[file_cnt].size = size; + size_t file_len = (char *) end - f; + size_t strlen_file = file_len - 1; + bool file_has_slash = false; + if (cu->cu_files[file_cnt].file[0] != '/' + && cu->cu_files[file_cnt].dir != NULL) + { + size_t dir_len = strlen (cu->cu_files[file_cnt].dir); + if (dir_len) + { + obstack_grow (&ob, cu->cu_files[file_cnt].dir, + dir_len); + strlen_file += dir_len; + if (cu->cu_files[file_cnt].dir[dir_len - 1] != '/') + { + obstack_1grow (&ob, '/'); + strlen_file++; + } + file_has_slash = true; + obstack_grow (&ob, cu->cu_files[file_cnt].file, + file_len); + cu->cu_files[file_cnt].file + = (char *) obstack_finish (&ob); + cu->cu_files[file_cnt].dir = NULL; + } + } + cu->cu_files[file_cnt].file_angle_brackets_encapsulated_no_slash + = (!file_has_slash + && cu->cu_files[file_cnt].file[0] == '<' + && cu->cu_files[file_cnt].file[strlen_file - 1] == '>' + && strchr (cu->cu_files[file_cnt].file, '/') == NULL); + } + + return 0; +} + +/* Estimate the amount of DIEs in the .debug_info section, based on the size + of that section. */ +static unsigned int +estimate_nr_dies (void) +{ + unsigned int average_die_size = 11; + unsigned int nr_dies = debug_sections[DEBUG_INFO].size / average_die_size; + return nr_dies; +} + +static size_t +emulate_htab (size_t initial, size_t final_nr_elements) +{ + size_t size = initial; + + /* Emulate creation. */ + size = higher_prime_number (size); + + /* Emulate growing till htab contains find_nr_elements. */ + while (1) + { + /* Emulate expansion trigger. */ + size_t nr_elements = size * 3 / 4; + while (!(size * 3 <= nr_elements * 4)) + nr_elements++; + + if (nr_elements > final_nr_elements) + { + nr_elements = final_nr_elements; + break; + } + + /* Emulate expansion. */ + size = size * 2; + size = higher_prime_number (size); + } + + return size; +} + +/* Print hash table statistics for hash table HTAB with message string MSG. */ +static void +htab_report (htab_t htab, const char *msg) +{ + double collisions = htab_collisions (htab); + unsigned int searches = htab->searches; + size_t elements = htab->n_elements; + size_t deleted = htab->n_deleted; + size_t adjusted_elements = elements - deleted; + size_t size = htab->size; + double occupancy = (double)elements / (double)size; + double adjusted_occupancy = (double)adjusted_elements / (double)size; + /* Indent unconditional fprintfs similar to conditional fprintfs to + left-align literal strings. */ + if (1) + fprintf (stderr, "htab: %s\n", msg); + if (1) + fprintf (stderr, " size: %zu\n", size); + if (elements > 0 && deleted == 0) + fprintf (stderr, " elements: %zu, occupancy: %f\n", elements, + occupancy); + if (deleted > 0) + fprintf (stderr, " elements (incl. deleted): %zu, occupancy: %f\n", + elements, occupancy); + if (deleted > 0) + fprintf (stderr, " elements (excl. deleted): %zu, occupancy: %f\n", + adjusted_elements, adjusted_occupancy); + if (elements > 0) + fprintf (stderr, " searches: %u, collisions: %f\n", searches, + collisions); +} + +/* Hash function for off_htab hash table. */ +static hashval_t +off_hash (const void *p) +{ + dw_die_ref die = (dw_die_ref) p; + + return die->die_offset / 6; +} + +/* Equality function for off_htab hash table. */ +static int +off_eq (const void *p, const void *q) +{ + return ((dw_die_ref) p)->die_offset == ((dw_die_ref) q)->die_offset; +} + +/* Hash table to map die_offset values to struct dw_die pointers. */ +static htab_t off_htab; + +/* After read_multifile off_htab is copied over to this variable. + Offsets in the alternate .debug_info are found using this hash table. */ +static htab_t alt_off_htab; + +/* Offset hash table for .debug_types section. */ +static htab_t types_off_htab; + +/* Function to add DIE into the hash table (and create the hash table + when not already created). */ +static void +off_htab_add_die (dw_cu_ref cu, dw_die_ref die, unsigned int *die_count) +{ + void **slot; + + if (unlikely (cu->cu_kind == CU_TYPES)) + { + if (types_off_htab == NULL) + { + types_off_htab = htab_try_create (100000, off_hash, off_eq, NULL); + if (types_off_htab == NULL) + dwz_oom (); + } + + slot = htab_find_slot (types_off_htab, die, INSERT); + if (slot == NULL) + dwz_oom (); + assert (*slot == NULL); + *slot = die; + return; + } + + if (off_htab == NULL) + { + unsigned int estimated_nr_dies = estimate_nr_dies (); + size_t default_initial_size = 100000; + size_t initial_size; + if (low_mem + || op_multifile + || (multifile_mode == 0 + && die_count_method == estimate + && (estimated_nr_dies >= low_mem_die_limit + || estimated_nr_dies >= max_die_limit))) + initial_size = default_initial_size; + else + { + size_t nr_dies; + if (die_count && *die_count != 0) + { + nr_dies = *die_count; + if (tracing) + fprintf (stderr, "Using die count %zu for off_htab" + " allocation\n", nr_dies); + } + else if (die_count_method == none) + nr_dies = 0; + else if (die_count_method == estimate) + { + nr_dies = estimated_nr_dies; + if (tracing) + fprintf (stderr, "Using die count estimate %zu for off_htab" + " allocation\n", nr_dies); + } + else + assert (false); + + if (nr_dies != 0) + { + size_t final_hashtab_size + = emulate_htab (default_initial_size, nr_dies); + initial_size = final_hashtab_size; + } + else + initial_size = default_initial_size; + } + off_htab = htab_try_create (initial_size, off_hash, off_eq, NULL); + if (tracing) + htab_report (off_htab, "off_htab allocation"); + if (off_htab == NULL) + dwz_oom (); + if (rd_multifile) + alt_off_htab = off_htab; + } + + slot = htab_find_slot_with_hash (off_htab, die, off_hash (die), INSERT); + if (slot == NULL) + dwz_oom (); + assert (*slot == NULL); + *slot = die; +} + +/* For DIE_OFFSET return dw_die_ref whose die_offset field is equal + to that value. Return NULL if no DIE is at that position (buggy + DWARF input?). */ +static dw_die_ref +off_htab_lookup (dw_cu_ref cu, unsigned int die_offset) +{ + struct dw_die die; + die.die_offset = die_offset; + if (cu == NULL) + return (dw_die_ref) htab_find_with_hash (off_htab, &die, off_hash (&die)); + if (unlikely (cu->cu_kind == CU_ALT)) + return (dw_die_ref) htab_find_with_hash (alt_off_htab, &die, + off_hash (&die)); + if (unlikely (cu->cu_kind == CU_TYPES)) + return (dw_die_ref) htab_find_with_hash (types_off_htab, &die, + off_hash (&die)); + return (dw_die_ref) htab_find_with_hash (off_htab, &die, off_hash (&die)); +} + +/* For a die attribute ATTR starting at PTR, with the die in CU, return the + pointer after the attribute. */ +static inline unsigned char * FORCE_INLINE +skip_attr (unsigned int cu_version, struct abbrev_attr *attr, + unsigned char *ptr) +{ + uint32_t form = attr->form; + + while (form == DW_FORM_indirect) + form = read_uleb128 (ptr); + return skip_attr_no_dw_form_indirect (cu_version, form, ptr); +} + +/* Return a pointer at which DIE's attribute AT is encoded, and fill in + its form into *FORMP. Return NULL if the attribute is not present. */ +static unsigned char * +get_AT (dw_die_ref die, enum dwarf_attribute at, enum dwarf_form *formp) +{ + struct abbrev_tag *t = die->die_abbrev; + unsigned int i; + unsigned char *ptr; + dw_cu_ref cu = die_cu (die); + if (unlikely (fi_multifile) && cu->cu_kind == CU_ALT) + ptr = alt_data[DEBUG_INFO]; + else if (cu->cu_kind == CU_TYPES) + ptr = debug_sections[DEBUG_TYPES].data; + else + ptr = debug_sections[DEBUG_INFO].data; + ptr += die->die_offset; + skip_leb128 (ptr); + for (i = 0; i < t->nattr; ++i) + { + uint32_t form = t->attr[i].form; + + while (form == DW_FORM_indirect) + form = read_uleb128 (ptr); + if (t->attr[i].attr == at) + { + *formp = form; + if (form == DW_FORM_implicit_const) + return (unsigned char *) &t->values[i]; + return ptr; + } + + ptr = skip_attr_no_dw_form_indirect (cu->cu_version, form, ptr); + } + return NULL; +} + +/* Return an integer attribute AT of DIE. Set *PRESENT to true + if found. */ +static uint64_t +get_AT_int (dw_die_ref die, enum dwarf_attribute at, bool *present, + enum dwarf_form *formp) +{ + unsigned char *ptr; + ptr = get_AT (die, at, formp); + *present = false; + if (ptr == NULL) + return 0; + *present = true; + switch (*formp) + { + case DW_FORM_ref_addr: + return read_size (ptr, die_cu (die)->cu_version == 2 ? ptr_size : 4); + case DW_FORM_addr: + return read_size (ptr, ptr_size); + case DW_FORM_flag_present: + return 1; + case DW_FORM_ref1: + case DW_FORM_flag: + case DW_FORM_data1: + return read_8 (ptr); + case DW_FORM_ref2: + case DW_FORM_data2: + return read_16 (ptr); + case DW_FORM_ref4: + case DW_FORM_data4: + case DW_FORM_sec_offset: + return read_32 (ptr); + case DW_FORM_ref8: + case DW_FORM_data8: + case DW_FORM_ref_sig8: + return read_64 (ptr); + case DW_FORM_sdata: + return read_sleb128 (ptr); + case DW_FORM_ref_udata: + case DW_FORM_udata: + return read_uleb128 (ptr); + case DW_FORM_implicit_const: + return *(uint64_t *)ptr; /* See get_AT. */ + default: + *present = false; + return 0; + } +} + +/* Return a pointer to string attribute AT in DIE, or NULL + if the attribute is not present. */ +static char * +get_AT_string (dw_die_ref die, enum dwarf_attribute at) +{ + enum dwarf_form form; + unsigned char *ptr; + ptr = get_AT (die, at, &form); + if (ptr == NULL) + return NULL; + switch (form) + { + case DW_FORM_string: + return (char *) ptr; + case DW_FORM_strp: + { + unsigned int strp = read_32 (ptr); + if (unlikely (fi_multifile) && die_cu (die)->cu_kind == CU_ALT) + { + if (strp >= alt_size[DEBUG_STR]) + return NULL; + return (char *) alt_data[DEBUG_STR] + strp; + } + if (strp >= debug_sections[DEBUG_STR].size) + return NULL; + return (char *) debug_sections[DEBUG_STR].data + strp; + } + case DW_FORM_line_strp: + { + unsigned int line_strp = read_32 (ptr); + if (line_strp >= debug_sections[DEBUG_LINE_STR].size) + return NULL; + else + return (char *) debug_sections[DEBUG_LINE_STR].data + line_strp; + } + default: + return NULL; + } +} + +/* Parse DWARF expression referenced or stored in DIE, starting at + PTR with LEN bytes. Return non-zero on error. If NEED_ADJUST + is non-NULL, set *NEED_ADJUST to true if it contains DIE references + that will need adjusting. Some opcodes cause DIE or referenced + DIEs as unsuitable for moving into partial units, or limit their + location. */ +static int +read_exprloc (DSO *dso, dw_die_ref die, unsigned char *ptr, size_t len, + bool *need_adjust) +{ + unsigned char *end = ptr + len; + unsigned char op; + GElf_Addr addr; + dw_die_ref ref; + dw_cu_ref cu; + + while (ptr < end) + { + op = *ptr++; + switch (op) + { + case DW_OP_addr: + die->die_no_multifile = 1; + ptr += ptr_size; + break; + case DW_OP_deref: + case DW_OP_dup: + case DW_OP_drop: + case DW_OP_over: + case DW_OP_swap: + case DW_OP_rot: + case DW_OP_xderef: + case DW_OP_abs: + case DW_OP_and: + case DW_OP_div: + case DW_OP_minus: + case DW_OP_mod: + case DW_OP_mul: + case DW_OP_neg: + case DW_OP_not: + case DW_OP_or: + case DW_OP_plus: + case DW_OP_shl: + case DW_OP_shr: + case DW_OP_shra: + case DW_OP_xor: + case DW_OP_eq: + case DW_OP_ge: + case DW_OP_gt: + case DW_OP_le: + case DW_OP_lt: + case DW_OP_ne: + case DW_OP_lit0 ... DW_OP_lit31: + case DW_OP_reg0 ... DW_OP_reg31: + case DW_OP_nop: + case DW_OP_push_object_address: + case DW_OP_form_tls_address: + case DW_OP_call_frame_cfa: + case DW_OP_stack_value: + case DW_OP_GNU_push_tls_address: + case DW_OP_GNU_uninit: + break; + case DW_OP_const1u: + case DW_OP_pick: + case DW_OP_deref_size: + case DW_OP_xderef_size: + case DW_OP_const1s: + ++ptr; + break; + case DW_OP_const2u: + case DW_OP_const2s: + case DW_OP_skip: + case DW_OP_bra: + ptr += 2; + break; + case DW_OP_call2: + case DW_OP_call4: + case DW_OP_GNU_parameter_ref: + if (op == DW_OP_call2) + addr = read_16 (ptr); + else + addr = read_32 (ptr); + cu = die_cu (die); + ref = off_htab_lookup (cu, cu->cu_offset + addr); + if (ref == NULL) + { + error (0, 0, "%s: Couldn't find DIE at [%" PRIx64 "] " + "referenced by %s from DIE at [%x]", + dso->filename, cu->cu_offset + addr, + get_DW_OP_str (op), die->die_offset); + return 1; + } + if (op == DW_OP_call2) + ref->die_op_call2_referenced = 1; + if (ref->die_ck_state == CK_KNOWN) + { + dw_die_ref d; + ref->die_ck_state = CK_BAD; + + d = ref; + while (!d->die_root + && d->die_parent->die_ck_state == CK_KNOWN) + { + d = d->die_parent; + d->die_ck_state = CK_BAD; + } + } + else + ref->die_ck_state = CK_BAD; + if (unlikely (low_mem)) + { + ref->die_referenced = 1; + /* As .debug_loc adjustment is done after + write_info finishes, we need to keep the referenced + DIEs around uncollapsed. */ + if (need_adjust) + ref->die_intercu_referenced = 1; + } + die->die_ck_state = CK_BAD; + if (need_adjust) + *need_adjust = true; + break; + case DW_OP_const4u: + case DW_OP_const4s: + ptr += 4; + break; + case DW_OP_call_ref: + case DW_OP_GNU_implicit_pointer: + case DW_OP_implicit_pointer: + case DW_OP_GNU_variable_value: + cu = die_cu (die); + addr = read_size (ptr, cu->cu_version == 2 ? ptr_size : 4); + if (cu->cu_version == 2) + ptr += ptr_size; + else + ptr += 4; + ref = off_htab_lookup (NULL, addr); + if (ref == NULL || (unlikely (low_mem) && ref->die_tag == 0)) + { + error (0, 0, "%s: Couldn't find DIE at [%" PRIx64 "] " + "referenced by %s from DIE at [%x]", + dso->filename, addr, get_DW_OP_str (op), die->die_offset); + return 1; + } + ref->die_no_multifile = 1; + if (unlikely (low_mem)) + { + ref->die_referenced = 1; + /* As .debug_loc adjustment is done after + write_info finishes, we need to keep the referenced + DIEs around uncollapsed. */ + if (die_cu (ref) != cu || need_adjust) + ref->die_intercu_referenced = 1; + } + die->die_ck_state = CK_BAD; + if (need_adjust) + *need_adjust = true; + if (op == DW_OP_GNU_implicit_pointer || op == DW_OP_implicit_pointer) + skip_leb128 (ptr); + break; + case DW_OP_const8u: + case DW_OP_const8s: + ptr += 8; + break; + case DW_OP_constu: + case DW_OP_plus_uconst: + case DW_OP_regx: + case DW_OP_piece: + case DW_OP_consts: + case DW_OP_breg0 ... DW_OP_breg31: + case DW_OP_fbreg: + skip_leb128 (ptr); + break; + case DW_OP_bregx: + case DW_OP_bit_piece: + skip_leb128 (ptr); + skip_leb128 (ptr); + break; + case DW_OP_implicit_value: + { + uint32_t leni = read_uleb128 (ptr); + ptr += leni; + } + break; + case DW_OP_GNU_entry_value: + case DW_OP_entry_value: + { + uint32_t leni = read_uleb128 (ptr); + if ((uint64_t) (end - ptr) < leni) + { + error (0, 0, "%s: %s with too large length", + get_DW_OP_str (op), dso->filename); + return 1; + } + if (read_exprloc (dso, die, ptr, leni, need_adjust)) + return 1; + ptr += leni; + } + break; + case DW_OP_GNU_convert: + case DW_OP_convert: + case DW_OP_GNU_reinterpret: + case DW_OP_reinterpret: + addr = read_uleb128 (ptr); + if (addr == 0) + break; + goto typed_dwarf; + case DW_OP_GNU_regval_type: + case DW_OP_regval_type: + skip_leb128 (ptr); + addr = read_uleb128 (ptr); + goto typed_dwarf; + case DW_OP_GNU_const_type: + case DW_OP_const_type: + addr = read_uleb128 (ptr); + ptr += *ptr + 1; + goto typed_dwarf; + case DW_OP_GNU_deref_type: + case DW_OP_deref_type: + ++ptr; + addr = read_uleb128 (ptr); + typed_dwarf: + cu = die_cu (die); + ref = off_htab_lookup (cu, cu->cu_offset + addr); + if (ref == NULL) + { + error (0, 0, "%s: Couldn't find DIE at [%" PRIx64 "] " + "referenced by %s from DIE at [%x]", + dso->filename, cu->cu_offset + addr, + get_DW_OP_str (op), die->die_offset); + return 1; + } + if (unlikely (low_mem)) + { + ref->die_referenced = 1; + /* As .debug_loc adjustment is done after + write_info finishes, we need to keep the referenced + DIEs around uncollapsed. */ + if (need_adjust) + ref->die_intercu_referenced = 1; + } + ref->die_op_type_referenced = 1; + die->die_ck_state = CK_BAD; + if (need_adjust) + *need_adjust = true; + break; + default: + error (0, 0, "%s: Unknown DWARF %s " + "referenced from DIE at [%x]", + dso->filename, get_DW_OP_str (op), + die->die_offset); + return 1; + } + } + if (die->die_ck_state != CK_BAD) + die->u.p1.die_hash = iterative_hash (end - len, len, die->u.p1.die_hash); + return 0; +} + +/* Add dummy die in CU at OFFSET. */ +static inline void FORCE_INLINE +add_dummy_die (dw_cu_ref cu, unsigned int offset) +{ + dw_die_ref ref; + struct dw_die ref_buf; + void **slot; + + memset (&ref_buf, '\0', offsetof (struct dw_die, die_child)); + ref_buf.die_offset = offset; + ref_buf.die_collapsed_child = 1; + ref_buf.die_referenced = 1; + ref_buf.die_intercu_referenced = 1; + if (off_htab == NULL) + { + ref = pool_alloc (dw_die, offsetof (struct dw_die, die_child)); + memcpy (ref, &ref_buf, offsetof (struct dw_die, die_child)); + off_htab_add_die (cu, ref, NULL); + return; + } + + slot + = htab_find_slot_with_hash (off_htab, &ref_buf, off_hash (&ref_buf), + INSERT); + if (slot == NULL) + dwz_oom (); + if (*slot != NULL) + return; + + ref = pool_alloc (dw_die, offsetof (struct dw_die, die_child)); + memcpy (ref, &ref_buf, offsetof (struct dw_die, die_child)); + *slot = (void *) ref; +} + +/* Add dummy DIEs for expr_loc at PTR. */ +static int +read_exprloc_low_mem_phase1 (DSO *dso, dw_die_ref die, unsigned char *ptr, + size_t len) +{ + unsigned char *end = ptr + len; + unsigned char op; + GElf_Addr addr; + dw_cu_ref cu; + + while (ptr < end) + { + op = *ptr++; + switch (op) + { + case DW_OP_addr: + ptr += ptr_size; + break; + case DW_OP_deref: + case DW_OP_dup: + case DW_OP_drop: + case DW_OP_over: + case DW_OP_swap: + case DW_OP_rot: + case DW_OP_xderef: + case DW_OP_abs: + case DW_OP_and: + case DW_OP_div: + case DW_OP_minus: + case DW_OP_mod: + case DW_OP_mul: + case DW_OP_neg: + case DW_OP_not: + case DW_OP_or: + case DW_OP_plus: + case DW_OP_shl: + case DW_OP_shr: + case DW_OP_shra: + case DW_OP_xor: + case DW_OP_eq: + case DW_OP_ge: + case DW_OP_gt: + case DW_OP_le: + case DW_OP_lt: + case DW_OP_ne: + case DW_OP_lit0 ... DW_OP_lit31: + case DW_OP_reg0 ... DW_OP_reg31: + case DW_OP_nop: + case DW_OP_push_object_address: + case DW_OP_form_tls_address: + case DW_OP_call_frame_cfa: + case DW_OP_stack_value: + case DW_OP_GNU_push_tls_address: + case DW_OP_GNU_uninit: + break; + case DW_OP_const1u: + case DW_OP_pick: + case DW_OP_deref_size: + case DW_OP_xderef_size: + case DW_OP_const1s: + ++ptr; + break; + case DW_OP_const2u: + case DW_OP_const2s: + case DW_OP_skip: + case DW_OP_bra: + ptr += 2; + break; + case DW_OP_call2: + case DW_OP_call4: + case DW_OP_GNU_parameter_ref: + if (op == DW_OP_call2) + read_16 (ptr); + else + read_32 (ptr); + break; + case DW_OP_const4u: + case DW_OP_const4s: + ptr += 4; + break; + case DW_OP_call_ref: + case DW_OP_GNU_implicit_pointer: + case DW_OP_implicit_pointer: + case DW_OP_GNU_variable_value: + cu = die_cu (die); + addr = read_size (ptr, cu->cu_version == 2 ? ptr_size : 4); + if (cu->cu_version == 2) + ptr += ptr_size; + else + ptr += 4; + /* Adding a dummy DIE ref to mark an intercu reference is only + necessary if die_cu (ref) != cu, but we don't track cu's during + low-mem phase1. */ + add_dummy_die (cu, addr); + if (op == DW_OP_GNU_implicit_pointer || op == DW_OP_implicit_pointer) + skip_leb128 (ptr); + break; + case DW_OP_const8u: + case DW_OP_const8s: + ptr += 8; + break; + case DW_OP_constu: + case DW_OP_plus_uconst: + case DW_OP_regx: + case DW_OP_piece: + case DW_OP_consts: + case DW_OP_breg0 ... DW_OP_breg31: + case DW_OP_fbreg: + skip_leb128 (ptr); + break; + case DW_OP_bregx: + case DW_OP_bit_piece: + skip_leb128 (ptr); + skip_leb128 (ptr); + break; + case DW_OP_implicit_value: + { + uint32_t leni = read_uleb128 (ptr); + ptr += leni; + } + break; + case DW_OP_GNU_entry_value: + case DW_OP_entry_value: + { + uint32_t leni = read_uleb128 (ptr); + if ((uint64_t) (end - ptr) < leni) + { + error (0, 0, "%s: %s with too large length", + get_DW_OP_str (op), dso->filename); + return 1; + } + if (read_exprloc_low_mem_phase1 (dso, die, ptr, leni)) + return 1; + ptr += leni; + } + break; + case DW_OP_GNU_convert: + case DW_OP_convert: + case DW_OP_GNU_reinterpret: + case DW_OP_reinterpret: + skip_leb128 (ptr); + break; + case DW_OP_GNU_regval_type: + case DW_OP_regval_type: + skip_leb128 (ptr); + skip_leb128 (ptr); + break; + case DW_OP_GNU_const_type: + case DW_OP_const_type: + read_uleb128 (ptr); + ptr += *ptr + 1; + break; + case DW_OP_GNU_deref_type: + case DW_OP_deref_type: + ++ptr; + skip_leb128 (ptr); + break; + default: + error (0, 0, "%s: Unknown DWARF %s " + "referenced from DIE at [%x]", + dso->filename, get_DW_OP_str (op), + die->die_offset); + return 1; + } + } + + return 0; +} + +/* Add dummy DIEs for loclist at OFFSET. */ +static int +read_loclist_low_mem_phase1 (DSO *dso, dw_cu_ref cu, dw_die_ref die, + GElf_Addr offset) +{ + unsigned char *ptr, *endsec; + GElf_Addr low, high; + size_t len = 0; + int sec; + + sec = cu->cu_version < 5 ? DEBUG_LOC : DEBUG_LOCLISTS; + ptr = debug_sections[sec].data; + if (ptr == NULL) + { + error (0, 0, "%s: loclistptr attribute, yet no %s section", + dso->filename, debug_sections[sec].name); + return 1; + } + if (offset >= debug_sections[sec].size) + { + error (0, 0, + "%s: loclistptr offset %Ld outside of %s section", + dso->filename, (long long) offset, debug_sections[sec].name); + return 1; + } + endsec = ptr + debug_sections[sec].size; + ptr += offset; +again: + while (ptr < endsec) + { + if (sec == DEBUG_LOC) + { + low = read_size (ptr, ptr_size); + high = read_size (ptr + ptr_size, ptr_size); + ptr += 2 * ptr_size; + if (low == 0 && high == 0) + break; + + if (low == ~ (GElf_Addr) 0 || (ptr_size == 4 && low == 0xffffffff)) + continue; + + len = read_16 (ptr); + } + else + { + uint8_t lle = *ptr++; + switch (lle) + { + case DW_LLE_end_of_list: + goto done; + + case DW_LLE_base_addressx: + skip_leb128 (ptr); + goto again; + + case DW_LLE_startx_endx: + skip_leb128 (ptr); + skip_leb128 (ptr); + len = read_uleb128 (ptr); + break; + + case DW_LLE_startx_length: + skip_leb128 (ptr); + skip_leb128 (ptr); + len = read_uleb128 (ptr); + break; + + case DW_LLE_offset_pair: + skip_leb128 (ptr); + skip_leb128 (ptr); + len = read_uleb128 (ptr); + break; + + case DW_LLE_default_location: + len = read_uleb128 (ptr); + break; + + case DW_LLE_base_address: + ptr += ptr_size; + goto again; + + case DW_LLE_start_end: + ptr += 2 * ptr_size; + len = read_uleb128 (ptr); + break; + + case DW_LLE_start_length: + ptr += ptr_size; + skip_leb128 (ptr); + len = read_uleb128 (ptr); + break; + + case DW_LLE_GNU_view_pair: + if (cu->cu_version != 5) + error (0, 0, + "%s: DW_LLE_GNU_view_pair used with DWARF version %u", + dso->filename, cu->cu_version); + skip_leb128 (ptr); + skip_leb128 (ptr); + goto again; + + default: + error (0, 0, + "%s: unhandled location list entry 0x%x in %s section", + dso->filename, lle, debug_sections[sec].name); + return 1; + } + } + + if (unlikely (!(ptr + len <= endsec))) + { + error (0, 0, + "%s: locexpr length 0x%Lx exceeds %s section", + dso->filename, (long long) len, debug_sections[sec].name); + return 1; + } + + if (len > 0) + if (read_exprloc_low_mem_phase1 (dso, die, ptr, len)) + return 1; + + ptr += len; + } + +done: + return 0; +} + +/* Add dummy dies for loc_exprs and loc_lists referenced from DIE. */ +static int +add_locexpr_dummy_dies (DSO *dso, dw_cu_ref cu, dw_die_ref die, + unsigned char *ptr, uint32_t form, unsigned int attr, + size_t len) +{ + if (form == DW_FORM_block1 && cu->cu_version < 4) + { + /* Old DWARF uses blocks instead of exprlocs. */ + switch (attr) + { + case DW_AT_frame_base: + case DW_AT_location: + case DW_AT_data_member_location: + case DW_AT_vtable_elem_location: + case DW_AT_byte_size: + case DW_AT_bit_offset: + case DW_AT_bit_size: + case DW_AT_string_length: + case DW_AT_lower_bound: + case DW_AT_return_addr: + case DW_AT_bit_stride: + case DW_AT_upper_bound: + case DW_AT_count: + case DW_AT_segment: + case DW_AT_static_link: + case DW_AT_use_location: + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_data_location: + case DW_AT_byte_stride: + case DW_AT_rank: + case DW_AT_call_value: + case DW_AT_call_target: + case DW_AT_call_target_clobbered: + case DW_AT_call_data_location: + case DW_AT_call_data_value: + case DW_AT_GNU_call_site_value: + case DW_AT_GNU_call_site_data_value: + case DW_AT_GNU_call_site_target: + case DW_AT_GNU_call_site_target_clobbered: + if (read_exprloc_low_mem_phase1 (dso, die, ptr, len)) + return 1; + default: + break; + } + + return 0; + } + + if (form == DW_FORM_exprloc) + return read_exprloc_low_mem_phase1 (dso, die, ptr, len); + + switch (attr) + { + case DW_AT_location: + case DW_AT_string_length: + case DW_AT_return_addr: + case DW_AT_data_member_location: + case DW_AT_frame_base: + case DW_AT_segment: + case DW_AT_static_link: + case DW_AT_use_location: + case DW_AT_vtable_elem_location: + if ((cu->cu_version < 4 && form == DW_FORM_data4) + || form == DW_FORM_sec_offset) + { + if (read_loclist_low_mem_phase1 (dso, cu, die, do_read_32 (ptr))) + return 1; + break; + } + else if (cu->cu_version < 4 && form == DW_FORM_data8) + { + if (read_loclist_low_mem_phase1 (dso, cu, die, do_read_64 (ptr))) + return 1; + break; + } + break; + default: + break; + } + + return 0; +} + +/* Structure recording a portion of .debug_loc section that will need + adjusting. */ +struct debug_loc_adjust +{ + /* Starting offset in .debug_loc that needs adjusting. */ + unsigned int start_offset; + /* End offset. This is used for hashing, as in theory some DIE + might be referencing a middle of a .debug_loc sequence (the address + part of it) referenced by a different DIE. */ + unsigned int end_offset; + /* Owning CU. We give up if the same .debug_loc part that needs adjusting + is owned by more than one CU. */ + dw_cu_ref cu; +}; +ALIGN_STRUCT (debug_loc_adjust) + +/* Hash table and obstack for recording .debug_loc and .debug_loclists + adjustment ranges. */ +static htab_t loc_htab; +static htab_t loclists_htab; + +/* Hash function for loc[lists]_htab. */ +static hashval_t +loc_hash (const void *p) +{ + struct debug_loc_adjust *a = (struct debug_loc_adjust *)p; + + return a->end_offset; +} + +/* Equality function for loc[lists]_htab. */ +static int +loc_eq (const void *p, const void *q) +{ + struct debug_loc_adjust *t1 = (struct debug_loc_adjust *)p; + struct debug_loc_adjust *t2 = (struct debug_loc_adjust *)q; + + return t1->end_offset == t2->end_offset; +} + +/* Parse .debug_loc portion starting at OFFSET, referenced by + DIE. Call read_exprloc on each of the DWARF expressions + contained in it. */ +static int +read_loclist (DSO *dso, dw_cu_ref cu, dw_die_ref die, GElf_Addr offset) +{ + unsigned char *ptr, *endsec; + GElf_Addr low, high; + size_t len; + int sec; + bool need_adjust = false; + + die->die_ck_state = CK_BAD; + sec = cu->cu_version < 5 ? DEBUG_LOC : DEBUG_LOCLISTS; + ptr = debug_sections[sec].data; + if (ptr == NULL) + { + error (0, 0, "%s: loclistptr attribute, yet no %s section", + dso->filename, debug_sections[sec].name); + return 1; + } + if (offset >= debug_sections[sec].size) + { + error (0, 0, + "%s: loclistptr offset %Ld outside of %s section", + dso->filename, (long long) offset, debug_sections[sec].name); + return 1; + } + endsec = ptr + debug_sections[sec].size; + ptr += offset; +again: + while (ptr < endsec) + { + if (cu->cu_version < 5) + { + low = read_size (ptr, ptr_size); + high = read_size (ptr + ptr_size, ptr_size); + ptr += 2 * ptr_size; + if (low == 0 && high == 0) + break; + + if (low == ~ (GElf_Addr) 0 || (ptr_size == 4 && low == 0xffffffff)) + continue; + + len = read_16 (ptr); + } + else + { + uint8_t lle = *ptr++; + switch (lle) + { + case DW_LLE_end_of_list: + goto done; + + case DW_LLE_base_addressx: + skip_leb128 (ptr); + goto again; + + case DW_LLE_startx_endx: + skip_leb128 (ptr); + skip_leb128 (ptr); + len = read_uleb128 (ptr); + break; + + case DW_LLE_startx_length: + skip_leb128 (ptr); + skip_leb128 (ptr); + len = read_uleb128 (ptr); + break; + + case DW_LLE_offset_pair: + skip_leb128 (ptr); + skip_leb128 (ptr); + len = read_uleb128 (ptr); + break; + + case DW_LLE_default_location: + len = read_uleb128 (ptr); + break; + + case DW_LLE_base_address: + ptr += ptr_size; + goto again; + + case DW_LLE_start_end: + ptr += 2 * ptr_size; + len = read_uleb128 (ptr); + break; + + case DW_LLE_start_length: + ptr += ptr_size; + skip_leb128 (ptr); + len = read_uleb128 (ptr); + break; + + case DW_LLE_GNU_view_pair: + if (cu->cu_version != 5) + error (0, 0, + "%s: DW_LLE_GNU_view_pair used with DWARF version %u", + dso->filename, cu->cu_version); + skip_leb128 (ptr); + skip_leb128 (ptr); + goto again; + + default: + error (0, 0, + "%s: unhandled location list entry 0x%x in %s section", + dso->filename, lle, debug_sections[sec].name); + return 1; + } + } + + if (unlikely (!(ptr + len <= endsec))) + { + error (0, 0, + "%s: locexpr length 0x%Lx exceeds %s section", + dso->filename, (long long) len, debug_sections[sec].name); + return 1; + } + + if (read_exprloc (dso, die, ptr, len, &need_adjust)) + return 1; + + ptr += len; + } + +done: + if (need_adjust) + { + struct debug_loc_adjust adj, *a; + void **slot; + + adj.start_offset = offset; + adj.end_offset = ptr - debug_sections[sec].data; + adj.cu = cu; + if (sec == DEBUG_LOC) + { + if (loc_htab == NULL) + { + loc_htab = htab_try_create (50, loc_hash, loc_eq, NULL); + if (loc_htab == NULL) + dwz_oom (); + } + slot = htab_find_slot_with_hash (loc_htab, &adj, adj.end_offset, + INSERT); + } + else + { + if (loclists_htab == NULL) + { + loclists_htab = htab_try_create (50, loc_hash, loc_eq, NULL); + if (loclists_htab == NULL) + dwz_oom (); + } + slot = htab_find_slot_with_hash (loclists_htab, &adj, adj.end_offset, + INSERT); + } + if (slot == NULL) + dwz_oom (); + if (*slot == NULL) + { + a = pool_alloc (debug_loc_adjust, sizeof (*a)); + *a = adj; + *slot = (void *) a; + } + else if (((struct debug_loc_adjust *)*slot)->cu != adj.cu) + { + error (0, 0, "%s: can't adjust %s section because multiple " + "CUs refer to it", dso->filename, debug_sections[sec].name); + return 1; + } + else if (((struct debug_loc_adjust *)*slot)->start_offset > offset) + ((struct debug_loc_adjust *)*slot)->start_offset = offset; + } + + return 0; +} + +/* Initialize die_odr_state field for DIE with CU. */ +static void +set_die_odr_state (dw_cu_ref cu, dw_die_ref die) +{ + unsigned char *ptr; + struct abbrev_tag *t; + unsigned int i; + bool decl_p; + bool name_p; + bool other_p; + + assert (die->die_odr_state == ODR_UNKNOWN); + die->die_odr_state = ODR_NONE; + + if (low_mem) + /* Todo: allow low-mem mode. */ + return; + + if (multifile_mode == 0) + /* We're in regular mode, enable the ODR optimization. */ + ; + else + /* One definition rule does not hold across executables and shared + libraries, so disable. */ + return; + + if (!die->die_toplevel) + /* A nested struct is not uniquely identified by its name. There may be a + different type with the same name nested in a different struct. */ + return; + + switch (cu->lang) + { + case DW_LANG_C_plus_plus: + case DW_LANG_C_plus_plus_03: + case DW_LANG_C_plus_plus_11: + case DW_LANG_C_plus_plus_14: + /* c++ defines one-definition-rule. */ + if (die->die_tag == DW_TAG_structure_type + || die->die_tag == DW_TAG_class_type + || die->die_tag == DW_TAG_union_type) + /* ODR holds for all types, but we limit the optimization to these + tags, which are the ones likely to profit from it. */ + ; + else + return; + break; + default: + return; + } + + ptr = debug_sections[DEBUG_INFO].data + die->die_offset; + skip_leb128 (ptr); + + t = die->die_abbrev; + + decl_p = false; + name_p = false; + other_p = false; + for (i = 0; i < t->nattr; ++i) + { + if (t->attr[i].attr == DW_AT_name) + { + name_p = true; + continue; + } + + if (t->attr[i].attr == DW_AT_declaration) + { + decl_p = true; + continue; + } + + other_p = true; + } + + if (!name_p) + /* Ignore anonymous types. */ + return; + + odr_active_p = true; + + if (decl_p && !other_p && die->die_child == NULL) + { + /* Detected a declaration with no attributes other than DW_AT_name and + DW_AT_declaration, and no children. */ + die->die_odr_state = ODR_DECL; + return; + } + + die->die_odr_state = ODR_DEF; +} + +/* Return the initialized die_odr_state field for DIE with CU. */ +static unsigned int +die_odr_state (dw_die_ref die) +{ + assert (die->die_odr_state != ODR_UNKNOWN); + return die->die_odr_state; +} + +/* This function computes u.p1.die_hash and die_ck_state of DIE. + The field u.p1.die_hash is an iterative hash of: + - the die_tag, + - for all attributes except DW_AT_sibling: the attribute code, + - for non-reference class attributes: the value of the attribute (magic + for DW_AT_*_file), + - for DW_FORM_ref_addr attributes: the value of the attribute, + - for reference class attributes that point into the subtree of TOP_DIE + (note, other references are intentionally ignored here): + ref->u.p1.die_enter - top_die->u.p1.die_enter, + - for all children: their hashes. + The field die_ck_state is set to CK_BAD if the die is unsuitable for + moving into a partial unit (contains code references or other reasons). + TOP_DIE is initially NULL when DW_TAG_*_unit or die_named_namespace dies + are walked. */ +static int +checksum_die (DSO *dso, dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die) +{ + unsigned short s; + struct abbrev_tag *t; + unsigned int i; + unsigned char *ptr; + dw_die_ref child; + bool only_hash_name_p; + hashval_t die_hash2; + + switch (die->die_ck_state) + { + case CK_UNKNOWN: + break; + case CK_KNOWN: + case CK_BAD: + return 0; + case CK_BEING_COMPUTED: + die->die_ck_state = CK_BAD; + return 0; + } + die->die_ck_state = CK_BEING_COMPUTED; + die->u.p1.die_hash = 0; + if (die->die_tag == DW_TAG_compile_unit + || die->die_tag == DW_TAG_partial_unit + || die->die_tag == DW_TAG_namespace + || die->die_tag == DW_TAG_module + || die->die_tag == DW_TAG_imported_unit) + die->die_ck_state = CK_BAD; + t = die->die_abbrev; + ptr = debug_sections[DEBUG_INFO].data + die->die_offset; + skip_leb128 (ptr); + s = die->die_tag; + die->u.p1.die_hash = iterative_hash_object (s, die->u.p1.die_hash); + if (dump_checksum_p) + fprintf (stderr, "DIE %x, hash: %x, tag\n", die->die_offset, + die->u.p1.die_hash); + if (uni_lang_p && die == top_die) + { + die->u.p1.die_hash + = iterative_hash_object (cu->lang, die->u.p1.die_hash); + if (dump_checksum_p) + fprintf (stderr, "DIE %x, hash: %x, lang\n", die->die_offset, + die->u.p1.die_hash); + } + + if (odr && die->die_odr_state == ODR_UNKNOWN) + set_die_odr_state (die_cu (die), die); + only_hash_name_p = odr && die_odr_state (die) != ODR_NONE; + die_hash2 = 0; + if (only_hash_name_p) + die_hash2 = die->u.p1.die_hash; + for (i = 0; i < t->nattr; ++i) + { + uint32_t form = t->attr[i].form; + size_t len = 0; + unsigned char *old_ptr; + bool handled = false; + int64_t svalue; + uint64_t value; + + while (form == DW_FORM_indirect) + form = read_uleb128 (ptr); + old_ptr = ptr; + + switch (t->attr[i].attr) + { + /* Ignore DW_AT_sibling attribute. */ + case DW_AT_sibling: + handled = true; + break; + /* These attributes reference code, prevent moving + DIEs with them. */ + case DW_AT_low_pc: + case DW_AT_high_pc: + case DW_AT_entry_pc: + case DW_AT_ranges: + case DW_AT_call_return_pc: + case DW_AT_call_pc: + die->die_ck_state = CK_BAD; + break; + case DW_AT_start_scope: + if (form == DW_FORM_sec_offset) + die->die_ck_state = CK_BAD; + break; + /* These attributes reference other sections, they + can't be moved to other files easily. */ + case DW_AT_stmt_list: + case DW_AT_macro_info: + case DW_AT_macros: + case DW_AT_GNU_macros: + if (!die->die_root) + die->die_no_multifile = 1; + break; + /* loclistptr attributes. */ + case DW_AT_location: + case DW_AT_string_length: + case DW_AT_return_addr: + case DW_AT_data_member_location: + case DW_AT_frame_base: + case DW_AT_segment: + case DW_AT_static_link: + case DW_AT_use_location: + case DW_AT_vtable_elem_location: + if ((cu->cu_version < 4 && form == DW_FORM_data4) + || form == DW_FORM_sec_offset) + { + if (read_loclist (dso, cu, die, read_32 (ptr))) + return 1; + ptr = old_ptr; + break; + } + else if (cu->cu_version < 4 && form == DW_FORM_data8) + { + if (read_loclist (dso, cu, die, read_64 (ptr))) + return 1; + ptr = old_ptr; + break; + } + break; + case DW_AT_decl_file: + case DW_AT_call_file: + switch (form) + { + case DW_FORM_data1: value = read_8 (ptr); handled = true; break; + case DW_FORM_data2: value = read_16 (ptr); handled = true; break; + case DW_FORM_data4: value = read_32 (ptr); handled = true; break; + case DW_FORM_data8: value = read_64 (ptr); handled = true; break; + case DW_FORM_udata: + value = read_uleb128 (ptr); handled = true; break; + case DW_FORM_sdata: + { + svalue = read_sleb128 (ptr); + if (svalue >= 0) + { + value = svalue; + handled = true; + break; + } + else + { + negative: + error (0, 0, "%s: negative value %" PRId64 " for %s", + dso->filename, svalue, + get_DW_AT_str (t->attr[i].attr)); + return 1; + } + } + case DW_FORM_implicit_const: + { + svalue = t->values[i]; + if (svalue >= 0) + { + value = svalue; + handled = true; + break; + } + else + goto negative; + } + default: + error (0, 0, "%s: Unhandled %s for %s", + dso->filename, get_DW_FORM_str (form), + get_DW_AT_str (t->attr[i].attr)); + return 1; + } + if (handled) + { + unsigned char *new_ptr = ptr; + ptr = old_ptr; + if (value > cu->cu_nfiles) + { + error (0, 0, "%s: Invalid %s file number %d", + dso->filename, get_DW_AT_str (t->attr[i].attr), + (int) value); + return 1; + } + if (value == 0) + handled = false; + else if (!ignore_locus && die->die_ck_state != CK_BAD) + { + struct dw_file *cu_file = &cu->cu_files[value - 1]; + size_t file_len = strlen (cu_file->file); + s = t->attr[i].attr; + hash_init_state (); + hash_update_state_object (die->u.p1.die_hash); + hash_update_state_object (s); + hash_update_state_object (cu_file->time); + hash_update_state_object (cu_file->size); + hash_update_state (cu_file->file, file_len + 1); + if (cu_file->dir) + { + hash_update_state (cu_file->dir, + strlen (cu_file->dir) + 1); + } + /* Ignore DW_AT_comp_dir for DW_AT_*_file <built-in> + etc. if immediately followed by DW_AT_*_line 0. */ + else if (cu_file->file_angle_brackets_encapsulated_no_slash + && i + 1 < t->nattr + && t->attr[i + 1].attr + == (t->attr[i].attr == DW_AT_decl_file + ? DW_AT_decl_line : DW_AT_call_line) + && t->attr[i + 1].form == DW_FORM_data1 + && *new_ptr == 0) + { + die->u.p1.die_hash = hash_digest (); + break; + } + + die->u.p1.die_hash = hash_digest (); + + if (cu->cu_comp_dir + && (cu_file->dir ? cu_file->dir[0] + : cu_file->file[0]) != '/') + die->u.p1.die_hash + = iterative_hash (cu->cu_comp_dir, + strlen (cu->cu_comp_dir) + 1, + die->u.p1.die_hash); + } + } + break; + case DW_AT_decl_line: + case DW_AT_decl_column: + case DW_AT_call_line: + case DW_AT_call_column: + if (ignore_locus) + { + handled = true; + break; + } + switch (form) + { + case DW_FORM_data1: value = read_8 (ptr); handled = true; break; + case DW_FORM_data2: value = read_16 (ptr); handled = true; break; + case DW_FORM_data4: value = read_32 (ptr); handled = true; break; + case DW_FORM_data8: value = read_64 (ptr); handled = true; break; + case DW_FORM_udata: + value = read_uleb128 (ptr); handled = true; break; + case DW_FORM_sdata: + { + svalue = read_sleb128 (ptr); + if (svalue >= 0) + { + value = svalue; + handled = true; + break; + } + else + goto negative; + } + case DW_FORM_implicit_const: + svalue = t->values[i]; + if (svalue >= 0) + { + value = svalue; + handled = true; + break; + } + else + goto negative; + default: + error (0, 0, "%s: Unhandled %s for %s", + dso->filename, get_DW_FORM_str (form), + get_DW_AT_str (t->attr[i].attr)); + return 1; + } + if (handled) + { + ptr = old_ptr; + s = t->attr[i].attr; + die->u.p1.die_hash + = iterative_hash_object (s, die->u.p1.die_hash); + die->u.p1.die_hash + = iterative_hash_object (value, die->u.p1.die_hash); + } + break; + default: + break; + } + + switch (form) + { + case DW_FORM_ref_addr: + if (unlikely (op_multifile || rd_multifile || fi_multifile)) + { + dw_die_ref ref; + + value = read_size (ptr, cu->cu_version == 2 + ? ptr_size : 4); + ptr += cu->cu_version == 2 ? ptr_size : 4; + if (die->die_ck_state != CK_BAD) + { + s = t->attr[i].attr; + die->u.p1.die_hash + = iterative_hash_object (s, die->u.p1.die_hash); + } + ref = off_htab_lookup (cu, value); + if (ref == NULL) + { + error (0, 0, "%s: Couldn't find DIE at [%" PRIx64 "] " + "referenced by %s from DIE at [%x]", + dso->filename, value, + get_DW_AT_str (t->attr[i].attr), die->die_offset); + return 1; + } + if (unlikely (op_multifile) && ref->die_collapsed_child) + ref = ref->die_parent; + if (cu == die_cu (ref)) + { + /* The reference was encoded using a section-relative + encoding, while if it could have been encoded using + CU-relative encoding. Typically, the latter is used, + because: + - it's potentially smaller, and + - it doesn't require a link-time relocation. */ + + /* Assert that the multifile only contains section-relative + encoding when necessary. */ + assert (!op_multifile && !rd_multifile); + + if (fi_multifile) + { + /* It's possible that the input DWARF contains this + sub-optimal reference. We currently don't optimize + this during single-file optimization, so it will still + be there during finalize_multifile. Bail out to handle + this conservatively. */ + die->die_ck_state = CK_BAD; + return 0; + } + } + /* Assert that during op_multifile, die belongs to the same object + as ref. */ + assert (!op_multifile || cu->cu_chunk == die_cu (ref)->cu_chunk); + handled = true; + break; + } + die->die_no_multifile = 1; + ptr += cu->cu_version == 2 ? ptr_size : 4; + break; + case DW_FORM_addr: + die->die_no_multifile = 1; + ptr += ptr_size; + break; + case DW_FORM_flag_present: + break; + case DW_FORM_implicit_const: + if (!handled && die->die_ck_state != CK_BAD) + { + handled = true; + s = t->attr[i].attr; + die->u.p1.die_hash + = iterative_hash_object (s, die->u.p1.die_hash); + die->u.p1.die_hash + = iterative_hash_object (t->values[i], die->u.p1.die_hash); + } + break; + case DW_FORM_flag: + case DW_FORM_data1: + ++ptr; + break; + case DW_FORM_data2: + ptr += 2; + break; + case DW_FORM_data4: + case DW_FORM_sec_offset: + ptr += 4; + break; + case DW_FORM_data8: + ptr += 8; + break; + case DW_FORM_data16: + ptr += 16; + break; + case DW_FORM_ref_sig8: + die->die_no_multifile = 1; + ptr += 8; + break; + case DW_FORM_sdata: + case DW_FORM_udata: + skip_leb128 (ptr); + break; + case DW_FORM_ref_udata: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + switch (form) + { + case DW_FORM_ref_udata: value = read_uleb128 (ptr); break; + case DW_FORM_ref1: value = read_8 (ptr); break; + case DW_FORM_ref2: value = read_16 (ptr); break; + case DW_FORM_ref4: value = read_32 (ptr); break; + case DW_FORM_ref8: value = read_64 (ptr); break; + default: abort (); + } + if (!handled) + { + dw_die_ref ref + = off_htab_lookup (cu, cu->cu_offset + value); + if (ref == NULL) + { + error (0, 0, "%s: Couldn't find DIE at [%" PRIx64 "] " + "referenced by %s from DIE at [%x]", + dso->filename, cu->cu_offset + value, + get_DW_AT_str (t->attr[i].attr), die->die_offset); + return 1; + } + if (die->die_ck_state != CK_BAD) + { + s = t->attr[i].attr; + die->u.p1.die_hash + = iterative_hash_object (s, die->u.p1.die_hash); + } + if (top_die + && !ref->die_collapsed_child + && ref->u.p1.die_enter >= top_die->u.p1.die_enter + && ref->u.p1.die_exit <= top_die->u.p1.die_exit) + { + if (die->die_ck_state != CK_BAD) + { + unsigned int val + = ref->u.p1.die_enter - top_die->u.p1.die_enter; + die->u.p1.die_hash + = iterative_hash_object (val, die->u.p1.die_hash); + } + } + handled = true; + } + break; + case DW_FORM_strp: + if (unlikely (op_multifile || rd_multifile || fi_multifile) + && die->die_ck_state != CK_BAD) + { + value = read_32 (ptr); + if (value >= debug_sections[DEBUG_STR].size) + die->die_ck_state = CK_BAD; + else + { + unsigned char *p = debug_sections[DEBUG_STR].data + value; + unsigned int l = strlen ((char *) p) + 1; + s = t->attr[i].attr; + die->u.p1.die_hash + = iterative_hash_object (s, die->u.p1.die_hash); + die->u.p1.die_hash + = iterative_hash (p, l, die->u.p1.die_hash); + handled = true; + } + } + else + { + ptr += 4; + if (only_hash_name_p && t->attr[i].attr == DW_AT_name) + { + s = t->attr[i].attr; + die_hash2 = iterative_hash_object (s, die_hash2); + die_hash2 + = iterative_hash (old_ptr, ptr - old_ptr, die_hash2); + } + } + break; + case DW_FORM_line_strp: + /* There is no .debug_line_str in the alt file, so we cannot + move this DIE unless we change the string reference. + This is not that bad because DW_FORM_line_strp is often + only used in the CU DIE for file name and comp_dir and we + don't move the CU DIE anyway. */ + die->die_ck_state = CK_BAD; + break; + case DW_FORM_string: + ptr = (unsigned char *) strchr ((char *)ptr, '\0') + 1; + if (only_hash_name_p && t->attr[i].attr == DW_AT_name) + { + s = t->attr[i].attr; + die_hash2 = iterative_hash_object (s, die_hash2); + die_hash2 + = iterative_hash (old_ptr, ptr - old_ptr, die_hash2); + } + break; + case DW_FORM_indirect: + abort (); + case DW_FORM_block1: + len = *ptr++; + break; + case DW_FORM_block2: + len = read_16 (ptr); + form = DW_FORM_block1; + break; + case DW_FORM_block4: + len = read_32 (ptr); + form = DW_FORM_block1; + break; + case DW_FORM_block: + len = read_uleb128 (ptr); + form = DW_FORM_block1; + break; + case DW_FORM_exprloc: + len = read_uleb128 (ptr); + break; + default: + abort (); + } + + if (form == DW_FORM_block1 && cu->cu_version < 4) + { + /* Old DWARF uses blocks instead of exprlocs. */ + switch (t->attr[i].attr) + { + case DW_AT_frame_base: + case DW_AT_location: + case DW_AT_data_member_location: + case DW_AT_vtable_elem_location: + case DW_AT_byte_size: + case DW_AT_bit_offset: + case DW_AT_bit_size: + case DW_AT_string_length: + case DW_AT_lower_bound: + case DW_AT_return_addr: + case DW_AT_bit_stride: + case DW_AT_upper_bound: + case DW_AT_count: + case DW_AT_segment: + case DW_AT_static_link: + case DW_AT_use_location: + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_data_location: + case DW_AT_byte_stride: + case DW_AT_rank: + case DW_AT_call_value: + case DW_AT_call_target: + case DW_AT_call_target_clobbered: + case DW_AT_call_data_location: + case DW_AT_call_data_value: + case DW_AT_GNU_call_site_value: + case DW_AT_GNU_call_site_data_value: + case DW_AT_GNU_call_site_target: + case DW_AT_GNU_call_site_target_clobbered: + if (die->die_ck_state != CK_BAD) + { + s = t->attr[i].attr; + die->u.p1.die_hash + = iterative_hash_object (s, die->u.p1.die_hash); + } + if (read_exprloc (dso, die, ptr, len, NULL)) + return 1; + handled = true; + default: + break; + } + } + else if (form == DW_FORM_exprloc) + { + if (die->die_ck_state != CK_BAD) + { + s = t->attr[i].attr; + die->u.p1.die_hash + = iterative_hash_object (s, die->u.p1.die_hash); + } + if (read_exprloc (dso, die, ptr, len, NULL)) + return 1; + handled = true; + } + ptr += len; /* Skip expr/blocks. */ + if (!handled && die->die_ck_state != CK_BAD) + { + s = t->attr[i].attr; + die->u.p1.die_hash = iterative_hash_object (s, die->u.p1.die_hash); + die->u.p1.die_hash + = iterative_hash (old_ptr, ptr - old_ptr, die->u.p1.die_hash); + } + + if (dump_checksum_p) + fprintf (stderr, "DIE %x, hash: %x, attr (%d)\n", die->die_offset, + die->u.p1.die_hash, i); + } + + for (child = die->die_child, i = 0; child; child = child->die_sib, ++i) + if (checksum_die (dso, cu, + top_die ? top_die + : child->die_named_namespace + ? NULL : child, child)) + return 1; + else if (die->die_ck_state != CK_BAD) + { + if (child->die_ck_state == CK_KNOWN) + { + die->u.p1.die_hash + = iterative_hash_object (child->u.p1.die_hash, + die->u.p1.die_hash); + if (dump_checksum_p) + fprintf (stderr, "DIE %x, hash: %x, child (%i)\n", + die->die_offset, die->u.p1.die_hash, i); + die->die_no_multifile + |= child->die_no_multifile; + } + else + die->die_ck_state = CK_BAD; + } + if (die->die_ck_state == CK_BEING_COMPUTED) + die->die_ck_state = CK_KNOWN; + + if (dump_checksum_p) + fprintf (stderr, "DIE %x, hash: %x, final\n", die->die_offset, + die->u.p1.die_hash); + + if (only_hash_name_p) + { + unsigned int tmp = die->u.p1.die_hash; + die->u.p1.die_hash = die_hash2; + die->u.p1.die_hash2 = tmp; + } + + return 0; +} + +/* Helper function for checksum_ref_die to sort DIE pointers + by increasing u.p1.die_hash. */ +static int +checksum_ref_die_cmp (const void *p, const void *q) +{ + dw_die_ref die1 = *(dw_die_ref *)p; + dw_die_ref die2 = *(dw_die_ref *)q; + if (die1->u.p1.die_hash < die2->u.p1.die_hash) + return -1; + if (die1->u.p1.die_hash > die2->u.p1.die_hash) + return 1; + /* The rest is just to keep the sort stable. If there is more than + one DIE with the same hash, we don't consider any of them as suitable + starting point for the walk. */ + if (die1->die_offset < die2->die_offset) + return -1; + if (die1->die_offset > die2->die_offset) + return 1; + return 0; +} + +/* This function is the second phase of hash computation, which computes + u.p1.die_ref_hash after u.p1.die_hash has been computed. + u.p1.die_ref_hash is an iterative hash of the references (other than + those checksummed already into u.p1.die_hash by checksum_die). + u.p1.die_ref_hash is only computed for the toplevel DIEs, i.e. children + of DW_TAG_*_unit or die_named_namespace DIEs. So, in the graph + containing DIEs as nodes and parent<->child and referrer<->referree edges + we virtually coalesce all children of toplevel DIEs into the + corresponding toplevel DIE ultimate parent node. The function has 4 + modes of operation: + + The first one is when TOP_DIE, SECOND_IDX and SECOND_HASH are all NULL, + this is when we walk through DW_TAG_*_unit and die_named_namespace DIEs + to reach their children. + + The second mode of operation is with TOP_DIE != NULL and both SECOND_IDX + and SECOND_HASH NULL. In this mode we optimistically assume there are no + cycles in the graph, first hash into TOP_DIE's u.p1.die_ref_hash its + u.p1.die_hash, push the TOP_DIE into a vector (in OB obstack), for each + reference if the referree isn't already toplevel DIE find its + (grand)*parent that is a toplevel DIE, recurse on that and if it computed + the referree toplevel DIE's u.p1.die_ref_hash (i.e. no cycle), + iteratively hash in the referree toplevel DIE's u.p1.die_ref_hash (and, + if referree isn't toplevel, before that also its relative position in the + subtree). When existing, remove the TOP_DIE from the vector and set + die_ref_hash_computed to note that it is computed and doesn't have to be + computed again. If there are no cycles, the return value of the function + is 0. If a cycle is found, things are more complicated. We can't just + not walk into DIEs we've already seen and compute u.p1.die_ref_hash for + toplevel DIEs on the cycle(s), because in different CUs matching cycles + might be starting computation of the hash from different nodes on the + cycle (the order of children of DW_TAG_*_unit or DW_TAG_namespace is + usually not significant in DWARF). So, if a cycle is found, the return + value is a minimum of the die_ref_seen indexes (positions in the + vector); at that point it makes no sense to further compute + u.p1.die_ref_hash of the toplevel DIEs on the cycle, but for references + to acyclic subgraphs we still continue computing their u.p1.die_ref_hash. + For DIEs on the cycle(s) pointers to them aren't immediately removed from + the vector and everything is handled only after reaching the TOP_DIE with + die_ref_seen equal to the minimum vector index (i.e. the first of + the DIEs on the cycle(s) we've seen). At this point in the vector + starting with that index should be a list of DIEs on the cycle, and all + references to (toplevel) DIEs not on that list from those DIEs should + have die_ref_hash_computed already set. If the cycle has matches in + different CUs, we should have the same set of u.p1.die_hash values in the + list in between all those CUs, but the order might be different. At this + point we try to find a DIE from which to start walk using third mode of + operation of this function. We can't base that decision on e.g. + die_offset, as it may be not just different between the CUs, but the + matching DIEs might be in different relative orders. So we look if there + is just a single DIE with lowest u.p1.die_hash value and use that in that + case, (and if not, look if there is just a single DIE with second lowest + u.p1.die_hash and so on up to 20th). If none is found, the more + expensive 4th mode of operation is used instead. + + The third mode of operation is when TOP_DIE, SECOND_IDX and SECOND_HASH + are all non-NULL. This mode is used when the initial TOP_DIE is + a uniquely chosen DIE on the cycle (same in each CU that has + matching cycle). In this mode into each TOP_DIE's u.p1.die_ref_hash + we hash in *SECOND_IDX (counter when in that walk the DIE has been + seen first) and relative referree positions in subtree, and hash in + referree toplevel u.p1.die_ref_hash into *SECOND_HASH, and finally + when the walk finishes, the caller will compute the final + u.p1.die_ref_hash for DIEs on the cycle(s) from their intermediate + u.p1.die_ref_hash and *SECOND_HASH. + + The last mode of operation is when TOP_DIE and SECOND_HASH + are non-NULL, but SECOND_IDX is NULL. This mode is used when no + suitable DIE from which to start walking the cycle has been discovered. + In that case we for each DIE on the cycle walk everything that hasn't + die_ref_hash_computed yet, for DIEs seen that have die_ref_hash_computed + hash in their u.p1.die_ref_hash, otherwise (DIEs on the current cycle(s)) + hash in their u.p1.die_hash. */ +static unsigned int +checksum_ref_die (dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die, + unsigned int *second_idx, hashval_t *second_hash) +{ + struct abbrev_tag *t; + unsigned int i, ret = 0; + unsigned char *ptr; + dw_die_ref child; + + if (top_die == die) + { + if (die->die_ref_hash_computed) + return 0; + if (die->die_ck_state != CK_KNOWN) + return 0; + if (die->die_ref_seen) + return second_hash != NULL ? 0 : die->die_ref_seen; + if (second_hash != NULL) + { + die->die_ref_seen = 1; + if (second_idx != NULL) + { + die->u.p1.die_ref_hash + = iterative_hash_object (*second_idx, die->u.p1.die_hash); + (*second_idx)++; + } + } + else + { + die->die_ref_seen + = obstack_object_size (&ob) / sizeof (void *) + 1; + obstack_ptr_grow (&ob, die); + die->u.p1.die_ref_hash = die->u.p1.die_hash; + } + } + else + assert (top_die == NULL || die->die_ck_state == CK_KNOWN); + t = die->die_abbrev; + for (i = 0; i < t->nattr; ++i) + if (t->attr[i].attr != DW_AT_sibling) + switch (t->attr[i].form) + { + case DW_FORM_ref_addr: + if (unlikely (op_multifile || rd_multifile || fi_multifile)) + i = -2U; + break; + case DW_FORM_ref_udata: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_indirect: + i = -2U; + break; + } + if (i == -1U) + { + ptr = debug_sections[DEBUG_INFO].data + die->die_offset; + skip_leb128 (ptr); + for (i = 0; i < t->nattr; ++i) + { + uint32_t form = t->attr[i].form; + size_t len = 0; + uint64_t value; + dw_die_ref ref, reft; + + while (form == DW_FORM_indirect) + form = read_uleb128 (ptr); + + switch (form) + { + case DW_FORM_ref_addr: + if (unlikely (op_multifile || rd_multifile || fi_multifile)) + { + value = read_size (ptr, cu->cu_version == 2 ? ptr_size : 4); + ptr += cu->cu_version == 2 ? ptr_size : 4; + assert (t->attr[i].attr != DW_AT_sibling); + if (top_die == NULL) + break; + ref = off_htab_lookup (cu, value); + goto finish_ref; + } + ptr += cu->cu_version == 2 ? ptr_size : 4; + break; + case DW_FORM_addr: + ptr += ptr_size; + break; + case DW_FORM_flag_present: + case DW_FORM_implicit_const: + break; + case DW_FORM_flag: + case DW_FORM_data1: + ++ptr; + break; + case DW_FORM_data2: + ptr += 2; + break; + case DW_FORM_data4: + case DW_FORM_sec_offset: + case DW_FORM_strp: + case DW_FORM_line_strp: + ptr += 4; + break; + case DW_FORM_data8: + case DW_FORM_ref_sig8: + ptr += 8; + break; + case DW_FORM_data16: + ptr += 16; + break; + case DW_FORM_sdata: + case DW_FORM_udata: + skip_leb128 (ptr); + break; + case DW_FORM_ref_udata: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + switch (form) + { + case DW_FORM_ref_udata: value = read_uleb128 (ptr); break; + case DW_FORM_ref1: value = read_8 (ptr); break; + case DW_FORM_ref2: value = read_16 (ptr); break; + case DW_FORM_ref4: value = read_32 (ptr); break; + case DW_FORM_ref8: value = read_64 (ptr); break; + default: abort (); + } + if (t->attr[i].attr == DW_AT_sibling || top_die == NULL) + break; + ref = off_htab_lookup (cu, cu->cu_offset + value); + if (ref->u.p1.die_enter >= top_die->u.p1.die_enter + && ref->u.p1.die_exit <= top_die->u.p1.die_exit) + break; + finish_ref: + reft = ref; + while (!reft->die_root + && reft->die_parent->die_tag != DW_TAG_compile_unit + && reft->die_parent->die_tag != DW_TAG_partial_unit + && !reft->die_parent->die_named_namespace) + reft = reft->die_parent; + if (reft->die_ck_state != CK_KNOWN || reft->die_root) + top_die->die_ck_state = CK_BAD; + else + { + unsigned int r = checksum_ref_die (die_cu (reft), reft, reft, + second_idx, second_hash); + if (ret == 0 || (r && r < ret)) + ret = r; + if (reft->die_ck_state != CK_KNOWN) + top_die->die_ck_state = CK_BAD; + else + top_die->die_no_multifile |= reft->die_no_multifile; + } + if (top_die->die_ck_state == CK_BAD) + { + if (top_die != die) + return ret; + i = t->nattr - 1; + break; + } + if (ret) + break; + if (reft != ref) + { + unsigned int val + = ref->u.p1.die_enter - reft->u.p1.die_enter; + if (second_hash != NULL && second_idx == NULL) + *second_hash + = iterative_hash_object (val, *second_hash); + else + top_die->u.p1.die_ref_hash + = iterative_hash_object (val, + top_die->u.p1.die_ref_hash); + } + if (second_hash) + { + if (second_idx == NULL && !reft->die_ref_hash_computed) + *second_hash + = iterative_hash_object (reft->u.p1.die_hash, + *second_hash); + else + *second_hash + = iterative_hash_object (reft->u.p1.die_ref_hash, + *second_hash); + } + else + top_die->u.p1.die_ref_hash + = iterative_hash_object (reft->u.p1.die_ref_hash, + top_die->u.p1.die_ref_hash); + break; + case DW_FORM_string: + ptr = (unsigned char *) strchr ((char *)ptr, '\0') + 1; + break; + case DW_FORM_indirect: + abort (); + case DW_FORM_block1: + len = *ptr++; + break; + case DW_FORM_block2: + len = read_16 (ptr); + form = DW_FORM_block1; + break; + case DW_FORM_block4: + len = read_32 (ptr); + form = DW_FORM_block1; + break; + case DW_FORM_block: + case DW_FORM_exprloc: + len = read_uleb128 (ptr); + form = DW_FORM_block1; + break; + default: + abort (); + } + + if (form == DW_FORM_block1) + ptr += len; + } + } + + if (top_die == NULL || top_die->die_ck_state != CK_BAD) + { + for (child = die->die_child; child; child = child->die_sib) + { + unsigned int r + = checksum_ref_die (cu, + top_die ? top_die + : child->die_named_namespace + ? NULL : child, child, + second_idx, second_hash); + if (top_die == NULL) + assert (r == 0 && obstack_object_size (&ob) == 0); + + if (ret == 0 || (r && r < ret)) + ret = r; + if (top_die && top_die->die_ck_state == CK_BAD) + break; + } + } + + if (top_die == die) + { + if (ret == 0) + { + if (second_hash != NULL) + return 0; + die->die_ref_seen = 0; + die->die_ref_hash_computed = 1; + obstack_blank_fast (&ob, -(int) sizeof (void *)); + return 0; + } + assert (ret <= die->die_ref_seen); + if (ret == die->die_ref_seen) + { + unsigned int first = die->die_ref_seen - 1; + dw_die_ref *arr; + unsigned int count + = obstack_object_size (&ob) / sizeof (void *) - first; + unsigned int idx, minidx; + hashval_t ref_hash = 0; + bool bad = false; + bool no_multifile = false; + + arr = (dw_die_ref *) obstack_base (&ob) + first; + for (i = 0; i < count; i++) + { + arr[i]->die_ref_seen = 0; + if (arr[i]->die_ck_state == CK_BAD) + bad = true; + else if (arr[i]->die_no_multifile) + no_multifile = true; + } + if (bad) + { + for (i = 0; i < count; i++) + arr[i]->die_ck_state = CK_BAD; + obstack_blank_fast (&ob, -(int) (count * sizeof (void *))); + return 0; + } + /* Find the DIE in the array with the smallest u.p1.die_hash. */ + for (i = 0, minidx = -1U, bad = true; i < count; i++) + { + if (no_multifile) + arr[i]->die_no_multifile = 1; + if (minidx == -1U + || arr[i]->u.p1.die_hash < arr[minidx]->u.p1.die_hash) + { + minidx = i; + bad = false; + } + else if (arr[i]->u.p1.die_hash == arr[minidx]->u.p1.die_hash) + bad = true; + } + if (bad) + { + unsigned int iter, limv; + /* If there is more than one smallest u.p1.die_hash, + look for second (up to 6th) smallest u.p1.die_hash + if there is just one of that value. */ + for (iter = 0; iter < 5; iter++) + { + limv = arr[minidx]->u.p1.die_hash; + for (i = 0, minidx = -1U, bad = true; i < count; i++) + if (arr[i]->u.p1.die_hash <= limv) + continue; + else if (minidx == -1U + || arr[i]->u.p1.die_hash + < arr[minidx]->u.p1.die_hash) + { + minidx = i; + bad = false; + } + else if (arr[i]->u.p1.die_hash + == arr[minidx]->u.p1.die_hash) + bad = true; + if (minidx == -1U || !bad) + break; + } + /* If all of 1st to 6th smallest u.p1.die_hash has more than + one DIE with that u.p1.die_hash, sort the array and find + the smallest u.p1.die_hash that only a single DIE has. */ + if (minidx != -1U && iter == 5) + { + unsigned int j; + qsort (arr, count, sizeof (void *), checksum_ref_die_cmp); + for (i = 0, minidx = -1U; i < count; i = j) + { + if (i + 1 == count + || arr[i + 1]->u.p1.die_hash + != arr[i]->u.p1.die_hash) + { + minidx = i; + break; + } + for (j = i + 1; j < count; j++) + if (arr[j]->u.p1.die_hash != arr[i]->u.p1.die_hash) + break; + } + } + } + if (checksum_cycle_opt && minidx != -1U) + { + idx = 0; + checksum_ref_die (die_cu (arr[minidx]), arr[minidx], + arr[minidx], &idx, &ref_hash); + assert (arr == (dw_die_ref *) obstack_base (&ob) + first); + for (i = 0; i < count; i++) + { + arr[i]->u.p1.die_ref_hash + = iterative_hash_object (arr[i]->u.p1.die_ref_hash, + ref_hash); + arr[i]->die_ref_hash_computed = 1; + arr[i]->die_ref_seen = 0; + } + } + else + { + /* If we get here, all u.p1.die_hash values in the arr array + are used by more than one DIE. Do the more expensive + computation as fallback. */ + for (i = 0; i < count; i++) + { + unsigned int j; + arr[i]->u.p1.die_ref_hash = arr[i]->u.p1.die_hash; + checksum_ref_die (die_cu (arr[i]), arr[i], arr[i], NULL, + &arr[i]->u.p1.die_ref_hash); + assert (arr == (dw_die_ref *) obstack_base (&ob) + first); + for (j = 0; j < count; j++) + arr[j]->die_ref_seen = 0; + } + for (i = 0; i < count; i++) + arr[i]->die_ref_hash_computed = 1; + } + obstack_blank_fast (&ob, -(int) (count * sizeof (void *))); + return 0; + } + } + return ret; +} + +/* Hash function for dup_htab. u.p1.die_ref_hash should have u.p1.die_hash + iteratively hashed into it already. */ +static hashval_t +die_hash (const void *p) +{ + dw_die_ref die = (dw_die_ref) p; + + return die->u.p1.die_ref_hash; +} + +/* Freelist of !die->die_toplevel DIEs, chained through die_sib fields. */ +static dw_die_ref die_nontoplevel_freelist; +/* Freelist of die->die_collapsed_child DIEs, chained through die_parent + fields. */ +static dw_die_ref die_collapsed_child_freelist; + +/* Return pointer after the attributes of a DIE from a cu with CU_VERSION + which uses abbrevs T and starts at PTR. */ +static unsigned char * +skip_attrs_1 (unsigned int cu_version, struct abbrev_tag *t, unsigned char *ptr) +{ + unsigned int i; + for (i = 0; i < t->nattr; ++i) + ptr = skip_attr (cu_version, &t->attr[i], ptr); + + return ptr; +} + +/* Return pointer after the attributes of a DIE from CU which uses abbrevs + T and starts at PTR. */ +static unsigned char * +skip_attrs (dw_cu_ref cu, struct abbrev_tag *t, unsigned char *ptr) +{ + return skip_attrs_1 (cu->cu_version, t, ptr); +} + +/* Expand children of TOP_DIE that have been collapsed by + collapse_child. CHECKSUM is true if checksum should be + computed - expansion is performed during read_debug_info + when duplicates are looked for - or false, if the expansion + is performed late (e.g. during compute_abbrevs or write_{info,types}. */ +static void +expand_child (dw_die_ref top_die, bool checksum) +{ + dw_cu_ref cu = die_cu (top_die); + dw_die_ref *diep = &top_die->die_child; + dw_die_ref parent = top_die, child; + unsigned char *ptr, *base; + struct abbrev_tag tag, *t; + dw_die_ref die; + unsigned int tick = checksum ? top_die->u.p1.die_enter + 1 : 0; + + if (unlikely (cu->cu_kind == CU_TYPES)) + base = debug_sections[DEBUG_TYPES].data; + else + base = debug_sections[DEBUG_INFO].data; + ptr = base + top_die->die_offset; + if (likely (checksum)) + ptr += top_die->die_size; + else + { + t = top_die->die_abbrev; + skip_leb128 (ptr); + ptr = skip_attrs (cu, t, ptr); + } + + while (1) + { + unsigned int die_offset = ptr - base; + void **slot; + struct dw_die diebuf; + dw_die_ref collapsed; + + tag.entry = read_uleb128 (ptr); + if (tag.entry == 0) + { + if (parent == top_die) + break; + diep = &parent->die_sib; + if (checksum) + parent->u.p1.die_exit = tick++; + parent = parent->die_parent; + continue; + } + + diebuf.die_offset = die_offset; + slot = htab_find_slot_with_hash (cu->cu_kind == CU_TYPES + ? types_off_htab : off_htab, + &diebuf, off_hash (&diebuf), NO_INSERT); + if (slot == NULL) + die = NULL; + else + die = (dw_die_ref) *slot; + if (die != NULL && !die->die_collapsed_child) + { + *diep = die; + die->die_parent = parent; + die->die_ck_state = CK_UNKNOWN; + die->die_ref_seen = 0; + assert (!checksum || die->u.p1.die_enter == tick); + if (die->die_abbrev->children) + { + diep = &die->die_child; + parent = die; + } + else + { + diep = &die->die_sib; + assert (!checksum || die->u.p1.die_exit == tick); + } + tick++; + if (checksum) + ptr = base + die_offset + die->die_size; + else + ptr = skip_attrs (cu, die->die_abbrev, ptr); + continue; + } + + collapsed = die; + t = htab_find_with_hash (cu->cu_abbrev, &tag, tag.entry); + if (die_nontoplevel_freelist) + { + die = die_nontoplevel_freelist; + die_nontoplevel_freelist = die->die_sib; + } + else + die = pool_alloc (dw_die, offsetof (struct dw_die, die_dup)); + memset (die, '\0', offsetof (struct dw_die, die_dup)); + *diep = die; + die->die_tag = t->tag; + die->die_abbrev = t; + die->die_offset = die_offset; + die->die_parent = parent; + if (checksum) + { + die->u.p1.die_enter = tick; + die->u.p1.die_exit = tick++; + } + if (t->children) + { + diep = &die->die_child; + parent = die; + } + else + diep = &die->die_sib; + + ptr = skip_attrs (cu, t, ptr); + die->die_size = (ptr - base) - die_offset; + if (collapsed != NULL) + { + die->die_referenced = collapsed->die_referenced; + *slot = (void *) die; + memset (collapsed, '\0', offsetof (struct dw_die, die_child)); + collapsed->die_parent = die_collapsed_child_freelist; + die_collapsed_child_freelist = collapsed; + } + } + assert (!checksum || top_die->u.p1.die_exit == tick); + top_die->die_collapsed_children = 0; + if (checksum && likely (cu->cu_kind != CU_TYPES)) + for (child = top_die->die_child; child; child = child->die_sib) + checksum_die (NULL, cu, top_die, child); +} + +/* Call expand_child on all collapsed toplevel children DIEs. */ +static bool +expand_children (dw_die_ref die) +{ + dw_die_ref child; + bool ret = false; + for (child = die->die_child; child; child = child->die_sib) + if (child->die_named_namespace) + ret |= expand_children (child); + else if (child->die_collapsed_children) + { + expand_child (child, false); + ret = true; + } + return ret; +} + +static unsigned odr_phase; + +/* Return 1 if DIE1 and DIE2 match. TOP_DIE1 and TOP_DIE2 + is the corresponding ultimate parent with die_toplevel + set. u.p1.die_hash and u.p1.die_ref_hash hashes should + hopefully ensure that in most cases this function actually + just verifies matching. */ +static int +die_eq_1 (dw_cu_ref cu1, dw_cu_ref cu2, + dw_die_ref top_die1, dw_die_ref top_die2, + dw_die_ref die1, dw_die_ref die2) +{ + struct abbrev_tag *t1, *t2; + unsigned int i, j; + unsigned char *ptr1, *ptr2; + dw_die_ref ref1, ref2; + dw_die_ref child1, child2; + bool only_compare_name_p; + +#define FAIL goto fail + if (die1 == die2 || die_safe_dup (die2) == die1) + return 1; + if (die1->u.p1.die_hash != die2->u.p1.die_hash + || die1->u.p1.die_ref_hash != die2->u.p1.die_ref_hash + || die1->die_tag != die2->die_tag + || (!odr && (die1->u.p1.die_exit - die1->u.p1.die_enter + != die2->u.p1.die_exit - die2->u.p1.die_enter)) + || die_safe_dup (die2) != NULL + || die1->die_ck_state != CK_KNOWN + || die2->die_ck_state != CK_KNOWN + || die1->die_toplevel != die2->die_toplevel) + return 0; + assert (!die1->die_root && !die2->die_root); + + if (uni_lang_p && die1 == top_die1 && die2 == top_die2 + && cu1->lang != cu2->lang) + return 0; + + only_compare_name_p + = odr && die1->die_odr_state != ODR_NONE && die2->die_odr_state != ODR_NONE; + + if (only_compare_name_p && odr_phase == 1) + { + const char *name1 = get_AT_string (die1, DW_AT_name); + const char *name2 = get_AT_string (die2, DW_AT_name); + // TODO: Handle DW_AT_linkage_name? + if (name1 == NULL || name2 == NULL) + return 0; + if (strcmp (name1, name2) != 0) + return 0; + } + else if (die1->u.p1.die_exit - die1->u.p1.die_enter + != die2->u.p1.die_exit - die2->u.p1.die_enter) + return 0; + + if (only_compare_name_p && odr_phase == 2 + && die1->die_odr_state == ODR_DEF && die2->die_odr_state == ODR_DEF) + { + if (die1->u.p1.die_hash2 != die2->u.p1.die_hash2) + return 0; + } + + t1 = die1->die_abbrev; + t2 = die2->die_abbrev; + if (likely (!fi_multifile)) + { + ptr1 = debug_sections[DEBUG_INFO].data + die1->die_offset; + ptr2 = debug_sections[DEBUG_INFO].data + die2->die_offset; + } + else + { + if (cu1->cu_kind == CU_ALT) + ptr1 = alt_data[DEBUG_INFO]; + else + ptr1 = debug_sections[DEBUG_INFO].data; + ptr1 += die1->die_offset; + if (cu2->cu_kind == CU_ALT) + ptr2 = alt_data[DEBUG_INFO]; + else + ptr2 = debug_sections[DEBUG_INFO].data; + ptr2 += die2->die_offset; + } + skip_leb128 (ptr1); + skip_leb128 (ptr2); + i = 0; + j = 0; + if (die1->die_toplevel) + { + for (ref1 = die1->die_parent, ref2 = die2->die_parent; ref1 && ref2; ) + { + const char *name1, *name2; + if ((ref1->die_tag == DW_TAG_compile_unit + || ref1->die_tag == DW_TAG_partial_unit) + && (ref2->die_tag == DW_TAG_compile_unit + || ref2->die_tag == DW_TAG_partial_unit)) + break; + if (ref1->die_tag != ref2->die_tag) + return 0; + if (!ref1->die_named_namespace || !ref2->die_named_namespace) + return 0; + name1 = get_AT_string (ref1, DW_AT_name); + name2 = get_AT_string (ref2, DW_AT_name); + if (strcmp (name1, name2)) + return 0; + ref1 = ref1->die_root ? NULL : ref1->die_parent; + ref2 = ref2->die_root ? NULL : ref2->die_parent; + } + if (ref1 == NULL || ref2 == NULL) + return 0; + /* For each toplevel die seen, record optimistically + that we expect them to match, to avoid recursing + on it again. If non-match is determined later, + die_eq wrapper undoes this (which is why the DIE + pointer is added to the vector). */ + if (!die2->die_op_type_referenced) + die2->die_remove = 1; + obstack_ptr_grow (&ob, die2); + if (likely (die2->die_nextdup == NULL)) + { + die2->die_dup = die1; + die2->die_nextdup = die1->die_nextdup; + obstack_ptr_grow (&ob, NULL); + } + else + { + dw_die_ref next; + for (next = die2; next->die_nextdup; next = next->die_nextdup) + next->die_dup = die1; + next->die_dup = die1; + next->die_nextdup = die1->die_nextdup; + obstack_ptr_grow (&ob, next); + } + die1->die_nextdup = die2; + } + + if (only_compare_name_p && odr_phase == 1) + return 1; + + while (1) + { + uint32_t form1, form2; + size_t len = 0; + unsigned char *old_ptr1; + unsigned char *old_ptr2; + uint64_t value1, value2; + + while (i < t1->nattr && t1->attr[i].attr == DW_AT_sibling) + { + form1 = t1->attr[i].form; + while (form1 == DW_FORM_indirect) + form1 = read_uleb128 (ptr1); + switch (form1) + { + case DW_FORM_ref_udata: skip_leb128 (ptr1); break; + case DW_FORM_ref1: ptr1++; break; + case DW_FORM_ref2: read_16 (ptr1); break; + case DW_FORM_ref4: read_32 (ptr1); break; + case DW_FORM_ref8: read_64 (ptr1); break; + default: FAIL; + } + i++; + } + while (j < t2->nattr && t2->attr[j].attr == DW_AT_sibling) + { + form2 = t2->attr[j].form; + while (form2 == DW_FORM_indirect) + form2 = read_uleb128 (ptr2); + switch (form2) + { + case DW_FORM_ref_udata: skip_leb128 (ptr2); break; + case DW_FORM_ref1: ptr2++; break; + case DW_FORM_ref2: read_16 (ptr2); break; + case DW_FORM_ref4: read_32 (ptr2); break; + case DW_FORM_ref8: read_64 (ptr2); break; + default: FAIL; + } + j++; + } + if (i == t1->nattr) + { + if (j != t2->nattr) + FAIL; + break; + } + if (j == t2->nattr) + FAIL; + + if (t1->attr[i].attr != t2->attr[j].attr) + FAIL; + + form1 = t1->attr[i].form; + while (form1 == DW_FORM_indirect) + form1 = read_uleb128 (ptr1); + form2 = t2->attr[j].form; + while (form2 == DW_FORM_indirect) + form2 = read_uleb128 (ptr2); + old_ptr1 = ptr1; + old_ptr2 = ptr2; + + switch (t1->attr[i].attr) + { + case DW_AT_sibling: + case DW_AT_low_pc: + case DW_AT_high_pc: + case DW_AT_entry_pc: + case DW_AT_ranges: + case DW_AT_call_return_pc: + case DW_AT_call_pc: + /* We shouldn't be hitting DIEs with attributes referencing + addresses and we should have removed DW_AT_subling. */ + abort (); + case DW_AT_decl_file: + case DW_AT_call_file: + switch (form1) + { + case DW_FORM_data1: value1 = read_8 (ptr1); break; + case DW_FORM_data2: value1 = read_16 (ptr1); break; + case DW_FORM_data4: value1 = read_32 (ptr1); break; + case DW_FORM_data8: value1 = read_64 (ptr1); break; + case DW_FORM_udata: value1 = read_uleb128 (ptr1); break; + case DW_FORM_sdata: value1 = read_sleb128 (ptr1); break; + case DW_FORM_implicit_const: value1 = t1->values[i]; break; + default: abort (); + } + switch (form2) + { + case DW_FORM_data1: value2 = read_8 (ptr2); break; + case DW_FORM_data2: value2 = read_16 (ptr2); break; + case DW_FORM_data4: value2 = read_32 (ptr2); break; + case DW_FORM_data8: value2 = read_64 (ptr2); break; + case DW_FORM_udata: value2 = read_uleb128 (ptr2); break; + case DW_FORM_sdata: value2 = read_sleb128 (ptr2); break; + case DW_FORM_implicit_const: value2 = t2->values[j]; break; + default: abort (); + } + if (ignore_locus) + { + i++; + j++; + continue; + } + if ((value1 == 0) ^ (value2 == 0)) + FAIL; + if (value1 != 0) + { + struct dw_file *cu_file1 + = &cu1->cu_files[value1 - 1]; + struct dw_file *cu_file2 + = &cu2->cu_files[value2 - 1]; + + if (cu_file1->time != cu_file2->time + || cu_file1->size != cu_file2->size + || strcmp (cu_file1->file, cu_file2->file)) + FAIL; + + if (cu_file1->dir != NULL) + { + if (cu_file2->dir == NULL + || strcmp (cu_file1->dir, cu_file2->dir)) + FAIL; + } + else if (cu_file2->dir != NULL) + FAIL; + /* Ignore DW_AT_comp_dir for DW_AT_*_file <built-in> + etc. if immediately followed by DW_AT_*_line 0. */ + else if (cu_file1->file_angle_brackets_encapsulated_no_slash + && i + 1 < t1->nattr + && j + 1 < t2->nattr + && t1->attr[i + 1].attr + == (t1->attr[i].attr == DW_AT_decl_file + ? DW_AT_decl_line : DW_AT_call_line) + && t1->attr[i + 1].form == DW_FORM_data1 + && t1->attr[i + 1].attr == t2->attr[j + 1].attr + && t2->attr[j + 1].form == DW_FORM_data1 + && *ptr1 == 0 + && *ptr2 == 0) + { + i++; + j++; + continue; + } + + if ((cu_file1->dir ? cu_file1->dir[0] : cu_file1->file[0]) + != '/') + { + if (cu1->cu_comp_dir != NULL) + { + if (cu2->cu_comp_dir == NULL + || strcmp (cu1->cu_comp_dir, cu2->cu_comp_dir)) + FAIL; + } + else if (cu2->cu_comp_dir != NULL) + FAIL; + } + } + i++; + j++; + continue; + case DW_AT_decl_line: + case DW_AT_decl_column: + case DW_AT_call_line: + case DW_AT_call_column: + if (ignore_locus) + { + old_ptr1 = NULL; + break; + } + switch (form1) + { + case DW_FORM_data1: value1 = read_8 (ptr1); break; + case DW_FORM_data2: value1 = read_16 (ptr1); break; + case DW_FORM_data4: value1 = read_32 (ptr1); break; + case DW_FORM_data8: value1 = read_64 (ptr1); break; + case DW_FORM_udata: value1 = read_uleb128 (ptr1); break; + case DW_FORM_sdata: value1 = read_sleb128 (ptr1); break; + case DW_FORM_implicit_const: value1 = t1->values[i]; break; + default: abort (); + } + switch (form2) + { + case DW_FORM_data1: value2 = read_8 (ptr2); break; + case DW_FORM_data2: value2 = read_16 (ptr2); break; + case DW_FORM_data4: value2 = read_32 (ptr2); break; + case DW_FORM_data8: value2 = read_64 (ptr2); break; + case DW_FORM_udata: value2 = read_uleb128 (ptr2); break; + case DW_FORM_sdata: value2 = read_sleb128 (ptr2); break; + case DW_FORM_implicit_const: value2 = t2->values[j]; break; + default: abort (); + } + if (value1 != value2) + FAIL; + i++; + j++; + continue; + default: + break; + } + + switch (form1) + { + case DW_FORM_ref_addr: + if (likely (!op_multifile && !rd_multifile && !fi_multifile)) + { + if (form1 != form2) + FAIL; + break; + } + /* FALLTHRU */ + case DW_FORM_ref_udata: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + switch (form2) + { + case DW_FORM_ref_addr: + if (likely (!op_multifile && !rd_multifile && !fi_multifile)) + FAIL; + break; + case DW_FORM_ref_udata: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + break; + default: + FAIL; + } + break; + default: + if (form1 != form2) + FAIL; + break; + } + + switch (form1) + { + case DW_FORM_addr: + ptr1 += ptr_size; + ptr2 += ptr_size; + break; + case DW_FORM_flag_present: + break; + case DW_FORM_implicit_const: + if ((!ignore_locus || old_ptr1) && t1->values[i] != t2->values[j]) + FAIL; + break; + case DW_FORM_flag: + case DW_FORM_data1: + ++ptr1; + ++ptr2; + break; + case DW_FORM_data2: + ptr1 += 2; + ptr2 += 2; + break; + case DW_FORM_data4: + case DW_FORM_sec_offset: + ptr1 += 4; + ptr2 += 4; + break; + case DW_FORM_data8: + case DW_FORM_ref_sig8: + ptr1 += 8; + ptr2 += 8; + break; + case DW_FORM_data16: + ptr1 += 16; + ptr2 += 16; + break; + case DW_FORM_sdata: + case DW_FORM_udata: + skip_leb128 (ptr1); + skip_leb128 (ptr2); + break; + case DW_FORM_strp: + if (unlikely (op_multifile || rd_multifile || fi_multifile)) + { + value1 = read_32 (ptr1); + value2 = read_32 (ptr2); + if (fi_multifile) + { + if (strcmp ((char *) (cu1->cu_kind == CU_ALT + ? alt_data[DEBUG_STR] + : debug_sections[DEBUG_STR].data) + + value1, + (char *) (cu2->cu_kind == CU_ALT + ? alt_data[DEBUG_STR] + : debug_sections[DEBUG_STR].data) + + value2) != 0) + FAIL; + i++; + j++; + continue; + } + if (strcmp ((char *) debug_sections[DEBUG_STR].data + value1, + (char *) debug_sections[DEBUG_STR].data + value2) + != 0) + FAIL; + i++; + j++; + continue; + } + ptr1 += 4; + ptr2 += 4; + break; + case DW_FORM_line_strp: + ptr1 += 4; + ptr2 += 4; + break; + case DW_FORM_string: + ptr1 = (unsigned char *) strchr ((char *)ptr1, '\0') + 1; + ptr2 = (unsigned char *) strchr ((char *)ptr2, '\0') + 1; + break; + case DW_FORM_indirect: + abort (); + case DW_FORM_block1: + len = *ptr1++; + ptr1 += len; + len = *ptr2++; + ptr2 += len; + break; + case DW_FORM_block2: + len = read_16 (ptr1); + ptr1 += len; + len = read_16 (ptr2); + ptr2 += len; + break; + case DW_FORM_block4: + len = read_32 (ptr1); + ptr1 += len; + len = read_32 (ptr2); + ptr2 += len; + break; + case DW_FORM_block: + case DW_FORM_exprloc: + len = read_uleb128 (ptr1); + ptr1 += len; + len = read_uleb128 (ptr2); + ptr2 += len; + break; + case DW_FORM_ref_addr: + if (likely (!op_multifile && !rd_multifile && !fi_multifile)) + { + ptr1 += cu1->cu_version == 2 ? ptr_size : 4; + ptr2 += cu2->cu_version == 2 ? ptr_size : 4; + break; + } + /* FALLTHRU */ + case DW_FORM_ref_udata: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + switch (form1) + { + case DW_FORM_ref_addr: + value1 = read_size (ptr1, cu1->cu_version == 2 + ? ptr_size : 4) + - cu1->cu_offset; + ptr1 += cu1->cu_version == 2 ? ptr_size : 4; + break; + case DW_FORM_ref_udata: + value1 = read_uleb128 (ptr1); + break; + case DW_FORM_ref1: + value1 = read_8 (ptr1); + break; + case DW_FORM_ref2: + value1 = read_16 (ptr1); + break; + case DW_FORM_ref4: + value1 = read_32 (ptr1); + break; + case DW_FORM_ref8: + value1 = read_64 (ptr1); + break; + default: abort (); + } + ref1 = off_htab_lookup (cu1, cu1->cu_offset + value1); + switch (form2) + { + case DW_FORM_ref_addr: + value2 = read_size (ptr2, cu2->cu_version == 2 + ? ptr_size : 4) + - cu2->cu_offset; + ptr2 += cu2->cu_version == 2 ? ptr_size : 4; + break; + case DW_FORM_ref_udata: + value2 = read_uleb128 (ptr2); + break; + case DW_FORM_ref1: + value2 = read_8 (ptr2); + break; + case DW_FORM_ref2: + value2 = read_16 (ptr2); + break; + case DW_FORM_ref4: + value2 = read_32 (ptr2); + break; + case DW_FORM_ref8: + value2 = read_64 (ptr2); + break; + default: abort (); + } + ref2 = off_htab_lookup (cu2, cu2->cu_offset + value2); + assert (ref1 != NULL && ref2 != NULL); + if (unlikely (op_multifile || low_mem)) + { + if (die1->die_collapsed_children && ref1->die_collapsed_child) + { + expand_child (die1, true); + ref1 = off_htab_lookup (cu1, cu1->cu_offset + value1); + } + assert (ref2->die_collapsed_child == 0); + } + if (likely (!ref1->die_collapsed_child) + && die_cu (ref1) == cu1 + && ref1->u.p1.die_enter >= top_die1->u.p1.die_enter + && ref1->u.p1.die_exit <= top_die1->u.p1.die_exit) + { + /* A reference into a subdie of the DIE being compared. */ + if (die_cu (ref2) != cu2 + || ref1->u.p1.die_enter - top_die1->u.p1.die_enter + != ref2->u.p1.die_enter - top_die2->u.p1.die_enter + || top_die1->u.p1.die_exit - ref1->u.p1.die_exit + != top_die2->u.p1.die_exit - ref2->u.p1.die_exit) + FAIL; + } + else + { + dw_die_ref reft1 = ref1, reft2 = ref2; + dw_cu_ref refcu1, refcu2; + while (reft1->die_toplevel == 0) + reft1 = reft1->die_parent; + while (reft2->die_toplevel == 0) + reft2 = reft2->die_parent; + if (unlikely (ref1->die_collapsed_child)) + { + if (ref1->die_tag + != ref2->u.p1.die_enter - reft2->u.p1.die_enter) + FAIL; + } + else if (ref1->u.p1.die_enter - reft1->u.p1.die_enter + != ref2->u.p1.die_enter - reft2->u.p1.die_enter) + FAIL; + refcu1 = die_cu (reft1); + refcu2 = die_cu (reft2); + if (unlikely (refcu1->cu_chunk == refcu2->cu_chunk) + && likely (!fi_multifile)) + { + if (reft1->die_dup + && die_cu (reft1->die_dup)->cu_chunk + == refcu1->cu_chunk) + reft1 = reft1->die_dup; + if (reft2->die_dup + && die_cu (reft2->die_dup)->cu_chunk + == refcu2->cu_chunk) + reft2 = reft2->die_dup; + if (reft2->die_offset < reft1->die_offset) + { + dw_die_ref tem = reft1; + reft1 = reft2; + reft2 = tem; + } + if (reft1->die_dup == NULL && reft2->die_dup != NULL) + { + dw_die_ref tem = reft1; + reft1 = reft2; + reft2 = tem; + } + } + /* If reft1 (die1 or whatever refers to it is already + in the hash table) already has a dup, follow to that + dup. Don't do the same for reft2, {{top_,}die,reft,child}2 + should always be from the current CU. */ + if (reft1->die_dup) + reft1 = reft1->die_dup; + refcu1 = die_cu (reft1); + refcu2 = die_cu (reft2); + if (die_eq_1 (refcu1, refcu2, reft1, reft2, reft1, reft2) == 0) + FAIL; + } + i++; + j++; + continue; + default: + abort (); + } + + if ((!ignore_locus || old_ptr1) + && (ptr1 - old_ptr1 != ptr2 - old_ptr2 + || memcmp (old_ptr1, old_ptr2, ptr1 - old_ptr1))) + FAIL; + i++; + j++; + } + + if (unlikely (op_multifile || low_mem)) + { + if (die1->die_collapsed_children) + expand_child (die1, true); + assert (die2->die_collapsed_children == 0); + } + + for (child1 = die1->die_child, child2 = die2->die_child; + child1 && child2; + child1 = child1->die_sib, child2 = child2->die_sib) + if (die_eq_1 (cu1, cu2, top_die1, top_die2, child1, child2) == 0) + FAIL; + + if (child1 || child2) + { + fail: + return 0; + } + + if (unlikely (fi_multifile)) + assert (cu1->cu_kind == CU_ALT && cu2->cu_kind != CU_ALT); + return 1; +} + +/* Wrapper around die_eq_1, used as equality function in + dup_htab hash table. If zero (non-match) is returned and + any toplevel DIEs have been pushed to the vector in ob obstack, + undo the optimistic assignment of die_dup and die_nextdup. */ +static int +die_eq (const void *p, const void *q) +{ + dw_die_ref die1 = (dw_die_ref) p; + dw_die_ref die2 = (dw_die_ref) q; + dw_die_ref *arr; + unsigned int i, count; + int ret; + + if (die1->u.p1.die_hash != die2->u.p1.die_hash + || die1->u.p1.die_ref_hash != die2->u.p1.die_ref_hash) + return 0; + ret = die_eq_1 (die_cu (die1), die_cu (die2), die1, die2, die1, die2); + count = obstack_object_size (&ob) / sizeof (void *); + arr = (dw_die_ref *) obstack_finish (&ob); + if (!ret) + for (i = count; i;) + { + dw_die_ref die; + i -= 2; + die = arr[i]->die_dup; + if (likely (arr[i + 1] == NULL)) + { + die->die_nextdup = arr[i]->die_nextdup; + arr[i]->die_nextdup = NULL; + arr[i]->die_dup = NULL; + } + else + { + dw_die_ref next; + + assert (die->die_nextdup == arr[i]); + for (next = arr[i]->die_nextdup; + next != arr[i + 1]; + next = next->die_nextdup) + { + assert (next->die_dup == die); + next->die_dup = arr[i]; + } + assert (next->die_dup == die); + next->die_dup = arr[i]; + die->die_nextdup = next->die_nextdup; + next->die_nextdup = NULL; + arr[i]->die_dup = NULL; + } + arr[i]->die_remove = 0; + } + obstack_free (&ob, (void *) arr); + return ret; +} + +/* Hash table for finding of matching toplevel DIEs (and all + its children together with it). */ +static htab_t dup_htab; + +/* After read_multifile dup_htab is moved to this variable. */ +static htab_t alt_dup_htab; + +/* First CU, start of the linked list of CUs, and the tail + of that list. Initially this contains just the original + CUs, later on newly created partial units are added + to the beginning of the list and optionally .debug_types + CUs are added to its tail. */ +static dw_cu_ref first_cu, last_cu; + +/* After read_multifile first_cu is copied to this variable. */ +static dw_cu_ref alt_first_cu; + +/* Compute approximate size of DIE and all its children together. */ +static unsigned long +calc_sizes (dw_die_ref die) +{ + unsigned long ret = die->die_size; + dw_die_ref child; + if (wr_multifile ? die->die_no_multifile : die->die_remove) + return 0; + for (child = die->die_child; child; child = child->die_sib) + ret += calc_sizes (child); + return ret; +} + +/* Verify the duplicate chains starting at DIE. If ORDERED, also check that + the duplicate chain is in the correct order. */ +static void +verify_dups (dw_die_ref die, bool ordered) +{ + dw_die_ref d, prev; + + assert (die->die_dup == NULL); + assert (die->die_collapsed_children == 0); + assert (die->die_remove == 0); + + for (prev = die, d = prev->die_nextdup; + d; + prev = d, d = prev->die_nextdup) + { + if (ordered) + assert (die_cu (prev)->cu_chunk <= die_cu (d)->cu_chunk); + assert (d->die_offset != -1U); + assert (d->die_dup == die); + assert (d->die_remove != d->die_op_type_referenced); + assert (d->die_tag == die->die_tag); + } +} + +/* Walk toplevel DIEs in tree rooted by PARENT, and see if they + match previously processed DIEs. */ +static int +find_dups (dw_die_ref parent) +{ + void **slot; + dw_die_ref child; + + if (stats_p && parent->die_root) + stats->root_cnt++; + + for (child = parent->die_child; child; child = child->die_sib) + { + if (child->die_ck_state == CK_KNOWN) + { + if (stats_p) + stats->lower_toplevel_with_checksum++; + if (child->die_dup != NULL) + continue; + slot = htab_find_slot_with_hash (dup_htab, child, + child->u.p1.die_ref_hash, + INSERT); + if (slot == NULL) + dwz_oom (); + if (*slot == NULL) + *slot = child; + } + else if (child->die_named_namespace) + { + if (stats_p) + stats->namespace_cnt++; + if (find_dups (child)) + return 1; + } + else if (stats_p) + stats->lower_toplevel++; + } + return 0; +} + +/* Like find_dups, but for the last multifile optimization phase, + where it only looks at duplicates in the common .debug_info + section. */ +static int +find_dups_fi (dw_die_ref parent) +{ + dw_die_ref child; + + for (child = parent->die_child; child; child = child->die_sib) + { + if (child->die_ck_state == CK_KNOWN) + { + if (child->die_dup != NULL) + continue; + htab_find_with_hash (alt_dup_htab, child, child->u.p1.die_ref_hash); + } + else if (child->die_named_namespace) + if (find_dups_fi (child)) + return 1; + } + return 0; +} + +/* Return value of DW_AT_name for DIE, or for its specification or abstract + origin. */ +static const char * +get_name (dw_die_ref die) +{ + if (die->die_collapsed_child) + return NULL; + const char *name = get_AT_string (die, DW_AT_name); + if (name) + return name; + dw_cu_ref cu = die_cu (die); + bool present; + enum dwarf_form form; + unsigned int value = get_AT_int (die, DW_AT_specification, &present, &form); + if (present) + { + dw_die_ref ref; + if (form != DW_FORM_ref_addr) + value = cu->cu_offset + value; + ref = off_htab_lookup (cu, value); + if (ref) + return get_name (ref); + } + value = get_AT_int (die, DW_AT_abstract_origin, &present, &form); + if (present) + { + dw_die_ref ref; + if (form != DW_FORM_ref_addr) + value = cu->cu_offset + value; + ref = off_htab_lookup (die_cu (die), value); + if (ref) + return get_name (ref); + } + return NULL; +} + +/* Dump type of DIE to stderr. */ +static void +dump_type (dw_die_ref die) +{ + bool present; + enum dwarf_form form; + if (die->die_collapsed_child) + return; + unsigned int value = get_AT_int (die, DW_AT_type, &present, &form); + if (!present) + return; + + dw_cu_ref cu = die_cu (die); + if (cu == NULL) + return; + + dw_die_ref ref; + if (form != DW_FORM_ref_addr) + value = cu->cu_offset + value; + fprintf (stderr, " (type: %x", value); + ref = off_htab_lookup (cu, value); + if (ref != NULL && !ref->die_collapsed_child) + { + const char *type_name = get_name (ref); + if (type_name) + fprintf (stderr, " %s", type_name); + fprintf (stderr, " %s", get_DW_TAG_name (ref->die_tag) + 7); + dump_type (ref); + } + fprintf (stderr, ")"); +} + +/* Dump DIE to stderr with INDENT. */ +static void +dump_die_with_indent (int indent, dw_die_ref die) +{ + if (die == NULL) + fprintf (stderr, "%*s null", indent, ""); + else if (die->die_offset == -1U) + { + fprintf (stderr, "%*s -1 %s", indent, "", + get_DW_TAG_name (die->die_tag) + 7); + dw_die_ref d = die->die_nextdup; + while (d) + { + const char *name = get_name (d); + fprintf (stderr, " -> %x %s %s", d->die_offset, name ? name : "", + get_DW_TAG_name (d->die_tag) + 7); + d = d->die_nextdup; + } + } + else if (die->die_collapsed_child) + { + fprintf (stderr, "%*s %x %c", indent, "", die->die_offset, + die->die_ck_state == CK_KNOWN ? 'O' : 'X'); + } + else + { + const char *name = get_name (die); + fprintf (stderr, "%*s %x %c %x", indent, "", die->die_offset, + die->die_ck_state == CK_KNOWN ? 'O' : 'X', + (unsigned) die->u.p1.die_hash); + if (odr && die->die_odr_state != ODR_NONE + && die->die_odr_state != ODR_UNKNOWN) + fprintf (stderr, "(%x)", (unsigned) die->u.p1.die_hash2); + fprintf (stderr, " %x %s %s", (unsigned) die->u.p1.die_ref_hash, + name ? name : "", get_DW_TAG_name (die->die_tag) + 7); + dump_type (die); + } + fprintf (stderr, "\n"); +} + +/* Dump DIE to stderr. */ +static void +dump_die (dw_die_ref die) +{ + dump_die_with_indent (0, die); +} + +static void +dump_dups (dw_die_ref die) +{ + dw_die_ref d; + for (d = die; d; d = d->die_nextdup) + dump_die (d); +} + +/* Dump DIE tree at tree depth DEPTH. */ +static void +dump_dies (int depth, dw_die_ref die) +{ + dw_die_ref child; + dump_die_with_indent (depth, die); + for (child = die->die_child; child; child = child->die_sib) + dump_dies (depth + 1, child); +} + +/* Hash table for .debug_str. Depending on multifile optimization + phase this hash table has 3 different hash/equality functions. + The first set is used to record tail optimized strings, during + write_multifile the whole .debug_str section is written as is, + plus then all the strings which are just suffixes of other + strings. E.g. if .debug_str section contains "foobar" string + and .debug_info section refers to the whole "foobar" string + as well as "bar" by referring to "foobar" + 3. + The second set is used during op_multifile and fi_multifile, + noting each string and in addition to that how many times it + has been seen (0, 1 or more than 1). If 0 then it isn't present + in the hash table, 1 has lowest bit of new_off clear, more than 1 + the LSB of new_off is set. + The final set is used during finalize_strp and afterwards, it is + then used to map strings to their location in the new .debug_str + section. */ +static htab_t strp_htab; +/* Current offset in .debug_str when adding the tail optimized strings. + This is initially the size of .debug_str section in the object, + and as unique tail optimized strings are found, this is increased + each time. */ +static unsigned int max_strp_off; + +/* At the end of read_multifile strp_htab is moved to this variable, + which is used to find strings in the shared .debug_str section. */ +static htab_t alt_strp_htab; + +/* Structure describing strings in strp_htab. */ +struct strp_entry +{ + /* Original .debug_str offset. */ + unsigned int off; + /* New .debug_str offset, or when using strp_{hash,eq}2 + this is initially iterative hash of the string with the + LSB bit used for whether the string has been seen just once + or more than once. */ + unsigned int new_off; +}; +ALIGN_STRUCT (strp_entry) + +/* Hash function in strp_htab used for discovery of tail optimized + strings. */ +static hashval_t +strp_hash (const void *p) +{ + struct strp_entry *s = (struct strp_entry *)p; + + return s->off; +} + +/* Corresponding equality function in strp_htab. */ +static int +strp_eq (const void *p, const void *q) +{ + struct strp_entry *s1 = (struct strp_entry *)p; + struct strp_entry *s2 = (struct strp_entry *)q; + + return s1->off == s2->off; +} + +/* Hash function in strp_htab used to find what strings are + used by more than one object. */ +static hashval_t +strp_hash2 (const void *p) +{ + struct strp_entry *s = (struct strp_entry *)p; + + return s->new_off & ~1U; +} + +/* Corresponding equality function in strp_htab. */ +static int +strp_eq2 (const void *p, const void *q) +{ + struct strp_entry *s1 = (struct strp_entry *)p; + struct strp_entry *s2 = (struct strp_entry *)q; + + return strcmp ((char *) debug_sections[DEBUG_STR].data + s1->off, + (char *) debug_sections[DEBUG_STR].data + s2->off) == 0; +} + +/* Hash function in strp_htab used from finalize_strp onwards, + mapping strings into strings in the new .debug_str section. */ +static hashval_t +strp_hash3 (const void *p) +{ + return hash (p, strlen (p)); +} + +/* Corresponding equality function in strp_htab. */ +static int +strp_eq3 (const void *p, const void *q) +{ + return strcmp (p, q) == 0; +} + +/* Called for each DW_FORM_strp offset seen during initial + .debug_{info,types,macro} parsing. Before fi_multifile phase + this records just tail optimized strings, during fi_multifile + it checks whether the string is already in the shared .debug_str + section and if not, notes that it will need to be added to the + new local .debug_str section. */ +static void +note_strp_offset (unsigned int off) +{ + void **slot; + struct strp_entry se; + + if (unlikely (fi_multifile)) + { + unsigned char *p; + unsigned int len; + hashval_t hash; + + p = debug_sections[DEBUG_STR].data + off; + len = strlen ((char *) p); + hash = hash (p, len); + if (alt_strp_htab) + { + if (htab_find_with_hash (alt_strp_htab, p, hash)) + return; + } + if (strp_htab == NULL) + { + unsigned int strp_count = debug_sections[DEBUG_STR].size / 64; + + if (strp_count < 100) + strp_count = 100; + strp_htab = htab_try_create (strp_count, strp_hash2, strp_eq2, NULL); + if (strp_htab == NULL) + dwz_oom (); + } + + se.off = off; + se.new_off = hash | 1; + slot = htab_find_slot_with_hash (strp_htab, &se, se.new_off & ~1U, INSERT); + if (slot == NULL) + dwz_oom (); + if (*slot == NULL) + { + struct strp_entry *s = pool_alloc (strp_entry, sizeof (*s)); + *s = se; + *slot = (void *) s; + } + return; + } + if (off >= debug_sections[DEBUG_STR].size || off == 0) + return; + if (debug_sections[DEBUG_STR].data[off - 1] == '\0') + return; + if (strp_htab == NULL) + { + if (multifile == NULL) + return; + strp_htab = htab_try_create (50, strp_hash, strp_eq, NULL); + if (strp_htab == NULL) + dwz_oom (); + max_strp_off = debug_sections[DEBUG_STR].size; + } + se.off = off; + slot = htab_find_slot_with_hash (strp_htab, &se, off, INSERT); + if (slot == NULL) + dwz_oom (); + if (*slot == NULL) + { + struct strp_entry *s = pool_alloc (strp_entry, sizeof (*s)); + s->off = off; + s->new_off = max_strp_off; + max_strp_off += strlen ((char *) debug_sections[DEBUG_STR].data + + off) + 1; + if (max_strp_off < s->new_off) + { + htab_delete (strp_htab); + strp_htab = NULL; + max_strp_off = 0; + multifile = NULL; + error (0, 0, ".debug_str too large for multi-file optimization"); + } + *slot = (void *) s; + } +} + +/* Map offset in original .debug_str section into + offset in new .debug_str, either the shared .debug_str + or new local .debug_str. */ +static unsigned +lookup_strp_offset (unsigned int off) +{ + struct strp_entry *s, se; + + if (unlikely (op_multifile || fi_multifile)) + { + unsigned char *p; + unsigned int len; + hashval_t hash; + + p = debug_sections[DEBUG_STR].data + off; + len = strlen ((char *) p); + hash = hash (p, len); + if (alt_strp_htab) + { + unsigned char *q = (unsigned char *) + htab_find_with_hash (alt_strp_htab, p, hash); + if (q != NULL) + return q - alt_data[DEBUG_STR]; + } + assert (strp_htab); + p = (unsigned char *) htab_find_with_hash (strp_htab, p, hash); + assert (p != NULL); + return p - debug_sections[DEBUG_STR].new_data; + } + if (off >= debug_sections[DEBUG_STR].size || off == 0) + return off + multi_str_off; + if (debug_sections[DEBUG_STR].data[off - 1] == '\0') + return off + multi_str_off; + se.off = off; + s = (struct strp_entry *) htab_find_with_hash (strp_htab, &se, off); + return s->new_off + multi_str_off; +} + +/* Note .debug_str offset during write_macro or compute_abbrevs, + return either DW_FORM_strp if the string will be in the local + .debug_str section, or DW_FORM_strp_sup / DW_FORM_GNU_strp_alt if it + will be in the shared .debug_str section. */ +static enum dwarf_form +note_strp_offset2 (unsigned int off) +{ + hashval_t hash; + struct strp_entry se; + unsigned char *p, *q; + + if (likely (fi_multifile)) + { + unsigned int len; + + if (alt_strp_htab) + { + p = debug_sections[DEBUG_STR].data + off; + len = strlen ((char *) p); + hash = hash (p, len); + if (htab_find_with_hash (alt_strp_htab, p, hash)) + return dwarf_5 ? DW_FORM_strp_sup : DW_FORM_GNU_strp_alt; + } + return DW_FORM_strp; + } + if (off >= debug_sections[DEBUG_STR].size) + return DW_FORM_strp; + p = debug_sections[DEBUG_STR].data + off; + q = (unsigned char *) strchr ((char *) p, '\0'); + hash = hash (p, q - p); + se.off = off; + se.new_off = hash & ~1U; + struct strp_entry *s = (struct strp_entry *) + htab_find_with_hash (strp_htab, &se, se.new_off); + assert (s != NULL); + s->new_off |= 1; + return DW_FORM_strp; +} + +/* Helper to record all strp_entry entries from strp_htab. + Called through htab_traverse. */ +static int +list_strp_entries (void **slot, void *data) +{ + struct strp_entry ***end = (struct strp_entry ***) data; + **end = (struct strp_entry *) *slot; + (*end)++; + return 1; +} + +/* Adapted from bfd/merge.c strrevcmp. */ +static int +strrevcmp (const void *p, const void *q) +{ + struct strp_entry *s1 = *(struct strp_entry **)p; + struct strp_entry *s2 = *(struct strp_entry **)q; + unsigned int len1 = s1->new_off & ~1U; + unsigned int len2 = s2->new_off & ~1U; + unsigned int len; + unsigned char *p1 = debug_sections[DEBUG_STR].data + s1->off; + unsigned char *p2 = debug_sections[DEBUG_STR].data + s2->off; + + if (p1[len1]) + len1++; + if (p2[len2]) + len2++; + p1 += len1; + p2 += len2; + len = len1; + if (len2 < len) + len = len2; + while (len) + { + p1--; + p2--; + if (*p1 != *p2) + { + if (*p1 < *p2) + return -1; + return 1; + } + len--; + } + if (len1 < len2) + return 1; + if (len1 > len2) + return -1; + assert (s1->off == s2->off); + return 0; +} + +/* Compute new .debug_str section, from strp_htab content, + replace strp_htab hash table with a new one, which maps strings + to new .debug_str locations. */ +static unsigned int * +finalize_strp (bool build_tail_offset_list) +{ + unsigned int count, new_count, i, *tail_offset_list = NULL; + unsigned int strp_index = 0, tail_offset_list_count = 0, k; + struct strp_entry **arr, **end; + unsigned char *p; + + if (strp_htab == NULL) + { + debug_sections[DEBUG_STR].new_data = NULL; + debug_sections[DEBUG_STR].new_size = 0; + return NULL; + } + count = htab_elements (strp_htab); + arr = (struct strp_entry **) + obstack_alloc (&ob, count * sizeof (struct strp_entry *)); + end = arr; + htab_traverse (strp_htab, list_strp_entries, (void *) &end); + for (i = 0; i < count; i++) + { + unsigned int len = strlen ((char *) debug_sections[DEBUG_STR].data + + arr[i]->off); + arr[i]->new_off = (len & ~1U) | (arr[i]->new_off & 1); + } + qsort (arr, count, sizeof (struct strp_entry *), strrevcmp); + htab_delete (strp_htab); + strp_htab = NULL; + new_count = count; + for (i = 0; i < count; i++) + if ((arr[i]->new_off & 1) == 0) + { + arr[i]->off = -1U; + arr[i]->new_off = -1U; + new_count--; + } + else + { + unsigned int len1, len2, lastlen, j; + unsigned char *p1, *p2; + len1 = arr[i]->new_off & ~1U; + p1 = debug_sections[DEBUG_STR].data + arr[i]->off; + if (p1[len1]) + len1++; + lastlen = len1; + arr[i]->new_off = strp_index; + strp_index += len1 + 1; + for (j = i + 1; j < count; j++) + { + len2 = arr[j]->new_off & ~1U; + p2 = debug_sections[DEBUG_STR].data + arr[j]->off; + if (p2[len2]) + len2++; + if (len2 >= lastlen) + break; + if (memcmp (p1 + len1 - len2, p2, len2 + 1) != 0) + break; + arr[j]->new_off = arr[i]->new_off + len1 - len2; + lastlen = len2; + tail_offset_list_count++; + } + i = j - 1; + } + debug_sections[DEBUG_STR].new_data = malloc (strp_index); + if (debug_sections[DEBUG_STR].new_data == NULL) + dwz_oom (); + debug_sections[DEBUG_STR].new_size = strp_index; + strp_htab = htab_try_create (new_count < 32 ? 32 : new_count, + strp_hash3, strp_eq3, NULL); + if (strp_htab == NULL) + dwz_oom (); + if (build_tail_offset_list && tail_offset_list_count++ != 0) + { + tail_offset_list + = mmap (NULL, tail_offset_list_count * sizeof (int), + PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (tail_offset_list == MAP_FAILED) + dwz_oom (); + } + for (i = 0, k = 0, p = debug_sections[DEBUG_STR].new_data; i < count; i++) + if (arr[i]->off == -1U && arr[i]->new_off == -1U) + continue; + else + { + unsigned int len = strlen ((char *) debug_sections[DEBUG_STR].data + + arr[i]->off) + 1; + unsigned int j; + void **slot; + + memcpy (p, debug_sections[DEBUG_STR].data + arr[i]->off, len); + slot = htab_find_slot_with_hash (strp_htab, p, + hash (p, len - 1), + INSERT); + if (slot == NULL) + dwz_oom (); + assert (*slot == NULL); + *slot = (void *) p; + for (j = i + 1; j < count; j++) + if (arr[j]->new_off >= arr[i]->new_off + len) + break; + else + { + unsigned char *q = p + arr[j]->new_off - arr[i]->new_off; + unsigned int l = len + arr[i]->new_off - arr[j]->new_off; + if (tail_offset_list != NULL) + tail_offset_list[k++] = arr[j]->new_off; + slot = htab_find_slot_with_hash (strp_htab, q, + hash (q, l - 1), + INSERT); + if (slot == NULL) + dwz_oom (); + assert (*slot == NULL); + *slot = (void *) q; + } + p += len; + i = j - 1; + } + assert (p == debug_sections[DEBUG_STR].new_data + strp_index); + if (tail_offset_list != NULL) + { + tail_offset_list[k++] = 0; + assert (k == tail_offset_list_count); + } + obstack_free (&ob, (void *) arr); + return tail_offset_list; +} + +enum mark_refs_mode +{ + MARK_REFS_FOLLOW_DUPS = 1, + MARK_REFS_RETURN_VAL = 2, + MARK_REFS_REFERENCED = 4 +}; + +/* Mark all DIEs referenced from DIE by setting die_ref_seen to 1, + unless already marked. */ +static bool +mark_refs (dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die, int mode) +{ + struct abbrev_tag *t; + unsigned int i; + unsigned char *ptr; + dw_die_ref child; + + t = die->die_abbrev; + for (i = 0; i < t->nattr; ++i) + if (t->attr[i].attr != DW_AT_sibling) + switch (t->attr[i].form) + { + case DW_FORM_ref_addr: + if (unlikely (op_multifile)) + i = -2U; + break; + case DW_FORM_ref_udata: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_indirect: + i = -2U; + break; + } + if (i == -1U) + { + if (unlikely (cu->cu_kind == CU_TYPES)) + ptr = debug_sections[DEBUG_TYPES].data; + else + ptr = debug_sections[DEBUG_INFO].data; + ptr += die->die_offset; + skip_leb128 (ptr); + for (i = 0; i < t->nattr; ++i) + { + uint32_t form = t->attr[i].form; + size_t len = 0; + uint64_t value; + dw_die_ref ref, reft; + + while (form == DW_FORM_indirect) + form = read_uleb128 (ptr); + + switch (form) + { + case DW_FORM_ref_addr: + if (unlikely (op_multifile)) + { + value = read_size (ptr, cu->cu_version == 2 + ? ptr_size : 4); + ptr += cu->cu_version == 2 ? ptr_size : 4; + assert (t->attr[i].attr != DW_AT_sibling); + ref = off_htab_lookup (cu, value); + if ((mode & MARK_REFS_REFERENCED) != 0) + ref->die_referenced = 1; + goto finish_ref; + } + ptr += cu->cu_version == 2 ? ptr_size : 4; + break; + case DW_FORM_addr: + ptr += ptr_size; + break; + case DW_FORM_flag_present: + case DW_FORM_implicit_const: + break; + case DW_FORM_flag: + case DW_FORM_data1: + ++ptr; + break; + case DW_FORM_data2: + ptr += 2; + break; + case DW_FORM_data4: + case DW_FORM_sec_offset: + case DW_FORM_strp: + case DW_FORM_line_strp: + ptr += 4; + break; + case DW_FORM_data8: + case DW_FORM_ref_sig8: + ptr += 8; + break; + case DW_FORM_data16: + ptr += 16; + break; + case DW_FORM_sdata: + case DW_FORM_udata: + skip_leb128 (ptr); + break; + case DW_FORM_ref_udata: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + switch (form) + { + case DW_FORM_ref_udata: value = read_uleb128 (ptr); break; + case DW_FORM_ref1: value = read_8 (ptr); break; + case DW_FORM_ref2: value = read_16 (ptr); break; + case DW_FORM_ref4: value = read_32 (ptr); break; + case DW_FORM_ref8: value = read_64 (ptr); break; + default: abort (); + } + if (t->attr[i].attr == DW_AT_sibling) + break; + ref = off_htab_lookup (cu, cu->cu_offset + value); + if ((mode & MARK_REFS_REFERENCED) != 0) + ref->die_referenced = 1; + if (!ref->die_collapsed_child + && ref->u.p1.die_enter >= top_die->u.p1.die_enter + && ref->u.p1.die_exit <= top_die->u.p1.die_exit) + break; + finish_ref: + reft = ref; + while (!reft->die_root + && reft->die_parent->die_tag != DW_TAG_compile_unit + && reft->die_parent->die_tag != DW_TAG_partial_unit + && !reft->die_parent->die_named_namespace) + reft = reft->die_parent; + if ((mode & MARK_REFS_FOLLOW_DUPS) && reft->die_dup != NULL) + { + reft = reft->die_dup; + if (die_cu (reft)->cu_kind == CU_PU) + break; + } + if (reft->die_ref_seen == 0) + { + if ((mode & MARK_REFS_RETURN_VAL)) + return false; + reft->die_ref_seen = 1; + mark_refs (die_cu (reft), reft, reft, mode); + } + break; + case DW_FORM_string: + ptr = (unsigned char *) strchr ((char *)ptr, '\0') + 1; + break; + case DW_FORM_indirect: + abort (); + case DW_FORM_block1: + len = *ptr++; + break; + case DW_FORM_block2: + len = read_16 (ptr); + form = DW_FORM_block1; + break; + case DW_FORM_block4: + len = read_32 (ptr); + form = DW_FORM_block1; + break; + case DW_FORM_block: + case DW_FORM_exprloc: + len = read_uleb128 (ptr); + form = DW_FORM_block1; + break; + default: + abort (); + } + + if (form == DW_FORM_block1) + ptr += len; + } + } + + for (child = die->die_child; child; child = child->die_sib) + if (!mark_refs (cu, top_die, child, mode)) + return false; + return true; +} + +/* Remove completely unneeded children of DIE and remove unreferenced DIEs + from offset hash tables. */ +static void +remove_dies (dw_cu_ref cu, dw_die_ref die, bool remove) +{ + dw_die_ref child, next; + if (die->die_toplevel && die->die_ref_seen == 0 && !low_mem) + remove = true; + for (child = die->die_child; child; child = next) + { + next = child->die_sib; + remove_dies (cu, child, remove); + } + if (die->die_referenced == 0) + { + htab_t h = cu->cu_kind == CU_TYPES ? types_off_htab : off_htab; + void **slot = htab_find_slot_with_hash (h, die, off_hash (die), + NO_INSERT); + if (slot != NULL) + htab_clear_slot (h, slot); + } + if (!remove) + return; + if (die->die_toplevel == 0) + { + memset (die, '\0', offsetof (struct dw_die, die_dup)); + die->die_sib = die_nontoplevel_freelist; + die_nontoplevel_freelist = die; + } + else + die->die_child = NULL; +} + +/* Remove unneeded children DIEs. During phase 0 die_ref_seen + of toplevel DIEs is computed, during phase 1 mark_refs is called + to find referenced DIEs, during phase 2 unneeded children DIEs + are removed. */ +static void +remove_unneeded (dw_cu_ref cu, dw_die_ref die, unsigned int phase) +{ + dw_die_ref child; + for (child = die->die_child; child; child = child->die_sib) + { + if (child->die_named_namespace) + { + remove_unneeded (cu, child, phase); + if (phase == 2) + child->die_ref_seen = 0; + } + else + switch (phase) + { + case 0: + child->die_ref_seen = child->die_dup == NULL; + break; + case 1: + if (child->die_dup == NULL || low_mem) + mark_refs (cu, child, child, MARK_REFS_REFERENCED); + break; + case 2: + remove_dies (cu, child, false); + child->die_ref_seen = 0; + break; + } + } +} + +/* Entries in meta_abbrev_htab, mapping .debug_abbrev section offsets + to abbrev hash tables. */ +struct meta_abbrev_entry +{ + /* .debug_abbrev offset. */ + unsigned int abbrev_off; + /* Corresponding hash table. */ + htab_t abbrev_htab; +}; + +/* Hash table for mapping of .debug_abbrev section offsets to + abbrev hash tables. */ +static htab_t meta_abbrev_htab; +/* Dummy entry used during OOM handling. */ +static struct meta_abbrev_entry meta_abbrev_fallback; + +/* Hash function for meta_abbrev_htab. */ +static hashval_t +meta_abbrev_hash (const void *p) +{ + struct meta_abbrev_entry *m = (struct meta_abbrev_entry *)p; + + return m->abbrev_off; +} + +/* Equality function for meta_abbrev_htab. */ +static int +meta_abbrev_eq (const void *p, const void *q) +{ + struct meta_abbrev_entry *m1 = (struct meta_abbrev_entry *)p; + struct meta_abbrev_entry *m2 = (struct meta_abbrev_entry *)q; + + return m1->abbrev_off == m2->abbrev_off; +} + +/* Delete function for meta_abbrev_htab. */ +static void +meta_abbrev_del (void *p) +{ + struct meta_abbrev_entry *m = (struct meta_abbrev_entry *)p; + + if (m->abbrev_htab != NULL) + htab_delete (m->abbrev_htab); +} + +/* Collapse children of TOP_DIE to decrease memory usage. */ +static void +collapse_child (dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die, + unsigned int *tick) +{ + dw_die_ref child, next; + bool has_children = die->die_child != NULL; + unsigned int tick_diff = *tick; + for (child = die->die_child; child; child = next) + { + next = child->die_sib; + (*tick)++; + collapse_child (cu, top_die, child, tick); + } + if (has_children) + (*tick)++; + if (top_die == die) + { + die->die_child = NULL; + die->die_collapsed_children = 1; + } + else if (die->die_referenced) + { + die->die_parent = top_die; + if (tick_diff <= 0xffff && !die->die_intercu_referenced) + { + dw_die_ref ref; + void **slot; + if (die_collapsed_child_freelist) + { + ref = die_collapsed_child_freelist; + die_collapsed_child_freelist = ref->die_parent; + } + else + ref = pool_alloc (dw_die, offsetof (struct dw_die, die_child)); + memcpy (ref, die, offsetof (struct dw_die, die_child)); + ref->die_collapsed_child = 1; + ref->die_tag = tick_diff; + slot = htab_find_slot_with_hash (cu->cu_kind == CU_TYPES + ? types_off_htab : off_htab, + ref, off_hash (ref), NO_INSERT); + assert (slot != NULL); + *slot = (void *) ref; + memset (die, '\0', offsetof (struct dw_die, die_dup)); + die->die_sib = die_nontoplevel_freelist; + die_nontoplevel_freelist = die; + } + else + { + die->die_child = NULL; + die->die_sib = NULL; + die->die_ref_seen = tick_diff; + } + } + else + { + memset (die, '\0', offsetof (struct dw_die, die_dup)); + die->die_sib = die_nontoplevel_freelist; + die_nontoplevel_freelist = die; + } +} + +/* Collapse children of all toplevel DIEs that can be collapsed. */ +static void +collapse_children (dw_cu_ref cu, dw_die_ref die) +{ + dw_die_ref child; + for (child = die->die_child; child; child = child->die_sib) + if (child->die_named_namespace) + collapse_children (cu, child); + else if (child->die_child == NULL) + continue; + else if (child->die_nextdup == NULL + || (child->die_dup != NULL + && (die_cu (child->die_dup)->cu_kind != CU_PU + || child->die_dup->die_nextdup != child))) + { + unsigned int tick = 0; + collapse_child (cu, child, child, &tick); + } +} + +/* Count the number of DIEs in the .debug_info section, and see if we run into + some limit. */ +static int +try_debug_info (DSO *dso) +{ + unsigned char *ptr, *endcu, *endsec; + unsigned int value; + htab_t abbrev = NULL; + unsigned int last_abbrev_offset = 0; + struct abbrev_tag tag, *t; + unsigned int ndies; + unsigned ret = 1; + int kind = DEBUG_INFO; + bool low_mem_die_limit_hit = false; + + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "try_debug_info\n"); + } + + if (tracing) + fprintf (stderr, "Counting DIEs\n"); + + ndies = 0; + ptr = debug_sections[kind].data; + endsec = ptr + debug_sections[kind].size; + while (ptr < endsec) + { + unsigned int culen; + int cu_version; + + /* Note header is one bigger with DWARF version 5. */ + if (ptr + (kind == DEBUG_TYPES ? 23 : 11) > endsec) + { + error (0, 0, "%s: %s CU header too small", dso->filename, + debug_sections[kind].name); + goto fail; + } + + endcu = ptr + 4; + culen = read_32 (ptr); + if (culen >= 0xfffffff0) + { + error (0, 0, "%s: 64-bit DWARF not supported", dso->filename); + goto fail; + } + endcu += culen; + + if (endcu > endsec) + { + error (0, 0, "%s: %s too small", dso->filename, + debug_sections[kind].name); + goto fail; + } + + cu_version = read_16 (ptr); + if (kind == DEBUG_TYPES && (cu_version < 2 || cu_version > 4)) + { + error (0, 0, "%s: DWARF version %d in .debug_types unhandled", + dso->filename, cu_version); + goto fail; + } + else if (cu_version < 2 || cu_version > 5) + { + error (0, 0, "%s: DWARF version %d in .debug_info unhandled", + dso->filename, cu_version); + goto fail; + } + + if (cu_version == 5) + { + value = read_8 (ptr); + if (value != DW_UT_compile && value != DW_UT_partial) + error (0, 0, "%s: DWARF CU type %s unhandled", dso->filename, + get_DW_UT_str (value)); + } + else + { + value = read_32 (ptr); + if (value >= debug_sections[DEBUG_ABBREV].size) + { + if (debug_sections[DEBUG_ABBREV].data == NULL) + error (0, 0, "%s: .debug_abbrev not present", dso->filename); + else + error (0, 0, "%s: DWARF CU abbrev offset too large", + dso->filename); + goto fail; + } + } + + if (ptr_size == 0) + { + ptr_size = read_8 (ptr); + if (ptr_size != 4 && ptr_size != 8) + { + error (0, 0, "%s: Invalid DWARF pointer size %d", + dso->filename, ptr_size); + goto fail; + } + } + else if (read_8 (ptr) != ptr_size) + { + error (0, 0, "%s: DWARF pointer size differs between CUs", + dso->filename); + goto fail; + } + + if (cu_version == 5) + { + /* Above we only checked for the smaller version 4 header size. */ + if (ptr + 4 > endsec) + { + error (0, 0, "%s: %s CU version 5 header too small", + dso->filename, debug_sections[kind].name); + goto fail; + } + value = read_32 (ptr); + if (value >= debug_sections[DEBUG_ABBREV].size) + { + if (debug_sections[DEBUG_ABBREV].data == NULL) + error (0, 0, "%s: .debug_abbrev not present", dso->filename); + else + error (0, 0, "%s: DWARF CU abbrev offset too large", + dso->filename); + goto fail; + } + } + + if (abbrev == NULL || value != last_abbrev_offset) + { + if (abbrev) + htab_delete (abbrev); + abbrev + = read_abbrev (dso, debug_sections[DEBUG_ABBREV].data + value); + if (abbrev == NULL) + { + error (0, 0, "%s: Couldn't read abbrev at offset 0x%x", + dso->filename, value); + goto fail; + } + } + last_abbrev_offset = value; + + while (ptr < endcu) + { + tag.entry = read_uleb128 (ptr); + if (tag.entry == 0) + continue; + if (ndies == max_die_limit) + { + error (0, 0, "%s: Too many DIEs, not optimizing", + dso->filename); + goto fail; + } + /* If we reach the DIE limit, signal the dwz caller that it + should retry with low_mem. */ + if (likely (!low_mem) && ndies == low_mem_die_limit) + { + if (tracing) + fprintf (stderr, "Hit low-mem die-limit\n"); + if (estimate_nr_dies () > max_die_limit) + /* Keep going, we still might hit the max die-limit. */ + low_mem_die_limit_hit = true; + else + { + ret = 2; + goto fail; + } + } + ndies++; + t = htab_find_with_hash (abbrev, &tag, tag.entry); + if (t == NULL) + { + error (0, 0, "%s: Could not find DWARF abbreviation %d", + dso->filename, tag.entry); + goto fail; + } + ptr = skip_attrs_1 (cu_version, t, ptr); + } + } + + if (low_mem_die_limit_hit) + ret = 2; + else + ret = 0; + + fail: + if (abbrev) + htab_delete (abbrev); + + return ret; +} + +/* Read language attribute encoded using FORM from location PTR, return + pointer to location after the attribute. Assign the attribute value + to *LANG. */ +static unsigned char * +read_lang (unsigned char *ptr, enum dwarf_form form, + enum dwarf_source_language *lang) +{ + bool error_p = false; + *lang = read_u16 (ptr, form, error_p); + if (unlikely (error_p)) + { + *lang = 0; + error (0, 0, "Invalid DW_AT_language attribute, ignoring"); + } + return ptr; +} + +/* First phase of the DWARF compression. Parse .debug_info section + (for kind == DEBUG_INFO) or .debug_types section (for kind == DEBUG_TYPES) + for each CU in it construct internal representation for the CU + and its DIE tree, compute checksums of DIEs and look for duplicates. */ +static int +read_debug_info (DSO *dso, int kind, unsigned int *die_count) +{ + unsigned char *ptr, *endcu, *endsec; + unsigned int value; + htab_t abbrev = NULL; + unsigned int last_abbrev_offset = 0; + unsigned int last_debug_line_off = 0; + struct dw_file *cu_files = NULL; + unsigned int cu_nfiles = 0; + bool note_strp_forms = multifile != NULL && !op_multifile + && !rd_multifile && !low_mem; + struct abbrev_tag tag, *t; + unsigned int cu_chunk = 0; + dw_cu_ref cu_tail = NULL, cu_collapse = NULL; + unsigned int cu_kind = rd_multifile ? CU_ALT + : kind == DEBUG_TYPES ? CU_TYPES : CU_NORMAL; + void *to_free = NULL; + int ret = 1; + unsigned int ndies; + bool low_mem_phase1 = low_mem && kind == DEBUG_INFO; + struct dw_cu cu_buf; + struct dw_die die_buf; + bool lang_p = odr || uni_lang_p; + + odr_active_p = false; + if (odr) + odr_phase = 1; + + unsigned int estimated_nr_dies = estimate_nr_dies (); + if (kind == DEBUG_INFO + && multifile_mode == 0 + && die_count_method == estimate) + { + bool do_count = (estimated_nr_dies > max_die_limit + || estimated_nr_dies > low_mem_die_limit); + if (tracing) + fprintf (stderr, "Using die count estimate %u to decide whether to" + " count DIEs: %s\n", estimated_nr_dies, + do_count ? "yes" : "no"); + if (do_count) + { + int try_ret = try_debug_info (dso); + if (try_ret != 0) + return try_ret; + } + } + + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "read_debug_info %s%s\n", + kind == DEBUG_INFO ? ".debug_info" : ".debug_types", + low_mem && kind == DEBUG_INFO ? " (low-mem)" : ""); + } + + if (likely (!fi_multifile && kind != DEBUG_TYPES)) + { + dup_htab = htab_try_create (100000, die_hash, die_eq, NULL); + if (dup_htab == NULL) + dwz_oom (); + } + if (unlikely (meta_abbrev_htab == NULL + && (op_multifile || rd_multifile || fi_multifile || low_mem))) + { + meta_abbrev_htab + = htab_try_create (500, meta_abbrev_hash, meta_abbrev_eq, + meta_abbrev_del); + if (meta_abbrev_htab == NULL) + dwz_oom (); + to_free = obstack_alloc (&ob2, 1); + } + + low_mem_phase2: + ndies = 0; + ptr = debug_sections[kind].data; + endsec = ptr + debug_sections[kind].size; + while (ptr < endsec) + { + unsigned int cu_offset = ptr - debug_sections[kind].data; + unsigned int tick = 0, culen; + int cu_version; + dw_cu_ref cu; + dw_die_ref *diep, parent, die; + bool present; + unsigned int debug_line_off; + unsigned int type_offset = 0; + + /* Note header is one bigger with DWARF version 5. */ + if (ptr + (kind == DEBUG_TYPES ? 23 : 11) > endsec) + { + error (0, 0, "%s: %s CU header too small", dso->filename, + debug_sections[kind].name); + goto fail; + } + + endcu = ptr + 4; + culen = read_32 (ptr); + if (culen >= 0xfffffff0) + { + error (0, 0, "%s: 64-bit DWARF not supported", dso->filename); + goto fail; + } + endcu += culen; + + if (endcu > endsec) + { + error (0, 0, "%s: %s too small", dso->filename, + debug_sections[kind].name); + goto fail; + } + + cu_version = read_16 (ptr); + if (kind == DEBUG_TYPES && (cu_version < 2 || cu_version > 4)) + { + error (0, 0, "%s: DWARF version %d in .debug_types unhandled", + dso->filename, cu_version); + goto fail; + } + else if (cu_version < 2 || cu_version > 5) + { + error (0, 0, "%s: DWARF version %d in .debug_info unhandled", + dso->filename, cu_version); + goto fail; + } + + if (cu_version == 5) + { + value = read_8 (ptr); + if (value != DW_UT_compile && value != DW_UT_partial) + { + error (0, 0, "%s: DWARF CU type %s unhandled", dso->filename, + get_DW_UT_str (value)); + goto fail; + } + } + else + { + value = read_32 (ptr); + if (value >= debug_sections[DEBUG_ABBREV].size) + { + if (debug_sections[DEBUG_ABBREV].data == NULL) + error (0, 0, "%s: .debug_abbrev not present", dso->filename); + else + error (0, 0, "%s: DWARF CU abbrev offset too large", + dso->filename); + goto fail; + } + } + + if (ptr_size == 0) + { + ptr_size = read_8 (ptr); + if (ptr_size != 4 && ptr_size != 8) + { + error (0, 0, "%s: Invalid DWARF pointer size %d", + dso->filename, ptr_size); + goto fail; + } + } + else if (read_8 (ptr) != ptr_size) + { + error (0, 0, "%s: DWARF pointer size differs between CUs", + dso->filename); + goto fail; + } + + if (cu_version == 5) + { + /* Above we only checked for the smaller version 4 header size. */ + if (ptr + 4 > endsec) + { + error (0, 0, "%s: %s CU version 5 header too small", + dso->filename, debug_sections[kind].name); + goto fail; + } + value = read_32 (ptr); + if (value >= debug_sections[DEBUG_ABBREV].size) + { + if (debug_sections[DEBUG_ABBREV].data == NULL) + error (0, 0, "%s: .debug_abbrev not present", dso->filename); + else + error (0, 0, "%s: DWARF CU abbrev offset too large", + dso->filename); + goto fail; + } + } + + if (unlikely (op_multifile)) + { + if (ptr == endcu) + { + dw_cu_ref cuf = cu_tail ? cu_tail->cu_next : first_cu; + /* Inside of optimize_multifile, DIE hashes are computed + only after all the CUs from a particular DSO or + executable have been parsed, as we follow + DW_FORM_ref_addr then. */ + for (cu = cuf; cu; cu = cu->cu_next) + if (checksum_die (dso, cu, NULL, cu->cu_die)) + goto fail; + + for (cu = cuf; cu; cu = cu->cu_next) + checksum_ref_die (cu, NULL, cu->cu_die, NULL, NULL); + + if (dump_dies_p) + for (cu = cuf; cu; cu = cu->cu_next) + dump_dies (0, cu->cu_die); + + for (cu = cuf; cu; cu = cu->cu_next) + if (find_dups (cu->cu_die)) + goto fail; + + for (cu = cuf; cu; cu = cu->cu_next) + remove_unneeded (cu, cu->cu_die, 0); + for (cu = cuf; cu; cu = cu->cu_next) + remove_unneeded (cu, cu->cu_die, 1); + for (cu = cuf; cu; cu = cu->cu_next) + remove_unneeded (cu, cu->cu_die, 2); + + if (cu_collapse == NULL) + cu_collapse = first_cu; + while (cu_collapse->cu_chunk < cu_chunk) + { + collapse_children (cu_collapse, cu_collapse->cu_die); + cu_collapse = cu_collapse->cu_next; + } + + cu_tail = last_cu; + cu_chunk++; + continue; + } + } + else + cu_chunk++; + + if (unlikely (meta_abbrev_htab != NULL)) + { + struct meta_abbrev_entry m, *mp; + void **slot; + m.abbrev_off = value; + slot = htab_find_slot_with_hash (meta_abbrev_htab, &m, + m.abbrev_off, INSERT); + if (slot == NULL) + dwz_oom (); + else if (*slot != NULL) + abbrev = ((struct meta_abbrev_entry *) *slot)->abbrev_htab; + else + { + *slot = (void *) &meta_abbrev_fallback; + abbrev + = read_abbrev (dso, debug_sections[DEBUG_ABBREV].data + value); + if (abbrev == NULL) + goto fail; + mp = (struct meta_abbrev_entry *) + obstack_alloc (&ob2, sizeof (*mp)); + mp->abbrev_off = value; + mp->abbrev_htab = abbrev; + *slot = (void *) mp; + } + } + else if (abbrev == NULL || value != last_abbrev_offset) + { + if (abbrev) + htab_delete (abbrev); + abbrev + = read_abbrev (dso, debug_sections[DEBUG_ABBREV].data + value); + if (abbrev == NULL) + { + error (0, 0, "%s: Couldn't read abbrev at offset 0x%x", + dso->filename, value); + + goto fail; + } + } + last_abbrev_offset = value; + + if (unlikely (kind == DEBUG_TYPES)) + { + ptr += 8; + type_offset = read_32 (ptr); + } + + if (unlikely (low_mem_phase1)) + cu = &cu_buf; + else + cu = pool_alloc (dw_cu, sizeof (struct dw_cu)); + memset (cu, '\0', sizeof (*cu)); + cu->cu_kind = cu_kind; + cu->cu_offset = cu_offset; + cu->cu_version = cu_version; + cu->cu_chunk = cu_chunk; + if (unlikely (op_multifile || low_mem)) + cu->cu_abbrev = abbrev; + diep = &cu->cu_die; + parent = NULL; + if (unlikely (low_mem_phase1)) + ; + else if (first_cu == NULL) + first_cu = last_cu = cu; + else + { + last_cu->cu_next = cu; + last_cu = cu; + } + + while (ptr < endcu) + { + unsigned int i; + unsigned int die_offset = ptr - debug_sections[kind].data; + + tag.entry = read_uleb128 (ptr); + if (tag.entry == 0) + { + if (unlikely (low_mem_phase1)) + continue; + if (parent) + { + diep = &parent->die_sib; + parent->u.p1.die_exit = tick++; + if (parent->die_root == 0) + parent = parent->die_parent; + else + parent = NULL; + } + else + diep = NULL; + continue; + } + if (diep == NULL) + { + error (0, 0, "%s: Wrong %s DIE tree", dso->filename, + debug_sections[kind].name); + goto fail; + } + t = htab_find_with_hash (abbrev, &tag, tag.entry); + if (t == NULL) + { + error (0, 0, "%s: Could not find DWARF abbreviation %d", + dso->filename, tag.entry); + goto fail; + } + if (likely (!op_multifile && !rd_multifile && !fi_multifile) + && likely (kind == DEBUG_INFO)) + { + if (ndies == max_die_limit) + { + error (0, 0, "%s: Too many DIEs, not optimizing", + dso->filename); + goto fail; + } + /* If we reach the DIE limit, silently signal the dwz + caller that it should retry with low_mem. */ + if (likely (!low_mem) && ndies == low_mem_die_limit) + { + if (tracing) + fprintf (stderr, "Hit low-mem die-limit\n"); + ret = 2; + goto fail; + } + } + ndies++; + if (unlikely (low_mem_phase1)) + die = &die_buf; + else if (parent == NULL + || parent->die_root + || parent->die_named_namespace) + { + die = pool_alloc (dw_die, sizeof (struct dw_die)); + memset (die, '\0', sizeof (struct dw_die)); + die->die_toplevel = 1; + } + else + { + if (die_nontoplevel_freelist) + { + die = die_nontoplevel_freelist; + die_nontoplevel_freelist = die->die_sib; + } + else + die = pool_alloc (dw_die, offsetof (struct dw_die, die_dup)); + memset (die, '\0', offsetof (struct dw_die, die_dup)); + } + *diep = die; + die->die_tag = t->tag; + die->die_abbrev = t; + die->die_offset = die_offset; + if (parent) + die->die_parent = parent; + else + { + die->die_root = 1; + die->die_parent = (dw_die_ref) cu; + } + die->u.p1.die_enter = tick; + die->u.p1.die_exit = tick++; + if (likely (!low_mem_phase1)) + { + if (t->children) + { + diep = &die->die_child; + parent = die; + } + else + diep = &die->die_sib; + } + for (i = 0; i < t->nattr; ++i) + { + uint32_t form = t->attr[i].form; + size_t len = 0; + + while (form == DW_FORM_indirect) + { + form = read_uleb128 (ptr); + if (ptr > endcu) + { + error (0, 0, "%s: Attributes extend beyond end of CU", + dso->filename); + goto fail; + } + } + + /* Get length of expr/blocks first. Canonicalize all, + except exprloc, to DW_FORM_block1. */ + switch (form) + { + case DW_FORM_block1: + len = *ptr++; + break; + case DW_FORM_block2: + len = read_16 (ptr); + form = DW_FORM_block1; + break; + case DW_FORM_block4: + len = read_32 (ptr); + form = DW_FORM_block1; + break; + case DW_FORM_block: + len = read_uleb128 (ptr); + form = DW_FORM_block1; + break; + case DW_FORM_exprloc: + len = read_uleb128 (ptr); + break; + default: + break; + } + + if (unlikely (low_mem_phase1) + && add_locexpr_dummy_dies (dso, cu, die, ptr, form, + t->attr[i].attr, len)) + goto fail; + + switch (form) + { + case DW_FORM_ref_addr: + if (unlikely (low_mem_phase1)) + { + unsigned int offset + = read_size (ptr, cu->cu_version == 2 ? ptr_size : 4); + add_dummy_die (cu, offset); + } + ptr += cu->cu_version == 2 ? ptr_size : 4; + break; + case DW_FORM_addr: + ptr += ptr_size; + break; + case DW_FORM_flag_present: + break; + case DW_FORM_implicit_const: + if (lang_p + && (die->die_tag == DW_TAG_compile_unit + || die->die_tag == DW_TAG_partial_unit) + && t->attr[i].attr == DW_AT_language) + cu->lang = t->values[i]; + break; + case DW_FORM_data1: + if (lang_p + && (die->die_tag == DW_TAG_compile_unit + || die->die_tag == DW_TAG_partial_unit) + && t->attr[i].attr == DW_AT_language) + cu->lang = *ptr; + /* FALLTHRU */ + case DW_FORM_ref1: + case DW_FORM_flag: + ++ptr; + break; + case DW_FORM_data2: + if (lang_p + && (die->die_tag == DW_TAG_compile_unit + || die->die_tag == DW_TAG_partial_unit) + && t->attr[i].attr == DW_AT_language) + cu->lang = do_read_16 (ptr); + /* FALLTHRU */ + case DW_FORM_ref2: + ptr += 2; + break; + case DW_FORM_data4: + if (lang_p + && (die->die_tag == DW_TAG_compile_unit + || die->die_tag == DW_TAG_partial_unit) + && t->attr[i].attr == DW_AT_language) + read_lang (ptr, form, &cu->lang); + /* FALLTHRU */ + case DW_FORM_ref4: + case DW_FORM_sec_offset: + ptr += 4; + break; + case DW_FORM_data8: + if (lang_p + && (die->die_tag == DW_TAG_compile_unit + || die->die_tag == DW_TAG_partial_unit) + && t->attr[i].attr == DW_AT_language) + read_lang (ptr, form, &cu->lang); + /* FALLTHRU */ + case DW_FORM_ref8: + case DW_FORM_ref_sig8: + ptr += 8; + break; + case DW_FORM_data16: + ptr += 16; + break; + case DW_FORM_sdata: + case DW_FORM_udata: + if (lang_p + && (die->die_tag == DW_TAG_compile_unit + || die->die_tag == DW_TAG_partial_unit) + && t->attr[i].attr == DW_AT_language) + { + ptr = read_lang (ptr, form, &cu->lang); + break; + } + /* FALLTHRU */ + case DW_FORM_ref_udata: + skip_leb128 (ptr); + break; + case DW_FORM_strp: + if (t->attr[i].attr == DW_AT_name + && (die->die_tag == DW_TAG_namespace + || die->die_tag == DW_TAG_module) + && !die->die_root + && (die->die_parent->die_root + || die->die_parent->die_named_namespace)) + die->die_named_namespace = 1; + if (note_strp_forms) + note_strp_offset (read_32 (ptr)); + else + ptr += 4; + break; + case DW_FORM_line_strp: + if (t->attr[i].attr == DW_AT_name + && (die->die_tag == DW_TAG_namespace + || die->die_tag == DW_TAG_module) + && !die->die_root + && (die->die_parent->die_root + || die->die_parent->die_named_namespace)) + die->die_named_namespace = 1; + /* Don't note strp, different string table. */ + ptr += 4; + break; + case DW_FORM_string: + ptr = (unsigned char *) strchr ((char *)ptr, '\0') + 1; + if (t->attr[i].attr == DW_AT_name + && (die->die_tag == DW_TAG_namespace + || die->die_tag == DW_TAG_module) + && !die->die_root + && (die->die_parent->die_root + || die->die_parent->die_named_namespace)) + die->die_named_namespace = 1; + break; + case DW_FORM_indirect: + abort (); + /* All expr/blocks lengths already handled above. + Just canonicalize exprloc to block1 too. */ + case DW_FORM_exprloc: + form = DW_FORM_block1; + break; + case DW_FORM_block1: + break; + case DW_FORM_block2: + case DW_FORM_block4: + case DW_FORM_block: + abort (); + default: + error (0, 0, "%s: Unknown DWARF %s at DIE [%x]", + dso->filename, get_DW_FORM_str (form), die_offset); + goto fail; + } + + if (ptr > endcu) + { + error (0, 0, "%s: Attributes extend beyond end of CU " + "for DIE [%x]", + dso->filename, die_offset); + goto fail; + } + + if (form == DW_FORM_block1) + { + if (len >= (size_t) (endcu - ptr)) + { + error (0, 0, "%s: Attributes extend beyond end of CU " + "for DIE [%x]", + dso->filename, die_offset); + goto fail; + } + + if (t->attr[i].attr > DW_AT_loclists_base + && (t->attr[i].attr < DW_AT_MIPS_fde + || t->attr[i].attr > DW_AT_MIPS_has_inlines) + && (t->attr[i].attr < DW_AT_sf_names + || t->attr[i].attr > DW_AT_body_end) + && (t->attr[i].attr < DW_AT_GNU_call_site_value + || t->attr[i].attr + > DW_AT_GNU_call_site_target_clobbered)) + { + error (0, 0, "%s: Unknown DWARF %s with " + "block DW_FORM for DIE [%x]", + dso->filename, get_DW_AT_str (t->attr[i].attr), + die_offset); + goto fail; + } + + ptr += len; + } + } + die->die_size = (ptr - debug_sections[kind].data) + - die_offset; + if (unlikely (low_mem)) + { + if (low_mem_phase1) + continue; + if (off_htab != NULL && kind == DEBUG_INFO) + { + void **slot + = htab_find_slot_with_hash (off_htab, die, off_hash (die), + INSERT); + if (slot == NULL) + dwz_oom (); + if (*slot != NULL) + { + dw_die_ref ref = (dw_die_ref) *slot; + assert (ref->die_collapsed_child); + die->die_referenced = 1; + die->die_intercu_referenced = 1; + memset (ref, '\0', offsetof (struct dw_die, die_child)); + ref->die_parent = die_collapsed_child_freelist; + die_collapsed_child_freelist = ref; + } + *slot = (void *) die; + continue; + } + } + + off_htab_add_die (cu, die, die_count); + } + + if (unlikely (low_mem_phase1)) + continue; + + if (cu->cu_die == NULL + || (cu->cu_die->die_tag != DW_TAG_compile_unit + && cu->cu_die->die_tag != DW_TAG_partial_unit + && cu->cu_die->die_tag != DW_TAG_type_unit) + || cu->cu_die->die_sib != NULL) + { + error (0, 0, "%s: %s section chunk doesn't contain a single" + " compile_unit or partial_unit", dso->filename, + debug_sections[kind].name); + goto fail; + } + + cu->cu_comp_dir = get_AT_string (cu->cu_die, DW_AT_comp_dir); + if (skip_producers_p + && skip_producer (get_AT_string (cu->cu_die, DW_AT_producer))) + { + cu->cu_die->die_remove = 1; + continue; + } + enum dwarf_form form; + debug_line_off + = get_AT_int (cu->cu_die, DW_AT_stmt_list, &present, &form); + if (present) + { + if (!(form == DW_FORM_sec_offset || form == DW_FORM_data4)) + { + error (0, 0, "%s: DW_AT_stmt_list not DW_FORM_sec_offset or" + " DW_FORM_data4", dso->filename); + goto fail; + } + + if (cu_files != NULL && last_debug_line_off == debug_line_off) + { + cu->cu_nfiles = cu_nfiles; + cu->cu_files = cu_files; + } + else + { + if (read_debug_line (dso, cu, debug_line_off)) + goto fail; + cu_nfiles = cu->cu_nfiles; + cu_files = cu->cu_files; + last_debug_line_off = debug_line_off; + } + } + + if (likely (!op_multifile && !rd_multifile && !fi_multifile) + && likely (kind == DEBUG_INFO)) + { + if (checksum_die (dso, cu, NULL, cu->cu_die)) + goto fail; + checksum_ref_die (cu, NULL, cu->cu_die, NULL, NULL); + if (odr) + { + dw_die_ref die; + FOREACH_LOW_TOPLEVEL_DIE_IN_CU (die, cu) + { + if (die->die_ck_state != CK_KNOWN) + continue; + if (die_odr_state (die) != ODR_NONE) + die->u.p1.die_ref_hash = die->u.p1.die_hash; + else + die->die_ref_hash_computed = 0; + } + checksum_ref_die (cu, NULL, cu->cu_die, NULL, NULL); + } + + if (dump_dies_p) + dump_dies (0, cu->cu_die); + + if (find_dups (cu->cu_die)) + goto fail; + } + if (unlikely (kind == DEBUG_TYPES)) + { + dw_die_ref ref = off_htab_lookup (cu, cu->cu_offset + type_offset); + if (ref == NULL) + { + error (0, 0, "%s: Couldn't find DIE at [%x] " + "referenced by type_offset from cu DIE at [%x]", + dso->filename, cu->cu_offset + type_offset, + cu->cu_die->die_offset); + goto fail; + } + if (unlikely (low_mem)) + { + ref->die_referenced = 1; + ref->die_intercu_referenced = 1; + } + } + if (unlikely (low_mem)) + { + remove_unneeded (cu, cu->cu_die, 1); + remove_unneeded (cu, cu->cu_die, 2); + collapse_children (cu, cu->cu_die); + } + } + + if (unlikely (low_mem_phase1)) + { + low_mem_phase1 = false; + cu_chunk = 0; + goto low_mem_phase2; + } + + if (unlikely (low_mem)) + ; + else if (unlikely (meta_abbrev_htab != NULL)) + { + dw_cu_ref cu; + + if (unlikely (op_multifile)) + for (cu = first_cu; cu; cu = cu->cu_next) + cu->cu_abbrev = NULL; + htab_delete (meta_abbrev_htab); + meta_abbrev_htab = NULL; + obstack_free (&ob2, to_free); + abbrev = NULL; + } + else if (abbrev) + htab_delete (abbrev); + + if (unlikely (kind == DEBUG_TYPES)) + return 0; + + if (unlikely (rd_multifile || fi_multifile)) + { + dw_cu_ref cu; + + /* Inside of read_multifile, DIE hashes are computed + only after all the PUs are parsed, as we follow + DW_FORM_ref_addr then. */ + for (cu = first_cu; cu; cu = cu->cu_next) + if (checksum_die (dso, cu, NULL, cu->cu_die)) + goto fail; + + for (cu = first_cu; cu; cu = cu->cu_next) + checksum_ref_die (cu, NULL, cu->cu_die, NULL, NULL); + + if (dump_dies_p) + for (cu = first_cu; cu; cu = cu->cu_next) + dump_dies (0, cu->cu_die); + + if (rd_multifile) + { + for (cu = first_cu; cu; cu = cu->cu_next) + if (find_dups (cu->cu_die)) + goto fail; + } + else + for (cu = first_cu; cu; cu = cu->cu_next) + if (find_dups_fi (cu->cu_die)) + goto fail; + + return 0; + } + + if (tracing) + { + if (op_multifile) + fprintf (stderr, "Die count: %u\n", ndies); + else + fprintf (stderr, "Die count: %u, %.2f%% of estimate\n", ndies, + (double)ndies / ((double)estimated_nr_dies / 100)); + } + if (tracing) + htab_report (off_htab, "off_htab post-parsing"); + if (stats_p) + stats->die_count = ndies; + if (die_count) + *die_count = ndies; + htab_delete (dup_htab); + dup_htab = NULL; + return 0; +fail: + if (unlikely (meta_abbrev_htab != NULL)) + { + dw_cu_ref cu; + + for (cu = first_cu; cu; cu = cu->cu_next) + cu->cu_abbrev = NULL; + htab_delete (meta_abbrev_htab); + meta_abbrev_htab = NULL; + obstack_free (&ob2, to_free); + } + else if (abbrev) + htab_delete (abbrev); + if (dup_htab && kind == DEBUG_INFO) + { + htab_delete (dup_htab); + dup_htab = NULL; + } + return ret; +} + +/* Compare function called from qsort, which should ensure that + dup candidate dies with the same set of referrer CUs are + adjacent. */ +static int +partition_cmp (const void *p, const void *q) +{ + dw_die_ref die1 = *(dw_die_ref *) p; + dw_die_ref die2 = *(dw_die_ref *) q; + dw_die_ref ref1, ref2; + dw_cu_ref last_cu1 = NULL, last_cu2 = NULL; + ref1 = die1; + ref2 = die2; + if (odr_active_p && odr_mode != ODR_BASIC) + { + while (ref1 && die_odr_state (ref1) == ODR_DECL) + ref1 = ref1->die_nextdup; + if (ref1 == NULL) + ref1 = die1; + while (ref2 && die_odr_state (ref2) == ODR_DECL) + ref2 = ref2->die_nextdup; + if (ref2 == NULL) + ref2 = die2; + } + for (;; ref1 = ref1->die_nextdup, ref2 = ref2->die_nextdup) + { + dw_cu_ref ref1cu = NULL; + dw_cu_ref ref2cu = NULL; + while (ref1 && (ref1cu = die_cu (ref1)) == last_cu1) + ref1 = ref1->die_nextdup; + while (ref2 && (ref2cu = die_cu (ref2)) == last_cu2) + ref2 = ref2->die_nextdup; + if (ref1 == NULL || ref2 == NULL) + break; + last_cu1 = ref1cu; + last_cu2 = ref2cu; + if (last_cu1->cu_offset < last_cu2->cu_offset) + return -1; + else if (last_cu1->cu_offset > last_cu2->cu_offset) + return 1; + } + if (ref1) + return -1; + if (ref2) + return 1; + /* The rest is just to keep sort stable. */ + if (die1->die_offset < die2->die_offset) + return -1; + if (die1->die_offset > die2->die_offset) + return 1; + return 0; +} + +/* Add duplicate chain for DIE to VEC. */ +static void +partition_found_dups (dw_die_ref die, struct obstack *vec) +{ + assert (die->die_ck_state == CK_KNOWN); + obstack_ptr_grow (vec, die); + if (unlikely (verify_dups_p)) + verify_dups (die, true); + + if (stats_p) + { + uint64_t length = 0; + for (; die; die = die->die_nextdup) + length++; + stats->dup_cnt += length; + stats->dup_chain_max_length = MAX (stats->dup_chain_max_length, length); + stats->dup_chain_cnt++; + } +} + +/* Sort duplication chain for HEAD, assuming the chain was formed by + die_eq. */ +static void +sort_dups (dw_die_ref head) +{ + dw_die_ref prev = NULL, die, next; + /* Sort the die_nextdup list by increasing die_cu ()->cu_chunk. + When it is originally added, child has the lowest + cu_offset, then the DIEs are sorted in the linked list + from highest cu_offset down to lowest or second lowest. */ + for (die = head->die_nextdup; die; prev = die, die = next) + { + next = die->die_nextdup; + die->die_nextdup = prev; + } + head->die_nextdup = prev; +} + +/* Merge duplicate chains D and D2, and return the head of the merged +chain. */ +static dw_die_ref +merge_dups (dw_die_ref d, dw_die_ref d2) +{ + if (d == NULL) + return d2; + if (d2 == NULL) + return d; + + dw_die_ref tail = NULL; + dw_die_ref head = NULL; + while (true) + { + dw_die_ref next; + if (d && d2) + { + if (d->die_offset < d2->die_offset) + { + next = d; + d = d->die_nextdup; + } + else + { + next = d2; + d2 = d2->die_nextdup; + } + } + else if (d) + { + next = d; + d = d->die_nextdup; + } + else if (d2) + { + next = d2; + d2 = d2->die_nextdup; + } + else + break; + if (!head) + head = next; + if (tail) + tail->die_nextdup = next; + tail = next; + } + + for (d = head; d; d = d->die_nextdup) + if (d == head) + d->die_dup = NULL; + else + d->die_dup = head; + + if (unlikely (verify_dups_p)) + verify_dups (head, true); + return head; +} + +static void +mark_singletons (dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die, + struct obstack *vec) +{ + struct abbrev_tag *t; + unsigned int i; + unsigned char *ptr; + dw_die_ref child; + bool found; + + t = die->die_abbrev; + + found = false; + for (i = 0; i < t->nattr; ++i) + { + struct abbrev_attr *attr = &t->attr[i]; + if (attr->attr == DW_AT_sibling) + continue; + switch (attr->form) + { + case DW_FORM_ref_udata: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_indirect: + found = true; + break; + } + } + if (!found) + goto handle_children; + + if (unlikely (cu->cu_kind == CU_TYPES)) + ptr = debug_sections[DEBUG_TYPES].data; + else + ptr = debug_sections[DEBUG_INFO].data; + ptr += die->die_offset; + skip_leb128 (ptr); + for (i = 0; i < t->nattr; ++i) + { + struct abbrev_attr *attr = &t->attr[i]; + uint32_t form = attr->form; + uint64_t value; + dw_die_ref ref, reft; + + while (form == DW_FORM_indirect) + form = read_uleb128 (ptr); + + switch (form) + { + case DW_FORM_ref_udata: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + switch (form) + { + case DW_FORM_ref_udata: value = read_uleb128 (ptr); break; + case DW_FORM_ref1: value = read_8 (ptr); break; + case DW_FORM_ref2: value = read_16 (ptr); break; + case DW_FORM_ref4: value = read_32 (ptr); break; + case DW_FORM_ref8: value = read_64 (ptr); break; + default: abort (); + } + if (t->attr[i].attr == DW_AT_sibling) + break; + ref = off_htab_lookup (cu, cu->cu_offset + value); + if (!ref->die_collapsed_child + && ref->u.p1.die_enter >= top_die->u.p1.die_enter + && ref->u.p1.die_exit <= top_die->u.p1.die_exit) + break; + reft = ref; + while (!reft->die_root + && reft->die_parent->die_tag != DW_TAG_compile_unit + && reft->die_parent->die_tag != DW_TAG_partial_unit + && !reft->die_parent->die_named_namespace) + reft = reft->die_parent; + if (reft->die_dup != NULL || reft->die_nextdup != NULL) + break; + if (reft->die_ref_seen) + break; + reft->die_ref_seen = 1; + partition_found_dups (reft, vec); + mark_singletons (die_cu (reft), reft, reft, vec); + break; + default: + ptr = skip_attr_no_dw_form_indirect (cu->cu_version, form, ptr); + break; + } + } + + handle_children: + for (child = die->die_child; child; child = child->die_sib) + mark_singletons (cu, top_die, child, vec); +} + +/* Split maximal duplicate chain DIE into smaller chains that contain + structurally equal defs, and combine the decls with one of those. + Return the first chain, and call partition_found_dups for the others. */ +static dw_die_ref +split_dups (dw_die_ref die, struct obstack *vec) +{ + dw_die_ref res = NULL; + + /* Count the DIEs in the duplicate chain. */ + unsigned count = 0; + dw_die_ref d; + for (d = die; d; d = d->die_nextdup) + count++; + assert (count >= 2); + + /* Break up the duplicate chain. */ + dw_die_ref arr[count]; + unsigned int i; + for (d = die, i = 0; d; d = d->die_nextdup, i++) + arr[i] = d; + for (i = 0; i < count; i++) + { + d = arr[i]; + d->die_nextdup = NULL; + d->die_dup = NULL; + } + + /* Build decls duplicate chain. */ + dw_die_ref tail = NULL; + dw_die_ref head = NULL; + for (i = 0; i < count; i++) + { + d = arr[i]; + if (die_odr_state (d) != ODR_DECL) + continue; + if (!head) + head = d; + if (tail) + tail->die_nextdup = d; + if (d != head) + d->die_dup = head; + tail = d; + } + dw_die_ref decls = head; + + /* Build def duplicate chains. */ + unsigned int j; + dw_die_ref d2; + for (i = 0; i < count; i++) + { + d = arr[i]; + if (d->die_dup || d->die_nextdup + || die_odr_state (d) == ODR_DECL) + continue; + tail = d; + for (j = i + 1; j < count; j++) + { + d2 = arr[j]; + if (d2->die_dup || d2->die_nextdup + || die_odr_state (d2) == ODR_DECL) + continue; + die_eq (d, d2); + } + sort_dups (d); + } + + if (odr_mode != ODR_BASIC) + { + /* Find the first duplicate chain containing defs. */ + dw_die_ref def = NULL; + for (i = 0; i < count; i++) + { + d = arr[i]; + if (die_odr_state (d) == ODR_DECL + || d->die_dup != NULL) + continue; + def = d; + break; + } + + /* Merge the def chain with the decls. */ + merge_dups (def, decls); + } + + for (i = 0; i < count; i++) + { + d = arr[i]; + if (d->die_dup == NULL) + /* If DIE is now either: + - no longer part of a duplicate chain, or + - head of a duplicate chain, + don't remove it. */ + d->die_remove = 0; + } + + /* Notice the new duplicate chains. */ + for (i = 0; i < count; i++) + { + d = arr[i]; + if (d->die_dup != NULL) + continue; + if (res == NULL) + res = d; + else + partition_found_dups (d, vec); + } + return res; +} + +/* Search for duplicate removal reference DIE candidates + in tree rooted by PARENT. */ +static void +partition_find_dups (struct obstack *vec, dw_die_ref parent) +{ + dw_die_ref child; + for (child = parent->die_child; child; child = child->die_sib) + { + if (child->die_nextdup != NULL + && child->die_dup == NULL + && child->die_offset != -1U) + { + dw_die_ref die; + + if (unlikely (op_multifile)) + { + /* If all the dups are from the same DSO or executable, + there is nothing in it to optimize in between different + objects. */ + unsigned int cu_chunk = die_cu (child)->cu_chunk; + for (die = child->die_nextdup; die; die = die->die_nextdup) + if (die_cu (die)->cu_chunk != cu_chunk) + break; + if (die == NULL) + continue; + } + sort_dups (child); + partition_found_dups (child, vec); + } + else if (child->die_named_namespace) + partition_find_dups (vec, child); + } +} + +/* Reorder duplicate chain DIE to make sure it doesn't start with a decl. */ +static dw_die_ref +reorder_dups (dw_die_ref die) +{ + unsigned decl_count = 0; + unsigned def_count = 0; + dw_die_ref d; + + if (die_odr_state (die) == ODR_NONE) + return die; + + for (d = die; d; d = d->die_nextdup) + { + if (die_odr_state (d) == ODR_DECL) + decl_count++; + else + def_count++; + } + if (def_count == 0 || decl_count == 0) + return die; + + if (die_odr_state (die) != ODR_DECL) + return die; + + dw_die_ref def = NULL; + dw_die_ref prev = NULL; + for (d = die; d; prev = d, d = d->die_nextdup) + { + if (die_odr_state (d) == ODR_DECL) + continue; + def = d; + break; + } + + assert (!die->die_remove); + assert (def->die_remove); + def->die_remove = 0; + die->die_remove = 1; + def->die_ref_seen = die->die_ref_seen; + dw_die_ref next = def->die_nextdup; + if (prev) + prev->die_nextdup = next; + def->die_nextdup = die; + for (d = def; d; prev = d, d = d->die_nextdup) + { + if (d == def) + d->die_dup = NULL; + else + d->die_dup = def; + } + + if (unlikely (verify_dups_p)) + verify_dups (def, false); + return def; +} + +/* Copy DIE tree of DIE, as children of new DIE PARENT. */ +static dw_die_ref +copy_die_tree (dw_die_ref parent, dw_die_ref die) +{ + dw_die_ref child, new_child, *diep; + dw_die_ref new_die; + if (die->die_toplevel) + { + new_die = pool_alloc (dw_die, sizeof (struct dw_die)); + memset (new_die, '\0', sizeof (*new_die)); + new_die->die_toplevel = 1; + die->die_dup = new_die; + new_die->die_nextdup = die; + if (!die->die_op_type_referenced) + die->die_remove = 1; + } + else + { + new_die = pool_alloc (dw_die, offsetof (struct dw_die, die_dup)); + memset (new_die, '\0', offsetof (struct dw_die, die_dup)); + } + new_die->die_parent = parent; + new_die->die_tag = die->die_tag; + new_die->die_offset = -1U; + new_die->die_size = die->die_size; + diep = &new_die->die_child; + for (child = die->die_child; child; child = child->die_sib) + { + new_child = copy_die_tree (new_die, child); + *diep = new_child; + diep = &new_child->die_sib; + } + return new_die; +} + +/* If all DIEs in the duplication chain DIE are in CUs with the same + language, return that language. Otherwise, return 0. */ +static enum dwarf_source_language +partition_lang (dw_die_ref die) +{ + enum dwarf_source_language lang; + dw_die_ref d; + + if (die == NULL) + return 0; + + lang = die_cu (die)->lang; + switch (lang) + { + case DW_LANG_C_plus_plus: + case DW_LANG_C_plus_plus_03: + case DW_LANG_C_plus_plus_11: + case DW_LANG_C_plus_plus_14: + break; + default: + return 0; + } + + for (d = die->die_nextdup; d; d = d->die_nextdup) + if (die_cu (d)->lang != lang) + return 0; + + return lang; +} + +/* Return how many bytes we need to encode VAL. */ +static unsigned int +nr_bytes_for (uint64_t val) +{ + unsigned int n; + + if (val == 0) + return 1; + + for (n = 0; val > 0; n++) + val = val >> 8; + + return n; +} + +/* Return true if duplicate chains REF1 and REF2 have the same set of + referrer CUs. If so, return the number of unique referrer CUs + in *CNT. */ +static inline unsigned int FORCE_INLINE +same_ref_cus_p (dw_die_ref ref1, dw_die_ref ref2, size_t *cnt) +{ + dw_cu_ref last_cu1 = NULL, last_cu2 = NULL; + + *cnt = 0; + + if (odr_active_p && odr_mode != ODR_BASIC) + { + dw_die_ref orig_ref1 = ref1, orig_ref2 = ref2; + while (ref1 && die_odr_state (ref1) == ODR_DECL) + ref1 = ref1->die_nextdup; + if (ref1 == NULL) + ref1 = orig_ref1; + while (ref2 && die_odr_state (ref2) == ODR_DECL) + ref2 = ref2->die_nextdup; + if (ref2 == NULL) + ref2 = orig_ref2; + } + for (;; ref1 = ref1->die_nextdup, ref2 = ref2->die_nextdup) + { + dw_cu_ref ref1cu = NULL; + dw_cu_ref ref2cu = NULL; + + while (ref1 && (ref1cu = die_cu (ref1)) == last_cu1) + ref1 = ref1->die_nextdup; + while (ref2 && (ref2cu = die_cu (ref2)) == last_cu2) + ref2 = ref2->die_nextdup; + if (ref1 == NULL || ref2 == NULL) + break; + + last_cu1 = ref1cu; + last_cu2 = ref2cu; + + if (last_cu1 != last_cu2) + break; + else + (*cnt)++; + } + + if (ref1 || ref2) + return false; + + return true; +} + +/* Return the number of unique referrer CUs in duplicate chain REF. */ +static inline size_t FORCE_INLINE +cnt_ref_cus (dw_die_ref ref) +{ + dw_cu_ref last_cu1 = NULL; + size_t cnt = 0; + + for (;; ref = ref->die_nextdup) + { + dw_cu_ref refcu = NULL; + while (ref && (refcu = die_cu (ref)) == last_cu1) + ref = ref->die_nextdup; + if (ref == NULL) + break; + last_cu1 = refcu; + cnt++; + } + + return cnt; +} + +/* Helper function of partition_dups_1. Decide what DIEs matching in + multiple CUs might be worthwhile to be moved into partial units, + construct those partial units. */ +static bool +partition_dups_1 (dw_die_ref *arr, size_t nr_partitions, size_t *partitions, + dw_cu_ref *first_partial_cu, + dw_cu_ref *last_partial_cu, + bool second_phase) +{ + size_t i, j, cnt; + bool ret = false; + size_t idx = 0; + for (idx = 0; idx < nr_partitions * 2; idx += 2) + { + i = partitions[idx]; + cnt = partitions[idx + 1]; + j = partitions[idx + 2]; + + if (arr[i]->die_dup != NULL) + continue; + + dw_die_ref ref; + size_t size = 0, k, orig_size, new_size, namespaces = 0; + unsigned int force = 0; + enum dwarf_source_language part_lang + = gen_cu_p ? partition_lang (arr[i]) : 0; + for (k = i; k < j; k++) + { + if (second_phase && arr[k]->die_ref_seen) + force++; + size += calc_sizes (arr[k]); + for (ref = arr[k]->die_parent; + ref->die_named_namespace && ref->die_dup == NULL; + ref = ref->die_parent) + { + ref->die_dup = arr[k]; + namespaces++; + } + } + /* If during second_phase there are some DIEs we want to force + into a partial unit because they are referenced from something + already forced into a partial unit, but also some DIEs with + the same set of referrers, try to see if we can put also those + into the partial unit. They can be put there only if they + don't refer to DIEs that won't be put into partial units. */ + if (unlikely (partition_dups_opt) + && second_phase && force && force < j - i) + { + /* First optimistically assume all such DIEs can be put there, + thus mark all such DIEs as going to be included, so that + even if one of those DIEs references another one from those + DIEs it can be included. */ + for (k = i; k < j; k++) + { + assert (arr[k]->die_ref_seen < 2); + if (arr[k]->die_ref_seen == 0) + arr[k]->die_ref_seen = 2; + } + for (k = i; k < j; k++) + if (arr[k]->die_ref_seen == 2 + && !mark_refs (die_cu (arr[k]), arr[k], arr[k], + (MARK_REFS_FOLLOW_DUPS | MARK_REFS_RETURN_VAL))) + break; + /* If that is not possible and some DIEs couldn't be included, + fallback to assume other DIEs won't be included. */ + if (k < j) + { + for (k = i; k < j; k++) + if (arr[k]->die_ref_seen == 2) + arr[k]->die_ref_seen = 0; + for (k = i; k < j; k++) + if (arr[k]->die_ref_seen == 0) + { + arr[k]->die_ref_seen = 2; + if (!mark_refs (die_cu (arr[k]), arr[k], arr[k], + (MARK_REFS_FOLLOW_DUPS + | MARK_REFS_RETURN_VAL))) + arr[k]->die_ref_seen = 0; + } + } + } + if (namespaces) + { + for (k = i; k < j; k++) + for (ref = arr[k]->die_parent; ref->die_named_namespace; + ref = ref->die_parent) + ref->die_dup = NULL; + } + orig_size = size * cnt; + /* Estimated size of CU header and DW_TAG_partial_unit + with DW_AT_stmt_list and DW_AT_comp_dir attributes + 21 (also child end byte). With DW_AT_language c++, 22. */ + size_t pu_size + = (/* CU Header: unit length (initial length). + 32-bit DWARF: 4 bytes, 64-bit DWARF: 12 bytes. */ + 4 + /* CU Header: version (uhalf). + 2 bytes. */ + + 2 + /* CU Header: debug_abbrev_offset (section offset). + 32-bit DWARF: 4 bytes, 64-bit DWARF: 8 bytes. */ + + 4 + /* CU Header: address_size (ubyte). + 1 byte. */ + + 1 + /* DWARF5 CU header: unit_type (ubyte). */ + + (die_cu (arr[i])->cu_version >= 5 ? 1 : 0) + /* CU Root DIE: abbreviation code (unsigned LEB128). + 1 or more bytes. Optimistically assume 1. */ + + 1 + /* CU Root DIE: DW_AT_stmt_list (lineptr). + 32-bit DWARF: 4 bytes, 64-bit DWARF: 8 bytes. */ + + 4 + /* CU Root DIE: DW_AT_comp_dir (string). + DW_FORM_strp: 32-bit DWARF: 4 bytes, 64-bit DWARF: 8 bytes. + DW_FORM_string: 1 or more bytes. + Assume 4 bytes. */ + + 4 + /* CU Root DIE: DW_AT_language (constant). + 1 or 2 bytes. */ + + ((uni_lang_p || part_lang) + ? nr_bytes_for (die_cu (arr[i])->lang) + : 0) + /* CU root DIE children terminator: abbreviation code 0 + (unsigned LEB128). + 1 byte. */ + + 1); + /* DW_TAG_imported_unit with DW_AT_import attribute + (5 or 9 bytes (the latter for DWARF2 and ptr_size 8)). */ + size_t import_size + = (die_cu (arr[i])->cu_version == 2 ? 1 + ptr_size : 5); + /* For DW_TAG_namespace or DW_TAG_module needed as + parents of the DIEs conservatively assume 10 bytes + for the abbrev index, DW_AT_name attribute and + DW_AT_sibling attribute and child end. */ + size_t namespace_size = 10; + new_size = (/* Size of DIEs. */ + size + /* Size of PU. */ + + pu_size + /* Size of imports. */ + + (part_lang != 0 ? 0 : import_size * cnt) + /* Size of namespace DIEs. */ + + namespace_size * namespaces); + if (!second_phase) + force = ((deduplication_mode == dm_inter_cu) + && (ignore_size || orig_size > new_size)); + if (force) + { + dw_die_ref die, *diep; + dw_cu_ref refcu = die_cu (arr[i]); + dw_cu_ref partial_cu = pool_alloc (dw_cu, sizeof (struct dw_cu)); + memset (partial_cu, '\0', sizeof (*partial_cu)); + if (stats_p) + { + if (!second_phase) + stats->pu_ph1_cnt++; + else + stats->pu_ph2_cnt++; + } + partial_cu->cu_kind = CU_PU; + partial_cu->cu_offset = *last_partial_cu == NULL + ? 0 : (*last_partial_cu)->cu_offset + 1; + if (dump_pus_p) + fprintf (stderr, "Partial unit (%s) @ 0x%x:\n", + second_phase ? "phase two" : "phase one", + partial_cu->cu_offset); + partial_cu->cu_version = refcu->cu_version; + if (uni_lang_p) + partial_cu->lang = refcu->lang; + if (*first_partial_cu == NULL) + *first_partial_cu = *last_partial_cu = partial_cu; + else + { + (*last_partial_cu)->cu_next = partial_cu; + *last_partial_cu = partial_cu; + } + die = pool_alloc (dw_die, sizeof (struct dw_die)); + memset (die, '\0', sizeof (*die)); + die->die_toplevel = 1; + partial_cu->cu_die = die; + die->die_tag = DW_TAG_partial_unit; + die->die_offset = -1U; + die->die_root = 1; + die->die_parent = (dw_die_ref) partial_cu; + die->die_nextdup = refcu->cu_die; + die->die_size = 9; + diep = &die->die_child; + for (k = i; k < j; k++) + { + dw_die_ref child; + if (second_phase && !arr[k]->die_ref_seen) + continue; + if (dump_pus_p) + dump_die (arr[k]); + child = copy_die_tree (die, arr[k]); + if (stats_p) + stats->pu_toplevel_die_cnt++; + for (ref = arr[k]->die_nextdup; ref; ref = ref->die_nextdup) + ref->die_dup = child; + if (unlikely (verify_dups_p)) + verify_dups (child, odr_mode == ODR_BASIC); + if (part_lang != 0) + { + die->die_tag = DW_TAG_compile_unit; + partial_cu->lang = part_lang; + } + if (namespaces) + { + for (ref = arr[k]->die_parent; + ref->die_named_namespace && ref->die_dup == NULL; + ref = ref->die_parent) + { + dw_die_ref namespc + = pool_alloc (dw_die, sizeof (struct dw_die)); + memset (namespc, '\0', sizeof (struct dw_die)); + namespc->die_toplevel = 1; + namespc->die_tag = ref->die_tag; + namespc->die_offset = -1U; + namespc->die_nextdup = ref; + namespc->die_child = child; + namespc->die_parent = die; + namespc->die_size = 9; + namespc->die_named_namespace = 1; + child->die_parent = namespc; + ref->die_dup = namespc; + child = namespc; + } + if (ref->die_dup != NULL) + { + dw_die_ref *diep2; + for (diep2 = &ref->die_dup->die_child->die_sib; + *diep2; diep2 = &(*diep2)->die_sib) + ; + *diep2 = child; + child->die_parent = ref->die_dup; + continue; + } + } + *diep = child; + diep = &child->die_sib; + } + if (namespaces) + { + for (k = i; k < j; k++) + { + if (second_phase && !arr[k]->die_ref_seen) + continue; + for (ref = arr[k]->die_parent; + ref->die_named_namespace; ref = ref->die_parent) + ref->die_dup = NULL; + } + } + } + else if (!second_phase) + ret = true; + if (second_phase) + { + dw_die_ref next; + for (k = i; k < j; k++) + { + if (arr[k]->die_dup != NULL) + continue; + for (ref = arr[k]; ref; ref = next) + { + dw_cu_ref refcu = die_cu (ref); + next = ref->die_nextdup; + ref->die_dup = NULL; + ref->die_nextdup = NULL; + ref->die_remove = 0; + /* If there are dups within a single CU + (arguably a bug in the DWARF producer), + keep them linked together, but don't link + DIEs across different CUs. */ + while (deduplication_mode != dm_none + && next && refcu == die_cu (next)) + { + dw_die_ref cur = next; + next = cur->die_nextdup; + cur->die_dup = ref; + cur->die_nextdup = ref->die_nextdup; + ref->die_nextdup = cur; + } + } + } + } + } + return ret; +} + +/* Partition the duplicate chains in array ARR with size VEC_SIZE, and store + the partitions on obstack ob2, with for each partition two entries: + the start and the number of unique reffer CUs. */ +static void +calculate_partitions (dw_die_ref *arr, size_t vec_size) +{ + size_t i, j; + for (i = 0; i < vec_size; i = j) + { + size_t cnt = 0; + for (j = i + 1; j < vec_size; j++) + { + size_t this_cnt; + if (!same_ref_cus_p (arr[i], arr[j], &this_cnt)) + break; + cnt = this_cnt; + } + if (cnt == 0) + cnt = cnt_ref_cus (arr[i]); + obstack_grow (&ob2, &i, sizeof (size_t)); + obstack_grow (&ob2, &cnt, sizeof (size_t)); + } + + /* Add element to mark end of partition list. This allows us to do + 'j = partitions[idx + 2]' for all partitions. */ + obstack_grow (&ob2, &j, sizeof (size_t)); + size_t zero = 0; + obstack_grow (&ob2, &zero, sizeof (size_t)); +} + +static inline void FORCE_INLINE +reset_die_ref_seen (void) +{ + dw_die_ref die; + dw_cu_ref cu; + FOREACH_CU_NORMAL_LOW_TOPLEVEL_DIE (cu, die) + die->die_ref_seen = 0; +} + +/* If the duplicate chain DIE consists of a singleton ODR_DEF die merged with + the ODR_DECL chain, return the singleton ODR_DEF die. Otherwise, return + NULL. */ +static inline dw_die_ref FORCE_INLINE +merged_singleton (dw_die_ref die) +{ + dw_die_ref res = NULL; + dw_die_ref d; + size_t decl_cnt = 0; + + for (d = die; d; d = d->die_nextdup) + switch (die_odr_state (d)) + { + case ODR_DEF: + if (res) + { + if (die_cu (res) == die_cu (d)) + continue; + else + return NULL; + } + else + res = d; + break; + case ODR_DECL: + decl_cnt++; + break; + default: + return NULL; + } + + if (decl_cnt == 0) + return NULL; + + return res; +} + +/* Decide what DIEs matching in multiple CUs might be worthwhile + to be moved into partial units, construct those partial units. */ +static int +partition_dups (void) +{ + dw_cu_ref cu, first_partial_cu = NULL, last_partial_cu = NULL; + size_t vec_size, i; + unsigned char *to_free; + dw_die_ref *arr; + + if (unlikely (fi_multifile)) + return 0; + + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "partition_dups\n"); + } + + to_free = obstack_alloc (&ob2, 1); + + if (odr_active_p) + odr_phase = 2; + + for (cu = first_cu; cu; cu = cu->cu_next) + partition_find_dups (&ob2, cu->cu_die); + vec_size = obstack_object_size (&ob2) / sizeof (void *); + + if (odr_active_p) + { + arr = (dw_die_ref *) obstack_base (&ob2); + if (progress_p) + { + report_progress (); + fprintf (stderr, "partition_dups split_dups\n"); + } + for (i = 0; i < vec_size; i++) + { + dw_die_ref die = arr[i]; + if (die_odr_state (die) == ODR_NONE) + continue; + die = split_dups (die, &ob2); + assert (die != NULL); + if (unlikely (verify_dups_p)) + verify_dups (die, true); + arr = (dw_die_ref *) obstack_base (&ob2); + arr[i] = die; + } + + vec_size = obstack_object_size (&ob2) / sizeof (void *); + + reset_die_ref_seen (); + for (i = 0; i < vec_size; i++) + { + dw_die_ref die = arr[i]; + if (die->die_dup == NULL + && die->die_nextdup == NULL) + die->die_ref_seen = 1; + } + for (i = 0; i < vec_size; i++) + { + dw_die_ref die = arr[i]; + if (die->die_dup == NULL + && die->die_nextdup == NULL) + mark_singletons (die_cu (die), die, die, &ob2); + else if (odr_mode != ODR_BASIC + && die_odr_state (die) != ODR_NONE) + { + dw_die_ref s = merged_singleton (die); + if (s) + mark_singletons (die_cu (s), s, s, &ob2); + } + else if (cnt_ref_cus (die) == 1) + mark_singletons (die_cu (die), die, die, &ob2); + + arr = (dw_die_ref *) obstack_base (&ob2); + } + + vec_size = obstack_object_size (&ob2) / sizeof (void *); + } + + if (odr_active_p) + odr_phase = 3; + + if (stats_p) + print_dups_stats (); + + if (vec_size != 0) + { + arr = (dw_die_ref *) obstack_finish (&ob2); + if (odr_active_p) + for (i = 0; i < vec_size; ++i) + { + assert (arr[i] != NULL); + if (unlikely (verify_dups_p)) + verify_dups (arr[i], true); + } + if (dump_dups_p) + { + for (i = 0; i < vec_size; ++i) + { + fprintf (stderr, "duplicate chain:\n"); + dump_dups (arr[i]); + } + } + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "partition_dups qsort\n"); + } + qsort (arr, vec_size, sizeof (dw_die_ref), partition_cmp); + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "partition_dups after qsort\n"); + } + + size_t *partitions = (size_t *) obstack_base (&ob2); + calculate_partitions (arr, vec_size); + size_t nr_partitions + = (obstack_object_size (&ob2) / sizeof (size_t)) / 2 - 1; + partitions = (size_t *) obstack_finish (&ob2); + if (stats_p) + stats->part_cnt += nr_partitions; + + if (odr_active_p && odr_mode != ODR_BASIC) + for (i = 0; i < vec_size; ++i) + arr[i] = reorder_dups (arr[i]); + if (partition_dups_1 (arr, nr_partitions, partitions, &first_partial_cu, + &last_partial_cu, false)) + { + for (i = 0; i < vec_size; i++) + arr[i]->die_ref_seen = arr[i]->die_dup != NULL; + for (i = 0; i < vec_size; i++) + if (arr[i]->die_dup != NULL) + mark_refs (die_cu (arr[i]), arr[i], arr[i], + MARK_REFS_FOLLOW_DUPS); + partition_dups_1 (arr, nr_partitions, partitions, &first_partial_cu, + &last_partial_cu, true); + for (i = 0; i < vec_size; i++) + arr[i]->die_ref_seen = 0; + } + } + if (first_partial_cu) + { + last_partial_cu->cu_next = first_cu; + first_cu = first_partial_cu; + } + obstack_free (&ob2, to_free); + + if (stats_p) + print_part_stats (); + + return 0; +} + +/* The create_import_tree function below and all its helper + data structures and functions attempt to optimize the size of + DW_TAG_imported_unit DIEs, from the initial assumption that + each CU that needs to include some newly created DW_TAG_partial_unit + will contain DW_TAG_imported_unit for each such partial unit (PU) + (so basically a bipartite graph with CUs and PUs as nodes + and DW_TAG_imported_unit DIEs as edges) into a tree, where some + of the partial units may also include DW_TAG_imported_unit + DIEs, or when beneficial new PUs are created to hold some + DW_TAG_imported_unit DIEs. */ + +struct import_edge; + +/* Structure describing details about a CU or PU (i.e. a node + in the graph). */ +struct import_cu +{ + /* Corresponding CU. CU->u1.cu_icu points back to this + structure while in create_import_tree. */ + dw_cu_ref cu; + /* Linked list of incoming resp. outgoing edges. */ + struct import_edge *incoming, *outgoing; + /* The tail of the linked list of incoming edges. */ + struct import_edge *incoming_tail; + /* Next import_cu (used to chain PUs together). */ + struct import_cu *next; + /* Number of incoming resp. outgoing edges. */ + unsigned int incoming_count, outgoing_count; + /* Index. Lowest indexes are assigned to partition_dups + created PUs (sorted by decreasing number of incoming + edges at the start), then referencing CUs + (similarly, sorted by decreasing number of outgoing + edges at the start), then optionally any PUs + created by create_import_tree. */ + unsigned int idx; +}; + +/* An edge in a linked list. */ +struct import_edge +{ + struct import_cu *icu; + struct import_edge *next; +}; + +/* Called through qsort to sort an array of edges by decreasing + incoming resp. outgoing_count (this is called when the graph + is bipartite, so CUs only have non-zero outgoing_count + and PUs only have non-zero incoming_count). */ +static int +import_edge_cmp (const void *p, const void *q) +{ + struct import_edge *e1 = (struct import_edge *) p; + struct import_edge *e2 = (struct import_edge *) q; + if (e1->icu->incoming_count > e2->icu->incoming_count) + return -1; + if (e1->icu->incoming_count < e2->icu->incoming_count) + return 1; + if (e1->icu->outgoing_count > e2->icu->outgoing_count) + return -1; + if (e1->icu->outgoing_count < e2->icu->outgoing_count) + return 1; + /* The rest is just to keep qsort stable. */ + if (e1->icu->cu->cu_offset < e2->icu->cu->cu_offset) + return -1; + if (e1->icu->cu->cu_offset > e2->icu->cu->cu_offset) + return 1; + return 0; +} + +/* Called through qsort to sort an array of CUs/PUs by decreasing + incoming resp. outgoing_count (this is called when the graph + is bipartite, so CUs only have non-zero outgoing_count + and PUs only have non-zero incoming_count). */ +static int +import_cu_cmp (const void *p, const void *q) +{ + struct import_cu *c1 = *(struct import_cu **) p; + struct import_cu *c2 = *(struct import_cu **) q; + if (c1->incoming_count > c2->incoming_count) + return -1; + if (c1->incoming_count < c2->incoming_count) + return 1; + if (c1->outgoing_count > c2->outgoing_count) + return -1; + if (c1->outgoing_count < c2->outgoing_count) + return 1; + /* The rest is just to keep qsort stable. */ + if (c1->cu->cu_offset < c2->cu->cu_offset) + return -1; + if (c1->cu->cu_offset > c2->cu->cu_offset) + return 1; + return 0; +} + +/* Freelist for removed edges. */ +static struct import_edge *edge_freelist; + +/* Prepare edge E to add to edge_freelist. */ +static inline void FORCE_INLINE +prepare_free_edge (struct import_edge *e UNUSED) +{ +#if DEVEL + e->icu = (void *)(uintptr_t)-1; +#endif +} + +/* Add edge E to edge_freelist. */ +static inline void FORCE_INLINE +free_edge (struct import_edge *e) +{ + prepare_free_edge (e); + e->next = edge_freelist; + edge_freelist = e; +} + +/* Add edge list starting at HEAD and ending at TAIL to edge_freelist. + Assume that prepare_free_edge has been called on all elements. */ +static inline void FORCE_INLINE +free_edges (struct import_edge *head, struct import_edge *tail) +{ +#if DEVEL + if (verify_edge_freelist) + { + struct import_edge *e; + for (e = head; e; e = e->next) + { + assert (e->icu == (void *)(uintptr_t)-1); + if (e == tail) + break; + } + assert (e != NULL); + } +#endif + tail->next = edge_freelist; + edge_freelist = head; +} + +/* Detach an edge from edge_freelist, and return it. */ +static inline struct import_edge * FORCE_INLINE +edge_from_freelist (void) +{ +#if DEVEL + assert (edge_freelist); +#endif + struct import_edge *e = edge_freelist; + edge_freelist = edge_freelist->next; +#if DEVEL + e->next = (void *)(uintptr_t)-1; +#endif + return e; +} + +/* Return edge_freelist, and set it to NULL. */ +static inline struct import_edge * FORCE_INLINE +first_edge_from_freelist (void) +{ +#if DEVEL + assert (edge_freelist); +#endif + struct import_edge *e = edge_freelist; +#if DEVEL + edge_freelist = NULL; +#endif + return e; +} + +/* Set edge_freelist to TAIL->next and return HEAD. Assume HEAD was returned + by first_edge_from_freelist, and TAIL is reachable from HEAD. */ +static inline struct import_edge * FORCE_INLINE +last_edge_from_freelist (struct import_edge *head, struct import_edge *tail) +{ +#if DEVEL + assert (!edge_freelist); + if (verify_edge_freelist) + { + struct import_edge *e; + for (e = head; e; e = e->next) + { + if (e == tail) + break; + } + assert (e != NULL); + } +#endif + edge_freelist = tail->next; + tail->next = NULL; + return head; +} + +/* Remove edges in linked list EP that refer to CUS, which + is an array of CUCOUNT CUs/PUs. If ADD is true, additionally + add a new edge at the end of the linked list and return it. */ +static struct import_edge * +remove_import_edges (struct import_edge **ep, struct import_edge **ep_tail, + struct import_cu **cus, unsigned int cucount, bool add) +{ + unsigned int i = 0; + struct import_edge *e, *efirst = NULL, *prev = NULL; + while (*ep) + if (i < cucount && (*ep)->icu == cus[i]) + { + e = *ep; + *ep = e->next; + if (efirst == NULL) + efirst = e; + else + free_edge (e); + i++; + if (ep_tail && *ep_tail == e) + *ep_tail = prev; + if (i == cucount && !add) + return NULL; + } + else + { + if (ep_tail) + prev = *ep; + ep = &(*ep)->next; + } + assert (i == cucount); + *ep = efirst; + efirst->next = NULL; + if (ep_tail) + *ep_tail = efirst; + return efirst; +} + +static void +dump_edges_1 (struct import_cu *ipu) +{ + fprintf (stderr, "idx: %u\n", ipu->idx); + fprintf (stderr, "cu: 0x%x\n", ipu->cu->cu_offset); + struct import_edge *e1; + for (e1 = ipu->incoming; e1; e1 = e1->next) + fprintf (stderr, "incoming: %u\n", e1->icu->idx); + for (e1 = ipu->outgoing; e1; e1 = e1->next) + fprintf (stderr, "outgoing: %u\n", e1->icu->idx); +} + +static void +dump_edges (const char *msg, struct import_cu **ipus, unsigned int npus, + unsigned int ncus) +{ + struct import_cu *ipu; + unsigned int i; + fprintf (stderr, "PRINT_EDGES: %s\n", msg); + fprintf (stderr, "PUs\n"); + for (ipu = ipus[0]; ipu; ipu = ipu->next) + dump_edges_1 (ipu); + fprintf (stderr, "CUs\n"); + for (i = 0; i < ncus; i++) + dump_edges_1 (ipus[i + npus]); +} + +/* Enumerate the different kinds of nodes in the import_cu/import_edge + graph. */ +enum node_kind { NODE_CU, NODE_PU_INITIAL, NODE_PU_NEW }; + +/* Return the node kind for node IDX, given that: + - [0, NPUS - 1] are initial PUs, + - [NPUS, NPUS + NCUS - 1] are CUs, and + - [NPUS + NCUS, ] are new PUs. */ +static enum node_kind +get_node_kind (unsigned int idx, unsigned int npus, unsigned int ncus) +{ + if (idx < npus) + return NODE_PU_INITIAL; + if (idx < npus + ncus) + return NODE_CU; + return NODE_PU_NEW; +} + +/* Verify an edge from SRC to DEST during create_import_tree phase PHASE. */ +static void +verify_edge (enum node_kind src, enum node_kind dest, unsigned int phase) +{ + if (phase == 1) + { + assert (src == NODE_CU && dest == NODE_PU_INITIAL); + return; + } + + assert (IMPLIES (src == NODE_CU, dest != NODE_CU)); + + if (phase == 2) + { + assert (IMPLIES (src == NODE_PU_NEW, dest == NODE_PU_INITIAL)); + assert (src != NODE_PU_INITIAL); + } + else + assert (IMPLIES (src == NODE_PU_NEW, dest != NODE_CU)); +} + +/* Helper function for debugging create_import_tree. Verify + various invariants for CU/PU IPU. */ +static void +verify_edges_1 (struct import_cu *ipu, unsigned int *ic, unsigned int *oc, + enum node_kind kind, unsigned int npus, unsigned int ncus, + unsigned int phase) +{ + struct import_edge *e1, *e2; + unsigned int last_idx, count; + enum node_kind kind2; + + for (last_idx = 0, count = 0, e1 = ipu->incoming; + e1; + last_idx = e1->icu->idx, count++, e1 = e1->next) + { + /* Verify that incoming edges are in ascending idx order. */ + assert (count == 0 || e1->icu->idx > last_idx); + + /* Verify that each incoming edge has a corresponding outgoing edge. */ + for (e2 = e1->icu->outgoing; e2; e2 = e2->next) + if (e2->icu == ipu) + break; + assert (e2); + + kind2 = get_node_kind (e1->icu->idx, npus, ncus); + verify_edge (kind2, kind, phase); + + if (count == ipu->incoming_count - 1) + assert (ipu->incoming_tail == e1); + } + + /* Verify the number of incoming edges. */ + assert (ipu->incoming_count == count); + + for (last_idx = 0, count = 0, e1 = ipu->outgoing; + e1; + last_idx = e1->icu->idx, count++, e1 = e1->next) + { + /* Verify that outgoing edges are in ascending idx order. */ + assert (count == 0 || e1->icu->idx > last_idx); + + /* Verify that each outgoing edge has a corresponding incoming edge. */ + for (e2 = e1->icu->incoming; e2; e2 = e2->next) + if (e2->icu == ipu) + break; + assert (e2); + + kind2 = get_node_kind (e1->icu->idx, npus, ncus); + verify_edge (kind, kind2, phase); + } + + /* Verify the number of outgoing edges. */ + assert (ipu->outgoing_count == count); + + *ic += ipu->incoming_count; + *oc += ipu->outgoing_count; +} + +/* Helper function for debugging create_import_tree. Call verify_edges_1 + on all CUs and PUs. */ +static void +verify_edges (struct import_cu **ipus, unsigned int npus, unsigned int ncus, + unsigned int phase) +{ + struct import_cu *ipu; + unsigned int i, ic, oc; + + ic = 0; + oc = 0; + + /* Verify initial PUs. */ + ipu = NULL; + for (i = 0; i < npus; ++i) + { + ipu = ipus[i]; + assert (ipu->cu != NULL); + if (i < npus - 1) + assert (ipu->next == ipus[i + 1]); + assert (ipu->incoming != NULL); + if (phase <= 2) + assert (ipu->outgoing == NULL); + verify_edges_1 (ipu, &ic, &oc, NODE_PU_INITIAL, npus, ncus, phase); + } + + /* Verify new PUs. */ + assert (ipu != NULL); + for (ipu = ipu->next; ipu; ipu = ipu->next) + { + assert (phase != 1); + assert (ipu->cu == NULL); + assert (ipu->incoming != NULL); + assert (ipu->outgoing != NULL); + verify_edges_1 (ipu, &ic, &oc, NODE_PU_NEW, npus, ncus, phase); + } + + /* Verify CUs. */ + for (i = 0; i < ncus; i++) + { + ipu = ipus[npus + i]; + assert (ipu->cu != NULL); + assert (ipu->next == NULL); + assert (ipu->incoming == NULL); + assert (ipu->outgoing != NULL); + verify_edges_1 (ipu, &ic, &oc, NODE_CU, npus, ncus, phase); + } + + /* Verify that the overall number of incoming and outgoing edges is + equal. */ + assert (ic == oc); +} + +#define BITVECTOR_TYPE unsigned int + +/* Return a bitvector containing NBITS bits. */ +static inline BITVECTOR_TYPE * +bitvector_alloc (unsigned nbits) +{ + size_t nbytes = (nbits / 8) + 1; + size_t size = nbytes + sizeof (BITVECTOR_TYPE); + BITVECTOR_TYPE *res = (BITVECTOR_TYPE *)malloc (size); + if (res == NULL) + dwz_oom (); + memset (res, 0, size); + return res; +} + +/* Set bit IDX in bitvector VECTOR. */ +static inline void FORCE_INLINE +bitvector_set_bit (BITVECTOR_TYPE *vector, unsigned idx) +{ + unsigned div = idx / (sizeof (BITVECTOR_TYPE) * 8); + unsigned mod = idx % (sizeof (BITVECTOR_TYPE) * 8); + vector[div] |= (1U << mod); +} + +/* Test bit IDX in bitvector VECTOR. */ +static inline bool FORCE_INLINE +bitvector_bit_p (BITVECTOR_TYPE *vector, unsigned idx) +{ + unsigned div = idx / (sizeof (BITVECTOR_TYPE) * 8); + unsigned mod = idx % (sizeof (BITVECTOR_TYPE) * 8); + return (vector[div] & (1U << mod)) != 0; +} + +/* Clear at least bits [A, B] in VECTOR, possibly more. */ +static inline void FORCE_INLINE +bitvector_clear_bits (BITVECTOR_TYPE *vector, unsigned int a, unsigned int b) +{ + unsigned int range_min = a / (sizeof (BITVECTOR_TYPE) * 8); + unsigned int range_max = b / (sizeof (BITVECTOR_TYPE) * 8); + memset (&vector[range_min], 0, + (range_max - range_min + 1) * sizeof (BITVECTOR_TYPE)); +} + +/* Function to optimize the size of DW_TAG_imported_unit DIEs by + creating an inclusion tree, instead of each CU importing all + PUs it needs directly, by optionally creating new PUs or + adding DW_TAG_imported_unit to the already created PUs. + At the end this function constructs any new PUs needed, and + adds DW_TAG_imported_unit DIEs to them as well as the CUs + and partition_dups created PUs. */ +static int +create_import_tree (void) +{ + dw_cu_ref pu, cu, last_partial_cu = NULL; + unsigned int i, new_pu_version = 2, min_cu_version, npus, ncus; + struct import_cu **ipus, *ipu, *icu; + unsigned int cu_off; + unsigned int puidx; + struct import_cu *last_pu, *pu_freelist = NULL; + unsigned char *to_free; + + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "create_import_tree phase 1\n"); + } + + /* size doesn't count anything already created before this + function (partial units etc.) or already preexisting, just + initially the cumulative sizes of DW_TAG_imported_unit DIEs + that would need to be added, and if some new DW_TAG_partial_unit + CUs are going to be created as a result of this routine, that size + too. DW_TAG_imported_unit has size 5 (for DWARF3+) or 1 + ptr_size + (DWARF2), DW_TAG_partial_unit has size 13/14 (11 CU header + 1 byte + abbrev number + 1 byte child end + 1 byte for DWARF5 unit_type). */ + unsigned int size = 0; + /* Size of DW_TAG_imported_unit if the same everywhere, otherwise + (mixing DWARF2 and DWARF3+ with ptr_size != 4) 0. */ + unsigned int edge_cost = 0; + /* Number of bytes needed for outgoing edges of PUs created by + this function (which all have DWARF version new_pu_version). */ + unsigned int new_edge_cost; + + /* If no PUs were created, there is nothing to do here. */ + if (first_cu == NULL || (fi_multifile ? alt_first_cu == NULL + : first_cu->cu_kind != CU_PU)) + return 0; + + edge_freelist = NULL; + to_free = obstack_alloc (&ob2, 1); + min_cu_version = first_cu->cu_version; + /* First construct a bipartite graph between CUs and PUs. */ + for (pu = fi_multifile ? alt_first_cu : first_cu, npus = 0; + pu && pu->cu_kind != CU_NORMAL; pu = pu->cu_next) + { + dw_die_ref die, rdie; + dw_cu_ref prev_cu; + + if (pu->cu_die->die_tag == DW_TAG_compile_unit) + continue; + + last_partial_cu = pu; + for (rdie = pu->cu_die->die_child; + rdie->die_named_namespace; rdie = rdie->die_child) + ; + if (unlikely (fi_multifile) && rdie->die_nextdup == NULL) + { + pu->u1.cu_icu = NULL; + continue; + } + npus++; + if (pu->cu_version > new_pu_version) + new_pu_version = pu->cu_version; + if (pu->cu_version < min_cu_version) + min_cu_version = pu->cu_version; + ipu = (struct import_cu *) obstack_alloc (&ob2, sizeof (*ipu)); + memset (ipu, 0, sizeof (*ipu)); + ipu->cu = pu; + pu->u1.cu_icu = ipu; + assert (rdie->die_toplevel); + dw_die_ref firstdie = NULL; + dw_cu_ref firstdiecu = NULL; + for (die = rdie->die_nextdup, prev_cu = NULL; + die; die = die->die_nextdup) + { + dw_cu_ref diecu = die_cu (die); + if (firstdie == NULL) + { + firstdie = die; + firstdiecu = die_cu (firstdie); + } + if (diecu == prev_cu || (die != firstdie && diecu == firstdiecu)) + continue; + ipu->incoming_count++; + size += 1 + (diecu->cu_version == 2 ? ptr_size : 4); + prev_cu = diecu; + } + ipu->incoming = (struct import_edge *) + obstack_alloc (&ob2, + ipu->incoming_count + * sizeof (*ipu->incoming)); + firstdie = NULL; + firstdiecu = NULL; + for (die = rdie->die_nextdup, i = 0, prev_cu = NULL; + die; die = die->die_nextdup) + { + dw_cu_ref diecu = die_cu (die); + if (firstdie == NULL) + { + firstdie = die; + firstdiecu = die_cu (firstdie); + } + if (diecu == prev_cu || (die != firstdie && diecu == firstdiecu)) + continue; + icu = diecu->u1.cu_icu; + if (icu == NULL) + { + icu = (struct import_cu *) + obstack_alloc (&ob2, sizeof (*ipu)); + memset (icu, 0, sizeof (*icu)); + icu->cu = diecu; + diecu->u1.cu_icu = icu; + } + ipu->incoming[i++].icu = icu; + icu->outgoing_count++; + prev_cu = diecu; + } + ipu->incoming_tail = &ipu->incoming[ipu->incoming_count - 1]; + } + if (npus == 0) + { + obstack_free (&ob2, to_free); + return 0; + } + for (cu = fi_multifile ? first_cu : pu, ncus = 0; cu; cu = cu->cu_next) + if (cu->u1.cu_icu) + { + ncus++; + if (cu->cu_version > new_pu_version) + new_pu_version = cu->cu_version; + if (cu->cu_version < min_cu_version) + min_cu_version = cu->cu_version; + cu->u1.cu_icu->outgoing + = (struct import_edge *) + obstack_alloc (&ob2, + cu->u1.cu_icu->outgoing_count + * sizeof (*cu->u1.cu_icu->outgoing)); + cu->u1.cu_icu->outgoing_count = 0; + } + if (ptr_size == 4 || min_cu_version > 2) + edge_cost = 5; + else if (new_pu_version == 2) + edge_cost = 1 + ptr_size; + new_edge_cost = new_pu_version == 2 ? 1 + ptr_size : 5; + for (pu = fi_multifile ? alt_first_cu : first_cu; + pu && pu->cu_kind != CU_NORMAL; pu = pu->cu_next) + { + ipu = pu->u1.cu_icu; + if (ipu == NULL) + continue; + for (i = 0; i < ipu->incoming_count; i++) + { + icu = ipu->incoming[i].icu; + icu->outgoing[icu->outgoing_count++].icu = ipu; + } + } + ipus = (struct import_cu **) + obstack_alloc (&ob2, (npus + ncus) * sizeof (*ipus)); + for (pu = fi_multifile ? alt_first_cu : first_cu, npus = 0; + pu && pu->cu_kind != CU_NORMAL; pu = pu->cu_next) + { + ipu = pu->u1.cu_icu; + if (ipu == NULL) + continue; + qsort (ipu->incoming, ipu->incoming_count, sizeof (*ipu->incoming), + import_edge_cmp); + for (i = 0; i < ipu->incoming_count; i++) + { + ipu->incoming[i].next + = i != ipu->incoming_count - 1 ? &ipu->incoming[i + 1] : NULL; + } + ipus[npus++] = ipu; + } + for (cu = fi_multifile ? first_cu : pu, ncus = 0; cu; cu = cu->cu_next) + if (cu->u1.cu_icu) + { + icu = cu->u1.cu_icu; + qsort (icu->outgoing, icu->outgoing_count, sizeof (*icu->outgoing), + import_edge_cmp); + for (i = 0; i < icu->outgoing_count - 1; i++) + icu->outgoing[i].next = &icu->outgoing[i + 1]; + icu->outgoing[i].next = NULL; + ipus[npus + ncus] = icu; + ncus++; + } + qsort (ipus, npus, sizeof (*ipus), import_cu_cmp); + qsort (ipus + npus, ncus, sizeof (*ipus), import_cu_cmp); + for (puidx = 0; puidx < npus; puidx++) + { + ipus[puidx]->idx = puidx; + if (puidx + 1 < npus) + ipus[puidx]->next = ipus[puidx + 1]; + } + for (; puidx < npus + ncus; puidx++) + ipus[puidx]->idx = puidx; + last_pu = ipus[npus - 1]; + if (unlikely (dump_edges_p)) + dump_edges ("phase 1", ipus, npus, ncus); + if (unlikely (verify_edges_p)) + verify_edges (ipus, npus, ncus, 1); + if (!import_opt_p) + goto opt_done; + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "create_import_tree phase 2\n"); + } + /* Now, for the above constructed bipartite graph, find K x,2 components + where x >= 5 (for DWARF3 and above or ptr_size 4, for DWARF2 and + ptr_size 8 it can be even x == 4) and add a new PU node, where all + CUs from the component will point to the new PU node and that new PU + will point to all the destination PUs. In theory with DWARF2 + and ptr_size 1 we could need x >= 9. + + The example below demonstrates the type of transformation. The + transformation is an optimization if the benefit of reducing the number + of imports (in other words, edges) is bigger than the cost of adding an + extra PU. OTOH, the transformation can be done in the presence of + additional incoming edges for PU_3 and PU_4. + + Before: After: + + CU_1---------->PU_3 CU_1 PU_3 + \ ^ ^ \ ^ + \ / / \ / + \ / / \ / + x----o / \ / + / \ / \ / + / \ / \ / + / \ / v / + CU_2 x CU_2----->PU_5 + \ / \ ^ \ + \ / \ / \ + \ / \ / \ + x----o \ / \ + / \ \ / \ + / \ \ / \ + / v v / v + CU_3---------->PU_4 CU_3 PU_4 + */ + for (i = 0; i < npus - 1; i++) + { + struct import_cu *pudst[2], *pusrc[10]; + struct import_edge *e1, *e2, *e3, *e4; + struct import_edge *e1next, *e2next, *e3next; + pudst[0] = ipus[i]; + for (e1 = pudst[0]->incoming; e1; e1 = e1next) + { + e1next = e1->next; + if (e1->icu->cu == NULL) + break; + for (e2 = e1->icu->outgoing; e2; e2 = e2next) + { + unsigned int srccount, dstcount, cost; + struct import_cu *npu = NULL; + struct import_edge **ep = NULL; + + e2next = e2->next; + if (e2->icu->idx <= pudst[0]->idx) + continue; + if (e2->icu->cu == NULL) + break; + + pudst[1] = e2->icu; + pusrc[0] = e1->icu; + srccount = 1; + cost = edge_cost; + if (!edge_cost) + cost = pusrc[0]->cu->cu_version == 2 ? 1 + ptr_size : 5; + for (e3 = e1next; e3; e3 = e3next) + { + e3next = e3->next; + if (e3->icu->cu == NULL) + break; + dstcount = 0; + for (e4 = e3->icu->outgoing; e4; e4 = e4->next) + { + if (e4->icu == pudst[0]) + dstcount++; + else if (e4->icu == pudst[1]) + { + dstcount++; + break; + } + else if (e4->icu->idx > pudst[1]->idx) + break; + } + if (dstcount != 2) + continue; + if (npu == NULL) + { + unsigned int header_size; + pusrc[srccount] = e3->icu; + header_size = (pusrc[srccount]->cu->cu_version >= 5 + ? 14 : 13); /* DWARF5 unit_type byte. */ + cost += edge_cost; + if (!edge_cost) + cost += pusrc[srccount]->cu->cu_version == 2 + ? 1 + ptr_size : 5; + srccount++; + if (ignore_size || ((dstcount - 1) * cost + > (header_size + + dstcount * new_edge_cost))) + { + unsigned int j; + + e2next = NULL; + if (pu_freelist) + { + npu = pu_freelist; + pu_freelist = pu_freelist->next; + } + else + npu = (struct import_cu *) + obstack_alloc (&ob2, sizeof (*npu)); + memset (npu, 0, sizeof (*npu)); + npu->incoming_count = srccount; + npu->outgoing_count = dstcount; + npu->idx = puidx++; + last_pu->next = npu; + last_pu = npu; + for (j = 0; j < srccount; j++) + { + if (e1next && e1next->icu == pusrc[j]) + e1next = e1next->next; + remove_import_edges (&pusrc[j]->outgoing, NULL, + pudst, dstcount, true)->icu + = npu; + pusrc[j]->outgoing_count -= dstcount - 1; + } + for (j = 0; j < dstcount; j++) + { + remove_import_edges (&pudst[j]->incoming, + &pudst[j]->incoming_tail, + pusrc, srccount, true)->icu + = npu; + pudst[j]->incoming_count -= srccount - 1; + } + npu->incoming = first_edge_from_freelist (); + for (j = 0, e4 = npu->incoming; j < srccount; j++) + { + e4->icu = pusrc[j]; + if (j == srccount - 1) + { + npu->incoming + = last_edge_from_freelist (npu->incoming, + e4); + npu->incoming_tail = e4; + ep = &e4->next; + } + else + e4 = e4->next; + } + npu->outgoing = first_edge_from_freelist (); + for (j = 0, e4 = npu->outgoing; j < dstcount; j++) + { + e4->icu = pudst[j]; + if (j == dstcount - 1) + npu->outgoing + = last_edge_from_freelist (npu->outgoing, e4); + else + e4 = e4->next; + } + size -= (dstcount - 1) * cost; + size += 13 + dstcount * new_edge_cost; + } + } + else + { + unsigned int j; + + pusrc[srccount] = e3->icu; + cost = edge_cost; + if (!edge_cost) + cost = pusrc[srccount]->cu->cu_version == 2 + ? 1 + ptr_size : 5; + if (e1next && e1next->icu == pusrc[srccount]) + e1next = e1next->next; + remove_import_edges (&pusrc[srccount]->outgoing, NULL, + pudst, dstcount, true)->icu = npu; + pusrc[srccount]->outgoing_count -= dstcount - 1; + for (j = 0; j < dstcount; j++) + { + remove_import_edges (&pudst[j]->incoming, + &pudst[j]->incoming_tail, + pusrc + srccount, 1, false); + pudst[j]->incoming_count--; + } + *ep = edge_from_freelist (); + npu->incoming_count++; + (*ep)->icu = pusrc[srccount]; + (*ep)->next = NULL; + npu->incoming_tail = *ep; + ep = &(*ep)->next; + size -= (dstcount - 1) * cost; + } + } + } + } + } + if (unlikely (dump_edges_p)) + dump_edges ("phase 2", ipus, npus, ncus); + if (unlikely (verify_edges_p)) + verify_edges (ipus, npus, ncus, 2); + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "create_import_tree phase 3\n"); + } + /* Try to merge PUs which have the same set of referrers if + beneficial. + + The example below demonstrates the type of transformation. The + transformation is an optimization because it reduces the number of import + statements (in other words, edges) as well as the number of PUs. It can + however not be done if PU_3 or PU_4 have additional incoming edges. + + Before: After: + + CU_1----->PU_3 CU_1 + \ ^ \ + \ / \ + \ / v + x PU_3_4 + / \ ^ + / \ / + / v / + CU_2----->PU_4 CU_2 + + Or, if one PU has a subset of referrers of the other, attempt to replace + all the incoming edges from the referrers intersection to the PU with + larger number of incoming edges by an edge from the other PU. + + The example below demonstrates the type of transformation. The + transformation is an optimization because it reduces the number of import + statements (in other words, edges). It can however not be done if PU_3 + has additional incoming edges. + + Before: After: + + CU_1----->PU_3 CU_1------>PU_3 + \ ^ ^ | + \ / / | + \ / / | + x / | + / \ / | + / \ / | + / \ / | + CU_2 \ CU_2 o + \ \ | + \ o | + \ | | + \ | | + \ | | + \ | | + v v v + CU_3----->PU_4 CU_3------>PU_4 + */ + /* Flag used during PU merging, set for PUs already considered + for merging for the given first PU. */ + BITVECTOR_TYPE *seen = bitvector_alloc (puidx); + unsigned int min_seen = UINT_MAX; + unsigned int max_seen = 0; + for (ipu = ipus[0]; ipu; ipu = ipu->next) + { + struct import_edge *e1, *e2, *e3, *e4, **e1p, **ep, *prev; + for (e1p = &ipu->incoming, e1 = *e1p; + e1; e1 = *e1p != e1 ? *e1p : (e1p = &e1->next, e1->next)) + { + for (e2 = e1->icu->outgoing; e2; e2 = e2->next) + { + unsigned int size_inc, size_dec; + struct import_cu *ipu2 = e2->icu, *ipusub, *ipusup; + /* True if IPU's src set might be a subset + of IPU2's src set. */ + bool maybe_subset; + /* True if IPU's src set might be a superset + of IPU2's src set. */ + bool maybe_superset; + unsigned int intersection; + + if (ipu2->idx <= ipu->idx || bitvector_bit_p (seen, ipu2->idx)) + continue; + bitvector_set_bit (seen, ipu2->idx); + min_seen = MIN (min_seen, ipu2->idx); + max_seen = MAX (max_seen, ipu2->idx); + maybe_subset = (e1 == ipu->incoming + && ipu->incoming_count <= ipu2->incoming_count); + maybe_superset = ipu->incoming_count >= ipu2->incoming_count; + if (maybe_superset) + { + /* If the referrer nodes of ipu are a superset of the + referrer nodes of ipu2, then ipu's last referrer node + should have index larger or equal to the last referrer + node of ipu2. */ + maybe_superset + = (ipu->incoming_tail->icu->idx + >= ipu2->incoming_tail->icu->idx); + } + if (maybe_subset) + { + /* If the referrer nodes of ipu are a subset of the + referrer nodes of ipu2, then ipu's last referrer node + should have index smaller or equal to the last referrer + node of ipu2. */ + maybe_subset + = (ipu->incoming_tail->icu->idx + <= ipu2->incoming_tail->icu->idx); + } + e3 = e1; + e4 = ipu2->incoming; + intersection = 0; + while ((maybe_subset || maybe_superset) && e3 && e4) + { + if (e3->icu == e4->icu) + { + intersection++; + e3 = e3->next; + e4 = e4->next; + continue; + } + if (e3->icu->idx < e4->icu->idx) + { + maybe_subset = false; + e3 = e3->next; + continue; + } + maybe_superset = false; + e4 = e4->next; + } + if (e3) + maybe_subset = false; + if (e4) + maybe_superset = false; + if ((!maybe_superset && !maybe_subset) || intersection < 2) + continue; + if (maybe_superset && maybe_subset) + { + if (unlikely (fi_multifile) && ipu2->idx < npus + ncus) + continue; + if (odr_active_p && odr_mode != ODR_BASIC + && ipu2->idx < npus + ncus) + continue; + /* If IPU and IPU2 have the same set of src nodes, then + (if beneficial, with edge_cost != 0 always), merge + IPU2 node into IPU, by removing all incoming edges + of IPU2 and moving over all outgoing edges of IPU2 + to IPU. */ + assert (ipu2->idx >= npus + ncus); + size_inc = 0; + if (edge_cost) + size_dec = 13 + ipu2->incoming_count * edge_cost; + else + { + size_dec = 13; + if (ipu->cu && ipu->cu->cu_version == 2) + { + if (ptr_size > 4) + size_inc = ipu2->outgoing_count * (ptr_size - 4); + else + size_dec += ipu2->outgoing_count * (4 - ptr_size); + } + for (e4 = ipu2->incoming; e4; e4 = e4->next) + size_dec += (e4->icu->cu + && e4->icu->cu->cu_version == 2) + ? 1 + ptr_size : 5; + } + if (!ignore_size || size_dec > size_inc) + { + struct import_cu **ipup; + for (e4 = ipu2->incoming, e3 = NULL; e4; e4 = e4->next) + { + remove_import_edges (&e4->icu->outgoing, NULL, &ipu2, + 1, false); + e4->icu->outgoing_count--; + prepare_free_edge (e4); + e3 = e4; + } + free_edges (ipu2->incoming, e3); + for (e4 = ipu2->outgoing; e4; e4 = e4->next) + { + for (ep = &e4->icu->incoming; *ep; ep = &(*ep)->next) + if ((*ep)->icu->idx >= ipu->idx) + break; + assert ((*ep)->icu != ipu); + if ((*ep)->icu == ipu2) + (*ep)->icu = ipu; + else + { + struct import_edge **ep2; + for (ep2 = &(*ep)->next; + *ep2; ep2 = &(*ep2)->next) + if ((*ep2)->icu == ipu2) + break; + e3 = *ep2; + *ep2 = e3->next; + e3->next = *ep; + *ep = e3; + e3->icu = ipu; + while (e4->icu->incoming_tail->next != NULL) + e4->icu->incoming_tail + = e4->icu->incoming_tail->next; + } + } + e3 = ipu->outgoing; + ep = &ipu->outgoing; + for (e4 = ipu2->outgoing; e3 && e4; ) + if (e3->icu->idx < e4->icu->idx) + { + *ep = e3; + ep = &e3->next; + e3 = e3->next; + } + else + { + assert (e3->icu != e4->icu); + *ep = e4; + ep = &e4->next; + e4 = e4->next; + } + if (e3) + *ep = e3; + else if (e4) + *ep = e4; + else + *ep = NULL; + ipu->outgoing_count += ipu2->outgoing_count; + size -= size_dec - size_inc; + if (ipu->idx >= npus + ncus) + ipup = &ipu->next; + else + ipup = &ipus[npus - 1]->next; + while (*ipup != ipu2) + ipup = &(*ipup)->next; + *ipup = ipu2->next; + ipu2->next = pu_freelist; + pu_freelist = ipu2; + continue; + } + } + if (maybe_superset) + { + ipusup = ipu; + ipusub = ipu2; + } + else + { + ipusub = ipu; + ipusup = ipu2; + } + /* If IPUSUB's src set is a subset of IPUSUP's src set + and intersection is at least 2, remove edges from + IPUSUB's src set to IPUSUP node and instead add + an edge from IPUSUB to IPUSUP. */ + size_inc = 0; + if (edge_cost) + size_dec = (ipusub->incoming_count - 1) * edge_cost; + else + { + size_inc = ipusub->cu && ipusub->cu->cu_version == 2 + ? 1 + ptr_size : 5; + size_dec = 0; + for (e3 = ipusub->incoming; e3; e3 = e3->next) + size_dec += (e3->icu->cu + && e3->icu->cu->cu_version == 2) + ? 1 + ptr_size : 5; + } + if (size_dec > size_inc + && (!fi_multifile || ipusub->idx >= npus + ncus)) + { + for (e3 = ipusub->incoming, ep = &ipusup->incoming, + prev = NULL; + e3; e3 = e3->next) + { + remove_import_edges (&e3->icu->outgoing, NULL, &ipusup, 1, + false); + e3->icu->outgoing_count--; + while ((*ep)->icu != e3->icu) + { + prev = *ep; + ep = &(*ep)->next; + } + e4 = *ep; + *ep = e4->next; + free_edge (e4); + if (ipusup->incoming_tail == e4) + ipusup->incoming_tail = prev; + } + for (ep = &ipusub->outgoing; *ep; ep = &(*ep)->next) + if ((*ep)->icu->idx >= ipusup->idx) + break; + assert (*ep == NULL || (*ep)->icu != ipusup); + e4 = edge_from_freelist (); + e4->icu = ipusup; + e4->next = *ep; + *ep = e4; + ipusub->outgoing_count++; + for (ep = &ipusup->incoming; *ep; ep = &(*ep)->next) + if ((*ep)->icu->idx >= ipusub->idx) + break; + assert (*ep == NULL || (*ep)->icu != ipusub); + e4 = edge_from_freelist (); + e4->icu = ipusub; + e4->next = *ep; + *ep = e4; + if (ipusup->incoming_tail->next == e4) + ipusup->incoming_tail = e4; + ipusup->incoming_count -= ipusub->incoming_count - 1; + size -= size_dec - size_inc; + if (ipusup == ipu) + break; + } + } + } + if (min_seen <= max_seen) + { + bitvector_clear_bits (seen, min_seen, max_seen); + min_seen = UINT_MAX; + max_seen = 0; + } + } + free (seen); + if (unlikely (dump_edges_p)) + dump_edges ("phase 3", ipus, npus, ncus); + if (unlikely (verify_edges_p)) + verify_edges (ipus, npus, ncus, 3); + opt_done: + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "create_import_tree phase 4 (create partial units)\n"); + } + /* Create DW_TAG_partial_unit (and containing dw_cu structures). */ + if (fi_multifile) + { + cu_off = 0; + last_partial_cu = NULL; + } + else + cu_off = last_partial_cu->cu_offset + 1; + for (ipu = ipus[npus - 1]->next; ipu; ipu = ipu->next) + { + dw_die_ref die; + dw_cu_ref partial_cu = pool_alloc (dw_cu, sizeof (struct dw_cu)); + memset (partial_cu, '\0', sizeof (*partial_cu)); + partial_cu->cu_kind = CU_PU; + partial_cu->cu_offset = cu_off++; + partial_cu->cu_version = new_pu_version; + partial_cu->u1.cu_icu = ipu; + if (unlikely (last_partial_cu == NULL)) + { + partial_cu->cu_next = first_cu; + first_cu = partial_cu; + } + else + { + partial_cu->cu_next = last_partial_cu->cu_next; + last_partial_cu->cu_next = partial_cu; + } + last_partial_cu = partial_cu; + die = pool_alloc (dw_die, sizeof (struct dw_die)); + memset (die, '\0', sizeof (struct dw_die)); + die->die_toplevel = 1; + partial_cu->cu_die = die; + die->die_tag = DW_TAG_partial_unit; + die->die_offset = -1U; + die->die_root = 1; + die->die_parent = (dw_die_ref) partial_cu; + die->die_size = 1; + ipu->cu = partial_cu; + } + /* Next add all needed DW_TAG_imported_unit DIEs. */ + for (cu = first_cu; cu; cu = cu->cu_next) + { + struct import_edge *e; + + icu = cu->u1.cu_icu; + if (icu == NULL) + continue; + for (e = icu->outgoing; e; e = e->next) + { + dw_die_ref *diep; + dw_die_ref die = pool_alloc (dw_die, sizeof (struct dw_die)); + memset (die, '\0', sizeof (*die)); + die->die_toplevel = 1; + die->die_tag = DW_TAG_imported_unit; + die->die_offset = -1U; + die->die_nextdup = e->icu->cu->cu_die; + die->die_parent = cu->cu_die; + assert (e->icu->cu->cu_die->die_tag == DW_TAG_partial_unit); + die->die_size = (cu->cu_version == 2 ? 1 + ptr_size : 5); + /* Put the new DW_TAG_imported_unit DIE after all typed DWARF + stack referenced base types and after all previously added + new DW_TAG_imported_unit DIEs. */ + for (diep = &die->die_parent->die_child; + *diep; diep = &(*diep)->die_sib) + if (!(*diep)->die_op_type_referenced + && ((*diep)->die_tag != DW_TAG_imported_unit + || (*diep)->die_offset != -1U)) + break; + die->die_sib = *diep; + *diep = die; + } + } + for (cu = first_cu; cu; cu = cu->cu_next) + cu->u1.cu_icu = NULL; + if (unlikely (fi_multifile)) + for (cu = alt_first_cu; cu; cu = cu->cu_next) + cu->u1.cu_icu = NULL; + obstack_free (&ob2, to_free); + return 0; +} + +/* Helper function for die_find_dup, when ORIG has collapsed children. */ +static dw_die_ref +die_find_collapsed_dup (dw_die_ref die, unsigned int *tick) +{ + dw_die_ref child, ret; + + for (child = die->die_child; child; child = child->die_sib) + if ((*tick)-- == 0) + return child; + else if (child->die_child == NULL) + continue; + else if ((ret = die_find_collapsed_dup (child, tick)) != NULL) + return ret; + (*tick)--; + return NULL; +} + +/* If DIE is equal to ORIG, return DUP, otherwise if DIE is + a child of ORIG, return corresponding child in DUP's subtree, + or return NULL. */ +static dw_die_ref +die_find_dup (dw_die_ref orig, dw_die_ref dup, dw_die_ref die) +{ + dw_die_ref orig_child, dup_child; + if (orig == die) + return dup; + if (orig->die_collapsed_children) + { + dw_die_ref ret; + unsigned int tick; + if (die->die_collapsed_child) + tick = die->die_tag - 1; + else + tick = die->die_ref_seen - 1; + assert (dup->die_collapsed_children == 0 + && die->die_parent == orig); + ret = die_find_collapsed_dup (dup, &tick); + assert (die->die_collapsed_child || ret->die_tag == die->die_tag); + return ret; + } + for (orig_child = orig->die_child, dup_child = dup->die_child; + orig_child; + orig_child = orig_child->die_sib, dup_child = dup_child->die_sib) + { + dw_die_ref ret = die_find_dup (orig_child, dup_child, die); + if (ret) + return ret; + } + return NULL; +} + +/* Return number of bytes needed to encode VAL using + uleb128. */ +static unsigned int +size_of_uleb128 (uint64_t val) +{ + unsigned int size; + for (size = 1; (val >>= 7) != 0; size++) + ; + return size; +} + +/* Return number of bytes needed to encode VAL using + sleb128. */ +static unsigned int +size_of_sleb128 (int64_t val) +{ + unsigned int size = 0; + unsigned char c; + do + { + c = val & 0x7f; + val >>= 7; + size++; + } + while ((val != 0 || (c & 0x40) != 0) + && (val != -1 || (c & 0x40) == 0)); + return size; +} + +/* Hash table mapping original file IDs to new ids. */ +static htab_t line_htab; +/* Current new maximum file ID. */ +static unsigned int max_line_id; + +struct line_entry +{ + /* File pointer. */ + struct dw_file *file; + /* Precomputed hash value. */ + unsigned int hash; + /* Corresponding new file ID. */ + unsigned int new_id; +}; +ALIGN_STRUCT (line_entry) + +/* Hash function in line_htab. */ +static hashval_t +line_hash (const void *p) +{ + struct line_entry *s = (struct line_entry *)p; + + return s->hash; +} + +/* Equality function in line_htab. */ +static int +line_eq (const void *p, const void *q) +{ + struct line_entry *s1 = (struct line_entry *)p; + struct line_entry *s2 = (struct line_entry *)q; + + if (s1->hash != s2->hash) + return 0; + if (s1->file == s2->file) + return 1; + if (strcmp (s1->file->file, s2->file->file) != 0) + return 0; + if ((s1->file->dir == NULL) ^ (s2->file->dir == NULL)) + return 0; + if (s1->file->dir && strcmp (s1->file->dir, s2->file->dir) != 0) + return 0; + return s1->file->time == s2->file->time && s1->file->size == s2->file->size; +} + +/* Map original file ID to new file ID. */ +static unsigned int +line_htab_lookup (dw_cu_ref cu, unsigned int id) +{ + void **slot; + struct line_entry le; + if (id == 0) + return 0; + assert (id <= cu->cu_nfiles); + le.file = &cu->cu_files[id - 1]; + hash_init_state (); + hash_update_state_object (le.file->time); + hash_update_state_object (le.file->size); + hash_update_state (le.file->file, strlen (le.file->file) + 1); + if (le.file->dir) + hash_update_state (le.file->dir, strlen (le.file->dir) + 1); + if (line_htab == NULL) + { + line_htab = htab_try_create (50, line_hash, line_eq, NULL); + if (line_htab == NULL) + dwz_oom (); + max_line_id = 1; + } + le.hash = hash_digest (); + slot = htab_find_slot_with_hash (line_htab, &le, le.hash, INSERT); + if (slot == NULL) + dwz_oom (); + if (*slot == NULL) + { + struct line_entry *l = pool_alloc (line_entry, sizeof (*l)); + l->file = le.file; + l->hash = le.hash; + l->new_id = max_line_id++; + *slot = (void *) l; + return l->new_id; + } + else + return ((struct line_entry *) *slot)->new_id; +} + +/* Hash table for finding duplicate .debug_macro opcode sequences. + This hash table is used with two different sets of hash/equality + callbacks. One is used either within handle_macro function (from within + optimize_multifile), or from handle_macro onwards (read_multifile). + The second set is used from read_macro onwards during fi_multifile. */ +static htab_t macro_htab; + +/* At the end of read_multifile macro_htab is copied to this variable. */ +static htab_t alt_macro_htab; + +struct macro_entry +{ + /* Start of the sequence. */ + unsigned char *ptr; + /* Precomputed hash value. LSB bit is used for a flag whether + a particular .debug_macro sequence is seen more than once. */ + unsigned int hash; + /* And it's length or 0 if non-shareable. */ + unsigned int len; +}; +ALIGN_STRUCT (macro_entry) + +/* Hash function in macro_htab. */ +static hashval_t +macro_hash (const void *p) +{ + struct macro_entry *m = (struct macro_entry *)p; + + return m->hash & ~1U; +} + +/* Equality function in macro_htab. */ +static int +macro_eq (const void *p, const void *q) +{ + struct macro_entry *m1 = (struct macro_entry *)p; + struct macro_entry *m2 = (struct macro_entry *)q; + unsigned char *p1, *p2, *s1, op; + unsigned int strp1, strp2; + + if (m1->hash != m2->hash || m1->len != m2->len) + return 0; + if (rd_multifile) + return 0; + + s1 = m1->ptr; + p2 = m2->ptr; + p1 = s1 + 3; + + while (1) + { + op = read_8 (p1); + if (op == 0) + break; + + switch (op) + { + case DW_MACRO_define: + case DW_MACRO_undef: + skip_leb128 (p1); + p1 = (unsigned char *) strchr ((char *) p1, '\0') + 1; + break; + case DW_MACRO_define_strp: + case DW_MACRO_undef_strp: + skip_leb128 (p1); + if (memcmp (s1, p2, p1 - s1) != 0) + return 0; + p2 += p1 - s1; + strp1 = read_32 (p1); + strp2 = read_32 (p2); + if (op_multifile) + { + if (strcmp ((char *) debug_sections[DEBUG_STR].data + strp1, + (char *) debug_sections[DEBUG_STR].data + strp2) + != 0) + return 0; + } + else if (lookup_strp_offset (strp2) != strp1) + return 0; + s1 = p1; + break; + default: + abort (); + } + } + return memcmp (s1, p2, p1 - s1) == 0; +} + +/* Hash function in macro_htab. */ +static hashval_t +macro_hash2 (const void *p) +{ + struct macro_entry *m = (struct macro_entry *)p; + + return m->ptr - debug_sections[DEBUG_MACRO].data; +} + +/* Equality function in macro_htab. */ +static int +macro_eq2 (const void *p, const void *q) +{ + struct macro_entry *m1 = (struct macro_entry *)p; + struct macro_entry *m2 = (struct macro_entry *)q; + return m1->ptr == m2->ptr; +} + +/* Parse .debug_macro section, either during write_multifile + or during fi_multifile phase. During write_multifile it + selects potentially shareable .debug_macro sequences and + writes them into debug_sections[DEBUG_MACRO].new_data + block it allocates. During fi_multifile it populates + macro_htab. In both cases it calls note_strp_offset + on DW_FORM_strp offsets. */ +static int +read_macro (DSO *dso) +{ + unsigned char *ptr, *endsec, *dst = NULL; + unsigned int version, flags, op, strp; + struct macro_entry me, *m; + + ptr = debug_sections[DEBUG_MACRO].data; + endsec = ptr + debug_sections[DEBUG_MACRO].size; + debug_sections[DEBUG_MACRO].new_size = 0; + if (!wr_multifile) + { + macro_htab = htab_try_create (50, macro_hash2, macro_eq2, NULL); + if (macro_htab == NULL) + dwz_oom (); + } + + while (ptr < endsec) + { + unsigned char *start = ptr, *s = ptr; + bool can_share = true; + hashval_t hash = 0; + unsigned int strp; + void **slot; + + if (ptr + 4 > endsec) + { + error (0, 0, "%s: .debug_macro header too small", dso->filename); + return 1; + } + + version = read_16 (ptr); + bool supported_version_p = version >= 4 && version <= 5; + if (!supported_version_p) + { + error (0, 0, "%s: Unhandled .debug_macro version %d", dso->filename, + version); + return 1; + } + flags = read_8 (ptr); + if ((flags & ~2U) != 0) + { + error (0, 0, "%s: Unhandled .debug_macro flags %d", dso->filename, + flags); + return 1; + } + if ((flags & 2) != 0) + { + ptr += 4; + can_share = false; + } + if (fi_multifile && alt_macro_htab == NULL) + can_share = false; + + op = -1U; + while (ptr < endsec) + { + op = read_8 (ptr); + if (op == 0) + break; + + switch (op) + { + case DW_MACRO_define: + case DW_MACRO_undef: + skip_leb128 (ptr); + ptr = (unsigned char *) strchr ((char *) ptr, '\0') + 1; + break; + case DW_MACRO_start_file: + skip_leb128 (ptr); + skip_leb128 (ptr); + can_share = false; + break; + case DW_MACRO_end_file: + can_share = false; + break; + case DW_MACRO_define_strp: + case DW_MACRO_undef_strp: + skip_leb128 (ptr); + strp = read_32 (ptr); + note_strp_offset (strp); + if (wr_multifile) + break; + if (can_share) + hash = iterative_hash (s, ptr - 4 - s, hash); + if (can_share) + { + unsigned char *p = debug_sections[DEBUG_STR].data + strp; + unsigned int len = strlen ((char *) p); + hash = iterative_hash (p, len, hash); + s = ptr; + } + break; + case DW_MACRO_import: + ptr += 4; + can_share = false; + break; + default: + error (0, 0, "%s: Unhandled .debug_macro opcode 0x%x", + dso->filename, op); + return 1; + } + } + if (op != 0) + { + error (0, 0, "%s: .debug_macro section not zero terminated", + dso->filename); + return 1; + } + if (wr_multifile) + { + if (can_share) + debug_sections[DEBUG_MACRO].new_size += ptr - start; + continue; + } + + me.ptr = start; + if (can_share) + { + hash = iterative_hash (s, ptr - s, hash); + me.hash = hash & ~1U; + me.len = ptr - start; + m = (struct macro_entry *) + htab_find_with_hash (alt_macro_htab, &me, me.hash); + if (m == NULL) + can_share = false; + else + me.hash = m->ptr - alt_data[DEBUG_MACRO]; + } + if (!can_share) + { + me.len = 0; + me.hash = debug_sections[DEBUG_MACRO].new_size; + debug_sections[DEBUG_MACRO].new_size += ptr - start; + } + slot + = htab_find_slot_with_hash (macro_htab, &me, + me.ptr - debug_sections[DEBUG_MACRO].data, + INSERT); + if (slot == NULL) + dwz_oom (); + else + { + assert (*slot == NULL); + m = pool_alloc (macro_entry, sizeof (*m)); + *m = me; + *slot = (void *) m; + } + } + + if (!wr_multifile) + return 0; + + debug_sections[DEBUG_MACRO].new_data + = (unsigned char *) malloc (debug_sections[DEBUG_MACRO].new_size); + if (debug_sections[DEBUG_MACRO].new_data == NULL) + dwz_oom (); + dst = debug_sections[DEBUG_MACRO].new_data; + for (ptr = debug_sections[DEBUG_MACRO].data; ptr < endsec; ) + { + unsigned char *start = ptr; + bool can_share = true; + + ptr += 2; + flags = read_8 (ptr); + if ((flags & 2) != 0) + { + ptr += 4; + can_share = false; + } + + while (1) + { + op = read_8 (ptr); + if (op == 0) + break; + + switch (op) + { + case DW_MACRO_define: + case DW_MACRO_undef: + skip_leb128 (ptr); + ptr = (unsigned char *) strchr ((char *) ptr, '\0') + 1; + break; + case DW_MACRO_start_file: + skip_leb128 (ptr); + skip_leb128 (ptr); + can_share = false; + break; + case DW_MACRO_end_file: + can_share = false; + break; + case DW_MACRO_define_strp: + case DW_MACRO_undef_strp: + skip_leb128 (ptr); + ptr += 4; + break; + case DW_MACRO_import: + ptr += 4; + can_share = false; + break; + default: + abort (); + } + } + if (can_share) + { + ptr = start + 3; + + while (1) + { + op = read_8 (ptr); + if (op == 0) + break; + + switch (op) + { + case DW_MACRO_define: + case DW_MACRO_undef: + skip_leb128 (ptr); + ptr = (unsigned char *) strchr ((char *) ptr, '\0') + 1; + break; + case DW_MACRO_define_strp: + case DW_MACRO_undef_strp: + skip_leb128 (ptr); + memcpy (dst, start, ptr - start); + dst += ptr - start; + strp = lookup_strp_offset (read_32 (ptr)); + write_32 (dst, strp); + start = ptr; + break; + default: + abort (); + } + } + memcpy (dst, start, ptr - start); + dst += ptr - start; + } + } + assert (dst == debug_sections[DEBUG_MACRO].new_data + + debug_sections[DEBUG_MACRO].new_size); + + return 0; +} + +/* Helper function for handle_macro, called through htab_traverse. + Write .debug_macro opcode sequence seen by more than one + executable or shared library. */ +static int +optimize_write_macro (void **slot, void *data) +{ + struct macro_entry *m = (struct macro_entry *) *slot; + unsigned char **pp = (unsigned char **) data; + unsigned char *s = m->ptr; + unsigned char *p = s + 3, *q, op; + unsigned int strp; + + if ((m->hash & 1) == 0) + return 1; + while (1) + { + op = read_8 (p); + if (op == 0) + break; + + switch (op) + { + case DW_MACRO_define: + case DW_MACRO_undef: + skip_leb128 (p); + p = (unsigned char *) strchr ((char *) p, '\0') + 1; + break; + case DW_MACRO_define_strp: + case DW_MACRO_undef_strp: + skip_leb128 (p); + memcpy (*pp, s, p - s); + *pp += p - s; + strp = read_32 (p); + q = *pp; + write_32 (q, lookup_strp_offset (strp)); + *pp += 4; + s = p; + break; + default: + abort (); + } + } + memcpy (*pp, s, p - s); + *pp += p - s; + return 1; +} + +/* Parse .debug_macro section, during optimize_multifile + or during read_multifile. It parses .debug_macro written + by write_multifile, so it only contains shareable sequences. + Find duplicate sequences, during optimize_multifile write them + into debug_sections[DEBUG_MACRO].new_data it allocates, + during read_multifile just populates macro_htab (soon to be + alt_macro_htab). */ +static void +handle_macro (void) +{ + unsigned char *ptr, *endsec, op; + unsigned char *to_free = NULL; + struct macro_entry me, *m; + + macro_htab = htab_try_create (50, macro_hash, macro_eq, NULL); + if (macro_htab == NULL) + dwz_oom (); + + endsec = debug_sections[DEBUG_MACRO].data + debug_sections[DEBUG_MACRO].size; + if (op_multifile) + { + debug_sections[DEBUG_MACRO].new_size = 0; + to_free = obstack_alloc (&ob, 1); + } + + for (ptr = debug_sections[DEBUG_MACRO].data; ptr < endsec; ) + { + unsigned char *start = ptr, *s = ptr, *p; + hashval_t hash = 0; + unsigned int len; + void **slot; + bool can_share = true; + + ptr += 3; + while (1) + { + op = read_8 (ptr); + if (op == 0) + break; + + switch (op) + { + case DW_MACRO_define: + case DW_MACRO_undef: + skip_leb128 (ptr); + ptr = (unsigned char *) strchr ((char *) ptr, '\0') + 1; + break; + case DW_MACRO_define_strp: + case DW_MACRO_undef_strp: + skip_leb128 (ptr); + hash = iterative_hash (s, ptr - s, hash); + p = debug_sections[DEBUG_STR].data + read_32 (ptr); + len = strlen ((char *) p); + hash = iterative_hash (p, len, hash); + if (op_multifile + /* This should only happen if there were multiple + same transparent units within a single object file. */ + && htab_find_with_hash (strp_htab, p, + hash (p, len)) == NULL) + can_share = false; + s = ptr; + break; + default: + abort (); + } + } + if (!can_share) + continue; + hash = iterative_hash (s, ptr - s, hash); + me.ptr = start; + me.hash = hash & ~1U; + me.len = ptr - start; + slot = htab_find_slot_with_hash (macro_htab, &me, me.hash, INSERT); + if (slot == NULL) + dwz_oom (); + else if (*slot != NULL) + { + m = (struct macro_entry *) *slot; + if (op_multifile && (m->hash & 1) == 0) + { + m->hash |= 1; + debug_sections[DEBUG_MACRO].new_size += me.len; + } + } + else if (op_multifile) + { + m = (struct macro_entry *) obstack_alloc (&ob, sizeof (*m)); + *m = me; + *slot = (void *) m; + } + else + { + m = pool_alloc (macro_entry, sizeof (*m)); + *m = me; + *slot = (void *) m; + } + } + + if (op_multifile) + { + if (debug_sections[DEBUG_MACRO].new_size) + { + unsigned char *p; + debug_sections[DEBUG_MACRO].new_data + = malloc (debug_sections[DEBUG_MACRO].new_size); + p = debug_sections[DEBUG_MACRO].new_data; + htab_traverse (macro_htab, optimize_write_macro, &p); + assert (p == debug_sections[DEBUG_MACRO].new_data + + debug_sections[DEBUG_MACRO].new_size); + htab_delete (macro_htab); + macro_htab = NULL; + } + obstack_free (&ob, (void *) to_free); + } +} + +/* Write new content of .debug_macro section during fi_multifile phase. */ +static void +write_macro (void) +{ + unsigned char *ptr, *endsec, *dst; + unsigned int op, strp; + struct macro_entry me, *m; + + endsec = debug_sections[DEBUG_MACRO].data + debug_sections[DEBUG_MACRO].size; + debug_sections[DEBUG_MACRO].new_data + = (unsigned char *) malloc (debug_sections[DEBUG_MACRO].new_size); + if (debug_sections[DEBUG_MACRO].new_data == NULL) + dwz_oom (); + dst = debug_sections[DEBUG_MACRO].new_data; + for (ptr = debug_sections[DEBUG_MACRO].data; ptr < endsec; ) + { + unsigned char *s = ptr; + unsigned char flags; + + me.ptr = ptr; + m = (struct macro_entry *) + htab_find_with_hash (macro_htab, &me, + me.ptr - debug_sections[DEBUG_MACRO].data); + if (m->len) + { + ptr += m->len; + continue; + } + + ptr += 2; + flags = read_8 (ptr); + if ((flags & 2) != 0) + ptr += 4; + + while (1) + { + op = read_8 (ptr); + if (op == 0) + break; + + switch (op) + { + case DW_MACRO_define: + case DW_MACRO_undef: + skip_leb128 (ptr); + ptr = (unsigned char *) strchr ((char *) ptr, '\0') + 1; + break; + case DW_MACRO_start_file: + skip_leb128 (ptr); + skip_leb128 (ptr); + break; + case DW_MACRO_end_file: + break; + case DW_MACRO_define_strp: + case DW_MACRO_undef_strp: + memcpy (dst, s, ptr - 1 - s); + dst += ptr - 1 - s; + s = ptr - 1; + skip_leb128 (ptr); + strp = read_32 (ptr); + switch (note_strp_offset2 (strp)) + { + case DW_FORM_GNU_strp_alt: + case DW_FORM_strp_sup: + *dst = op == DW_MACRO_define_strp + ? DW_MACRO_define_sup + : DW_MACRO_undef_sup; + dst++; + s++; + break; + default: + break; + } + memcpy (dst, s, ptr - 4 - s); + dst += ptr - 4 - s; + write_32 (dst, lookup_strp_offset (strp)); + s = ptr; + break; + case DW_MACRO_import: + memcpy (dst, s, ptr - 1 - s); + dst += ptr - 1 - s; + me.ptr = debug_sections[DEBUG_MACRO].data + read_32 (ptr); + m = (struct macro_entry *) + htab_find_with_hash (macro_htab, &me, + me.ptr + - debug_sections[DEBUG_MACRO].data); + if (m->len) + *dst = DW_MACRO_import_sup; + else + *dst = DW_MACRO_import; + dst++; + write_32 (dst, m->hash); + s = ptr; + break; + default: + abort (); + } + } + memcpy (dst, s, ptr - s); + dst += ptr - s; + } + assert (dst == debug_sections[DEBUG_MACRO].new_data + + debug_sections[DEBUG_MACRO].new_size); +} + +/* Compute new abbreviations for DIE (with reference DIE REF). + T is a temporary buffer. Fill in *NDIES - number of DIEs + in the tree, and record pairs of referrer/referree DIEs for + intra-CU references into obstack vector VEC. */ +static int +build_abbrevs_for_die (htab_t h, dw_cu_ref cu, dw_die_ref die, + dw_cu_ref refcu, dw_die_ref ref, + struct abbrev_tag *t, unsigned int *ndies, + struct obstack *vec, bool recompute) +{ + dw_die_ref child, ref_child, sib = NULL, origin = NULL; + unsigned int i, j; + uint64_t low_pc = 0; + void **slot; + + if (unlikely (recompute) && die->u.p2.die_new_abbrev != NULL) + { + if (cu->cu_intracu_form == DW_FORM_ref_udata) + die->die_ref_seen = 1; + else + { + die->die_size -= size_of_uleb128 (die->u.p2.die_new_abbrev->entry) + + die->u.p2.die_intracu_udata_size; + die->die_ref_seen = 0; + } + for (child = die->die_child; child; child = child->die_sib) + if (build_abbrevs_for_die (h, cu, child, NULL, NULL, t, ndies, vec, + true)) + return 1; + return 0; + } + + die->u.p2.die_new_abbrev = NULL; + die->u.p2.die_new_offset = 0; + die->u.p2.die_intracu_udata_size = 0; + die->die_ref_seen = 0; + + if (wr_multifile ? die->die_no_multifile : die->die_remove) + return 0; + t->entry = 0; + t->tag = die->die_tag; + t->children = die->die_child != NULL; + t->op_type_referenced = false; + t->nusers = 1; + if (die->die_offset == -1U) + { + if (ref != NULL) + ; + else if (die_safe_nextdup (die) && die->die_nextdup->die_dup == die) + { + ref = die->die_nextdup; + if (ref != NULL) + refcu = die_cu (ref); + } + if (ref == NULL) + origin = die->die_nextdup; + } + else + { + ref = die; + refcu = cu; + if (wr_multifile + && (die->die_root || die->die_named_namespace)) + origin = die; + } + if (die->die_child && die->die_sib) + for (sib = die->die_sib; sib; sib = sib->die_sib) + if (wr_multifile ? !sib->die_no_multifile : !sib->die_remove) + break; + if (ref != NULL && origin == NULL) + { + unsigned char *base + = cu->cu_kind == CU_TYPES + ? debug_sections[DEBUG_TYPES].data + : debug_sections[DEBUG_INFO].data; + unsigned char *ptr = base + ref->die_offset; + struct abbrev_tag *reft = ref->die_abbrev; + + skip_leb128 (ptr); + /* No longer count the abbrev uleb128 size in die_size. + We'll add it back after determining the new abbrevs. */ + if (unlikely (wr_multifile || op_multifile || fi_multifile) + || unlikely (recompute)) + i = -1U; + else + for (i = 0; i < reft->nattr; i++) + switch (reft->attr[i].form) + { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + case DW_FORM_indirect: + i = -2U; + break; + case DW_FORM_data4: + case DW_FORM_data8: + if (reft->attr[i].attr == DW_AT_high_pc) + i = -2U; + break; + case DW_FORM_addr: + if (reft->attr[i].attr == DW_AT_high_pc + && cu->cu_version >= 4) + i = -2U; + break; + default: + break; + } + if (i != -1U) + { + die->die_size -= ptr - (base + ref->die_offset); + /* If there are no references, size stays the same + and no need to walk the actual attribute values. */ + for (i = 0; i < reft->nattr; i++) + { + t->attr[i].attr = reft->attr[i].attr; + t->attr[i].form = reft->attr[i].form; + if (t->attr[i].form == DW_FORM_implicit_const) + t->values[i] = reft->values[i]; + } + t->nattr = reft->nattr; + } + else + { + die->die_size = 0; + /* Otherwise, we need to walk the actual attributes. */ + for (i = 0, j = 0; i < reft->nattr; ++i) + { + uint32_t form = reft->attr[i].form; + size_t len = 0; + dw_die_ref refd; + uint64_t value = 0; + unsigned char *orig_ptr = ptr; + + while (form == DW_FORM_indirect) + form = read_uleb128 (ptr); + + if (unlikely (wr_multifile || op_multifile) + && (reft->attr[i].attr == DW_AT_decl_file + || reft->attr[i].attr == DW_AT_call_file)) + { + switch (form) + { + case DW_FORM_data1: value = read_8 (ptr); break; + case DW_FORM_data2: value = read_16 (ptr); break; + case DW_FORM_data4: value = read_32 (ptr); break; + case DW_FORM_data8: value = read_64 (ptr); break; + case DW_FORM_udata: value = read_uleb128 (ptr); break; + case DW_FORM_sdata: value = read_sleb128 (ptr); break; + case DW_FORM_implicit_const: + value = reft->values[i]; + break; + default: + error (0, 0, "Unhandled %s for %s", + get_DW_FORM_str (form), + get_DW_AT_str (reft->attr[i].attr)); + return 1; + } + value = line_htab_lookup (refcu, value); + if (form != DW_FORM_implicit_const) + { + if (value <= 0xff) + { + form = DW_FORM_data1; + die->die_size++; + } + else if (value <= 0xffff) + { + form = DW_FORM_data2; + die->die_size += 2; + } + else if (value <= 0xffffffff) + { + form = DW_FORM_data4; + die->die_size += 4; + } + else + { + form = DW_FORM_data8; + die->die_size += 8; + } + } + t->attr[j].attr = reft->attr[i].attr; + t->attr[j].form = form; + if (form == DW_FORM_implicit_const) + t->values[j] = value; + j++; + continue; + } + + if (unlikely (fi_multifile) + && (reft->attr[i].attr == DW_AT_GNU_macros + || reft->attr[i].attr == DW_AT_macros) + && alt_macro_htab != NULL) + { + struct macro_entry me, *m; + + switch (form) + { + case DW_FORM_data4: + case DW_FORM_sec_offset: + value = read_32 (ptr); + break; + default: + error (0, 0, "Unhandled %s for %s", + get_DW_FORM_str (form), + get_DW_AT_str (reft->attr[i].attr)); + return 1; + } + me.ptr = debug_sections[DEBUG_MACRO].data + value; + m = (struct macro_entry *) + htab_find_with_hash (macro_htab, &me, value); + if (m->len) + { + error (0, 0, "%s referencing transparent include", + get_DW_AT_str (reft->attr[i].attr)); + return 1; + } + ptr -= 4; + } + + switch (form) + { + case DW_FORM_ref_addr: + if (unlikely (fi_multifile)) + { + dw_die_ref refdt; + value = read_size (ptr, + refcu->cu_version == 2 + ? ptr_size : 4); + ptr += refcu->cu_version == 2 ? ptr_size : 4; + refd = off_htab_lookup (NULL, value); + assert (refd != NULL); + refdt = refd; + while (refdt->die_toplevel == 0) + refdt = refdt->die_parent; + if (refdt->die_dup + && !refdt->die_op_type_referenced + && die_cu (refdt->die_dup)->cu_kind == CU_ALT) + { + t->attr[j].attr = reft->attr[i].attr; + t->attr[j++].form + = dwarf_5 ? DW_FORM_ref_sup4 : DW_FORM_GNU_ref_alt; + die->die_size += 4; + continue; + } + break; + } + ptr += refcu->cu_version == 2 ? ptr_size : 4; + break; + case DW_FORM_addr: + ptr += ptr_size; + if (reft->attr[i].attr == DW_AT_low_pc + && cu->cu_version >= 4) + low_pc = read_size (ptr - ptr_size, ptr_size); + else if (reft->attr[i].attr == DW_AT_high_pc + && low_pc) + { + uint64_t high_pc = read_size (ptr - ptr_size, ptr_size); + /* If both DW_AT_low_pc and DW_AT_high_pc attributes + are present and have DW_FORM_addr, attempt to shrink + the DIE by using DW_FORM_udata or DW_FORM_data4 + form for the latter in DWARF4+. Don't try + DW_FORM_data[12], that might increase .debug_abbrev + size too much or increase the uleb128 size of too + many abbrev numbers. */ + if (high_pc > low_pc) + { + unsigned int nform = 0; + unsigned int sz = size_of_uleb128 (high_pc - low_pc); + if (sz <= 4 && sz <= (unsigned) ptr_size) + nform = DW_FORM_udata; + else if (ptr_size > 4 + && high_pc - low_pc <= 0xffffffff) + { + nform = DW_FORM_data4; + sz = 4; + } + else if (sz <= (unsigned) ptr_size) + nform = DW_FORM_udata; + if (nform) + { + t->attr[j].attr = reft->attr[i].attr; + t->attr[j++].form = nform; + die->die_size += sz; + continue; + } + } + } + break; + case DW_FORM_flag_present: + case DW_FORM_implicit_const: + break; + case DW_FORM_flag: + case DW_FORM_data1: + ++ptr; + break; + case DW_FORM_data2: + ptr += 2; + break; + case DW_FORM_data4: + if (reft->attr[i].attr == DW_AT_high_pc) + { + uint32_t range_len = read_32 (ptr); + unsigned int sz = size_of_uleb128 (range_len); + if (sz <= 4) + { + t->attr[j].attr = reft->attr[i].attr; + t->attr[j++].form = DW_FORM_udata; + die->die_size += sz; + continue; + } + break; + } + ptr += 4; + break; + case DW_FORM_sec_offset: + ptr += 4; + break; + case DW_FORM_data8: + if (reft->attr[i].attr == DW_AT_high_pc) + { + unsigned int nform = 0; + uint64_t range_len = read_64 (ptr); + unsigned int sz = size_of_uleb128 (range_len); + if (sz <= 4) + nform = DW_FORM_udata; + else if (range_len <= 0xffffffff) + { + nform = DW_FORM_data4; + sz = 4; + } + else if (sz <= 8) + nform = DW_FORM_udata; + if (nform) + { + t->attr[j].attr = reft->attr[i].attr; + t->attr[j++].form = nform; + die->die_size += sz; + continue; + } + break; + } + ptr += 8; + break; + case DW_FORM_ref_sig8: + ptr += 8; + break; + case DW_FORM_data16: + ptr += 16; + break; + case DW_FORM_sdata: + case DW_FORM_udata: + skip_leb128 (ptr); + break; + case DW_FORM_strp: + if (unlikely (op_multifile || fi_multifile)) + { + form = note_strp_offset2 (read_32 (ptr)); + if (form != DW_FORM_strp) + { + t->attr[j].attr = reft->attr[i].attr; + t->attr[j++].form = form; + die->die_size += 4; + continue; + } + } + else + ptr += 4; + break; + case DW_FORM_line_strp: + /* Since we don't register the line_strp we cannot + change the form in the case of multifile. */ + ptr += 4; + break; + case DW_FORM_string: + ptr = (unsigned char *) strchr ((char *)ptr, '\0') + 1; + break; + case DW_FORM_indirect: + abort (); + case DW_FORM_block1: + len = *ptr++; + break; + case DW_FORM_block2: + len = read_16 (ptr); + form = DW_FORM_block1; + break; + case DW_FORM_block4: + len = read_32 (ptr); + form = DW_FORM_block1; + break; + case DW_FORM_block: + case DW_FORM_exprloc: + len = read_uleb128 (ptr); + form = DW_FORM_block1; + break; + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + switch (form) + { + case DW_FORM_ref1: value = read_8 (ptr); break; + case DW_FORM_ref2: value = read_16 (ptr); break; + case DW_FORM_ref4: value = read_32 (ptr); break; + case DW_FORM_ref8: value = read_64 (ptr); break; + case DW_FORM_ref_udata: value = read_uleb128 (ptr); break; + default: abort (); + } + if (reft->attr[i].attr == DW_AT_sibling) + { + if (sib == NULL) + continue; + form = DW_FORM_ref4; + refd = sib; + } + else + { + dw_die_ref refdt; + refd = off_htab_lookup (refcu, refcu->cu_offset + value); + assert (refd != NULL); + refdt = refd; + while (refdt->die_toplevel == 0) + refdt = refdt->die_parent; + if (refdt->die_dup && refdt->die_op_type_referenced) + { + if (cu == die_cu (refdt)) + form = DW_FORM_ref4; + else if (cu == die_cu (refdt->die_dup)) + { + form = DW_FORM_ref4; + refd = die_find_dup (refdt, refdt->die_dup, + refd); + } + else + form = DW_FORM_ref_addr; + } + else + { + if (refdt->die_dup) + refd = die_find_dup (refdt, refdt->die_dup, refd); + if (cu == die_cu (refd)) + form = DW_FORM_ref4; + else if (die_cu (refd)->cu_kind == CU_ALT) + form = (dwarf_5 + ? DW_FORM_ref_sup4 : DW_FORM_GNU_ref_alt); + else + form = DW_FORM_ref_addr; + } + } + if (form == DW_FORM_ref_addr) + die->die_size += cu->cu_version == 2 ? ptr_size : 4; + else if (form == DW_FORM_GNU_ref_alt + || form == DW_FORM_ref_sup4) + die->die_size += 4; + else + { + if (unlikely (recompute)) + form = cu->cu_intracu_form; + if (likely (!recompute) || form == DW_FORM_ref_udata) + { + obstack_ptr_grow (vec, die); + obstack_ptr_grow (vec, refd); + } + } + t->attr[j].attr = reft->attr[i].attr; + t->attr[j++].form = form; + continue; + default: + abort (); + } + + if (form == DW_FORM_block1) + ptr += len; + t->attr[j].attr = reft->attr[i].attr; + t->attr[j].form = reft->attr[i].form; + if (reft->attr[i].form == DW_FORM_implicit_const) + t->values[j] = reft->values[i]; + j++; + die->die_size += ptr - orig_ptr; + } + t->nattr = j; + } + } + else + switch (die->die_tag) + { + case DW_TAG_partial_unit: + case DW_TAG_compile_unit: + t->nattr = 0; + die->die_size = 0; + if (origin == NULL) + break; + refcu = die_cu (origin); + if (refcu->cu_nfiles) + { + t->attr[0].attr = DW_AT_stmt_list; + t->attr[0].form = cu->cu_version < 4 + ? DW_FORM_data4 : DW_FORM_sec_offset; + die->die_size += 4; + t->nattr++; + } + if (uni_lang_p || cu->cu_die->die_tag == DW_TAG_compile_unit) + { + unsigned int lang_size = nr_bytes_for (cu->lang); + die->die_size += lang_size; + t->attr[t->nattr].attr = DW_AT_language; + switch (lang_size) + { + case 1: + t->attr[t->nattr].form = DW_FORM_data1; + break; + case 2: + t->attr[t->nattr].form = DW_FORM_data2; + break; + default: + abort (); + } + t->nattr++; + } + if (refcu->cu_comp_dir) + { + enum dwarf_form form; + unsigned char *ptr = get_AT (origin, DW_AT_comp_dir, &form); + assert (ptr && (form == DW_FORM_string + || form == DW_FORM_strp + || form == DW_FORM_line_strp)); + if (form == DW_FORM_strp) + { + if (unlikely (op_multifile || fi_multifile)) + form = note_strp_offset2 (read_32 (ptr)); + die->die_size += 4; + } + else if (form == DW_FORM_line_strp) + die->die_size += 4; + else + die->die_size + += strlen (refcu->cu_comp_dir) + 1; + t->attr[t->nattr].attr = DW_AT_comp_dir; + t->attr[t->nattr].form = form; + t->nattr++; + } + break; + case DW_TAG_namespace: + case DW_TAG_module: + { + enum dwarf_form form; + unsigned char *ptr = get_AT (origin, DW_AT_name, &form); + assert (ptr && (form == DW_FORM_string + || form == DW_FORM_strp + || form == DW_FORM_line_strp)); + if (form == DW_FORM_strp) + { + if (unlikely (op_multifile || fi_multifile)) + form = note_strp_offset2 (read_32 (ptr)); + die->die_size = 4; + } + else if (form == DW_FORM_line_strp) + die->die_size += 4; + else + die->die_size = strlen ((char *) ptr) + 1; + t->attr[0].attr = DW_AT_name; + t->attr[0].form = form; + t->nattr = 1; + if (sib) + { + t->attr[1].attr = DW_AT_sibling; + t->attr[1].form = DW_FORM_ref4; + obstack_ptr_grow (vec, die); + obstack_ptr_grow (vec, sib); + t->nattr++; + } + break; + } + case DW_TAG_imported_unit: + t->attr[0].attr = DW_AT_import; + t->nattr = 1; + if (die_cu (die->die_nextdup)->cu_kind == CU_ALT) + { + t->attr[0].form = dwarf_5 ? DW_FORM_ref_sup4 : DW_FORM_GNU_ref_alt; + die->die_size = 4; + } + else + { + t->attr[0].form = DW_FORM_ref_addr; + die->die_size = cu->cu_version == 2 ? ptr_size : 4; + } + break; + default: + abort (); + } + compute_abbrev_hash (t); + slot = htab_find_slot_with_hash (h, t, t->hash, + recompute ? NO_INSERT : INSERT); + if (slot == NULL) + dwz_oom (); + if (unlikely (recompute)) + assert (*slot); + if (*slot) + { + if (likely (!recompute)) + ((struct abbrev_tag *)*slot)->nusers++; + die->u.p2.die_new_abbrev = (struct abbrev_tag *)*slot; + } + else + { + struct abbrev_tag *newt = pool_clone_abbrev (t); + *slot = newt; + die->u.p2.die_new_abbrev = newt; + } + (*ndies)++; + if (ref != NULL && ref != die) + { + for (child = die->die_child, ref_child = ref->die_child; + child; child = child->die_sib, ref_child = ref_child->die_sib) + if (build_abbrevs_for_die (h, cu, child, refcu, ref_child, + t, ndies, vec, recompute)) + return 1; + } + else + for (child = die->die_child; child; child = child->die_sib) + if (build_abbrevs_for_die (h, cu, child, NULL, NULL, t, ndies, vec, + recompute)) + return 1; + return 0; +} + +/* Build new abbreviations for CU. T, NDIES and VEC arguments like + for build_abbrevs_for_die. */ +static int +build_abbrevs (dw_cu_ref cu, struct abbrev_tag *t, unsigned int *ndies, + struct obstack *vec) +{ + htab_t h = htab_try_create (50, abbrev_hash, abbrev_eq2, NULL); + + if (h == NULL) + dwz_oom (); + + if (build_abbrevs_for_die (h, cu, cu->cu_die, NULL, NULL, t, ndies, vec, + false)) + { + htab_delete (h); + return 1; + } + + cu->cu_new_abbrev = h; + return 0; +} + +/* Helper to record all abbrevs from the hash table into ob obstack + vector. Called through htab_traverse. */ +static int +list_abbrevs (void **slot, void *data) +{ + struct obstack *obp = (struct obstack *) data; + obstack_ptr_grow (obp, *slot); + return 1; +} + +/* Comparison function for abbreviations. Used for CUs that + need 128 or more abbreviations. Use lowest numbers (i.e. sort earlier) + abbrevs used for typed DWARF stack referenced DIEs, then sort + by decreasing number of users (abbrev numbers are uleb128 encoded, + the bigger number of them that can be 1 byte encoded the better). */ +static int +abbrev_cmp (const void *p, const void *q) +{ + struct abbrev_tag *t1 = *(struct abbrev_tag **)p; + struct abbrev_tag *t2 = *(struct abbrev_tag **)q; + unsigned int i; + + if (t1->op_type_referenced && !t2->op_type_referenced) + return -1; + if (!t1->op_type_referenced && t2->op_type_referenced) + return 1; + if (t1->nusers > t2->nusers) + return -1; + if (t1->nusers < t2->nusers) + return 1; + /* The rest just so that we have a stable sort. */ + if (t1->tag < t2->tag) + return -1; + if (t1->tag > t2->tag) + return 1; + if (t1->nattr < t2->nattr) + return -1; + if (t1->nattr > t2->nattr) + return 1; + if (t1->children && !t2->children) + return -1; + if (!t1->children && t2->children) + return 1; + for (i = 0; i < t1->nattr; i++) + { + if (t1->attr[i].attr < t2->attr[i].attr) + return -1; + if (t1->attr[i].attr > t2->attr[i].attr) + return 1; + if (t1->attr[i].form < t2->attr[i].form) + return -1; + if (t1->attr[i].form > t2->attr[i].form) + return 1; + if (t1->attr[i].form == DW_FORM_implicit_const) + { + if (t1->values[i] < t2->values[i]) + return -1; + if (t1->values[i] > t2->values[i]) + return 1; + } + } + return 0; +} + +/* First phase of computation of u.p2.die_new_offset and + new CU sizes. */ +static unsigned int +init_new_die_offsets (dw_die_ref die, unsigned int off, + unsigned int intracusize) +{ + dw_die_ref child; + unsigned int i; + struct abbrev_tag *t = die->u.p2.die_new_abbrev; + if (wr_multifile ? die->die_no_multifile : die->die_remove) + return off; + die->u.p2.die_new_offset = off; + if (likely (die->die_ref_seen == 0)) + { + die->die_size += size_of_uleb128 (die->u.p2.die_new_abbrev->entry); + die->u.p2.die_intracu_udata_size = 0; + for (i = 0; i < t->nattr; ++i) + switch (t->attr[i].form) + { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref_udata: + die->u.p2.die_intracu_udata_size += intracusize; + break; + default: + break; + } + die->die_size += die->u.p2.die_intracu_udata_size; + } + off += die->die_size; + for (child = die->die_child; child; child = child->die_sib) + off = init_new_die_offsets (child, off, intracusize); + if (die->die_child) + off++; + return off; +} + +/* Second phase of computation of u.p2.die_new_offset and + new CU sizes. This step is called possibly many times, + for deciding if DW_FORM_ref_udata is worthwhile. + init_new_die_offsets starts with assuming each uleb128 will + need maximum number of bytes for the CU of the given size, + each new invocation of this function (except the last) + will shrink one or more uleb128s. Each shrinking can create + new opportunities to shrink other uleb128s. */ +static unsigned int +update_new_die_offsets (dw_die_ref die, unsigned int off, + dw_die_ref **intracuvec) +{ + dw_die_ref child; + if (wr_multifile ? die->die_no_multifile : die->die_remove) + return off; + assert (off <= die->u.p2.die_new_offset); + die->u.p2.die_new_offset = off; + if ((*intracuvec)[0] == die) + { + unsigned int intracu_udata_size = 0; + assert (die->u.p2.die_intracu_udata_size); + while ((*intracuvec)[0] == die) + { + intracu_udata_size + += size_of_uleb128 ((*intracuvec)[1]->u.p2.die_new_offset); + *intracuvec += 2; + } + assert (die->u.p2.die_intracu_udata_size >= intracu_udata_size); + die->die_size -= die->u.p2.die_intracu_udata_size - intracu_udata_size; + die->u.p2.die_intracu_udata_size = intracu_udata_size; + } + else + assert (die->u.p2.die_intracu_udata_size == 0 || die->die_ref_seen); + off += die->die_size; + for (child = die->die_child; child; child = child->die_sib) + off = update_new_die_offsets (child, off, intracuvec); + if (die->die_child) + off++; + return off; +} + +/* Final phase of computation of u.p2.die_new_offset. Called when already + decided what intra-CU form will be used. Can return -1U if + a problem is detected and the tool should give up. */ +static unsigned int +finalize_new_die_offsets (dw_cu_ref cu, dw_die_ref die, unsigned int off, + unsigned int intracusize, dw_die_ref **intracuvec) +{ + dw_die_ref child; + unsigned int ref_seen = die->die_ref_seen; + if (wr_multifile ? die->die_no_multifile : die->die_remove) + return off; + die->u.p2.die_new_offset = off; + die->die_ref_seen = 0; + /* As we aren't adjusting sizes of exprloc, if in the new layout + a DIE referenced through DW_OP_call2 is placed after 64K into + the CU, punt. */ + if (die->die_op_call2_referenced && off >= 65536) + return -1U; + /* Similarly punt if + DW_OP_GNU_{{regval,const,deref}_type,convert,reinterpret} + references a DIE that needs more uleb128 bytes to encode + the new offset compared to uleb128 bytes to encode the old offset. + GCC emits DW_TAG_base_type dies referenced that way at the + beginning of the CU and we try to preserve that, so this shouldn't + trigger for GCC generated code. */ + if (die->die_op_type_referenced + && !wr_multifile + && size_of_uleb128 (off) + > size_of_uleb128 (die->die_offset - cu->cu_offset)) + return -1U; + if ((*intracuvec)[0] == die) + { + unsigned int intracu_udata_size = 0; + assert (die->u.p2.die_intracu_udata_size); + while ((*intracuvec)[0] == die) + { + intracu_udata_size += intracusize; + *intracuvec += 2; + } + if (intracusize != 0) + { + die->die_size + -= die->u.p2.die_intracu_udata_size - intracu_udata_size; + die->u.p2.die_intracu_udata_size = intracu_udata_size; + } + } + else + assert (die->u.p2.die_intracu_udata_size == 0 || ref_seen); + off += die->die_size; + for (child = die->die_child; child; child = child->die_sib) + { + off = finalize_new_die_offsets (cu, child, off, intracusize, intracuvec); + if (off == -1U) + return off; + } + if (die->die_child) + off++; + return off; +} + +/* Comparison function, called through qsort, to sort CUs + by increasing number of needed new abbreviations. */ +static int +cu_abbrev_cmp (const void *p, const void *q) +{ + dw_cu_ref cu1 = *(dw_cu_ref *)p; + dw_cu_ref cu2 = *(dw_cu_ref *)q; + unsigned int nabbrevs1 = htab_elements (cu1->cu_new_abbrev); + unsigned int nabbrevs2 = htab_elements (cu2->cu_new_abbrev); + + if (nabbrevs1 < nabbrevs2) + return -1; + if (nabbrevs1 > nabbrevs2) + return 1; + /* The rest is just to get stable sort. */ + if (cu1->cu_kind != CU_PU && cu2->cu_kind == CU_PU) + return -1; + if (cu1->cu_kind == CU_PU && cu2->cu_kind != CU_PU) + return 1; + if (cu1->cu_offset < cu2->cu_offset) + return -1; + if (cu1->cu_offset > cu2->cu_offset) + return 1; + return 0; +} + +/* Compute new abbreviations for all CUs, size the new + .debug_abbrev section and all new .debug_info CUs. */ +static int +compute_abbrevs (DSO *dso) +{ + unsigned long total_size = 0, types_size = 0, abbrev_size = 0; + dw_cu_ref cu, *cuarr; + struct abbrev_tag *t; + unsigned int ncus, nlargeabbrevs = 0, i, laststart; + + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "compute_abbrevs\n"); + } + + t = (struct abbrev_tag *) + obstack_alloc (&ob2, + sizeof (*t) + + (max_nattr + 4) * sizeof (struct abbrev_attr) + + (max_nattr + 4) * sizeof (int64_t)); + t->values = (int64_t *) &t->attr[max_nattr + 4]; + for (cu = first_cu, ncus = 0; cu; cu = cu->cu_next) + { + unsigned int intracu, ndies = 0, tagsize = 0, nchildren = 0; + unsigned int nabbrevs, diesize, cusize, off, intracusize; + struct abbrev_tag **arr; + dw_die_ref *intracuarr, *intracuvec; + enum dwarf_form intracuform = DW_FORM_ref4; + dw_die_ref child, *lastotr, child_next, *last; + unsigned int headersz = (cu->cu_kind == CU_TYPES + ? 23 : (cu->cu_version >= 5 ? 12 : 11)); + + if (unlikely (fi_multifile) && cu->cu_die->die_remove) + continue; + if (unlikely (low_mem) && cu->cu_kind != CU_PU) + expand_children (cu->cu_die); + ncus++; + if (build_abbrevs (cu, t, &ndies, &ob2)) + return 1; + nabbrevs = htab_elements (cu->cu_new_abbrev); + htab_traverse (cu->cu_new_abbrev, list_abbrevs, &ob); + assert (obstack_object_size (&ob) == nabbrevs * sizeof (void *)); + arr = (struct abbrev_tag **) obstack_finish (&ob); + intracu = obstack_object_size (&ob2) / sizeof (void *) / 2; + obstack_ptr_grow (&ob2, NULL); + intracuarr = (dw_die_ref *) obstack_finish (&ob2); + if (nabbrevs >= 128) + { + unsigned int limit, uleb128_size; + + for (child = cu->cu_die->die_child; child; child = child->die_sib) + if (child->die_op_type_referenced && !wr_multifile) + { + child->u.p2.die_new_abbrev->op_type_referenced = 1; + /* If the old offset was close to uleb128 boundary, ensure + that DW_TAG_compile_unit gets small abbrev number + as well. */ + if (size_of_uleb128 (child->die_offset - cu->cu_offset) + < size_of_uleb128 (child->die_offset - cu->cu_offset + 1)) + cu->cu_die->u.p2.die_new_abbrev->op_type_referenced = 1; + } + qsort (arr, nabbrevs, sizeof (*arr), abbrev_cmp); + for (i = 0, limit = 128, uleb128_size = 1; i < nabbrevs; i++) + { + if (i + 1 == limit) + { + limit <<= 7; + uleb128_size++; + } + arr[i]->entry = i + 1; + tagsize += arr[i]->nusers * uleb128_size; + if (arr[i]->children) + nchildren += arr[i]->nusers; + } + nlargeabbrevs++; + } + else + { + tagsize = ndies; + for (i = 0; i < nabbrevs; i++) + { + arr[i]->entry = i + 1; + if (arr[i]->children) + nchildren += arr[i]->nusers; + } + } + + /* Move all base types with die_op_type_reference + to front, to increase the likelyhood that the offset + will fit. */ + for (last = &cu->cu_die->die_child, lastotr = last, child = *last; + child; child = child_next) + { + child_next = child->die_sib; + if (child->die_op_type_referenced) + { + if (lastotr != last) + { + child->die_sib = *lastotr; + *lastotr = child; + lastotr = &child->die_sib; + *last = child_next; + continue; + } + lastotr = &child->die_sib; + } + last = &child->die_sib; + } + + cu->u2.cu_largest_entry = nabbrevs; + diesize = calc_sizes (cu->cu_die); + cusize = headersz + tagsize + diesize + nchildren; + intracusize = size_of_uleb128 (cusize + intracu); + do + { + i = size_of_uleb128 (cusize + intracu * intracusize); + if (i == intracusize) + break; + intracusize = i; + } + while (1); + cu->initial_intracusize = intracusize; + off = init_new_die_offsets (cu->cu_die, headersz, intracusize); + do + { + intracuvec = intracuarr; + i = update_new_die_offsets (cu->cu_die, headersz, &intracuvec); + assert (*intracuvec == NULL); + if (i == off) + break; + assert (i < off); + off = i; + } + while (1); + if (cusize + intracu <= 256) + { + intracuform = DW_FORM_ref1; + intracusize = 1; + cusize += intracu; + } + else if (cusize + intracu * 2 <= 65536) + { + intracuform = DW_FORM_ref2; + intracusize = 2; + cusize += intracu * 2; + } + else + { + cusize += intracu * 4; + intracusize = 4; + } + if (off <= cusize) + { + intracuform = DW_FORM_ref_udata; + intracusize = 0; + cusize = off; + } + + intracuvec = intracuarr; + off = finalize_new_die_offsets (cu, cu->cu_die, headersz, intracusize, + &intracuvec); + if (off == -1U) + { + error (0, 0, "%s: DW_OP_call2 or typed DWARF stack referenced DIE" + " layed out at too big offset", dso->filename); + return 1; + } + assert (*intracuvec == NULL && off == cusize); + cu->cu_intracu_form = intracuform; + + if (intracuform != DW_FORM_ref4) + { + unsigned int j; + htab_empty (cu->cu_new_abbrev); + for (i = 0; i < nabbrevs; i++) + { + void **slot; + for (j = 0; j < arr[i]->nattr; j++) + if (arr[i]->attr[j].form == DW_FORM_ref4) + arr[i]->attr[j].form = intracuform; + compute_abbrev_hash (arr[i]); + slot = htab_find_slot_with_hash (cu->cu_new_abbrev, arr[i], + arr[i]->hash, INSERT); + if (slot == NULL) + dwz_oom (); + assert (slot != NULL && *slot == NULL); + *slot = arr[i]; + } + } + obstack_free (&ob, (void *) arr); + obstack_free (&ob2, (void *) intracuarr); + if (cu->cu_kind == CU_TYPES) + { + cu->cu_new_offset = types_size; + types_size += cusize; + } + else + { + cu->cu_new_offset = (wr_multifile ? multi_info_off : 0) + total_size; + total_size += cusize; + } + + if (unlikely (low_mem) && cu->cu_kind != CU_PU) + collapse_children (cu, cu->cu_die); + } + if (wr_multifile) + total_size += 11; /* See the end of write_info. */ + obstack_free (&ob2, (void *) t); + cuarr = (dw_cu_ref *) obstack_alloc (&ob2, ncus * sizeof (dw_cu_ref)); + for (cu = first_cu, i = 0; cu; cu = cu->cu_next) + if (cu->u1.cu_new_abbrev_owner == NULL + && (likely (!fi_multifile) + || cu->cu_kind != CU_NORMAL + || !cu->cu_die->die_remove)) + cuarr[i++] = cu; + assert (i == ncus); + qsort (cuarr, ncus, sizeof (dw_cu_ref), cu_abbrev_cmp); + /* For CUs with < 128 abbrevs, try to see if either all of the + abbrevs are at < 128 positions in >= 128 abbrev CUs, or + can be merged with some other small abbrev table to form + a < 128 abbrev table. */ + laststart = ncus - nlargeabbrevs; + for (i = ncus - 1; i != -1U; i--) + { + struct abbrev_tag **arr; + unsigned int nabbrevs, j, k, nattempts; + + if (cuarr[i]->u1.cu_new_abbrev_owner != NULL) + continue; + nabbrevs = htab_elements (cuarr[i]->cu_new_abbrev); + htab_traverse (cuarr[i]->cu_new_abbrev, list_abbrevs, &ob2); + assert (obstack_object_size (&ob2) == nabbrevs * sizeof (void *)); + arr = (struct abbrev_tag **) obstack_finish (&ob2); + if (nabbrevs >= 128) + { + nattempts = 0; + for (j = i + 1; j < ncus; j++) + { + unsigned int entry; + if (cuarr[j]->u1.cu_new_abbrev_owner) + continue; + if (++nattempts == 100) + break; + entry = cuarr[j]->u2.cu_largest_entry; + for (k = 0; k < nabbrevs; k++) + { + struct abbrev_tag *t + = htab_find_with_hash (cuarr[j]->cu_new_abbrev, + arr[k], arr[k]->hash); + if (t == NULL) + { + ++entry; + if (size_of_uleb128 (entry) + != size_of_uleb128 (arr[k]->entry)) + break; + } + else if (size_of_uleb128 (t->entry) + != size_of_uleb128 (arr[k]->entry)) + break; + } + if (k != nabbrevs) + continue; + entry = cuarr[j]->u2.cu_largest_entry; + for (k = 0; k < nabbrevs; k++) + { + void **slot + = htab_find_slot_with_hash (cuarr[j]->cu_new_abbrev, + arr[k], arr[k]->hash, + INSERT); + if (slot == NULL) + dwz_oom (); + if (*slot != NULL) + arr[k]->entry = ((struct abbrev_tag *) *slot)->entry; + else + { + struct abbrev_tag *newt; + arr[k]->entry = ++entry; + newt = pool_clone_abbrev (arr[k]); + *slot = newt; + } + } + cuarr[j]->u2.cu_largest_entry = entry; + cuarr[i]->u1.cu_new_abbrev_owner = cuarr[j]; + break; + } + obstack_free (&ob2, (void *) arr); + continue; + } + /* Don't search all CUs, that might be too expensive. So just search + 100 of >= 128 abbrev tables, if there are more than 100, different + set each time. We are looking for a full match (i.e. that + cuarr[i] abbrevs are a subset of cuarr[j] abbrevs, and all of them + are in the low positions. */ + for (j = laststart, nattempts = -1U; nlargeabbrevs; j++) + { + if (j == ncus) + j -= nlargeabbrevs; + if (nattempts != -1U && j == laststart) + break; + if (nattempts == -1U) + nattempts = 0; + if (cuarr[j]->u1.cu_new_abbrev_owner) + continue; + if (++nattempts == 100) + break; + for (k = 0; k < nabbrevs; k++) + { + struct abbrev_tag *t + = htab_find_with_hash (cuarr[j]->cu_new_abbrev, + arr[k], arr[k]->hash); + if (t == NULL || t->entry >= 128) + break; + } + if (k == nabbrevs) + { + for (k = 0; k < nabbrevs; k++) + { + struct abbrev_tag *t + = htab_find_with_hash (cuarr[j]->cu_new_abbrev, + arr[k], arr[k]->hash); + arr[k]->entry = t->entry; + } + cuarr[i]->u1.cu_new_abbrev_owner = cuarr[j]; + break; + } + } + if (nlargeabbrevs > 100) + laststart = j; + if (cuarr[i]->u1.cu_new_abbrev_owner == NULL) + { + unsigned int maxdups = 0, maxdupidx = 0; + /* Next search up to 100 of small abbrev CUs, looking + for best match. */ + nattempts = 0; + for (j = i + 1; j < ncus - nlargeabbrevs; j++) + { + unsigned int curdups = 0; + if (cuarr[j]->u1.cu_new_abbrev_owner) + continue; + if (++nattempts == 100) + break; + for (k = 0; k < nabbrevs; k++) + { + struct abbrev_tag *t + = htab_find_with_hash (cuarr[j]->cu_new_abbrev, + arr[k], arr[k]->hash); + if (t != NULL) + curdups++; + } + if (curdups > maxdups + && cuarr[j]->u2.cu_largest_entry - curdups + nabbrevs < 128) + { + maxdups = curdups; + maxdupidx = j; + if (maxdups == nabbrevs) + break; + } + } + if (maxdups) + { + unsigned int entry = cuarr[maxdupidx]->u2.cu_largest_entry; + j = maxdupidx; + for (k = 0; k < nabbrevs; k++) + { + void **slot + = htab_find_slot_with_hash (cuarr[j]->cu_new_abbrev, + arr[k], arr[k]->hash, + INSERT); + if (slot == NULL) + dwz_oom (); + if (*slot != NULL) + arr[k]->entry = ((struct abbrev_tag *) *slot)->entry; + else + { + struct abbrev_tag *newt; + arr[k]->entry = ++entry; + newt = pool_clone_abbrev (arr[k]); + *slot = newt; + } + } + cuarr[j]->u2.cu_largest_entry = entry; + cuarr[i]->u1.cu_new_abbrev_owner = cuarr[j]; + } + } + obstack_free (&ob2, (void *) arr); + } + obstack_free (&ob2, (void *) t); + for (cu = first_cu; cu; cu = cu->cu_next) + { + struct abbrev_tag **arr; + unsigned int nabbrevs, j; + + if (unlikely (fi_multifile) + && cu->cu_kind == CU_NORMAL + && cu->cu_die->die_remove) + continue; + if (cu->u1.cu_new_abbrev_owner != NULL) + { + cu->u2.cu_new_abbrev_offset = -1U; + if (cu->cu_new_abbrev) + htab_delete (cu->cu_new_abbrev); + cu->cu_new_abbrev = NULL; + continue; + } + cu->u2.cu_new_abbrev_offset + = (wr_multifile ? multi_abbrev_off : 0) + abbrev_size; + nabbrevs = htab_elements (cu->cu_new_abbrev); + htab_traverse (cu->cu_new_abbrev, list_abbrevs, &ob); + assert (obstack_object_size (&ob) == nabbrevs * sizeof (void *)); + arr = (struct abbrev_tag **) obstack_finish (&ob); + for (i = 0; i < nabbrevs; i++) + { + abbrev_size += size_of_uleb128 (arr[i]->entry); + abbrev_size += size_of_uleb128 (arr[i]->tag); + abbrev_size += 1; + for (j = 0; j < arr[i]->nattr; j++) + { + abbrev_size += size_of_uleb128 (arr[i]->attr[j].attr); + abbrev_size += size_of_uleb128 (arr[i]->attr[j].form); + if (arr[i]->attr[j].form == DW_FORM_implicit_const) + abbrev_size += size_of_sleb128 (arr[i]->values[j]); + } + abbrev_size += 2; + } + abbrev_size += 1; + obstack_free (&ob, (void *) arr); + } + for (cu = first_cu; cu; cu = cu->cu_next) + if (unlikely (fi_multifile) + && cu->cu_kind == CU_NORMAL + && cu->cu_die->die_remove) + continue; + else if (cu->u2.cu_new_abbrev_offset == -1U) + { + dw_cu_ref owner = cu; + unsigned int cu_new_abbrev_offset; + while (owner->u1.cu_new_abbrev_owner != NULL) + owner = owner->u1.cu_new_abbrev_owner; + cu_new_abbrev_offset = owner->u2.cu_new_abbrev_offset; + owner = cu; + while (owner->u1.cu_new_abbrev_owner != NULL) + { + owner->u2.cu_new_abbrev_offset = cu_new_abbrev_offset; + owner = owner->u1.cu_new_abbrev_owner; + } + } + debug_sections[DEBUG_INFO].new_size = total_size; + debug_sections[DEBUG_ABBREV].new_size = abbrev_size; + debug_sections[DEBUG_TYPES].new_size = types_size; + return 0; +} + +/* Comparison function, sort abbreviations by increasing + entry value. */ +static int +abbrev_entry_cmp (const void *p, const void *q) +{ + struct abbrev_tag *t1 = *(struct abbrev_tag **)p; + struct abbrev_tag *t2 = *(struct abbrev_tag **)q; + + if (t1->entry < t2->entry) + return -1; + if (t1->entry > t2->entry) + return 1; + return 0; +} + +/* Construct the new .debug_abbrev section + in malloced memory, store it as debug_sections[DEBUG_ABBREV].new_data. */ +static void +write_abbrev (void) +{ + dw_cu_ref cu; + unsigned char *abbrev = malloc (debug_sections[DEBUG_ABBREV].new_size); + unsigned char *ptr = abbrev; + + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "write_abbrev\n"); + } + + if (abbrev == NULL) + dwz_oom (); + + debug_sections[DEBUG_ABBREV].new_data = abbrev; + for (cu = first_cu; cu; cu = cu->cu_next) + { + struct abbrev_tag **arr; + unsigned int nabbrevs, i, j; + + if (unlikely (fi_multifile) + && cu->cu_kind == CU_NORMAL + && cu->cu_die->die_remove) + continue; + if (cu->u1.cu_new_abbrev_owner != NULL) + continue; + nabbrevs = htab_elements (cu->cu_new_abbrev); + htab_traverse (cu->cu_new_abbrev, list_abbrevs, &ob); + assert (obstack_object_size (&ob) == nabbrevs * sizeof (void *)); + arr = (struct abbrev_tag **) obstack_finish (&ob); + qsort (arr, nabbrevs, sizeof (*arr), abbrev_entry_cmp); + for (i = 0; i < nabbrevs; i++) + { + write_uleb128 (ptr, arr[i]->entry); + write_uleb128 (ptr, arr[i]->tag); + *ptr++ = arr[i]->children ? DW_CHILDREN_yes : DW_CHILDREN_no; + for (j = 0; j < arr[i]->nattr; j++) + { + write_uleb128 (ptr, arr[i]->attr[j].attr); + write_uleb128 (ptr, arr[i]->attr[j].form); + if (arr[i]->attr[j].form == DW_FORM_implicit_const) + write_sleb128 (ptr, arr[i]->values[j]); + } + *ptr++ = 0; + *ptr++ = 0; + } + *ptr++ = 0; + obstack_free (&ob, (void *) arr); + if (likely (!low_mem)) + { + htab_delete (cu->cu_new_abbrev); + cu->cu_new_abbrev = NULL; + } + } + assert (abbrev + debug_sections[DEBUG_ABBREV].new_size == ptr); +} + +/* Adjust DWARF expression starting at PTR, LEN bytes long, referenced by + DIE, with REF being the original DIE. */ +static void +adjust_exprloc (dw_cu_ref cu, dw_die_ref die, dw_cu_ref refcu, + dw_die_ref ref, unsigned char *ptr, size_t len) +{ + unsigned char *end = ptr + len, *orig_ptr = NULL; + unsigned char op; + uint32_t leni; + GElf_Addr addr; + dw_die_ref refd, refdt; + + while (ptr < end) + { + op = *ptr++; + switch (op) + { + case DW_OP_addr: + ptr += ptr_size; + break; + case DW_OP_deref: + case DW_OP_dup: + case DW_OP_drop: + case DW_OP_over: + case DW_OP_swap: + case DW_OP_rot: + case DW_OP_xderef: + case DW_OP_abs: + case DW_OP_and: + case DW_OP_div: + case DW_OP_minus: + case DW_OP_mod: + case DW_OP_mul: + case DW_OP_neg: + case DW_OP_not: + case DW_OP_or: + case DW_OP_plus: + case DW_OP_shl: + case DW_OP_shr: + case DW_OP_shra: + case DW_OP_xor: + case DW_OP_eq: + case DW_OP_ge: + case DW_OP_gt: + case DW_OP_le: + case DW_OP_lt: + case DW_OP_ne: + case DW_OP_lit0 ... DW_OP_lit31: + case DW_OP_reg0 ... DW_OP_reg31: + case DW_OP_nop: + case DW_OP_push_object_address: + case DW_OP_form_tls_address: + case DW_OP_call_frame_cfa: + case DW_OP_stack_value: + case DW_OP_GNU_push_tls_address: + case DW_OP_GNU_uninit: + break; + case DW_OP_const1u: + case DW_OP_pick: + case DW_OP_deref_size: + case DW_OP_xderef_size: + case DW_OP_const1s: + ++ptr; + break; + case DW_OP_const2u: + case DW_OP_const2s: + case DW_OP_skip: + case DW_OP_bra: + ptr += 2; + break; + case DW_OP_call2: + case DW_OP_call4: + case DW_OP_GNU_parameter_ref: + if (op == DW_OP_call2) + addr = read_16 (ptr); + else + addr = read_32 (ptr); + refd = off_htab_lookup (refcu, refcu->cu_offset + addr); + assert (refd != NULL && !refd->die_remove); + if (op == DW_OP_call2) + { + assert (refd->u.p2.die_new_offset <= 65535); + ptr -= 2; + write_16 (ptr, refd->u.p2.die_new_offset); + } + else + { + ptr -= 4; + write_32 (ptr, refd->u.p2.die_new_offset); + } + break; + case DW_OP_const4u: + case DW_OP_const4s: + ptr += 4; + break; + case DW_OP_call_ref: + case DW_OP_GNU_implicit_pointer: + case DW_OP_implicit_pointer: + case DW_OP_GNU_variable_value: + addr = read_size (ptr, refcu->cu_version == 2 ? ptr_size : 4); + assert (cu->cu_version == refcu->cu_version); + refd = off_htab_lookup (NULL, addr); + assert (refd != NULL); + refdt = refd; + while (refdt->die_toplevel == 0) + refdt = refdt->die_parent; + if (refdt->die_dup && !refdt->die_op_type_referenced) + refd = die_find_dup (refdt, refdt->die_dup, refd); + write_size (ptr, cu->cu_version == 2 ? ptr_size : 4, + die_cu (refd)->cu_new_offset + + refd->u.p2.die_new_offset); + if (cu->cu_version == 2) + ptr += ptr_size; + else + ptr += 4; + if (op == DW_OP_GNU_implicit_pointer || op == DW_OP_implicit_pointer) + skip_leb128 (ptr); + break; + case DW_OP_const8u: + case DW_OP_const8s: + ptr += 8; + break; + case DW_OP_constu: + case DW_OP_plus_uconst: + case DW_OP_regx: + case DW_OP_piece: + case DW_OP_consts: + case DW_OP_breg0 ... DW_OP_breg31: + case DW_OP_fbreg: + skip_leb128 (ptr); + break; + case DW_OP_bregx: + case DW_OP_bit_piece: + skip_leb128 (ptr); + skip_leb128 (ptr); + break; + case DW_OP_implicit_value: + leni = read_uleb128 (ptr); + ptr += leni; + break; + case DW_OP_GNU_entry_value: + case DW_OP_entry_value: + leni = read_uleb128 (ptr); + assert ((uint64_t) (end - ptr) >= leni); + adjust_exprloc (cu, die, refcu, ref, ptr, leni); + ptr += leni; + break; + case DW_OP_GNU_convert: + case DW_OP_convert: + case DW_OP_GNU_reinterpret: + case DW_OP_reinterpret: + orig_ptr = ptr; + addr = read_uleb128 (ptr); + if (addr == 0) + break; + goto typed_dwarf; + case DW_OP_GNU_regval_type: + case DW_OP_regval_type: + skip_leb128 (ptr); + orig_ptr = ptr; + addr = read_uleb128 (ptr); + goto typed_dwarf; + case DW_OP_GNU_const_type: + case DW_OP_const_type: + orig_ptr = ptr; + addr = read_uleb128 (ptr); + goto typed_dwarf; + case DW_OP_GNU_deref_type: + case DW_OP_deref_type: + ++ptr; + orig_ptr = ptr; + addr = read_uleb128 (ptr); + typed_dwarf: + refd = off_htab_lookup (refcu, refcu->cu_offset + addr); + assert (refd != NULL && refd->die_op_type_referenced); + leni = ptr - orig_ptr; + assert (size_of_uleb128 (refd->u.p2.die_new_offset) <= leni); + ptr = orig_ptr; + write_uleb128 (ptr, refd->u.p2.die_new_offset); + /* If the new offset ends up being shorter uleb128 + encoded than the old, pad it up to make it still valid, + but not shortest, uleb128. Changing sizes of + exprloc would be a nightmare. Another alternative would + be to pad with DW_OP_nop after the op. */ + if (ptr < orig_ptr + leni) + { + ptr[-1] |= 0x80; + while (ptr < orig_ptr + leni - 1) + *ptr++ = 0x80; + *ptr++ = 0; + } + if (op == DW_OP_GNU_const_type || op == DW_OP_const_type) + ptr += *ptr + 1; + break; + default: + abort (); + } + } +} + +/* Write DW_TAG_unit_* DIE (with ORIGIN being the corresponding original DIE) to + memory starting at PTR, return pointer after the DIE. */ +static unsigned char * +write_unit_die (unsigned char *ptr, dw_die_ref die, dw_die_ref origin) +{ + struct abbrev_tag *t = die->u.p2.die_new_abbrev; + unsigned int i; + + for (i = 0; i < t->nattr; ++i) + { + struct abbrev_attr *attr = &t->attr[i]; + switch (attr->attr) + { + case DW_AT_stmt_list: + { + enum dwarf_form form; + unsigned char *p = get_AT (origin, DW_AT_stmt_list, &form); + assert (p && (form == DW_FORM_sec_offset + || form == DW_FORM_data4)); + if (wr_multifile) + write_32 (ptr, multi_line_off); + else if (op_multifile) + write_32 (ptr, 0); + else + { + memcpy (ptr, p, 4); + ptr += 4; + } + } + break; + case DW_AT_comp_dir: + { + enum dwarf_form form; + unsigned char *p = get_AT (origin, DW_AT_comp_dir, &form); + assert (p); + assert (form == attr->form + || (form == DW_FORM_strp + && (attr->form == DW_FORM_GNU_strp_alt + || attr->form == DW_FORM_strp_sup))); + if (form == DW_FORM_strp) + { + if (unlikely (wr_multifile || op_multifile || fi_multifile)) + { + unsigned int strp = lookup_strp_offset (read_32 (p)); + write_32 (ptr, strp); + } + else + { + memcpy (ptr, p, 4); + ptr += 4; + } + } + else if (form == DW_FORM_line_strp) + { + memcpy (ptr, p, 4); + ptr += 4; + } + else + { + size_t len = strlen ((char *) p) + 1; + memcpy (ptr, p, len); + ptr += len; + } + } + break; + case DW_AT_language: + { + enum dwarf_source_language lang = die_cu (die)->lang; + unsigned int lang_size = nr_bytes_for (lang); + write_size (ptr, lang_size, lang); + ptr += lang_size; + } + break; + default: + assert (false); + break; + } + } + + return ptr; +} + +/* Write DIE (with REF being the corresponding original DIE) to + memory starting at PTR, return pointer after the DIE. */ +static unsigned char * +write_die (unsigned char *ptr, dw_cu_ref cu, dw_die_ref die, + dw_cu_ref refcu, dw_die_ref ref, unsigned int *die_count) +{ + uint64_t low_pc = 0; + dw_die_ref child, sib = NULL, origin = NULL; + struct abbrev_tag *t; + + if (wr_multifile ? die->die_no_multifile : die->die_remove) + return ptr; + if (die_count) + (*die_count)++; + if (die->die_offset == -1U) + { + if (ref != NULL) + ; + else if (die_safe_nextdup (die) && die->die_nextdup->die_dup == die) + { + ref = die->die_nextdup; + refcu = die_cu (ref); + } + if (ref == NULL) + origin = die->die_nextdup; + } + else + { + ref = die; + refcu = cu; + if (wr_multifile + && (die->die_root || die->die_named_namespace)) + origin = die; + } + if (die->die_child && die->die_sib) + for (sib = die->die_sib; sib; sib = sib->die_sib) + if (wr_multifile ? !sib->die_no_multifile : !sib->die_remove) + break; + t = die->u.p2.die_new_abbrev; + write_uleb128 (ptr, t->entry); + if (ref != NULL && origin == NULL) + { + unsigned char *base + = cu->cu_kind == CU_TYPES + ? debug_sections[DEBUG_TYPES].data + : debug_sections[DEBUG_INFO].data; + unsigned char *inptr = base + ref->die_offset; + struct abbrev_tag *reft = ref->die_abbrev; + unsigned int i, j; + + skip_leb128 (inptr); + for (i = 0, j = 0; i < reft->nattr; ++i) + { + uint32_t form = reft->attr[i].form; + size_t len = 0; + uint64_t value; + unsigned char *orig_ptr = inptr; + + while (form == DW_FORM_indirect) + form = read_uleb128 (inptr); + + if (unlikely (wr_multifile || op_multifile) + && (reft->attr[i].attr == DW_AT_decl_file + || reft->attr[i].attr == DW_AT_call_file)) + { + switch (form) + { + case DW_FORM_data1: + value = read_8 (inptr); + break; + case DW_FORM_data2: + value = read_16 (inptr); + break; + case DW_FORM_data4: + value = read_32 (inptr); + break; + case DW_FORM_data8: + value = read_64 (inptr); + break; + case DW_FORM_udata: + value = read_uleb128 (inptr); + break; + case DW_FORM_sdata: + value = read_sleb128 (inptr); + break; + case DW_FORM_implicit_const: + /* DW_FORM_implicit_const should have been updated + already when computing abbrevs. */ + j++; + continue; + default: abort (); + } + value = line_htab_lookup (refcu, value); + switch (t->attr[j].form) + { + case DW_FORM_data1: write_8 (ptr, value); break; + case DW_FORM_data2: write_16 (ptr, value); break; + case DW_FORM_data4: write_32 (ptr, value); break; + case DW_FORM_data8: write_64 (ptr, value); break; + case DW_FORM_udata: write_uleb128 (ptr, value); break; + case DW_FORM_sdata: write_sleb128 (ptr, value); break; + default: abort (); + } + j++; + continue; + } + + if (unlikely (fi_multifile) + && (reft->attr[i].attr == DW_AT_GNU_macros + || reft->attr[i].attr == DW_AT_macros) + && alt_macro_htab != NULL) + { + struct macro_entry me, *m; + + memcpy (ptr, orig_ptr, inptr - orig_ptr); + ptr += inptr - orig_ptr; + value = read_32 (inptr); + me.ptr = debug_sections[DEBUG_MACRO].data + value; + m = (struct macro_entry *) + htab_find_with_hash (macro_htab, &me, value); + write_32 (ptr, m->hash); + j++; + continue; + } + + switch (form) + { + case DW_FORM_ref_addr: + { + dw_die_ref refd, refdt; + if (t->attr[j].form != DW_FORM_GNU_ref_alt + && t->attr[j].form != DW_FORM_ref_sup4) + { + memcpy (ptr, orig_ptr, inptr - orig_ptr); + ptr += inptr - orig_ptr; + } + value = read_size (inptr, refcu->cu_version == 2 + ? ptr_size : 4); + inptr += refcu->cu_version == 2 ? ptr_size : 4; + refd = off_htab_lookup (NULL, value); + if (refd == NULL || refd->die_tag == 0) + error (1, 0, "Couldn't find DIE at DW_FORM_ref_addr offset" + " 0x%" PRIx64, value); + assert (refd != NULL); + refdt = refd; + while (refdt->die_toplevel == 0) + refdt = refdt->die_parent; + if (refdt->die_dup && !refdt->die_op_type_referenced) + { + refd = die_find_dup (refdt, refdt->die_dup, refd); + if (t->attr[j].form == DW_FORM_GNU_ref_alt + || t->attr[j].form == DW_FORM_ref_sup4) + { + assert (die_cu (refd)->cu_kind == CU_ALT); + write_32 (ptr, refd->die_offset); + j++; + continue; + } + } + assert (refd->u.p2.die_new_offset + && t->attr[j].form != DW_FORM_GNU_ref_alt + && t->attr[j].form != DW_FORM_ref_sup4); + value = die_cu (refd)->cu_new_offset + + refd->u.p2.die_new_offset; + write_size (ptr, cu->cu_version == 2 ? ptr_size : 4, + value); + ptr += cu->cu_version == 2 ? ptr_size : 4; + if (unlikely (op_multifile)) + assert (die_cu (refd)->cu_kind == CU_PU); + j++; + continue; + } + case DW_FORM_addr: + inptr += ptr_size; + if (reft->attr[i].attr == DW_AT_low_pc) + low_pc = read_size (inptr - ptr_size, ptr_size); + if (reft->attr[i].attr == DW_AT_high_pc + && t->attr[j].form != reft->attr[i].form) + { + uint64_t high_pc = read_size (inptr - ptr_size, ptr_size); + switch (t->attr[j].form) + { + case DW_FORM_udata: + write_uleb128 (ptr, high_pc - low_pc); + break; + case DW_FORM_data4: + write_32 (ptr, high_pc - low_pc); + break; + default: + abort (); + } + j++; + continue; + } + break; + case DW_FORM_flag_present: + case DW_FORM_implicit_const: + break; + case DW_FORM_flag: + case DW_FORM_data1: + ++inptr; + break; + case DW_FORM_data2: + inptr += 2; + break; + case DW_FORM_data4: + if (reft->attr[i].attr == DW_AT_high_pc + && t->attr[j].form != reft->attr[i].form) + { + uint32_t range_len = read_32 (inptr); + switch (t->attr[j].form) + { + case DW_FORM_udata: + write_uleb128 (ptr, range_len); + break; + default: + abort (); + } + j++; + continue; + } + inptr += 4; + break; + case DW_FORM_sec_offset: + inptr += 4; + break; + case DW_FORM_data8: + if (reft->attr[i].attr == DW_AT_high_pc + && t->attr[j].form != reft->attr[i].form) + { + uint64_t range_len = read_64 (inptr); + switch (t->attr[j].form) + { + case DW_FORM_udata: + write_uleb128 (ptr, range_len); + break; + case DW_FORM_data4: + write_32 (ptr, range_len); + break; + default: + abort (); + } + j++; + continue; + } + inptr += 8; + break; + case DW_FORM_ref_sig8: + inptr += 8; + break; + case DW_FORM_data16: + inptr += 16; + break; + case DW_FORM_sdata: + case DW_FORM_udata: + skip_leb128 (inptr); + break; + case DW_FORM_strp: + if (unlikely (wr_multifile || op_multifile || fi_multifile)) + { + unsigned int strp = lookup_strp_offset (read_32 (inptr)); + memcpy (ptr, orig_ptr, inptr - 4 - orig_ptr); + ptr += inptr - 4 - orig_ptr; + write_32 (ptr, strp); + j++; + continue; + } + inptr += 4; + break; + case DW_FORM_line_strp: + inptr += 4; + break; + case DW_FORM_string: + inptr = (unsigned char *) strchr ((char *)inptr, '\0') + 1; + break; + case DW_FORM_indirect: + abort (); + case DW_FORM_block1: + len = *inptr++; + break; + case DW_FORM_block2: + len = read_16 (inptr); + form = DW_FORM_block1; + break; + case DW_FORM_block4: + len = read_32 (inptr); + form = DW_FORM_block1; + break; + case DW_FORM_block: + len = read_uleb128 (inptr); + form = DW_FORM_block1; + break; + case DW_FORM_exprloc: + len = read_uleb128 (inptr); + break; + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + switch (form) + { + case DW_FORM_ref1: value = read_8 (inptr); break; + case DW_FORM_ref2: value = read_16 (inptr); break; + case DW_FORM_ref4: value = read_32 (inptr); break; + case DW_FORM_ref8: value = read_64 (inptr); break; + case DW_FORM_ref_udata: value = read_uleb128 (inptr); break; + default: abort (); + } + if (reft->attr[i].attr == DW_AT_sibling) + { + if (j == t->nattr + || t->attr[j].attr != DW_AT_sibling) + continue; + assert (sib); + value = sib->u.p2.die_new_offset; + } + else + { + dw_die_ref refdt, refd + = off_htab_lookup (refcu, refcu->cu_offset + value); + assert (refd != NULL); + refdt = refd; + while (refdt->die_toplevel == 0) + refdt = refdt->die_parent; + if (refdt->die_dup && refdt->die_op_type_referenced + && cu->cu_kind != CU_PU) + { + if (cu == die_cu (refdt->die_dup)) + refd = die_find_dup (refdt, refdt->die_dup, refd); + } + else if (refdt->die_dup) + refd = die_find_dup (refdt, refdt->die_dup, refd); + if (t->attr[j].form == DW_FORM_GNU_ref_alt + || t->attr[j].form == DW_FORM_ref_sup4) + { + value = refd->die_offset; + assert (die_cu (refd)->cu_kind == CU_ALT); + } + else + { + dw_cu_ref refdcu = die_cu (refd); + value = refd->u.p2.die_new_offset; + assert (IMPLIES (cu->cu_kind == CU_PU, + die_cu (refd)->cu_kind == CU_PU)); + assert (value && refdcu->cu_kind != CU_ALT); + if (t->attr[j].form == DW_FORM_ref_addr) + { + value += refdcu->cu_new_offset; + if (unlikely (op_multifile)) + assert (refdcu->cu_kind == CU_PU); + } + else + assert (refdcu == cu); + } + } + switch (t->attr[j].form) + { + case DW_FORM_ref1: write_8 (ptr, value); break; + case DW_FORM_ref2: write_16 (ptr, value); break; + case DW_FORM_ref4: write_32 (ptr, value); break; + case DW_FORM_ref_udata: write_uleb128 (ptr, value); break; + case DW_FORM_ref_addr: + write_size (ptr, cu->cu_version == 2 ? ptr_size : 4, + value); + ptr += cu->cu_version == 2 ? ptr_size : 4; + break; + case DW_FORM_GNU_ref_alt: + case DW_FORM_ref_sup4: + write_32 (ptr, value); + break; + case DW_FORM_ref_sup8: + write_64 (ptr, value); + break; + default: + abort (); + } + j++; + continue; + default: + abort (); + } + + if (form == DW_FORM_block1 || form == DW_FORM_exprloc) + inptr += len; + + memcpy (ptr, orig_ptr, inptr - orig_ptr); + ptr += inptr - orig_ptr; + + /* Old DWARF uses blocks instead of exprlocs. */ + if (form == DW_FORM_block1 && cu->cu_version < 4) + switch (reft->attr[i].attr) + { + case DW_AT_frame_base: + case DW_AT_location: + case DW_AT_data_member_location: + case DW_AT_vtable_elem_location: + case DW_AT_byte_size: + case DW_AT_bit_offset: + case DW_AT_bit_size: + case DW_AT_string_length: + case DW_AT_lower_bound: + case DW_AT_return_addr: + case DW_AT_bit_stride: + case DW_AT_upper_bound: + case DW_AT_count: + case DW_AT_segment: + case DW_AT_static_link: + case DW_AT_use_location: + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_data_location: + case DW_AT_byte_stride: + case DW_AT_rank: + case DW_AT_call_value: + case DW_AT_call_target: + case DW_AT_call_target_clobbered: + case DW_AT_call_data_location: + case DW_AT_call_data_value: + case DW_AT_GNU_call_site_value: + case DW_AT_GNU_call_site_data_value: + case DW_AT_GNU_call_site_target: + case DW_AT_GNU_call_site_target_clobbered: + adjust_exprloc (cu, die, refcu, ref, ptr - len, len); + default: + break; + } + else if (form == DW_FORM_exprloc) + adjust_exprloc (cu, die, refcu, ref, ptr - len, len); + j++; + } + assert (j == t->nattr); + } + else + switch (die->die_tag) + { + case DW_TAG_partial_unit: + case DW_TAG_compile_unit: + ptr = write_unit_die (ptr, die, origin); + break; + case DW_TAG_namespace: + case DW_TAG_module: + { + enum dwarf_form form; + unsigned char *p = get_AT (origin, DW_AT_name, &form); + assert (p && (form == t->attr[0].form + || (form == DW_FORM_strp + && (t->attr[0].form == DW_FORM_GNU_strp_alt + || t->attr[0].form == DW_FORM_strp_sup)))); + if (form == DW_FORM_strp) + { + if (unlikely (wr_multifile || op_multifile || fi_multifile)) + { + unsigned int strp = lookup_strp_offset (read_32 (p)); + write_32 (ptr, strp); + } + else + { + memcpy (ptr, p, 4); + ptr += 4; + } + } + else if (form == DW_FORM_line_strp) + { + memcpy (ptr, p, 4); + ptr += 4; + } + else + { + size_t len = strlen ((char *) p) + 1; + memcpy (ptr, p, len); + ptr += len; + } + if (t->nattr > 1) + { + assert (sib); + switch (t->attr[1].form) + { + case DW_FORM_ref1: + write_8 (ptr, sib->u.p2.die_new_offset); + break; + case DW_FORM_ref2: + write_16 (ptr, sib->u.p2.die_new_offset); + break; + case DW_FORM_ref4: + write_32 (ptr, sib->u.p2.die_new_offset); + break; + case DW_FORM_ref_udata: + write_uleb128 (ptr, sib->u.p2.die_new_offset); + break; + default: + abort (); + } + } + break; + } + case DW_TAG_imported_unit: + refcu = die_cu (origin); + if (t->attr[0].form == DW_FORM_GNU_ref_alt + || t->attr[0].form == DW_FORM_ref_sup4) + { + assert (refcu->cu_kind == CU_ALT); + write_32 (ptr, origin->die_offset); + break; + } + assert (refcu->cu_kind != CU_ALT); + write_size (ptr, cu->cu_version == 2 ? ptr_size : 4, + refcu->cu_new_offset + + origin->u.p2.die_new_offset); + ptr += cu->cu_version == 2 ? ptr_size : 4; + break; + default: + abort (); + } + if (ref != NULL && ref != die) + { + dw_die_ref ref_child; + for (child = die->die_child, ref_child = ref->die_child; + child; child = child->die_sib, ref_child = ref_child->die_sib) + ptr = write_die (ptr, cu, child, refcu, ref_child, die_count); + } + else + for (child = die->die_child; child; child = child->die_sib) + ptr = write_die (ptr, cu, child, NULL, NULL, die_count); + if (die->die_child) + write_8 (ptr, 0); + return ptr; +} + +/* Recompute abbrevs for CU. If any children were collapsed during + compute_abbrevs, their ->u.p2.die_new_abbrev and ->u.p2.die_new_offset + fields are no longer available and need to be computed again. */ +static void +recompute_abbrevs (dw_cu_ref cu, unsigned int cu_size) +{ + unsigned int headersz = (cu->cu_kind == CU_TYPES + ? 23 : (cu->cu_version >= 5 ? 12 : 11)); + struct abbrev_tag *t; + unsigned int ndies = 0, intracusize, off, i; + dw_die_ref *intracuarr, *intracuvec; + + t = (struct abbrev_tag *) + obstack_alloc (&ob2, + sizeof (*t) + + (max_nattr + 4) * sizeof (struct abbrev_attr) + + (max_nattr + 4) * sizeof (int64_t)); + t->values = (int64_t *) &t->attr[max_nattr + 4]; + + build_abbrevs_for_die (cu->u1.cu_new_abbrev_owner + ? cu->u1.cu_new_abbrev_owner->cu_new_abbrev + : cu->cu_new_abbrev, cu, cu->cu_die, NULL, NULL, t, + &ndies, &ob2, true); + + obstack_ptr_grow (&ob2, NULL); + intracuarr = (dw_die_ref *) obstack_finish (&ob2); + if (cu->cu_intracu_form != DW_FORM_ref_udata) + { + switch (cu->cu_intracu_form) + { + case DW_FORM_ref1: intracusize = 1; break; + case DW_FORM_ref2: intracusize = 2; break; + case DW_FORM_ref4: intracusize = 4; break; + default: abort (); + } + off = init_new_die_offsets (cu->cu_die, headersz, intracusize); + } + else + { + intracusize = cu->initial_intracusize; + + off = init_new_die_offsets (cu->cu_die, headersz, intracusize); + do + { + intracuvec = intracuarr; + i = update_new_die_offsets (cu->cu_die, headersz, &intracuvec); + assert (*intracuvec == NULL); + if (i == off) + break; + assert (i < off); + off = i; + } + while (1); + + intracuvec = intracuarr; + off = finalize_new_die_offsets (cu, cu->cu_die, headersz, 0, + &intracuvec); + assert (*intracuvec == NULL); + } + obstack_free (&ob2, (void *) t); + assert (off == cu_size); +} + +/* Construct new .debug_info section in malloced memory, + store it to debug_sections[DEBUG_INFO].new_data. */ +static void +write_info (unsigned int *die_count) +{ + dw_cu_ref cu, cu_next; + unsigned char *info = malloc (debug_sections[DEBUG_INFO].new_size); + unsigned char *ptr = info; + + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "write_info\n"); + } + + if (info == NULL) + dwz_oom (); + if (die_count) + *die_count = 0; + debug_sections[DEBUG_INFO].new_data = info; + cu = first_cu; + if (unlikely (fi_multifile)) + while (cu + && cu->cu_kind == CU_NORMAL + && cu->cu_die->die_remove) + cu = cu->cu_next; + for (; cu; cu = cu_next) + { + unsigned long next_off = debug_sections[DEBUG_INFO].new_size; + /* Ignore .debug_types CUs. */ + if (cu->cu_kind == CU_TYPES) + break; + cu_next = cu->cu_next; + if (unlikely (fi_multifile)) + while (cu_next + && cu_next->cu_kind == CU_NORMAL + && cu_next->cu_die->die_remove) + cu_next = cu_next->cu_next; + if (cu_next && cu_next->cu_kind == CU_TYPES) + cu_next = NULL; + if (cu_next) + next_off = cu_next->cu_new_offset; + else if (wr_multifile) + next_off += multi_info_off - 11L; + if (unlikely (low_mem) + && cu->cu_kind != CU_PU + && expand_children (cu->cu_die)) + recompute_abbrevs (cu, next_off - cu->cu_new_offset); + /* Write CU header. */ + write_32 (ptr, next_off - cu->cu_new_offset - 4); + write_16 (ptr, cu->cu_version); + if (cu->cu_version >= 5) + { + *ptr++ = (cu->cu_die->die_tag == DW_TAG_compile_unit + ? DW_UT_compile : DW_UT_partial); + write_8 (ptr, ptr_size); + } + write_32 (ptr, cu->u2.cu_new_abbrev_offset); + if (cu->cu_version < 5) + write_8 (ptr, ptr_size); + ptr = write_die (ptr, cu, cu->cu_die, NULL, NULL, die_count); + assert (info + (next_off - (wr_multifile ? multi_info_off : 0)) == ptr); + if (unlikely (low_mem) && cu->cu_kind != CU_PU) + collapse_children (cu, cu->cu_die); + } + if (wr_multifile) + { + /* And terminate the contribution by the current object file. */ + write_32 (ptr, 7); + write_16 (ptr, 2); + write_32 (ptr, 0); + write_8 (ptr, ptr_size); + } + assert (info + debug_sections[DEBUG_INFO].new_size == ptr); +} + +/* Adjust .debug_loc range determined by *SLOT, called through + htab_traverse. */ +static int +adjust_loclist (void **slot, void *data) +{ + struct debug_loc_adjust *adj = (struct debug_loc_adjust *) *slot; + unsigned char *ptr, *endsec; + GElf_Addr low, high; + size_t len; + + (void)data; + + ptr = debug_sections[DEBUG_LOC].new_data + adj->start_offset; + endsec = ptr + debug_sections[DEBUG_LOC].size; + while (ptr < endsec) + { + low = read_size (ptr, ptr_size); + high = read_size (ptr + ptr_size, ptr_size); + ptr += 2 * ptr_size; + if (low == 0 && high == 0) + break; + + if (low == ~ (GElf_Addr) 0 || (ptr_size == 4 && low == 0xffffffff)) + continue; + + len = read_16 (ptr); + assert (ptr + len <= endsec); + + adjust_exprloc (adj->cu, adj->cu->cu_die, adj->cu, adj->cu->cu_die, + ptr, len); + + ptr += len; + } + + return 1; +} + +/* Adjust .debug_loclists range determined by *SLOT, called through + htab_traverse. */ +static int +adjust_loclists (void **slot, void *data) +{ + struct debug_loc_adjust *adj = (struct debug_loc_adjust *) *slot; + unsigned char *ptr, *endsec; + size_t len = 0; + + (void)data; + + ptr = debug_sections[DEBUG_LOCLISTS].new_data + adj->start_offset; + endsec = ptr + debug_sections[DEBUG_LOCLISTS].size; + +again: + while (ptr < endsec) + { + uint8_t lle = *ptr++; + switch (lle) + { + case DW_LLE_end_of_list: + goto done; + + case DW_LLE_base_addressx: + skip_leb128 (ptr); + goto again; + + case DW_LLE_startx_endx: + skip_leb128 (ptr); + skip_leb128 (ptr); + len = read_uleb128 (ptr); + break; + + case DW_LLE_startx_length: + skip_leb128 (ptr); + skip_leb128 (ptr); + len = read_uleb128 (ptr); + break; + + case DW_LLE_offset_pair: + skip_leb128 (ptr); + skip_leb128 (ptr); + len = read_uleb128 (ptr); + break; + + case DW_LLE_default_location: + len = read_uleb128 (ptr); + break; + + case DW_LLE_base_address: + ptr += ptr_size; + goto again; + + case DW_LLE_start_end: + ptr += 2 * ptr_size; + len = read_uleb128 (ptr); + break; + + case DW_LLE_start_length: + ptr += ptr_size; + skip_leb128 (ptr); + len = read_uleb128 (ptr); + break; + + case DW_LLE_GNU_view_pair: + /* Note we cannot check CU version here, but we'll get a + warning on the original parsing if CU version is not 5.*/ + skip_leb128 (ptr); + skip_leb128 (ptr); + goto again; + + default: + error (0, 0, "unhandled location list entry 0x%x", lle); + return 1; + } + + assert (ptr + len <= endsec); + + adjust_exprloc (adj->cu, adj->cu->cu_die, adj->cu, adj->cu->cu_die, + ptr, len); + + ptr += len; + } + +done: + return 1; +} + +/* Create new .debug_loc section in malloced memory if .debug_loc + needs to be adjusted. */ +static void +write_loc (void) +{ + unsigned char *loc; + if (loc_htab == NULL) + return; + loc = malloc (debug_sections[DEBUG_LOC].size); + if (loc == NULL) + dwz_oom (); + memcpy (loc, debug_sections[DEBUG_LOC].data, debug_sections[DEBUG_LOC].size); + debug_sections[DEBUG_LOC].new_data = loc; + htab_traverse (loc_htab, adjust_loclist, NULL); +} + +/* Create new .debug_loclists section in malloced memory if .debug_loclists + needs to be adjusted. */ +static void +write_loclists (void) +{ + unsigned char *loc; + if (loclists_htab == NULL) + return; + loc = malloc (debug_sections[DEBUG_LOCLISTS].size); + if (loc == NULL) + dwz_oom (); + memcpy (loc, debug_sections[DEBUG_LOCLISTS].data, + debug_sections[DEBUG_LOCLISTS].size); + debug_sections[DEBUG_LOCLISTS].new_data = loc; + htab_traverse (loclists_htab, adjust_loclists, NULL); +} + +/* Create new .debug_types section in malloced memory. */ +static void +write_types (void) +{ + dw_cu_ref cu; + unsigned char *types, *ptr, *inptr; + dw_die_ref ref; + + if (debug_sections[DEBUG_TYPES].data == NULL) + return; + types = malloc (debug_sections[DEBUG_TYPES].new_size); + if (types == NULL) + dwz_oom (); + debug_sections[DEBUG_TYPES].new_data = types; + ptr = types; + for (cu = first_cu; cu; cu = cu->cu_next) + { + unsigned long next_off = debug_sections[DEBUG_TYPES].new_size; + /* Ignore .debug_info CUs. */ + if (cu->cu_kind != CU_TYPES) + continue; + if (cu->cu_next) + next_off = cu->cu_next->cu_new_offset; + if (unlikely (low_mem) + && expand_children (cu->cu_die)) + recompute_abbrevs (cu, next_off - cu->cu_new_offset); + /* Write CU header. */ + write_32 (ptr, next_off - cu->cu_new_offset - 4); + write_16 (ptr, cu->cu_version); + write_32 (ptr, cu->u2.cu_new_abbrev_offset); + write_8 (ptr, ptr_size); + inptr = debug_sections[DEBUG_TYPES].data + cu->cu_offset + 19; + memcpy (ptr, inptr - 8, 8); + ptr += 8; + ref = off_htab_lookup (cu, cu->cu_offset + read_32 (inptr)); + assert (ref && die_safe_dup(ref) == NULL); + write_32 (ptr, ref->u.p2.die_new_offset); + ptr = write_die (ptr, cu, cu->cu_die, NULL, NULL, NULL); + assert (types + next_off == ptr); + if (unlikely (low_mem)) + collapse_children (cu, cu->cu_die); + } + assert (types + debug_sections[DEBUG_TYPES].new_size == ptr); +} + +/* Construct new .debug_aranges section in malloced memory, + store it to debug_sections[DEBUG_ARANGES].new_data. */ +static int +write_aranges (DSO *dso) +{ + dw_cu_ref cu, cufirst = NULL, cucur; + unsigned char *aranges, *ptr, *end; + + if (debug_sections[DEBUG_ARANGES].data == NULL) + return 0; + + aranges = malloc (debug_sections[DEBUG_ARANGES].size); + if (aranges == NULL) + dwz_oom (); + memcpy (aranges, debug_sections[DEBUG_ARANGES].data, + debug_sections[DEBUG_ARANGES].size); + debug_sections[DEBUG_ARANGES].new_data = aranges; + ptr = aranges; + end = aranges + debug_sections[DEBUG_ARANGES].size; + for (cu = first_cu; cu; cu = cu->cu_next) + if (cu->cu_kind != CU_PU) + break; + cufirst = cu; + while (ptr < end) + { + unsigned int culen, value, cuoff; + + if (end - ptr < 12) + { + error (0, 0, "%s: Corrupted .debug_aranges section", + dso->filename); + return 1; + } + culen = read_32 (ptr); + if (culen >= 0xfffffff0) + { + error (0, 0, "%s: 64-bit DWARF not supported", dso->filename); + return 1; + } + + value = read_16 (ptr); + if (value != 2) + { + error (0, 0, "%s: DWARF version %d in .debug_aranges unhandled", + dso->filename, value); + return 1; + } + + cuoff = read_32 (ptr); + cucur = cu; + /* Optimistically assume that .debug_aranges CU offsets only increase, + otherwise this might be too expensive and need a hash table. */ + for (; cu; cu = cu->cu_next) + { + if (cu->cu_kind == CU_TYPES) + { + cu = NULL; + break; + } + else if (cu->cu_offset == cuoff) + break; + } + if (cu == NULL) + { + for (cu = cufirst; cu != cucur; cu = cu->cu_next) + if (cu->cu_offset == cuoff) + break; + if (cu == cucur) + { + error (0, 0, "%s: Couldn't find CU for .debug_aranges " + "offset 0x%x", dso->filename, cuoff); + return 1; + } + } + if (unlikely (fi_multifile) + && cu->cu_kind == CU_NORMAL + && cu->cu_die->die_remove) + { + error (0, 0, "%s: Partial unit referenced in .debug_aranges", + dso->filename); + return 1; + } + ptr -= 4; + write_32 (ptr, cu->cu_new_offset); + ptr += culen - 6; + } + return 0; +} + +/* Helper function of write_gdb_index, called through qsort. + Sort an array of unsigned integer pairs, by increasing + first integer. The first integer is the TU offset + in the .gdb_index TU table, the second is its index in + the TU table from the start of that table. */ +static int +gdb_index_tu_cmp (const void *p, const void *q) +{ + unsigned int *t1 = (unsigned int *) p; + unsigned int *t2 = (unsigned int *) q; + + if (t1[0] < t2[0]) + return -1; + if (t1[0] > t2[0]) + return 1; + + if (t1[1] < t2[1]) + return -1; + if (t1[1] > t2[1]) + return 1; + return 0; +} + +/* Construct new .gdb_index section in malloced memory + if it needs adjustment. */ +static void +write_gdb_index (void) +{ + dw_cu_ref cu, cu_next, first_tu = NULL; + unsigned char *gdb_index, *ptr, *inptr, *end; + unsigned int ncus = 0, npus = 0, ntus = 0, ndelcus = 0, ver; + unsigned int culistoff, cutypesoff, addressoff, symboloff, constoff; + unsigned int *tuindices = NULL, tuidx = 0, *cumap = NULL, i, j, k; + bool fail = false; + + debug_sections[GDB_INDEX].new_size = 0; + if (likely (!op_multifile) + && (debug_sections[GDB_INDEX].data == NULL + || debug_sections[GDB_INDEX].size < 0x18)) + return; + inptr = (unsigned char *) debug_sections[GDB_INDEX].data; + if (unlikely (op_multifile)) + ver = multi_gdb_index_ver; + else + ver = buf_read_ule32 (inptr); + if (ver < 4 || ver > 8) + return; + + for (cu = first_cu; cu; cu = cu->cu_next) + if (cu->cu_kind == CU_PU) + npus++; + else if (cu->cu_kind == CU_NORMAL) + { + ncus++; + if (unlikely (fi_multifile) && cu->cu_die->die_remove) + ndelcus++; + } + else if (cu->cu_kind == CU_TYPES) + ntus++; + + /* Starting with version 7 CU indexes are limited to 24 bits, + so if we have more CUs, give up. */ + if (npus + ncus + ntus - ndelcus >= (1U << 24)) + return; + + if (unlikely (op_multifile)) + { + assert (ncus == 0 && ntus == 0); + debug_sections[GDB_INDEX].new_size + = 0x18 + npus * 16 + 16; + gdb_index = malloc (debug_sections[GDB_INDEX].new_size); + if (gdb_index == NULL) + dwz_oom (); + debug_sections[GDB_INDEX].new_data = gdb_index; + /* Write new header. */ + buf_write_le32 (gdb_index + 0x00, ver); + buf_write_le32 (gdb_index + 0x04, 0x18); + buf_write_le32 (gdb_index + 0x08, 0x18 + npus * 16); + buf_write_le32 (gdb_index + 0x0c, 0x18 + npus * 16); + buf_write_le32 (gdb_index + 0x10, 0x18 + npus * 16); + buf_write_le32 (gdb_index + 0x14, 0x18 + npus * 16 + 16); + ptr = gdb_index + 0x18; + /* Write new CU list. */ + for (cu = first_cu; cu; cu = cu->cu_next) + { + unsigned long next_off = debug_sections[DEBUG_INFO].new_size; + if (cu->cu_next) + next_off = cu->cu_next->cu_new_offset; + buf_write_le64 (ptr, cu->cu_new_offset); + buf_write_le64 (ptr + 8, next_off - cu->cu_new_offset); + ptr += 16; + } + /* Write an empty hash table (with two entries). */ + memset (ptr, '\0', 16); + return; + } + + culistoff = buf_read_ule32 (inptr + 0x04); + cutypesoff = buf_read_ule32 (inptr + 0x08); + addressoff = buf_read_ule32 (inptr + 0x0c); + symboloff = buf_read_ule32 (inptr + 0x10); + constoff = buf_read_ule32 (inptr + 0x14); + if (culistoff != 0x18 + || cutypesoff != 0x18 + ncus * 16 + || addressoff != cutypesoff + ntus * 24 + || symboloff < addressoff + || ((symboloff - addressoff) % 20) != 0 + || constoff < symboloff + || ((constoff - symboloff) & (constoff - symboloff - 1)) != 0 + || ((constoff - symboloff) & 7) != 0 + || debug_sections[GDB_INDEX].size < constoff) + return; + inptr += 0x18; + if (ndelcus) + cumap = (unsigned int *) + obstack_alloc (&ob2, ncus * sizeof (unsigned int)); + for (cu = first_cu, i = 0, j = 0; cu; cu = cu->cu_next) + if (cu->cu_kind == CU_NORMAL) + { + if (buf_read_ule64 (inptr) != cu->cu_offset) + { + if (cumap) + obstack_free (&ob2, (void *) cumap); + return; + } + inptr += 16; + if (cumap) + { + if (cu->cu_die->die_remove) + cumap[i++] = -1U; + else + cumap[i++] = j++; + } + } + else if (cu->cu_kind == CU_TYPES) + { + if (tuindices == NULL) + { + tuindices = (unsigned int *) + obstack_alloc (&ob2, ntus * 2 * sizeof (unsigned int)); + first_tu = cu; + } + tuindices[2 * tuidx] = buf_read_ule64 (inptr); + tuindices[2 * tuidx + 1] = tuidx * 24; + tuidx++; + inptr += 24; + } + if (ntus) + { + qsort (tuindices, ntus, 2 * sizeof (unsigned int), gdb_index_tu_cmp); + for (tuidx = 0, cu = first_tu; tuidx < ntus; tuidx++, cu = cu->cu_next) + if (tuindices[2 * tuidx] != cu->cu_offset) + { + if (cumap) + obstack_free (&ob2, (void *) cumap); + else + obstack_free (&ob2, (void *) tuindices); + return; + } + } + + if (multifile + && !fi_multifile + && !low_mem + && multi_gdb_index_ver < ver) + multi_gdb_index_ver = ver; + + debug_sections[GDB_INDEX].new_size + = debug_sections[GDB_INDEX].size + npus * 16 - ndelcus * 16; + gdb_index = malloc (debug_sections[GDB_INDEX].new_size); + if (gdb_index == NULL) + dwz_oom (); + debug_sections[GDB_INDEX].new_data = gdb_index; + /* Write new header. */ + buf_write_le32 (gdb_index + 0x00, ver); + buf_write_le32 (gdb_index + 0x04, culistoff); + buf_write_le32 (gdb_index + 0x08, cutypesoff + npus * 16 - ndelcus * 16); + buf_write_le32 (gdb_index + 0x0c, addressoff + npus * 16 - ndelcus * 16); + buf_write_le32 (gdb_index + 0x10, symboloff + npus * 16 - ndelcus * 16); + buf_write_le32 (gdb_index + 0x14, constoff + npus * 16 - ndelcus * 16); + ptr = gdb_index + 0x18; + /* Write new CU list. */ + for (cu = first_cu; cu; cu = cu_next) + { + unsigned long next_off = debug_sections[DEBUG_INFO].new_size; + if (cu->cu_kind == CU_TYPES) + break; + cu_next = cu->cu_next; + if (unlikely (fi_multifile)) + { + while (cu_next + && cu_next->cu_kind == CU_NORMAL + && cu_next->cu_die->die_remove) + cu_next = cu_next->cu_next; + if (cu->cu_die->die_remove) + continue; + } + if (cu_next && cu_next->cu_kind != CU_TYPES) + next_off = cu_next->cu_new_offset; + buf_write_le64 (ptr, cu->cu_new_offset); + buf_write_le64 (ptr + 8, next_off - cu->cu_new_offset); + ptr += 16; + } + /* Write new TU list. */ + for (tuidx = 0; cu; cu = cu->cu_next, tuidx++) + { + unsigned char *p; + unsigned int tuoff = tuindices[2 * tuidx + 1]; + dw_die_ref ref; + assert (cu->cu_kind == CU_TYPES); + buf_write_le64 (ptr + tuoff, cu->cu_new_offset); + p = debug_sections[DEBUG_TYPES].data + cu->cu_offset + 19; + ref = off_htab_lookup (cu, cu->cu_offset + read_32 (p)); + assert (ref && ref->die_dup == NULL); + buf_write_le64 (ptr + tuoff + 8, ref->u.p2.die_new_offset); + p -= 12; + buf_write_le64 (ptr + tuoff + 16, read_64 (p)); + } + ptr += ntus * 24; + end = inptr + (symboloff - addressoff); + /* Copy address area, adjusting all CU indexes. */ + while (inptr < end) + { + memcpy (ptr, inptr, 16); + i = buf_read_ule32 (inptr + 16); + if (cumap && i < ncus) + { + if (cumap[i] == -1U) + fail = true; + i = cumap[i] + npus; + } + else + i += npus - ndelcus; + buf_write_le32 (ptr + 16, i); + ptr += 20; + inptr += 20; + } + /* Copy the symbol hash table. */ + memcpy (ptr, inptr, constoff - symboloff); + /* Clear the const pool initially. */ + memset (ptr + (constoff - symboloff), '\0', + debug_sections[GDB_INDEX].size - constoff); + ptr = ptr + (constoff - symboloff); + end = inptr + (constoff - symboloff); + /* Finally copy over const objects into the const pool, strings as is, + CU vectors with CU indexes adjusted. */ + while (inptr < end) + { + unsigned int name = buf_read_ule32 (inptr); + unsigned int cuvec = buf_read_ule32 (inptr + 4); + + inptr += 8; + if (name == 0 && cuvec == 0) + continue; + if (name > debug_sections[GDB_INDEX].size - constoff - 1 + || cuvec > debug_sections[GDB_INDEX].size - constoff - 4) + { + fail: + free (gdb_index); + debug_sections[GDB_INDEX].new_size = 0; + return; + } + if (ptr[name] == '\0') + { + unsigned char *strend = end + name; + while (*strend != '\0') + { + if (strend + 1 + == end + (debug_sections[GDB_INDEX].size - constoff)) + goto fail; + strend++; + } + memcpy (ptr + name, end + name, strend + 1 - (end + name)); + } + if (buf_read_ule32 (ptr + cuvec) == 0) + { + unsigned int count = buf_read_ule32 (end + cuvec); + if (count * 4 + > debug_sections[GDB_INDEX].size - constoff - cuvec - 4) + goto fail; + buf_write_le32 (ptr + cuvec, count); + for (i = 0; i < count; i++) + { + j = buf_read_ule32 (end + cuvec + (i + 1) * 4); + if (ver >= 7) + k = j & ((1U << 24) - 1); + else + k = j; + if (cumap && k < ncus) + { + if (cumap[k] == -1U) + fail = true; + k = cumap[k] + npus; + } + else + k += npus - ndelcus; + if (ver >= 7) + j = (j & (~0U << 24)) | k; + else + j = k; + buf_write_le32 (ptr + cuvec + (i + 1) * 4, j); + } + } + } + if (cumap) + obstack_free (&ob2, (void *) cumap); + else if (tuindices) + obstack_free (&ob2, (void *) tuindices); + if (fail) + { + free (debug_sections[GDB_INDEX].new_data); + debug_sections[GDB_INDEX].new_data = NULL; + debug_sections[GDB_INDEX].new_size = 0; + } +} + +/* Return a string from section SEC at offset OFFSET. */ +static const char * +strptr (DSO *dso, int sec, off_t offset) +{ + Elf_Scn *scn; + Elf_Data *data; + + scn = dso->scn[sec]; + if (offset >= 0 && (GElf_Addr) offset < dso->shdr[sec].sh_size) + { + data = NULL; + while ((data = elf_rawdata (scn, data)) != NULL) + { + if (data->d_buf + && offset >= data->d_off + && offset < (off_t) (data->d_off + data->d_size)) + return (const char *) data->d_buf + (offset - data->d_off); + } + } + + return NULL; +} + +/* Initialize do_read_* and do_write_* callbacks based on + ENDIANITY. */ +static void +init_endian (int endianity) +{ + if (endianity == ELFDATA2LSB) + { + do_read_16 = buf_read_ule16; + do_read_32 = buf_read_ule32; + do_read_64 = buf_read_ule64; + do_write_16 = buf_write_le16; + do_write_32 = buf_write_le32; + do_write_64 = buf_write_le64; + } + else if (endianity == ELFDATA2MSB) + { + do_read_16 = buf_read_ube16; + do_read_32 = buf_read_ube32; + do_read_64 = buf_read_ube64; + do_write_16 = buf_write_be16; + do_write_32 = buf_write_be32; + do_write_64 = buf_write_be64; + } + else + abort (); +} + +/* Read DWARF sections from DSO. */ +static int +read_dwarf (DSO *dso, bool quieter, unsigned int *die_count) +{ + Elf_Data *data; + Elf_Scn *scn; + int i, j; + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + if (! (dso->shdr[i].sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR)) + && dso->shdr[i].sh_size) + { + const char *name = strptr (dso, dso->ehdr.e_shstrndx, + dso->shdr[i].sh_name); + + if (strncmp (name, ".debug_", sizeof (".debug_") - 1) == 0 + || strcmp (name, ".gdb_index") == 0 + || strcmp (name, ".gnu_debugaltlink") == 0) + { + if (dso->shdr[i].sh_flags & SHF_COMPRESSED) + { + error (0, 0, + "%s: Found compressed %s section, not attempting dwz" + " compression", + dso->filename, name); + return 1; + } + for (j = 0; debug_sections[j].name; ++j) + if (strcmp (name, debug_sections[j].name) == 0) + { + if (debug_sections[j].data) + { + error (0, 0, "%s: Found two copies of %s section", + dso->filename, name); + return 1; + } + + scn = dso->scn[i]; + data = elf_rawdata (scn, NULL); + assert (data != NULL); + if (data->d_buf == NULL) + { + error (0, 0, "%s: Found empty %s section, not attempting" + " dwz compression", dso->filename, name); + return 1; + } + assert (elf_rawdata (scn, data) == NULL); + assert (data->d_off == 0); + assert (data->d_size == dso->shdr[i].sh_size); + debug_sections[j].data = data->d_buf; + debug_sections[j].size = data->d_size; + debug_sections[j].new_size = data->d_size; + debug_sections[j].sec = i; + break; + } + + if (debug_sections[j].name == NULL) + { + error (0, 0, "%s: Unknown debugging section %s", + dso->filename, name); + return 1; + } + } + } + + if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB + || dso->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) + init_endian (dso->ehdr.e_ident[EI_DATA]); + else + { + error (0, 0, "%s: Wrong ELF data encoding", dso->filename); + return 1; + } + + if (debug_sections[DEBUG_INFO].data == NULL + && !rd_multifile) + { + if (!quieter) + error (0, 0, "%s: .debug_info section not present", + dso->filename); + return 3; + } + + if (debug_sections[DEBUG_SUP].data != NULL && !(dwarf_5 && rd_multifile)) + { + error (0, 0, "%s: .debug_sup section already present", + dso->filename); + return 1; + } + + if (debug_sections[GNU_DEBUGALTLINK].data != NULL) + { + error (0, 0, "%s: .gnu_debugaltlink section already present", + dso->filename); + return 1; + } + + int res; + res = read_debug_info (dso, DEBUG_INFO, die_count); + if (res == 0 && stats_p) + print_parse_stats (); + return res; +} + +/* Open an ELF file NAME. */ +static DSO * +fdopen_dso (int fd, const char *name) +{ + Elf *elf = NULL; + GElf_Ehdr ehdr; + int i; + DSO *dso = NULL; + + elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (elf == NULL) + { + error (0, 0, "cannot open ELF file: %s", elf_errmsg (-1)); + goto error_out; + } + + if (elf_kind (elf) != ELF_K_ELF) + { + error (0, 0, "\"%s\" is not an ELF file", name); + goto error_out; + } + + if (gelf_getehdr (elf, &ehdr) == NULL) + { + error (0, 0, "cannot get the ELF header: %s", + elf_errmsg (-1)); + goto error_out; + } + + if (ehdr.e_type != ET_DYN && ehdr.e_type != ET_EXEC + && (!rd_multifile || ehdr.e_type != ET_REL)) + { + error (0, 0, "\"%s\" is not a shared library", name); + goto error_out; + } + + /* Allocate DSO structure. */ + dso = (DSO *) + malloc (sizeof(DSO) + ehdr.e_shnum * sizeof(GElf_Shdr) + + ehdr.e_shnum * sizeof(Elf_Scn *) + + strlen (name) + 1); + if (!dso) + { + error (0, ENOMEM, "Could not open DSO"); + goto error_out; + } + + elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT | ELF_F_PERMISSIVE); + + memset (dso, 0, sizeof(DSO)); + dso->elf = elf; + dso->ehdr = ehdr; + dso->scn = (Elf_Scn **) &dso->shdr[ehdr.e_shnum]; + + for (i = 0; i < ehdr.e_shnum; ++i) + { + dso->scn[i] = elf_getscn (elf, i); + gelf_getshdr (dso->scn[i], dso->shdr + i); + } + + dso->filename = (const char *) &dso->scn[ehdr.e_shnum]; + strcpy ((char *) &dso->scn[ehdr.e_shnum], name); + return dso; + +error_out: + free (dso); + if (elf) + elf_end (elf); + if (fd != -1) + close (fd); + return NULL; +} + +/* Implicit arg for compare_section_numbers. Could be passed in as explicit arg + when using qsort_r instead. */ +static DSO *compare_section_numbers_implicit_arg; + +/* Helper function for sort_section_numbers. */ +static int +compare_section_numbers (const void *p1, const void *p2) +{ + DSO *dso = compare_section_numbers_implicit_arg; + const int i1 = *(const int *)p1; + const int i2 = *(const int *)p2; + GElf_Off o1; + GElf_Off o2; + + /* Keep element 0 at 0. */ + if (i1 == 0 || i2 == 0) + { + if (i1 == i2) + return 0; + if (i1 == 0) + return -1; + if (i2 == 0) + return 1; + } + + /* Get file offsets. */ + o1 = (i1 == dso->ehdr.e_shnum + ? dso->ehdr.e_shoff + : dso->shdr[i1].sh_offset); + o2 = (i2 == dso->ehdr.e_shnum + ? dso->ehdr.e_shoff + : dso->shdr[i2].sh_offset); + + /* Compare file offsets. */ + if (o1 < o2) + return -1; + if (o1 > o2) + return 1; + + /* In case file offset is the same, keep the original relative order. */ + if (i1 < i2) + return -1; + if (i1 > i2) + return 1; + + return 0; +} + +/* Sort SORTED_SECTION_NUMBERS in file offset order. */ +static void +sort_section_numbers (DSO *dso, unsigned int *sorted_section_numbers) +{ + unsigned int i; + unsigned int nr_sections = dso->ehdr.e_shnum; + + /* Treat section header table as another section, with index + dso->ehdr.e_shnum. */ + nr_sections += 1; + + for (i = 0; i < nr_sections; ++i) + sorted_section_numbers[i] = i; + + compare_section_numbers_implicit_arg = dso; + qsort (sorted_section_numbers, nr_sections, + sizeof (sorted_section_numbers[0]), compare_section_numbers); + compare_section_numbers_implicit_arg = NULL; + + assert (sorted_section_numbers[0] == 0); +} + +/* Verify file offset and size of sections and section header table. */ +static void +verify_sections (DSO *dso, unsigned int *sorted_section_numbers, + GElf_Off *distance, int addsec, GElf_Off addsize, + GElf_Ehdr ehdr) +{ + int i, j; + int prev, update_prev; + GElf_Off offset, prev_offset, prev_size; + GElf_Off section_header_table_size + = dso->ehdr.e_shentsize * ehdr.e_shnum; + + prev = -1; + for (i = 1; i < (dso->ehdr.e_shnum + 1); ++i, prev = update_prev) + { + j = sorted_section_numbers[i]; + + if (j != dso->ehdr.e_shnum && dso->shdr[j].sh_type == SHT_NOBITS) + { + update_prev = prev; + continue; + } + update_prev = j; + + if (prev == -1) + continue; + + offset = (j == dso->ehdr.e_shnum + ? ehdr.e_shoff + : dso->shdr[j].sh_offset); + + prev_offset = (prev == dso->ehdr.e_shnum + ? ehdr.e_shoff + : dso->shdr[prev].sh_offset); + + prev_size = (prev == dso->ehdr.e_shnum + ? section_header_table_size + : (dso->shdr[prev].sh_type == SHT_NOBITS + ? 0 + : dso->shdr[prev].sh_size)); + + if (distance != NULL) + assert ((prev_offset + prev_size + distance[prev] + + (prev == addsec ? addsize : 0)) + == offset); + else + assert ((prev_offset + prev_size + (prev == addsec ? addsize : 0)) + <= offset); + } +} + +/* Calculate distance between sections and section header table. */ +static int +calculate_section_distance (DSO *dso, unsigned int *sorted_section_numbers, + GElf_Off *distance) +{ + int i, j; + int prev, update_prev; + GElf_Off offset, prev_offset, prev_size; + GElf_Off section_header_table_size + = dso->ehdr.e_shentsize * dso->ehdr.e_shnum; + + prev = -1; + for (i = 1; i < (dso->ehdr.e_shnum + 1); ++i, prev = update_prev) + { + j = sorted_section_numbers[i]; + + if (j != dso->ehdr.e_shnum && dso->shdr[j].sh_type == SHT_NOBITS) + { + update_prev = prev; + continue; + } + update_prev = j; + + if (prev == -1) + continue; + + offset = (j == dso->ehdr.e_shnum + ? dso->ehdr.e_shoff + : dso->shdr[j].sh_offset); + + prev_offset = (prev == dso->ehdr.e_shnum + ? dso->ehdr.e_shoff + : dso->shdr[prev].sh_offset); + + prev_size = (prev == dso->ehdr.e_shnum + ? section_header_table_size + : dso->shdr[prev].sh_size); + + if (prev_offset + prev_size > offset) + { + error (0, 0, "Section overlap detected"); + if (prev == dso->ehdr.e_shnum) + error (0, 0, "Section header table: [0x%llx, 0x%llx)", + (unsigned long long)prev_offset, + (unsigned long long)(prev_offset + prev_size)); + else + error (0, 0, "Section %d: [0x%llx, 0x%llx)", j, + (unsigned long long)prev_offset, + (unsigned long long)(prev_offset + prev_size)); + if (j == dso->ehdr.e_shnum) + error (0, 0, "Section header table: 0x%llx", + (unsigned long long)offset); + else + error (0, 0, "Section %d: 0x%llx", j, (unsigned long long)offset); + return 1; + } + + distance[prev] = offset - (prev_offset + prev_size); + } + + verify_sections (dso, sorted_section_numbers, distance, -1, 0, dso->ehdr); + + return 0; +} + +/* Store new ELF into FILE. debug_sections array contains + new_data/new_size pairs where needed. */ +static int +write_dso (DSO *dso, const char *file, struct stat *st, bool save_to_temp) +{ + Elf *elf = NULL; + GElf_Ehdr ehdr; + GElf_Off min_shoff = ~(GElf_Off) 0; + char *e_ident; + int fd, i, j, addsec = -1, ret; + GElf_Off off, diff, addsize = 0; + char *filename = NULL; + GElf_Word shstrtabadd = 0; + char *shstrtab = NULL; + bool remove_sections[SECTION_COUNT]; + GElf_Off distance[dso->ehdr.e_shnum + 1]; + /* Array of sections and section header table sorted by file offset. */ + unsigned int sorted_section_numbers[dso->ehdr.e_shnum + 1]; + GElf_Off old_sh_offset[dso->ehdr.e_shnum]; + + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "write_dso\n"); + } + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + old_sh_offset[i] = dso->shdr[i].sh_offset; + + memset (remove_sections, '\0', sizeof (remove_sections)); + ehdr = dso->ehdr; + + sort_section_numbers (dso, sorted_section_numbers); + if (calculate_section_distance (dso, sorted_section_numbers, distance)) + return 1; + + for (i = 0; debug_sections[i].name; i++) + if (debug_sections[i].new_size != debug_sections[i].size) + { + if (debug_sections[i].size == 0 + && debug_sections[i].sec == 0) + { + unsigned int len; + if (addsec == -1) + for (j = 0; debug_sections[j].name; j++) + if (debug_sections[j].new_size + && debug_sections[j].sec + && debug_sections[j].sec > addsec) + addsec = debug_sections[j].sec; + ehdr.e_shnum++; + if (ehdr.e_shoff < min_shoff) + min_shoff = ehdr.e_shoff; + for (j = 1; j < dso->ehdr.e_shnum; ++j) + { + if (dso->shdr[j].sh_offset > ehdr.e_shoff) + dso->shdr[j].sh_offset += ehdr.e_shentsize; + if (dso->shdr[j].sh_link > (unsigned int) addsec) + dso->shdr[j].sh_link++; + if ((dso->shdr[j].sh_type == SHT_REL + || dso->shdr[j].sh_type == SHT_RELA + || (dso->shdr[j].sh_flags & SHF_INFO_LINK)) + && dso->shdr[j].sh_info + > (unsigned int) addsec) + dso->shdr[j].sh_info++; + } + if (dso->ehdr.e_shstrndx > addsec) + ehdr.e_shstrndx++; + len = strlen (debug_sections[i].name) + 1; + dso->shdr[dso->ehdr.e_shstrndx].sh_size += len; + if (dso->shdr[dso->ehdr.e_shstrndx].sh_offset < min_shoff) + min_shoff = dso->shdr[dso->ehdr.e_shstrndx].sh_offset; + for (j = 1; j < dso->ehdr.e_shnum; ++j) + if (dso->shdr[j].sh_offset + > dso->shdr[dso->ehdr.e_shstrndx].sh_offset) + dso->shdr[j].sh_offset += len; + if (ehdr.e_shoff > dso->shdr[dso->ehdr.e_shstrndx].sh_offset) + ehdr.e_shoff += len; + shstrtabadd += len; + diff = debug_sections[i].new_size; + addsize += diff; + off = dso->shdr[addsec].sh_offset; + } + else + { + diff = (GElf_Off) debug_sections[i].new_size + - (GElf_Off) dso->shdr[debug_sections[i].sec].sh_size; + off = dso->shdr[debug_sections[i].sec].sh_offset; + } + if (off < min_shoff) + min_shoff = off; + for (j = 1; j < dso->ehdr.e_shnum; ++j) + if (dso->shdr[j].sh_offset > off) + dso->shdr[j].sh_offset += diff; + if (ehdr.e_shoff > off) + ehdr.e_shoff += diff; + dso->shdr[debug_sections[i].sec].sh_size + = debug_sections[i].new_size; + if (debug_sections[i].new_size == 0) + { + remove_sections[i] = true; + ehdr.e_shnum--; + if (ehdr.e_shoff < min_shoff) + min_shoff = ehdr.e_shoff; + for (j = 1; j < dso->ehdr.e_shnum; ++j) + { + if (dso->shdr[j].sh_offset > ehdr.e_shoff) + dso->shdr[j].sh_offset -= ehdr.e_shentsize; + if (dso->shdr[j].sh_link + > (unsigned int) debug_sections[i].sec) + dso->shdr[j].sh_link--; + if ((dso->shdr[j].sh_type == SHT_REL + || dso->shdr[j].sh_type == SHT_RELA + || (dso->shdr[j].sh_flags & SHF_INFO_LINK)) + && dso->shdr[j].sh_info + > (unsigned int) debug_sections[i].sec) + dso->shdr[j].sh_info--; + } + if (dso->ehdr.e_shstrndx > debug_sections[i].sec) + ehdr.e_shstrndx--; + } + } + + /* Verify that we did not change section layout, by checking that the + distances between sections and section header table remained the same. */ + verify_sections (dso, sorted_section_numbers, distance, addsec, addsize, + ehdr); + + if (min_shoff != ~(GElf_Off) 0) + { + for (j = 1; j < dso->ehdr.e_shnum; ++j) + if (dso->shdr[j].sh_offset >= min_shoff + && dso->shdr[j].sh_addralign > 1 + && (dso->shdr[j].sh_offset & (dso->shdr[j].sh_addralign - 1)) != 0) + break; + if (j < dso->ehdr.e_shnum + || (ehdr.e_shoff >= min_shoff + && (ehdr.e_shoff & (ehdr.e_ident[EI_CLASS] == ELFCLASS64 + ? 7 : 3)) != 0)) + { + /* Need to fix up sh_offset/e_shoff. Punt if all the sections + >= min_shoff aren't non-ALLOC. */ + GElf_Off last_shoff = 0; + int k = -1; + int l; + for (l = 1; l <= dso->ehdr.e_shnum; ++l) + { + j = sorted_section_numbers[l]; + if (j == dso->ehdr.e_shnum) + continue; + else if (!last_shoff + && (dso->shdr[j].sh_offset < min_shoff + || (dso->shdr[j].sh_offset == min_shoff + && (dso->shdr[j].sh_size == 0 + || dso->shdr[j].sh_type == SHT_NOBITS)))) + continue; + else if (dso->shdr[j].sh_type == SHT_NOBITS) + continue; + else if ((dso->shdr[j].sh_flags & SHF_ALLOC) != 0) + { + error (0, 0, "Allocatable section in %s after " + "non-allocatable ones", dso->filename); + return 1; + } + else + { + assert (dso->shdr[j].sh_offset >= last_shoff); + + if (k == -1) + k = l; + last_shoff = dso->shdr[j].sh_offset + dso->shdr[j].sh_size; + } + } + last_shoff = min_shoff; + for (l = k; l <= dso->ehdr.e_shnum; ++l) + { + j = sorted_section_numbers[l]; + if (j == dso->ehdr.e_shnum) + { + if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) + ehdr.e_shoff = (last_shoff + 7) & -8; + else + ehdr.e_shoff = (last_shoff + 3) & -4; + last_shoff = ehdr.e_shoff + ehdr.e_shnum * ehdr.e_shentsize; + continue; + } + /* Ignore SHT_NOBITS sections. */ + if (dso->shdr[j].sh_type == SHT_NOBITS) + continue; + dso->shdr[j].sh_offset = last_shoff; + if (dso->shdr[j].sh_addralign > 1) + dso->shdr[j].sh_offset + = (last_shoff + dso->shdr[j].sh_addralign - 1) + & ~(dso->shdr[j].sh_addralign - (GElf_Off) 1); + last_shoff = dso->shdr[j].sh_offset + dso->shdr[j].sh_size; + if (addsec != -1 && j == addsec) + last_shoff += addsize; + } + } + } + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + if (dso->shdr[i].sh_type == SHT_NOBITS) + dso->shdr[i].sh_offset = old_sh_offset[i]; + + verify_sections (dso, sorted_section_numbers, NULL, addsec, addsize, + ehdr); + + if (shstrtabadd != 0) + { + shstrtab = (char *) malloc (dso->shdr[dso->ehdr.e_shstrndx].sh_size); + if (shstrtab == NULL) + { + error (0, ENOMEM, "Failed to adjust .shstrtab for %s", + dso->filename); + return 1; + } + } + + if (file == NULL) + { + size_t len = strlen (dso->filename); + filename = alloca (len + sizeof (".#dwz#.XXXXXX")); + memcpy (filename, dso->filename, len); + memcpy (filename + len, ".#dwz#.XXXXXX", sizeof (".#dwz#.XXXXXX")); + fd = mkstemp (filename); + file = (const char *) filename; + if (fd == -1) + { + error (0, errno, "Failed to create temporary file for %s", + dso->filename); + free (shstrtab); + return 1; + } + } + else + { + fd = open (file, O_RDWR | O_CREAT, 0600); + if (fd == -1) + { + error (0, errno, "Failed to open %s for writing", file); + free (shstrtab); + return 1; + } + } + + elf = elf_begin (fd, ELF_C_WRITE, NULL); + if (elf == NULL) + { + error (0, 0, "cannot open ELF file: %s", elf_errmsg (-1)); + unlink (file); + close (fd); + free (shstrtab); + return 1; + } + + /* Some gelf_newehdr implementations don't return the resulting + ElfNN_Ehdr, so we have to do it the hard way instead of: + e_ident = (char *) gelf_newehdr (elf, gelf_getclass (dso->elf)); */ + switch (gelf_getclass (dso->elf)) + { + case ELFCLASS32: + e_ident = (char *) elf32_newehdr (elf); + break; + case ELFCLASS64: + e_ident = (char *) elf64_newehdr (elf); + break; + default: + e_ident = NULL; + break; + } + + if (e_ident == NULL + /* This is here just for the gelfx wrapper, so that gelf_update_ehdr + already has the correct ELF class. */ + || memcpy (e_ident, dso->ehdr.e_ident, EI_NIDENT) == NULL + || gelf_update_ehdr (elf, &ehdr) == 0 + || gelf_newphdr (elf, ehdr.e_phnum) == 0) + { + error (0, 0, "Could not create new ELF headers"); + unlink (file); + elf_end (elf); + close (fd); + free (shstrtab); + return 1; + } + elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT | ELF_F_PERMISSIVE); + for (i = 0; i < ehdr.e_phnum; ++i) + { + GElf_Phdr *phdr, phdr_mem; + phdr = gelf_getphdr (dso->elf, i, &phdr_mem); + gelf_update_phdr (elf, i, phdr); + } + + for (i = 1; i < dso->ehdr.e_shnum; ++i) + { + Elf_Scn *scn; + Elf_Data *data1, *data2; + + for (j = 0; debug_sections[j].name; j++) + if (i == debug_sections[j].sec) + break; + if (debug_sections[j].name && remove_sections[j]) + continue; + scn = elf_newscn (elf); + elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY); + gelf_update_shdr (scn, &dso->shdr[i]); + data1 = elf_getdata (dso->scn[i], NULL); + data2 = elf_newdata (scn); + memcpy (data2, data1, sizeof (*data1)); + if (debug_sections[j].name + && debug_sections[j].new_data != NULL) + { + data2->d_buf = debug_sections[j].new_data; + data2->d_size = dso->shdr[i].sh_size; + } + if (i == dso->ehdr.e_shstrndx && shstrtabadd) + { + memcpy (shstrtab, data1->d_buf, + dso->shdr[dso->ehdr.e_shstrndx].sh_size + - shstrtabadd); + data2->d_buf = shstrtab; + data2->d_size = dso->shdr[i].sh_size; + } + if (i == addsec) + { + GElf_Word sh_name = dso->shdr[dso->ehdr.e_shstrndx].sh_size + - shstrtabadd; + GElf_Shdr shdr; + + off = dso->shdr[i].sh_offset + dso->shdr[i].sh_size; + for (j = 0; debug_sections[j].name; j++) + if (debug_sections[j].new_size + && debug_sections[j].size == 0 + && debug_sections[j].sec == 0) + { + scn = elf_newscn (elf); + elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY); + memset (&shdr, '\0', sizeof (shdr)); + shdr.sh_name = sh_name; + sh_name += strlen (debug_sections[j].name) + 1; + strcpy (shstrtab + shdr.sh_name, debug_sections[j].name); + shdr.sh_type = SHT_PROGBITS; + shdr.sh_offset = off; + shdr.sh_size = debug_sections[j].new_size; + shdr.sh_addralign = 1; + off += shdr.sh_size; + gelf_update_shdr (scn, &shdr); + data2 = elf_newdata (scn); + data2->d_buf = debug_sections[j].new_data; + data2->d_type = ELF_T_BYTE; + data2->d_version = EV_CURRENT; + data2->d_size = shdr.sh_size; + data2->d_off = 0; + data2->d_align = 1; + } + } + } + + if (elf_update (elf, ELF_C_WRITE_MMAP) == -1) + { + error (0, 0, "%s: elf_update failed", dso->filename); + unlink (file); + elf_end (elf); + close (fd); + free (shstrtab); + return 1; + } + + if (elf_end (elf) < 0) + { + error (0, 0, "elf_end failed: %s", elf_errmsg (elf_errno ())); + unlink (file); + elf_end (elf); + close (fd); + free (shstrtab); + return 1; + } + + free (shstrtab); + ret = fchown (fd, st->st_uid, st->st_gid); + fchmod (fd, st->st_mode & 07777); + close (fd); + + if (filename != NULL && rename (filename, dso->filename)) + { + error (0, errno, "Failed to rename temporary file over %s", + dso->filename); + unlink (file); + /* | (ret & 1) to silence up __wur warning for fchown. */ + return 1 | (ret & 1); + } + if (save_to_temp) + { + const char *prefix = "dwz."; + size_t buf_len = strlen (prefix) + strlen (dso->filename) + 1; + char *buf = (char *)alloca (buf_len); + size_t offset = 0; + strcpy (&buf[offset], prefix); + offset += strlen (prefix); + strcpy (&buf[offset], dso->filename); + offset += strlen (dso->filename); + assert (offset == buf_len - 1); + assert (buf[offset] == '\0'); + unlink (buf); + if (link (dso->filename, buf) != 0) + { + error (0, errno, "Failed to link file: %s", dso->filename); + return 1; + } + } + return 0; +} + +/* Free memory and clear global variables. */ +static void +cleanup (void) +{ + dw_cu_ref cu; + unsigned int i; + + for (cu = first_cu; cu; cu = cu->cu_next) + { + if (cu->cu_new_abbrev) + htab_delete (cu->cu_new_abbrev); + cu->cu_new_abbrev = NULL; + } + if (off_htab != NULL) + { + if (tracing) + htab_report (off_htab, "off_htab final"); + htab_delete (off_htab); + } + off_htab = NULL; + if (types_off_htab != NULL) + htab_delete (types_off_htab); + types_off_htab = NULL; + if (loc_htab != NULL) + htab_delete (loc_htab); + loc_htab = NULL; + if (loclists_htab != NULL) + htab_delete (loclists_htab); + loclists_htab = NULL; + if (dup_htab != NULL) + htab_delete (dup_htab); + dup_htab = NULL; + if (strp_htab != NULL) + htab_delete (strp_htab); + strp_htab = NULL; + if (line_htab != NULL) + htab_delete (line_htab); + line_htab = NULL; + if (macro_htab != NULL) + htab_delete (macro_htab); + macro_htab = NULL; + if (meta_abbrev_htab != NULL) + htab_delete (meta_abbrev_htab); + meta_abbrev_htab = NULL; + + for (i = 0; i < SAVED_SECTIONS; ++i) + { + free (saved_new_data[i]); + saved_new_data[i] = NULL; + } + + obstack_free (&ob2, NULL); + obstack_free (&ob, NULL); + memset (&ob2, '\0', sizeof (ob2)); + memset (&ob, '\0', sizeof (ob2)); + die_nontoplevel_freelist = NULL; + die_collapsed_child_freelist = NULL; + pool_destroy (NULL); + first_cu = NULL; + last_cu = NULL; + ptr_size = 0; + max_nattr = 0; + do_read_16 = NULL; + do_read_32 = NULL; + do_read_64 = NULL; + do_write_16 = NULL; + do_write_32 = NULL; + do_write_64 = NULL; + edge_freelist = NULL; + multifile_mode = 0; + max_strp_off = 0; + max_line_id = 0; +} + +/* Propagate the die_no_multifile property along the duplicate chain of which + DIE is a member. If the property was changed on any die, set *CHANGED to + true. */ +static void +propagate_multifile_duplicate_chain (dw_die_ref die, bool *changed) +{ + dw_die_ref dup = first_dup (die); + if (!dup) + return; + + while (dup && dup->die_offset == -1U) + dup = dup->die_nextdup; + if (dup != die) + return; + + bool any_no_multifile = false; + bool any_multifile = false; + bool prop_needed = false; + dw_die_ref d; + for (d = dup; d && !prop_needed; d = d->die_nextdup) + { + if (d->die_no_multifile) + any_no_multifile = true; + else + any_multifile = true; + prop_needed = any_no_multifile && any_multifile; + } + if (!prop_needed) + return; + + *changed = true; + + for (d = dup; d; d = d->die_nextdup) + d->die_no_multifile = 1; +} + +/* Propagate the die_no_multifile property backwards along the outgoing + references of DIE, which is a member of CU and of the subtree of lower + toplevel die TOP_DIE. If the property was changed on any die, set *CHANGED + to true. */ +static void +propagate_multifile_refs_backward (dw_cu_ref cu, dw_die_ref top_die, + dw_die_ref die, bool *changed) +{ + struct abbrev_tag *t = die->die_abbrev; + unsigned int i; + unsigned char *ptr; + dw_die_ref child; + + if (die->die_offset == -1U) + return; + + ptr = debug_sections[DEBUG_INFO].data + die->die_offset; + skip_leb128 (ptr); + for (i = 0; i < t->nattr; ++i) + { + uint32_t form = t->attr[i].form; + uint64_t value; + dw_die_ref ref, reft; + + while (form == DW_FORM_indirect) + form = read_uleb128 (ptr); + + switch (form) + { + case DW_FORM_ref_addr: + value = read_size (ptr, cu->cu_version == 2 ? ptr_size : 4); + ptr += cu->cu_version == 2 ? ptr_size : 4; + ref = off_htab_lookup (cu, value); + goto finish_ref; + break; + case DW_FORM_ref_udata: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + switch (form) + { + case DW_FORM_ref_udata: value = read_uleb128 (ptr); break; + case DW_FORM_ref1: value = read_8 (ptr); break; + case DW_FORM_ref2: value = read_16 (ptr); break; + case DW_FORM_ref4: value = read_32 (ptr); break; + case DW_FORM_ref8: value = read_64 (ptr); break; + default: abort (); + } + if (t->attr[i].attr == DW_AT_sibling) + break; + ref = off_htab_lookup (cu, cu->cu_offset + value); + finish_ref: + reft = ref; + while (!reft->die_root + && reft->die_parent->die_tag != DW_TAG_compile_unit + && reft->die_parent->die_tag != DW_TAG_partial_unit + && !reft->die_parent->die_named_namespace) + reft = reft->die_parent; + if (reft->die_root) + ; + else if (reft->die_ck_state == CK_KNOWN + && !top_die->die_no_multifile && reft->die_no_multifile) + { + top_die->die_no_multifile = 1; + *changed = true; + } + break; + default: + ptr = skip_attr_no_dw_form_indirect (cu->cu_version, form, ptr); + } + } + + for (child = die->die_child; child; child = child->die_sib) + propagate_multifile_refs_backward (cu, top_die, child, changed); +} + +/* Do propagation of the die_no_multifile property that was not covered in + checksum_die and checksum_ref_die. */ +static void +propagate_multifile (void) +{ + bool changed; + dw_cu_ref cu; + dw_die_ref die; + + changed = false; + + FOREACH_CU_NORMAL_LOW_TOPLEVEL_DIE (cu, die) + propagate_multifile_duplicate_chain (die, &changed); + + if (!changed) + return; + + do + { + changed = false; + + FOREACH_CU_NORMAL_LOW_TOPLEVEL_DIE (cu, die) + propagate_multifile_refs_backward (cu, die, die, &changed); + + FOREACH_CU_NORMAL_LOW_TOPLEVEL_DIE (cu, die) + propagate_multifile_duplicate_chain (die, &changed); + } + while (changed); +} + +/* Returns true if DIE contains any toplevel children that can be + potentially shared between different executables or shared libraries. */ +static bool +check_multifile (dw_die_ref die) +{ + dw_die_ref child; + + die->die_no_multifile = 1; + for (child = die->die_child; child; child = child->die_sib) + if (child->die_named_namespace) + { + if (check_multifile (child)) + die->die_no_multifile = 0; + } + else if (child->die_offset == -1U) + { + if (child->die_nextdup && child->die_nextdup->die_dup == child) + { + if (child->die_nextdup->die_ck_state == CK_KNOWN + && child->die_nextdup->die_no_multifile == 0) + { + child->die_no_multifile = 0; + die->die_no_multifile = 0; + } + else + child->die_no_multifile = 1; + } + else + child->die_no_multifile = 1; + } + else + { + child->die_op_type_referenced = 0; + if (child->die_dup == NULL + && child->die_ck_state == CK_KNOWN + && child->die_no_multifile == 0) + die->die_no_multifile = 0; + else + child->die_no_multifile = 1; + } + return die->die_no_multifile == 0; +} + +/* Helper function for write_multifile_strp to sort strp_entry + by increasing new_off. */ +static int +strp_off_cmp (const void *p, const void *q) +{ + struct strp_entry *s1 = *(struct strp_entry **)p; + struct strp_entry *s2 = *(struct strp_entry **)q; + if (s1->new_off < s2->new_off) + return -1; + if (s1->new_off > s2->new_off) + return 1; + return 0; +} + +/* Write tail optimized strings into the temporary .debug_str file. */ +static int +write_multifile_strp (void) +{ + unsigned int count = htab_elements (strp_htab), i, buf_alloc, buf_size; + struct strp_entry **arr = (struct strp_entry **) + obstack_alloc (&ob, count * sizeof (*arr)); + struct strp_entry **end = arr; + unsigned char *buf; + int ret = 0; + + htab_traverse (strp_htab, list_strp_entries, (void *) &end); + assert (arr + count == end); + qsort (arr, count, sizeof (struct strp_entry *), strp_off_cmp); + buf_alloc = max_strp_off - debug_sections[DEBUG_STR].size; + if (buf_alloc > 131072) + buf_alloc = 131072; + buf = (unsigned char *) obstack_alloc (&ob, buf_alloc); + buf_size = 0; + for (i = 0; i < count; i++) + { + unsigned char *p = debug_sections[DEBUG_STR].data + arr[i]->off; + unsigned int len = strlen ((char *) p) + 1; + if (buf_alloc - buf_size < len) + { + if (buf_size + && write (multi_str_fd, buf, buf_size) != (ssize_t) buf_size) + { + ret = 1; + break; + } + buf_size = 0; + if (buf_alloc < len) + { + if (write (multi_str_fd, p, len) != (ssize_t) len) + { + ret = 1; + break; + } + continue; + } + } + memcpy (buf + buf_size, p, len); + buf_size += len; + } + if (buf_size + && ret == 0 + && write (multi_str_fd, buf, buf_size) != (ssize_t) buf_size) + ret = 1; + obstack_free (&ob, (void *) arr); + return ret; +} + +/* Hold some statistics on the line entries so we know whether to emit + time and/or sizes. Used by list_line_entries used by + write_multifile_line. */ +struct line_stats +{ + struct line_entry ***end; + bool has_time; + bool has_size; +}; + +/* Helper to find the end of the line_htab entries and other stats. + Called through htab_traverse. */ +static int +list_line_entries (void **slot, void *data) +{ + struct line_stats *stats = (struct line_stats *) data; + struct line_entry *entry = (struct line_entry *) *slot; + struct line_entry ***end = stats->end; + **end = entry; + (*end)++; + if (entry->file->time != 0) + stats->has_time = true; + if (entry->file->size != 0) + stats->has_size = true; + return 1; +} + +/* Helper function for write_multifile_strp to sort line_entry + by increasing new_id. */ +static int +line_id_cmp (const void *p, const void *q) +{ + struct line_entry *s1 = *(struct line_entry **)p; + struct line_entry *s2 = *(struct line_entry **)q; + if (s1->new_id < s2->new_id) + return -1; + if (s1->new_id > s2->new_id) + return 1; + return 0; +} + +/* Write a minimal .debug_line entry. If not op_multifile, write it + into the temporary .debug_line file (if line_htab is non-NULL, fill + its directory and file table from it, otherwise emit an entry + with no directories or files), if op_multifile, store the entry + into debug_sections[DEBUG_LINE].new_data which it allocates. */ +static int +write_multifile_line (void) +{ + unsigned int filecnt = 0, dircnt = 0, filetbllen = 0, dirtbllen = 0; + unsigned int header_len, len, i, j; + unsigned char *line, *ptr; + struct line_entry **filearr = NULL; + struct line_stats line_stats; + unsigned int *diridx = NULL, *dirarr = NULL; + unsigned char buf[45]; /* Max header_len, see below. */ + int ret = 0; + + line_stats.has_time = line_stats.has_size = false; + if (line_htab) + { + struct line_entry **end; + filecnt = htab_elements (line_htab); + filearr = (struct line_entry **) + obstack_alloc (&ob, filecnt * sizeof (*filearr)); + end = filearr; + line_stats.end = &end; + htab_traverse (line_htab, list_line_entries, (void *) &line_stats); + assert (filearr + filecnt == end); + diridx = (unsigned int *) + obstack_alloc (&ob, filecnt * sizeof (*diridx)); + qsort (filearr, filecnt, sizeof (struct line_entry *), line_id_cmp); + for (i = 0; i < filecnt; i++) + { + unsigned int direntrylen = 0; + const char *file = filearr[i]->file->file; + if (filearr[i]->file->dir == NULL) + { + const char *r = strrchr (file, '/'), *s; + + j = 0; + direntrylen = r ? r - file : 0; + while (direntrylen && file[direntrylen - 1] == '/') + direntrylen--; + if (direntrylen) + { + direntrylen++; + for (j = 0; j < dircnt; j++) + if (filearr[dirarr[j]]->file->dir == NULL + && strncmp (filearr[dirarr[j]]->file->file, + file, direntrylen) == 0) + { + s = filearr[dirarr[j]]->file->file + direntrylen; + while (*s == '/') + s++; + if (strchr (s, '/')) + continue; + break; + } + j++; + file = r + 1; + } + } + else + { + for (j = 0; j < dircnt; j++) + if (filearr[dirarr[j]]->file->dir + && strcmp (filearr[dirarr[j]]->file->dir, + filearr[i]->file->dir) == 0) + break; + j++; + direntrylen = strlen (filearr[i]->file->dir) + 1; + } + if (j <= dircnt) + diridx[i] = j; + else + { + obstack_int_grow (&ob, i); + diridx[i] = ++dircnt; + dirarr = (unsigned int *) obstack_base (&ob); + dirtbllen += direntrylen; + } + filetbllen += strlen (file) + 1; + filetbllen += size_of_uleb128 (diridx[i]); + if (lowest_line_version < 5 || line_stats.has_time) + filetbllen += size_of_uleb128 (filearr[i]->file->time); + if (lowest_line_version < 5 || line_stats.has_size) + filetbllen += size_of_uleb128 (filearr[i]->file->size); + } + dirarr = (unsigned int *) obstack_finish (&ob); + } + + /* standard .debug_line "header" length (both version 2 and 5): + unit_length (4) + version (2) + header_length (4) + + min_instr_length (1) + default_is_stmt (1) + line_base (1) + + line_range (1) + opcode_base (1) = 15 + + version 2 adds 2 bytes, one zero byte to terminate dir and file lists. + + version 5 adds at least 11 bytes, max_ops_per_instr (1) + + address_size (1) + segment_size (1) + dir_entry_format_cnt (1) + + format_pair (2), file_entry_format_cnt (1) + file_format_pairs + (4). Plus dircnt (uleb128) + format_pair (2) if has_time + + format_pair (2) if has_size) + filecnt (uleb128). + + version 5 also has 2 extra 6 byte "<dwz>" string entries for dir + and file entry zero, plus one for the zero file entry dir idx. + */ + header_len = 15; + if (lowest_line_version < 5) + header_len += 2; + else + { + header_len += 11; + header_len += size_of_uleb128 (dircnt + 1); + header_len += size_of_uleb128 (filecnt + 1); + if (line_stats.has_time) + header_len += 2; + if (line_stats.has_size) + header_len += 2; + header_len += 2 * 6 + 1; + } + len = header_len + filetbllen + dirtbllen; + if (unlikely (op_multifile)) + { + debug_sections[DEBUG_LINE].new_size = len; + debug_sections[DEBUG_LINE].new_data = malloc (len); + if (debug_sections[DEBUG_LINE].new_data == NULL) + dwz_oom (); + line = debug_sections[DEBUG_LINE].new_data; + } + else + { + if (multi_line_off + len < multi_line_off) + { + if (line_htab) + obstack_free (&ob, (void *) filearr); + return 1; + } + + if (len == header_len) + { + line = buf; + assert (sizeof (buf) >= header_len); + } + else + line = (unsigned char *) obstack_alloc (&ob, len); + } + ptr = line; + write_32 (ptr, len - 4); /* Total length. */ + if (lowest_line_version < 5) + write_16 (ptr, 2); /* DWARF version. */ + else + { + write_16 (ptr, 5); /* DWARF version. */ + write_8 (ptr, multi_ptr_size); /* Address size. */ + write_8 (ptr, 0); /* Segment size. */ + } + write_32 (ptr, /* Header length. */ + len - (lowest_line_version < 5 ? 10 : 12)); + write_8 (ptr, 1); /* Minimum insn length. */ + if (lowest_line_version >= 5) + write_8 (ptr, 1); /* Maximum ops per instr. */ + write_8 (ptr, 1); /* Default is_stmt. */ + write_8 (ptr, 0); /* Line base. */ + write_8 (ptr, 1); /* Line range. */ + write_8 (ptr, 1); /* Opcode base. */ + + if (lowest_line_version >= 5) + { + write_8 (ptr, 1); /* Dir entry format count. */ + write_uleb128 (ptr, DW_LNCT_path); + write_uleb128 (ptr, DW_FORM_string); + write_uleb128 (ptr, dircnt + 1); /* Dir cnt. */ + memcpy (ptr, "<dwz>", 6); /* Zero entry empty dir path. */ + ptr += 6; + } + + for (i = 0; i < dircnt; i++) + { + unsigned int l; + if (filearr[dirarr[i]]->file->dir) + { + l = strlen (filearr[dirarr[i]]->file->dir) + 1; + memcpy (ptr, filearr[dirarr[i]]->file->dir, l); + } + else + { + const char *file = filearr[dirarr[i]]->file->file; + const char *r = strrchr (file, '/'); + + while (r && r > file && r[-1] == '/') + r--; + l = r - file + 1; + memcpy (ptr, file, l - 1); + ptr[l - 1] = '\0'; + } + ptr += l; + } + if (lowest_line_version < 5) + write_8 (ptr, 0); /* Terminate dir table. */ + else + { + unsigned int format_cnt = 2 + line_stats.has_size + line_stats.has_time; + write_8 (ptr, format_cnt); /* File entry format count. */ + write_uleb128 (ptr, DW_LNCT_path); + write_uleb128 (ptr, DW_FORM_string); + write_uleb128 (ptr, DW_LNCT_directory_index); + write_uleb128 (ptr, DW_FORM_udata); + if (line_stats.has_time) + { + write_uleb128 (ptr, DW_LNCT_timestamp); + write_uleb128 (ptr, DW_FORM_udata); + } + if (line_stats.has_size) + { + write_uleb128 (ptr, DW_LNCT_size); + write_uleb128 (ptr, DW_FORM_udata); + } + write_uleb128 (ptr, filecnt + 1); /* File names cnt. */ + memcpy (ptr, "<dwz>", 6); /* Zero entry empty file path. */ + ptr += 6; + write_8 (ptr, 0); /* Zero entry zero diridx. */ + if (line_stats.has_time) + write_8 (ptr, 0); + if (line_stats.has_size) + write_8 (ptr, 0); + } + + for (i = 0; i < filecnt; i++) + { + const char *file = filearr[i]->file->file; + unsigned int l; + if (diridx[i] && filearr[i]->file->dir == NULL) + file = strrchr (file, '/') + 1; + l = strlen (file) + 1; + memcpy (ptr, file, l); + ptr += l; + write_uleb128 (ptr, diridx[i]); + if (lowest_line_version < 5 || line_stats.has_time) + write_uleb128 (ptr, filearr[i]->file->time); + if (lowest_line_version < 5 || line_stats.has_size) + write_uleb128 (ptr, filearr[i]->file->size); + } + if (lowest_line_version < 5) + write_8 (ptr, 0); /* Terminate file table. */ + assert (ptr == line + len); + + if (likely (!op_multifile)) + { + if (write (multi_line_fd, line, len) != (ssize_t) len) + ret = 1; + else + multi_line_off += len; + if (line_htab) + obstack_free (&ob, (void *) filearr); + else if (line != buf) + obstack_free (&ob, (void *) line); + } + else if (line_htab) + obstack_free (&ob, (void *) filearr); + return ret; +} + +#if DEVEL +/* In struct dw_die we have a union u with fields p1 and p2. The p1 field is + used during phase 1, after which the space is reused for the p2 field + during phase 2. Clear the p2 field to get rid of values stored to p1 + during phase 1. */ +static int +clear_p2_field (void) +{ + dw_cu_ref cu; + dw_die_ref die; + + FOREACH_DIE (cu, die) + { + assert (die->die_collapsed_child == 0); + die->u.p2.die_new_abbrev = NULL; + die->u.p2.die_new_offset = 0; + die->u.p2.die_intracu_udata_size = 0; + } + + return 0; +} +#endif + +/* Helper structure for file state. */ +struct file_result +{ + /* -3: Uninitialized. + -2: Already processed under different name. + -1: Ignore. + 0: Processed, changed. + 1: Processed, unchanged. */ + int res; + int ret; + size_t hardlink_to; + unsigned int die_count; + bool skip_multifile; + bool low_mem_p; +}; + +/* Collect potentially shareable DIEs, strings and .debug_macro + opcode sequences into temporary .debug_* files. */ +static int +write_multifile_1 (DSO *dso, struct file_result *res) +{ + dw_cu_ref cu; + bool any_cus = false; + unsigned int i; + int ret = 0; + + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "write_multifile\n"); + } + + if (multi_ehdr.e_ident[0] == '\0') + multi_ehdr = dso->ehdr; + + if (multifile_force_ptr_size && ptr_size != multifile_force_ptr_size) + { + error (0, 0, "File %s skipped for multi-file optimization, different" + " pointer size", dso->filename); + res->skip_multifile = true; + return 1; + } + else if (multi_ptr_size == 0) + multi_ptr_size = ptr_size; + else if (ptr_size != multi_ptr_size) + { + error (0, 0, "Multi-file optimization not allowed for different" + " pointer sizes"); + multifile = NULL; + return 1; + } + else + { + /* Same ptr_size. */ + } + + int endianity = (do_read_32 == buf_read_ule32 + ? ELFDATA2LSB + : ELFDATA2MSB); + if (multifile_force_endian && endianity != multifile_force_endian) + { + error (0, 0, "File %s skipped for multi-file optimization, different" + " endianity", dso->filename); + res->skip_multifile = true; + return 1; + } + else if (multi_endian == 0) + multi_endian = endianity; + else if (multi_endian != endianity) + { + error (0, 0, "Multi-file optimization not allowed for different" + " endianity"); + multifile = NULL; + return 1; + } + else + { + /* Same endianity. */ + } + +#if DEVEL + clear_p2_field (); +#endif + + for (i = 0; i < SAVED_SECTIONS; i++) + { + saved_new_data[i] = debug_sections[i].new_data; + saved_new_size[i] = debug_sections[i].new_size; + debug_sections[i].new_data = NULL; + debug_sections[i].new_size = debug_sections[i].size; + } + propagate_multifile (); + for (cu = first_cu; cu && cu->cu_kind != CU_TYPES; cu = cu->cu_next) + { + cu->u1.cu_new_abbrev_owner = NULL; + cu->u2.cu_new_abbrev_offset = 0; + cu->cu_new_offset = 0; + any_cus |= check_multifile (cu->cu_die); + } + if (any_cus) + { + dw_cu_ref *cup; + + for (cup = &first_cu; *cup && (*cup)->cu_kind != CU_TYPES; ) + { + if ((*cup)->cu_new_abbrev) + htab_delete ((*cup)->cu_new_abbrev); + + if ((*cup)->cu_die->die_no_multifile == 0) + cup = &(*cup)->cu_next; + else + *cup = (*cup)->cu_next; + } + *cup = NULL; + multifile_mode = MULTIFILE_MODE_WR; + if (tracing) + fprintf (stderr, "Write-multifile %s\n", dso->filename); + if (compute_abbrevs (NULL)) + ret = 1; + else if (debug_sections[DEBUG_MACRO].data && read_macro (dso)) + ret = 1; + else if ((unsigned int) (multi_info_off + + debug_sections[DEBUG_INFO].new_size) + < multi_info_off + || (unsigned int) (multi_abbrev_off + + debug_sections[DEBUG_ABBREV].new_size) + < multi_abbrev_off + || (unsigned int) (multi_str_off + + (max_strp_off ? max_strp_off + : debug_sections[DEBUG_ABBREV].size)) + < multi_str_off + || (unsigned int) (multi_macro_off + + debug_sections[DEBUG_MACRO].new_size) + < multi_macro_off) + { + error (0, 0, "Multifile temporary files too large"); + multifile = NULL; + ret = 1; + } + else + { + const char *mfile; + write_abbrev (); + write_info (NULL); + /* Any error in this is fatal for multifile handling of further + files. */ + mfile = multifile; + multifile = NULL; + if (write (multi_abbrev_fd, debug_sections[DEBUG_ABBREV].new_data, + debug_sections[DEBUG_ABBREV].new_size) + != (ssize_t) debug_sections[DEBUG_ABBREV].new_size + || write (multi_info_fd, debug_sections[DEBUG_INFO].new_data, + debug_sections[DEBUG_INFO].new_size) + != (ssize_t) debug_sections[DEBUG_INFO].new_size + || write (multi_str_fd, debug_sections[DEBUG_STR].data, + debug_sections[DEBUG_STR].size) + != (ssize_t) debug_sections[DEBUG_STR].size + || (debug_sections[DEBUG_MACRO].new_data + && write (multi_macro_fd, + debug_sections[DEBUG_MACRO].new_data, + debug_sections[DEBUG_MACRO].new_size) + != (ssize_t) debug_sections[DEBUG_MACRO].new_size) + || (strp_htab != NULL && write_multifile_strp ()) + || write_multifile_line ()) + { + error (0, 0, "Error writing multi-file temporary files"); + ret = 1; + } + else + { + multi_info_off += debug_sections[DEBUG_INFO].new_size; + multi_abbrev_off += debug_sections[DEBUG_ABBREV].new_size; + multi_str_off += max_strp_off ? max_strp_off + : debug_sections[DEBUG_STR].size; + multi_macro_off += debug_sections[DEBUG_MACRO].new_size; + multifile = mfile; + } + } + } + multifile_mode = 0; + for (i = 0; i < SAVED_SECTIONS; i++) + { + free (debug_sections[i].new_data); + debug_sections[i].new_data = saved_new_data[i]; + debug_sections[i].new_size = saved_new_size[i]; + saved_new_data[i] = NULL; + } + return ret; +} + +struct pipe +{ + int readfd; + int writefd; +}; + +static bool write_multifile_parallel_p; +static int child_id; +static struct pipe *pipes; + +/* Get token. */ +static void +get_token (void) +{ + int n = child_id; + int readfd = pipes[n].readfd; + int writefd = pipes[n].writefd; + close (writefd); + char buf; + read (readfd, &buf, 1); + close (readfd); +} + +/* Pass token to child N. */ +static void +pass_token (int n) +{ + int readfd = pipes[n].readfd; + int writefd = pipes[n].writefd; + close (readfd); + char buf = '\0'; + write (writefd, &buf, 1); + close (writefd); +} + +/* Wrapper around write_multifile_1 that ensures write_multifile_1 is called + in file order. */ +static int +write_multifile (DSO *dso, struct file_result *res) +{ + int ret; + + if (write_multifile_parallel_p) + { + get_token (); + + multi_info_off = lseek (multi_info_fd, 0L, SEEK_END); + multi_abbrev_off = lseek (multi_abbrev_fd, 0L, SEEK_END); + multi_line_off = lseek (multi_line_fd, 0L, SEEK_END); + multi_str_off = lseek (multi_str_fd, 0L, SEEK_END); + multi_macro_off = lseek (multi_macro_fd, 0L, SEEK_END); + } + + ret = write_multifile_1 (dso, res); + + return ret; +} + +/* During fi_multifile phase, see what DIEs in a partial unit + contain no children worth keeping where all real DIEs have + dups in the shared .debug_info section and what remains is + just the DW_TAG_partial_unit, a single DW_TAG_imported_unit + and perhaps some empty named namespaces. Then all the + references to that partial unit can be replaced by references + to the shared partial unit DW_TAG_import_unit has been importing. */ +static bool +remove_empty_pu (dw_die_ref die) +{ + dw_die_ref child = die->die_child, dup = NULL; + if (!die->die_named_namespace) + { + if (die->die_tag != DW_TAG_partial_unit + || child == NULL + || child->die_tag != DW_TAG_imported_unit + || child->die_offset != -1U) + return false; + if (die->die_abbrev->nattr > 2) + return false; + if (die->die_abbrev->nattr + && die->die_abbrev->attr[0].attr != DW_AT_stmt_list) + return false; + if (die->die_abbrev->nattr == 2 + && die->die_abbrev->attr[1].attr != DW_AT_comp_dir) + return false; + dup = child->die_nextdup; + child = child->die_sib; + } + else + { + if (die->die_abbrev->nattr > 2) + return false; + if (die->die_abbrev->nattr + && die->die_abbrev->attr[0].attr != DW_AT_name) + return false; + if (die->die_abbrev->nattr == 2 + && die->die_abbrev->attr[1].attr != DW_AT_sibling) + return false; + } + for (; child; child = child->die_sib) + if (!child->die_named_namespace) + { + if (!child->die_remove) + /* Signal that DIE can't be removed, but + perhaps we could still remove_empty_pu + some named namespaces that are children of DIE. */ + dup = die; + if (dup == NULL && die->die_named_namespace) + dup = child->die_dup->die_parent; + } + else if (!remove_empty_pu (child)) + return false; + else if (dup == NULL && die->die_named_namespace) + dup = child->die_dup->die_parent; + if (dup == NULL || dup == die) + return false; + die->die_remove = 1; + assert (dup->die_tag == die->die_tag); + die->die_dup = dup; + return true; +} + +/* Call remove_empty_pu on all partial units. */ +static int +remove_empty_pus (void) +{ + dw_cu_ref cu; + for (cu = first_cu; cu; cu = cu->cu_next) + if (cu->cu_kind == CU_NORMAL + && cu->cu_die->die_tag == DW_TAG_partial_unit) + remove_empty_pu (cu->cu_die); + return 0; +} + +/* Handle compression of a single file FILE. If OUTFILE is + non-NULL, the result will be stored into that file, otherwise + the result will be written into a temporary file that is renamed + over FILE. */ +static int +dwz (const char *file, const char *outfile, struct file_result *res) +{ + DSO *dso; + int ret = 0, fd; + unsigned int i; + struct stat st; + + if (res->res == -1) + return 1; + + res->res = -1; + fd = open (file, O_RDONLY); + if (fd < 0) + { + error (0, errno, "Failed to open input file %s", file); + return 1; + } + if (fstat (fd, &st) < 0) + { + close (fd); + error (0, errno, "Failed to stat input file %s", file); + return 1; + } + + res->res = 1; + + if (tracing) + { + fprintf (stderr, "Compressing %s", file); + if (multifile_mode == 0) + ; + else if (low_mem) + fprintf (stderr, " in low-mem mode"); + else if (fi_multifile) + fprintf (stderr, " in finalize-multifile mode"); + else + abort (); + fprintf (stderr, "\n"); + } + + dso = fdopen_dso (fd, file); + if (dso == NULL) + return 1; + + obstack_alloc_failed_handler = dwz_oom; + if (setjmp (oom_buf)) + { + error (0, ENOMEM, "%s: Could not allocate memory", dso->filename); + + cleanup (); + ret = 1; + } + else + { + obstack_init (&ob); + obstack_init (&ob2); + + unsigned int *die_count = multifile ? &res->die_count : NULL; + ret = read_dwarf (dso, quiet && outfile == NULL, die_count); + if (ret) + cleanup (); + else if (partition_dups () + || create_import_tree () + || (unlikely (fi_multifile) + && (remove_empty_pus () + || read_macro (dso))) + || read_debug_info (dso, DEBUG_TYPES, NULL) +#if DEVEL + || clear_p2_field () +#endif + || compute_abbrevs (dso) + || (unlikely (fi_multifile) && (finalize_strp (false), 0))) + { + cleanup (); + ret = 1; + } + else if (!(ignore_size || force_p) + && ((debug_sections[DEBUG_INFO].new_size + + debug_sections[DEBUG_ABBREV].new_size + + debug_sections[DEBUG_STR].new_size + + debug_sections[DEBUG_MACRO].new_size + + debug_sections[DEBUG_TYPES].new_size) + >= (debug_sections[DEBUG_INFO].size + + debug_sections[DEBUG_ABBREV].size + + debug_sections[DEBUG_STR].size + + debug_sections[DEBUG_MACRO].size + + debug_sections[DEBUG_TYPES].size))) + { + if (!quiet || outfile != NULL) + error (0, 0, "%s: DWARF compression not beneficial " + "- old size %ld new size %ld", dso->filename, + (unsigned long) (debug_sections[DEBUG_INFO].size + + debug_sections[DEBUG_ABBREV].size + + debug_sections[DEBUG_STR].size + + debug_sections[DEBUG_MACRO].size + + debug_sections[DEBUG_TYPES].size), + (unsigned long) (debug_sections[DEBUG_INFO].new_size + + debug_sections[DEBUG_ABBREV].new_size + + debug_sections[DEBUG_STR].new_size + + debug_sections[DEBUG_MACRO].new_size + + debug_sections[DEBUG_TYPES].new_size)); + + if (multifile && !fi_multifile && !low_mem) + write_multifile (dso, res); + + cleanup (); + if (outfile != NULL) + ret = 1; + } + else if (write_aranges (dso)) + { + failure: + cleanup (); + ret = 1; + } + else + { + if (unlikely (fi_multifile)) + { + size_t len; + const char *name = multifile_name; + enum debug_section_kind sec_kind; + unsigned char *ptr; + if (multifile_name == NULL) + { + if (!multifile_relative) + name = multifile; + else + { + char *p1 = realpath (file, NULL); + char *p2 = realpath (multifile, NULL); + char *p3, *p4, *p5, *p6; + unsigned int dotdot = 0; + if (p1 == NULL || p2 == NULL) + { + if (p1) + free (p1); + else if (p2) + free (p2); + error (0, 0, "Could not compute relative multifile " + "pathname from %s to %s", + file, multifile); + goto failure; + } + p3 = p1; + p4 = p2; + do + { + p5 = strchr (p3, '/'); + p6 = strchr (p4, '/'); + if (p5 == NULL + || p6 == NULL + || p5 - p3 != p6 - p4 + || memcmp (p3, p4, p5 - p3) != 0) + break; + p3 = p5 + 1; + p4 = p6 + 1; + } + while (1); + while (p5 != NULL) + { + dotdot++; + p5 = strchr (p5 + 1, '/'); + } + len = strlen (p4); + p3 = (char *) malloc (dotdot * 3 + len + 1); + if (p3 == NULL) + dwz_oom (); + p5 = p3; + while (dotdot) + { + memcpy (p5, "../", 3); + p5 += 3; + dotdot--; + } + memcpy (p5, p4, len + 1); + free (p1); + free (p2); + name = p3; + } + } + len = strlen (name) + 1; + sec_kind = dwarf_5 ? DEBUG_SUP : GNU_DEBUGALTLINK; + debug_sections[sec_kind].new_size + = len + 0x14 + (dwarf_5 ? 4 : 0); + debug_sections[sec_kind].new_data + = malloc (debug_sections[sec_kind].new_size); + if (debug_sections[sec_kind].new_data == NULL) + dwz_oom (); + ptr = debug_sections[sec_kind].new_data; + if (dwarf_5) + { + write_16 (ptr, 5); + write_8 (ptr, 0); + } + memcpy (ptr, name, len); + ptr += len; + if (dwarf_5) + write_uleb128 (ptr, 0x14); + memcpy (ptr, multifile_sha1, 0x14); + if (name != multifile_name && name != multifile) + free ((void *) name); + write_macro (); + } + write_abbrev (); + write_info (die_count); + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "write_loc\n"); + } + write_loc (); + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "write_loclists\n"); + } + write_loclists (); + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "write_types\n"); + } + write_types (); + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "write_gdb_index\n"); + } + write_gdb_index (); + /* These sections are optional and it is unclear + how to adjust them. Just remove them. */ + debug_sections[DEBUG_PUBNAMES].new_data = NULL; + debug_sections[DEBUG_PUBNAMES].new_size = 0; + debug_sections[DEBUG_PUBTYPES].new_data = NULL; + debug_sections[DEBUG_PUBTYPES].new_size = 0; + debug_sections[DEBUG_GNU_PUBNAMES].new_data = NULL; + debug_sections[DEBUG_GNU_PUBNAMES].new_size = 0; + debug_sections[DEBUG_GNU_PUBTYPES].new_data = NULL; + debug_sections[DEBUG_GNU_PUBTYPES].new_size = 0; + + if (multifile && !fi_multifile && !low_mem) + write_multifile (dso, res); + + bool save_to_temp = save_temps && multifile && multifile_mode == 0; + cleanup (); + + if (write_dso (dso, outfile, &st, save_to_temp)) + ret = 1; + else + res->res = 0; + + if (unlikely (progress_p)) + report_progress (); + } + } + + for (i = 0; debug_sections[i].name; ++i) + { + debug_sections[i].data = NULL; + debug_sections[i].size = 0; + free (debug_sections[i].new_data); + debug_sections[i].new_data = NULL; + debug_sections[i].new_size = 0; + debug_sections[i].sec = 0; + } + + if (elf_end (dso->elf) < 0) + { + error (0, 0, "elf_end failed: %s", elf_errmsg (elf_errno ())); + ret = 1; + } + close (fd); + + free (dso); + if (ret == 3) + { + ret = (outfile != NULL) ? 1 : 0; + res->res = -1; + } + return ret; +} + +/* In order to free all malloced memory at the end of optimize_multifile, + communicate .debug_str tail optimized offset list from optimize_multifile + to read_multifile using an mmapped chunk of memory pointed by this + variable. */ +static unsigned int *strp_tail_off_list; + +/* Process temporary .debug_* files, see what can be beneficially shared + and write a new ET_REL file, containing the shared .debug_* sections. */ +static int +optimize_multifile (unsigned int *die_count) +{ + DSO dsobuf, *dso; + int fd = -1; + volatile int vfd = -1; + unsigned int i; + Elf *elf = NULL; + Elf *volatile velf = NULL; + GElf_Shdr shdr; + Elf_Scn *scn; + Elf_Data *data; + char *e_ident; + static const char shstrtab_gnu[] + = "\0.shstrtab\0.note.gnu.build-id\0.gdb_index\0" + ".debug_info\0.debug_abbrev\0.debug_line\0.debug_str\0.debug_macro"; + static const char shstrtab_dwarf5[] + = "\0.shstrtab\0.gdb_index\0" + ".debug_info\0.debug_abbrev\0.debug_line\0.debug_str\0.debug_macro\0" + ".debug_sup"; + const char *const shstrtab = dwarf_5 ? shstrtab_dwarf5 : shstrtab_gnu; + const size_t shstrtab_len = (dwarf_5 + ? sizeof shstrtab_dwarf5 + : sizeof shstrtab_gnu); + const char *p; + unsigned char note[0x24], *np, *supp; + struct sha1_ctx ctx; + + if (multi_ehdr.e_ident[0] == '\0' + || multi_ptr_size == 0 + || multi_endian == 0) + return -1; + + if (multi_line_off == 0) + { + init_endian (multi_endian); + if (write_multifile_line ()) + { + error (0, 0, "Error writing multi-file temporary files"); + return -1; + } + } + + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "optimize_multifile\n"); + } + + debug_sections[DEBUG_INFO].size = multi_info_off; + debug_sections[DEBUG_INFO].data + = (multi_info_off + ? mmap (NULL, multi_info_off, PROT_READ, MAP_PRIVATE, multi_info_fd, 0) + : NULL); + debug_sections[DEBUG_ABBREV].size = multi_abbrev_off; + debug_sections[DEBUG_ABBREV].data + = (multi_abbrev_off + ? mmap (NULL, multi_abbrev_off, PROT_READ, MAP_PRIVATE, + multi_abbrev_fd, 0) + : NULL); + debug_sections[DEBUG_LINE].size = multi_line_off; + debug_sections[DEBUG_LINE].data + = mmap (NULL, multi_line_off, PROT_READ, MAP_PRIVATE, multi_line_fd, 0); + debug_sections[DEBUG_STR].size = multi_str_off; + debug_sections[DEBUG_STR].data + = multi_str_off + ? mmap (NULL, multi_str_off, PROT_READ, MAP_PRIVATE, multi_str_fd, 0) + : NULL; + debug_sections[DEBUG_MACRO].size = multi_macro_off; + debug_sections[DEBUG_MACRO].data + = multi_macro_off + ? mmap (NULL, multi_macro_off, PROT_READ, MAP_PRIVATE, multi_macro_fd, 0) + : NULL; + if (debug_sections[DEBUG_INFO].data == MAP_FAILED + || debug_sections[DEBUG_ABBREV].data == MAP_FAILED + || debug_sections[DEBUG_LINE].data == MAP_FAILED + || debug_sections[DEBUG_STR].data == MAP_FAILED + || debug_sections[DEBUG_MACRO].data == MAP_FAILED) + { + error (0, 0, "Error mmapping multi-file temporary files"); + fail: + cleanup (); + if (velf) + elf_end (velf); + if (vfd != -1) + { + unlink (multifile); + close (vfd); + } + if (debug_sections[DEBUG_INFO].data != MAP_FAILED) + munmap (debug_sections[DEBUG_INFO].data, + debug_sections[DEBUG_INFO].size); + if (debug_sections[DEBUG_ABBREV].data != MAP_FAILED) + munmap (debug_sections[DEBUG_ABBREV].data, + debug_sections[DEBUG_ABBREV].size); + if (debug_sections[DEBUG_LINE].data != MAP_FAILED) + munmap (debug_sections[DEBUG_LINE].data, + debug_sections[DEBUG_LINE].size); + if (debug_sections[DEBUG_STR].data != MAP_FAILED + && debug_sections[DEBUG_STR].data != NULL) + munmap (debug_sections[DEBUG_STR].data, + debug_sections[DEBUG_STR].size); + if (debug_sections[DEBUG_MACRO].data != MAP_FAILED + && debug_sections[DEBUG_MACRO].data != NULL) + munmap (debug_sections[DEBUG_MACRO].data, + debug_sections[DEBUG_MACRO].size); + return -1; + } + + init_endian (multi_endian); + ptr_size = multi_ptr_size; + memset (&dsobuf, '\0', sizeof (dsobuf)); + dso = &dsobuf; + dso->filename = multifile; + if (tracing) + fprintf (stderr, "Optimize-multifile\n"); + multifile_mode = MULTIFILE_MODE_OP; + + obstack_alloc_failed_handler = dwz_oom; + if (unoptimized_multifile) + { + for (i = 0; i < SAVED_SECTIONS; i++) + { + debug_sections[i].new_data = debug_sections[i].data; + debug_sections[i].new_size = debug_sections[i].size; + } + } + else if (setjmp (oom_buf)) + { + error (0, ENOMEM, "%s: Could not allocate memory", dso->filename); + goto fail; + } + else + { + dw_cu_ref *cup; + unsigned char *p, *q; + unsigned int strp_count; + + obstack_init (&ob); + obstack_init (&ob2); + + if (read_debug_info (dso, DEBUG_INFO, NULL) + || partition_dups ()) + goto fail; + +#if DEVEL + clear_p2_field (); +#endif + + for (cup = &first_cu; *cup && (*cup)->cu_kind == CU_PU; + cup = &(*cup)->cu_next) + ; + + *cup = NULL; + + strp_count = debug_sections[DEBUG_STR].size / 64; + if (strp_count < 64) + strp_count = 64; + strp_htab = htab_try_create (strp_count, + strp_hash2, strp_eq2, NULL); + if (strp_htab == NULL) + dwz_oom (); + + for (p = debug_sections[DEBUG_STR].data; + p < debug_sections[DEBUG_STR].data + debug_sections[DEBUG_STR].size; + p = q + 1) + { + void **slot; + struct strp_entry se; + hashval_t hash; + + q = (unsigned char *) strchr ((char *) p, '\0'); + hash = hash (p, q - p); + se.off = p - debug_sections[DEBUG_STR].data; + se.new_off = hash & ~1U; + slot = htab_find_slot_with_hash (strp_htab, &se, se.new_off, INSERT); + if (slot == NULL) + dwz_oom (); + if (*slot == NULL) + { + struct strp_entry *s = pool_alloc (strp_entry, sizeof (*s)); + *s = se; + *slot = (void *) s; + } + else + ((struct strp_entry *) *slot)->new_off |= 1; + } + + if (first_cu != NULL) + { + if (compute_abbrevs (dso)) + goto fail; + + strp_tail_off_list = finalize_strp (true); + + write_abbrev (); + write_info (die_count); + write_gdb_index (); + if (write_multifile_line ()) + goto fail; + } + else + strp_tail_off_list = finalize_strp (true); + + if (debug_sections[DEBUG_MACRO].data) + handle_macro (); + } + + np = note; + write_32 (np, sizeof ("GNU")); + write_32 (np, 0x14); + write_32 (np, NT_GNU_BUILD_ID); + + supp = NULL; + if (dwarf_5) + { + debug_sections[DEBUG_SUP].new_size = 0x14 + 5; + debug_sections[DEBUG_SUP].new_data + = malloc (debug_sections[DEBUG_SUP].new_size); + if (debug_sections[DEBUG_SUP].new_data == NULL) + dwz_oom (); + supp = debug_sections[DEBUG_SUP].new_data; + write_16 (supp, 5); + write_8 (supp, 1); + write_8 (supp, 0); + write_uleb128 (supp, 0x14); + } + + cleanup (); + fd = open (multifile, O_RDWR | O_CREAT, 0600); + vfd = fd; + if (fd < 0) + { + error (0, errno, "Failed to open multi-file common file %s", multifile); + goto fail; + } + + elf = elf_begin (fd, ELF_C_WRITE, NULL); + velf = elf; + if (elf == NULL) + { + error (0, 0, "cannot open ELF file: %s", elf_errmsg (-1)); + goto fail; + } + + multi_ehdr.e_type = ET_REL; + multi_ehdr.e_entry = 0; + multi_ehdr.e_phoff = 0; + multi_ehdr.e_phnum = 0; + multi_ehdr.e_shoff = multi_ehdr.e_ehsize; + multi_ehdr.e_shnum = 2; + if (!dwarf_5) + { + multi_ehdr.e_shoff += 0x24; + multi_ehdr.e_shnum++; + } + for (i = 0; debug_sections[i].name; i++) + if (debug_sections[i].new_size) + { + multi_ehdr.e_shoff += debug_sections[i].new_size; + multi_ehdr.e_shnum++; + } + multi_ehdr.e_shstrndx = multi_ehdr.e_shnum - 1; + + /* Some gelf_newehdr implementations don't return the resulting + ElfNN_Ehdr, so we have to do it the hard way instead of: + e_ident = (char *) gelf_newehdr (elf, gelf_getclass (dso->elf)); */ + switch (multi_ehdr.e_ident[EI_CLASS]) + { + case ELFCLASS32: + e_ident = (char *) elf32_newehdr (elf); + multi_ehdr.e_shoff = (multi_ehdr.e_shoff + 3) & -4; + break; + case ELFCLASS64: + e_ident = (char *) elf64_newehdr (elf); + multi_ehdr.e_shoff = (multi_ehdr.e_shoff + 7) & -8; + break; + default: + e_ident = NULL; + break; + } + + if (e_ident == NULL + /* This is here just for the gelfx wrapper, so that gelf_update_ehdr + already has the correct ELF class. */ + || memcpy (e_ident, multi_ehdr.e_ident, EI_NIDENT) == NULL + || gelf_update_ehdr (elf, &multi_ehdr) == 0) + { + error (0, 0, "Could not create new ELF headers"); + goto fail; + } + elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT | ELF_F_PERMISSIVE); + + sha1_init_ctx (&ctx); + for (i = 0; debug_sections[i].name; i++) + { + if (debug_sections[i].new_size == 0) + continue; + sha1_process_bytes (debug_sections[i].new_data, + debug_sections[i].new_size, &ctx); + } + sha1_finish_ctx (&ctx, multifile_sha1); + + memcpy (np, "GNU", sizeof ("GNU")); + memcpy (np + 4, multifile_sha1, 0x14); + + if (dwarf_5) + memcpy (supp, multifile_sha1, 0x14); + + memset (&shdr, '\0', sizeof (shdr)); + shdr.sh_offset = multi_ehdr.e_ehsize; + if (!dwarf_5) + { + shdr.sh_type = SHT_NOTE; + shdr.sh_addralign = 4; + shdr.sh_size = 0x24; + scn = elf_newscn (elf); + elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY); + shdr.sh_name = (strchr (shstrtab + 1, '\0') + 1) - shstrtab; + gelf_update_shdr (scn, &shdr); + data = elf_newdata (scn); + data->d_buf = (char *) note; + data->d_type = ELF_T_BYTE; + data->d_version = EV_CURRENT; + data->d_size = shdr.sh_size; + data->d_off = 0; + data->d_align = 1; + } + + shdr.sh_type = SHT_PROGBITS; + shdr.sh_offset += shdr.sh_size; + shdr.sh_addralign = 1; + for (i = 0; debug_sections[i].name; i++) + { + if (debug_sections[i].new_size == 0) + continue; + scn = elf_newscn (elf); + elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY); + for (p = shstrtab + 1; p < shstrtab + shstrtab_len; + p = strchr (p, '\0') + 1) + if (strcmp (p, debug_sections[i].name) == 0) + { + shdr.sh_name = p - shstrtab; + break; + } + shdr.sh_size = debug_sections[i].new_size; + if (i == DEBUG_STR) + { + shdr.sh_flags = SHF_MERGE | SHF_STRINGS; + shdr.sh_entsize = 1; + } + else + { + shdr.sh_flags = 0; + shdr.sh_entsize = 0; + } + gelf_update_shdr (scn, &shdr); + data = elf_newdata (scn); + data->d_buf = debug_sections[i].new_data; + data->d_type = ELF_T_BYTE; + data->d_version = EV_CURRENT; + data->d_size = shdr.sh_size; + data->d_off = 0; + data->d_align = 1; + shdr.sh_offset += shdr.sh_size; + } + scn = elf_newscn (elf); + elf_flagscn (scn, ELF_C_SET, ELF_F_DIRTY); + shdr.sh_name = 1; + shdr.sh_offset = multi_ehdr.e_shoff + + multi_ehdr.e_shnum * multi_ehdr.e_shentsize; + shdr.sh_size = shstrtab_len; + shdr.sh_type = SHT_STRTAB; + shdr.sh_flags = 0; + shdr.sh_entsize = 0; + gelf_update_shdr (scn, &shdr); + data = elf_newdata (scn); + data->d_buf = (char *) shstrtab; + data->d_type = ELF_T_BYTE; + data->d_version = EV_CURRENT; + data->d_size = shdr.sh_size; + data->d_off = 0; + data->d_align = 1; + + if (elf_update (elf, ELF_C_WRITE_MMAP) == -1) + { + error (0, 0, "%s: elf_update failed", multifile); + goto fail; + } + + if (elf_end (elf) < 0) + { + error (0, 0, "elf_end failed: %s", elf_errmsg (elf_errno ())); + goto fail; + } + + fchmod (fd, 0644); + + if (dwarf_5) + { + free (debug_sections[DEBUG_SUP].new_data); + debug_sections[DEBUG_SUP].new_data = NULL; + } + munmap (debug_sections[DEBUG_INFO].data, debug_sections[DEBUG_INFO].size); + munmap (debug_sections[DEBUG_ABBREV].data, + debug_sections[DEBUG_ABBREV].size); + munmap (debug_sections[DEBUG_LINE].data, debug_sections[DEBUG_LINE].size); + if (debug_sections[DEBUG_STR].data) + munmap (debug_sections[DEBUG_STR].data, debug_sections[DEBUG_STR].size); + if (debug_sections[DEBUG_MACRO].data) + munmap (debug_sections[DEBUG_MACRO].data, + debug_sections[DEBUG_MACRO].size); + + for (i = 0; debug_sections[i].name; ++i) + { + debug_sections[i].data = NULL; + debug_sections[i].size = 0; + if (!unoptimized_multifile) + free (debug_sections[i].new_data); + debug_sections[i].new_data = NULL; + debug_sections[i].new_size = 0; + debug_sections[i].sec = 0; + } + + return fd; +} + +/* Parse the .debug_* sections from shared ET_REL file written + by optimize_multifile into data structures for fi_multifile + phase. */ +static DSO * +read_multifile (int fd, unsigned int die_count) +{ + DSO *dso, *volatile ret; + unsigned int i; + + if (unlikely (progress_p)) + { + report_progress (); + fprintf (stderr, "read_multifile\n"); + } + + if (tracing) + fprintf (stderr, "Read-multifile\n"); + + multifile_mode = MULTIFILE_MODE_RD; + dso = fdopen_dso (fd, multifile); + if (dso == NULL) + { + multifile_mode = 0; + return NULL; + } + + ret = dso; + obstack_alloc_failed_handler = dwz_oom; + if (setjmp (oom_buf)) + { + error (0, ENOMEM, "%s: Could not allocate memory", dso->filename); + + fail: + elf_end (dso->elf); + close (fd); + free (dso); + ret = NULL; + alt_off_htab = NULL; + } + else + { + obstack_init (&ob); + obstack_init (&ob2); + + if (read_dwarf (dso, false, &die_count)) + goto fail; + + if (debug_sections[DEBUG_STR].size) + { + unsigned char *p, *q; + unsigned int strp_count = debug_sections[DEBUG_STR].size / 64; + void **slot; + unsigned int *pi; + + if (strp_count < 100) + strp_count = 100; + strp_htab = htab_try_create (strp_count, strp_hash3, strp_eq3, NULL); + if (strp_htab == NULL) + dwz_oom (); + for (p = debug_sections[DEBUG_STR].data; + p < debug_sections[DEBUG_STR].data + + debug_sections[DEBUG_STR].size; p = q) + { + q = (unsigned char *) strchr ((char *) p, '\0') + 1; + slot = htab_find_slot_with_hash (strp_htab, p, + hash (p, q - p - 1), INSERT); + if (slot == NULL) + dwz_oom (); + assert (*slot == NULL); + *slot = (void *) p; + } + if (strp_tail_off_list) + { + for (pi = strp_tail_off_list; *pi; pi++) + { + p = debug_sections[DEBUG_STR].data + *pi; + q = (unsigned char *) strchr ((char *) p, '\0'); + slot = htab_find_slot_with_hash (strp_htab, p, + hash (p, q - p), INSERT); + if (slot == NULL) + dwz_oom (); + assert (*slot == NULL); + *slot = (void *) p; + } + pi++; + munmap (strp_tail_off_list, + (char *) pi - (char *) strp_tail_off_list); + } + } + + if (debug_sections[DEBUG_MACRO].data) + handle_macro (); + + alt_strp_htab = strp_htab; + strp_htab = NULL; + alt_off_htab = off_htab; + off_htab = NULL; + alt_dup_htab = dup_htab; + dup_htab = NULL; + alt_macro_htab = macro_htab; + macro_htab = NULL; + alt_first_cu = first_cu; + alt_pool = finalize_pool (); + alt_ob = ob; + alt_ob2 = ob2; + memset (&ob, '\0', sizeof (ob)); + memset (&ob2, '\0', sizeof (ob2)); + for (i = 0; i < SAVED_SECTIONS; i++) + { + alt_data[i] = debug_sections[i].data; + alt_size[i] = debug_sections[i].size; + } + } + + cleanup (); + + for (i = 0; debug_sections[i].name; ++i) + { + debug_sections[i].data = NULL; + debug_sections[i].size = 0; + debug_sections[i].new_data = NULL; + debug_sections[i].new_size = 0; + debug_sections[i].sec = 0; + } + + return ret; +} + +/* Clear all die_nextdup fields among toplevel children + of DIE. */ +static void +alt_clear_dups (dw_die_ref die) +{ + dw_die_ref child; + assert (die->die_dup == NULL); + die->die_nextdup = NULL; + for (child = die->die_child; child; child = child->die_sib) + { + assert (child->die_dup == NULL); + child->die_nextdup = NULL; + if (child->die_named_namespace) + alt_clear_dups (child); + } +} + +/* Create a temporary file using NAME. Return the corresponding file + descriptor if successful, otherwise return -1. */ +static int +make_temp_file (const char *name) +{ + const char *tmpdir = "/tmp/"; + const char *template_suffix = ".XXXXXX"; + int fd; + size_t buf_len, offset, name_len; + char *buf; + + if (save_temps) + { + FILE *f = fopen (name, "w+"); + if (f == NULL) + fd = -1; + else + fd = fileno (f); + return fd; + } + + name_len = strlen (name); + buf_len = (strlen (tmpdir) + + name_len + + strlen (template_suffix) + + 1); + if (buf_len < name_len) + return -1; + buf = (char *)malloc (buf_len); + if (buf == NULL) + return -1; + offset = 0; + + strcpy (&buf[offset], tmpdir); + offset += strlen (tmpdir); + + strcpy (&buf[offset], name); + offset += name_len; + + strcpy (&buf[offset], template_suffix); + offset += strlen (template_suffix); + + assert (offset == buf_len - 1); + assert (buf[offset] == '\0'); + + fd = mkstemp (buf); + if (fd == -1) + goto done; + + /* Unlink the filename, such that the file is disposed of once the file + descriptor is closed. */ + unlink (buf); + + done: + free (buf); + return fd; +} + +/* As dwz, but retry with MULTIFILE_MODE_LOW_MEM if the low_mem_die_limit + is hit. */ +static int +dwz_with_low_mem (const char *file, const char *outfile, + struct file_result *res) +{ + int ret; + + res->low_mem_p = false; + + ret = (low_mem_die_limit == 0 + ? 2 + : dwz (file, outfile, res)); + + if (ret == 2) + { + multifile_mode = MULTIFILE_MODE_LOW_MEM; + res->low_mem_p = true; + + ret = dwz (file, outfile, res); + } + + return ret; +} + +/* Initialize struct file_result RES. */ +static void +init_file_result (struct file_result *res) +{ + res->die_count = 0; + res->res = -3; + res->skip_multifile = false; + res->low_mem_p = false; +} + +/* Dwarf-compress FILE. If OUTFILE, write to result to OUTFILE, otherwise + modify FILE. */ +static int +dwz_one_file (const char *file, const char *outfile) +{ + struct file_result res; + + if (stats_p) + init_stats (file); + + init_file_result (&res); + + return dwz_with_low_mem (file, outfile, &res); +} + +/* Helper structure for hardlink discovery. */ +struct hl_stat +{ + dev_t dev; + ino_t ino; + nlink_t nlink; +}; + +/* Detect which FILES are hardlinks, and mark those in RESA. */ +static bool +detect_hardlinks (int nr_files, char *files[], struct file_result *resa) +{ + bool found = false; + struct hl_stat hl_stat[nr_files]; + int i; + + /* Try to open all files. */ + for (i = 0; i < nr_files; i++) + { + struct file_result *res = &resa[i]; + int fd; + struct stat st; + + const char *file = files[i]; + res->res = -1; + + fd = open (file, O_RDONLY); + if (fd < 0) + error (0, errno, "Failed to open input file %s", file); + else if (fstat (fd, &st) < 0) + error (0, errno, "Failed to stat input file %s", file); + else + { + res->res = 1; + hl_stat[i].dev = st.st_dev; + hl_stat[i].ino = st.st_ino; + hl_stat[i].nlink = st.st_nlink; + } + + close (fd); + } + + /* Detect hard links. */ + for (i = 0; i < nr_files; i++) + { + struct file_result *res = &resa[i]; + int n; + for (n = 0; n != i; n++) + if (resa[n].res >= 0 + && hl_stat[n].nlink > 1 + && hl_stat[n].dev == hl_stat[i].dev + && hl_stat[n].ino == hl_stat[i].ino) + break; + if (n == i) + continue; + res->res = -2; + res->hardlink_to = n; + found = true; + } + + return found; +} + +/* Update the FILES marked as hardlink in RESA. */ +static void +update_hardlinks (int nr_files, char *files[], struct file_result *resa) +{ + int i; + + /* Update hardlinks. */ + for (i = 0; i < nr_files; i++) + { + struct file_result *res = &resa[i]; + const char *file = files[i]; + size_t n; + if (res->res != -2) + continue; + n = res->hardlink_to; + + /* If a hardlink to this has been processed before + and we didn't change it, just assume the same + state. */ + if (resa[n].res == 1) + { + if (tracing) + fprintf (stderr, "Skipping hardlink %s to unchanged file\n", + file); + continue; + } + + /* If it changed, try to hardlink it again. */ + if (resa[n].res == 0) + { + size_t len = strlen (file); + char *filename = alloca (len + sizeof (".#dwz#.XXXXXX")); + int fd2; + if (tracing) + fprintf (stderr, "Updating hardlink %s to changed file\n", + file); + memcpy (filename, file, len); + memcpy (filename + len, ".#dwz#.XXXXXX", + sizeof (".#dwz#.XXXXXX")); + fd2 = mkstemp (filename); + if (fd2 >= 0) + { + close (fd2); + unlink (filename); + if (link (files[n], filename) == 0) + { + if (rename (filename, file) == 0) + ; + else + unlink (filename); + } + } + } + } +} + +/* Encode child process exit status. */ +static int +encode_child_exit_status (int thisret, struct file_result *res) +{ + assert (thisret == 0 || thisret == 1); + if (thisret == 0 && res->low_mem_p) + thisret = 2; + assert (res->res >= -3 && res->res <= 1); + return (thisret + + ((res->res + 3) << 2) + + ((res->skip_multifile ? 1 : 0) << 5)); +} + +/* Decode child process exit status. */ +static int +decode_child_exit_status (int state, struct file_result *res) +{ + int ret; + if (!WIFEXITED (state)) + error (1, 0, "Child dwz process got killed"); + int status = WEXITSTATUS (state); + ret = status & 0x3; + status >>= 2; + + res->low_mem_p = false; + if (ret == 2) + { + ret = 0; + res->low_mem_p = true; + } + + res->res = (int)(status & 0x7) - 3; + status >>= 3; + + res->skip_multifile = (status & 0x1) ? true : false; + + return ret; +} + +/* Wait on child exit with PID, update PIDS and RES. */ +static void +wait_child_exit (pid_t pid, pid_t *pids, int nr_pids, + struct file_result *resa) +{ + int state; + pid_t got_pid = waitpid (pid, &state, 0); + + int i; + for (i = 0; i < nr_pids; ++i) + if (pids[i] == got_pid) + { + pids[i] = 0; + break; + } + assert (i < nr_pids); + + resa[i].ret = decode_child_exit_status (state, &resa[i]); +} + +static int *workset; +static int workset_size = 0; +int current_multifile_owner = -1; +int current_multifile_owner_file_idx = -1; + +/* Wait on exit of chilren in PIDS, update RESA. */ +static void +wait_children_exit (pid_t *pids, int nr_files, struct file_result *resa) +{ + int i; + for (i = 0; i < nr_files; i++) + { + struct file_result *res = &resa[i]; + if (pids[i] == 0) + continue; + wait_child_exit (pids[i], &pids[i], 1, res); + if (current_multifile_owner_file_idx == -1 + || i < current_multifile_owner_file_idx) + continue; + assert (i == current_multifile_owner_file_idx); + current_multifile_owner++; + if (current_multifile_owner == workset_size) + continue; + current_multifile_owner_file_idx + = workset[current_multifile_owner]; + pass_token (current_multifile_owner); + } +} + +/* Dwarf-compress FILES. If HARDLINK, detect if some files are hardlinks and + if so, dwarf-compress just one, and relink the others. */ +static int +dwz_files_1 (int nr_files, char *files[], bool hardlink, + struct file_result *resa) +{ + int ret = 0; + int i, j; + const char *file; + int successcount = 0; + + for (i = 0; i < nr_files; ++i) + init_file_result (&resa[i]); + + if (multifile) + { + if (multifile_force_ptr_size) + multi_ptr_size = multifile_force_ptr_size; + if (multifile_force_endian) + multi_endian = multifile_force_endian; + + multi_info_fd = make_temp_file ("dwz.debug_info"); + multi_abbrev_fd = make_temp_file ("dwz.debug_abbrev"); + multi_line_fd = make_temp_file ("dwz.debug_line"); + multi_str_fd = make_temp_file ("dwz.debug_str"); + multi_macro_fd = make_temp_file ("dwz.debug_macro"); + if (multi_info_fd == -1 + || multi_abbrev_fd == -1 + || multi_line_fd == -1 + || multi_str_fd == -1 + || multi_macro_fd == -1) + { + error (0, 0, "Could not create multifile temporary files"); + multifile = NULL; + } + } + + if (hardlink) + hardlink = detect_hardlinks (nr_files, files, resa); + + workset = malloc (nr_files * sizeof (int)); + if (workset == NULL) + error (1, ENOMEM, "failed to allocate workset array"); + for (i = 0; i < nr_files; i++) + { + struct file_result *res = &resa[i]; + if (res->res == -2) + /* Skip hard links. */ + continue; + workset[workset_size] = i; + workset_size++; + } + + bool initial_parallel_p = max_forks > 1; + if (initial_parallel_p && multifile) + { + if (multifile_force_ptr_size != 0 && multifile_force_endian != 0) + { + write_multifile_parallel_p = true; + pipes = malloc (workset_size * 2 * sizeof (int)); + if (pipes == NULL) + error (1, ENOMEM, "failed to allocate pipes array"); + for (i = 0; i < workset_size; i++) + { + int fds[2]; + if (pipe (fds) != 0) + error (1, ENOMEM, "failed to initialize pipe"); + pipes[i].readfd = fds[0]; + pipes[i].writefd = fds[1]; + } + } + else + initial_parallel_p = false; + } + if (initial_parallel_p) + { + pid_t pids[nr_files]; + int nr_forks = 0; + for (i = 0; i < nr_files; i++) + pids[i] = 0; + for (j = 0; j < workset_size; j++) + { + int i = workset[j]; + + if (nr_forks == max_forks) + { + if (multifile == NULL) + wait_child_exit (-1, pids, i, resa); + else + { + int k = current_multifile_owner_file_idx; + wait_child_exit (pids[k], &pids[k], 1, &resa[k]); + current_multifile_owner++; + current_multifile_owner_file_idx + = workset[current_multifile_owner]; + pass_token (current_multifile_owner); + } + nr_forks--; + } + + pid_t fork_res = fork (); + assert (fork_res != -1); + if (fork_res == 0) + { + child_id = j; + file = files[i]; + struct file_result *res = &resa[i]; + int thisret = dwz_with_low_mem (file, NULL, res); + return encode_child_exit_status (thisret, res); + } + else + { + if (multifile && j == 0) + { + current_multifile_owner = j; + current_multifile_owner_file_idx + = workset[current_multifile_owner]; + pass_token (current_multifile_owner); + } + pids[i] = fork_res; + nr_forks++; + } + } + if (nr_forks > 0) + wait_children_exit (pids, nr_files, resa); + } + else + { + for (j = 0; j < workset_size; j++) + { + int i = workset[j]; + file = files[i]; + struct file_result *res = &resa[i]; + if (stats_p) + init_stats (file); + res->ret = dwz_with_low_mem (file, NULL, res); + } + } + + for (j = 0; j < workset_size; j++) + { + int i = workset[j]; + struct file_result *res = &resa[i]; + int thisret = res->ret; + if (thisret == 1) + ret = 1; + else if (!res->low_mem_p && !res->skip_multifile && res->res >= 0) + successcount++; + } + + if (hardlink) + update_hardlinks (nr_files, files, resa); + + if (multifile == NULL) + return ret; + + if (successcount < 2) + { + error (0, 0, "Too few files for multifile optimization"); + return ret; + } + + if (write_multifile_parallel_p) + { + multi_info_off = lseek (multi_info_fd, 0L, SEEK_END); + multi_abbrev_off = lseek (multi_abbrev_fd, 0L, SEEK_END); + multi_line_off = lseek (multi_line_fd, 0L, SEEK_END); + multi_str_off = lseek (multi_str_fd, 0L, SEEK_END); + multi_macro_off = lseek (multi_macro_fd, 0L, SEEK_END); + } + if (multi_info_off == 0 && multi_str_off == 0 && multi_macro_off == 0) + { + if (!quiet) + error (0, 0, "No suitable DWARF found for multifile optimization"); + return ret; + } + + if (write_multifile_parallel_p) + { + /* We reproduce here what happens when we run sequentially. This is a + kludge that probably needs to be replaced by IPC. */ + for (i = 0; i < nr_files; i++) + { + struct file_result *res = &resa[i]; + if (!res->low_mem_p && !res->skip_multifile && res->res >= 0) + { + int fd = open (files[i], O_RDONLY); + if (fd < 0) + return ret; + DSO *dso = fdopen_dso (fd, files[i]); + if (dso == NULL) + { + close (fd); + return ret; + } + assert (multi_ehdr.e_ident[0] == '\0'); + multi_ehdr = dso->ehdr; + break; + } + } + } + + unsigned int multifile_die_count = 0; + int multi_fd = optimize_multifile (&multifile_die_count); + DSO *dso; + if (multi_fd == -1) + return 1; + + dso = read_multifile (multi_fd, multifile_die_count); + if (dso == NULL) + { + ret = 1; + goto cleanup; + } + + workset_size = 0; + for (i = 0; i < nr_files; i++) + { + struct file_result *res = &resa[i]; + /* Don't process again files that couldn't + be processed successfully. Also skip hard links. */ + if (res->res == -1 || res->res == -2 + || res->skip_multifile) + continue; + workset[workset_size] = i; + workset_size++; + } + + bool finalize_multifile_parallel_p = max_forks > 1; + if (finalize_multifile_parallel_p) + { + pid_t pids[nr_files]; + int nr_forks = 0; + for (i = 0; i < nr_files; i++) + pids[i] = 0; + for (j = 0; j < workset_size; j++) + { + int i = workset[j]; + + if (nr_forks == max_forks) + { + wait_child_exit (-1, pids, i, resa); + nr_forks--; + } + + pid_t fork_res = fork (); + assert (fork_res != -1); + if (fork_res == 0) + { + file = files[i]; + struct file_result *res = &resa[i]; + multifile_mode = MULTIFILE_MODE_FI; + int thisret = dwz (file, NULL, res); + return encode_child_exit_status (thisret, res); + } + else + { + pids[i] = fork_res; + nr_forks++; + } + } + if (nr_forks > 0) + wait_children_exit (pids, nr_files, resa); + } + else + { + for (j = 0; j < workset_size; j++) + { + int i = workset[j]; + struct file_result *res = &resa[i]; + dw_cu_ref cu; + file = files[i]; + if (stats_p) + init_stats (file); + multifile_mode = MULTIFILE_MODE_FI; + for (cu = alt_first_cu; cu; cu = cu->cu_next) + alt_clear_dups (cu->cu_die); + res->ret = dwz (file, NULL, res); + } + } + + for (j = 0; j < workset_size; j++) + { + int i = workset[j]; + struct file_result *res = &resa[i]; + int thisret = res->ret; + ret |= thisret; + } + + if (hardlink) + update_hardlinks (nr_files, files, resa); + + elf_end (dso->elf); + close (multi_fd); + free (dso); + + cleanup: + cleanup (); + + strp_htab = alt_strp_htab; + off_htab = alt_off_htab; + dup_htab = alt_dup_htab; + macro_htab = alt_macro_htab; + ob = alt_ob; + ob2 = alt_ob2; + cleanup (); + pool_destroy (alt_pool); + + return ret; +} + +/* Wrapper around dwz_files_1 that takes care of malloc and free of resa. */ +static int +dwz_files (int nr_files, char *files[], bool hardlink) +{ + int ret; + struct file_result *resa + = (struct file_result *) malloc ((nr_files) * sizeof (*resa)); + if (resa == NULL) + error (1, ENOMEM, "failed to allocate result array"); + + ret = dwz_files_1 (nr_files, files, hardlink, resa); + + free (resa); + return ret; +} + +int +main (int argc, char *argv[]) +{ + int ret; + const char *outfile; + bool hardlink; + int nr_files; + char **files; + + state = XXH64_createState (); + + if (elf_version (EV_CURRENT) == EV_NONE) + error (1, 0, "library out of date"); + + outfile = NULL; + hardlink = false; + parse_args (argc, argv, &hardlink, &outfile); + nr_files = argc - optind; + files = &argv[optind]; + + if (nr_files <= 1) + { + const char *file = nr_files == 0 ? "a.out" : files[0]; + + if (multifile != NULL) + { + error (0, 0, "Too few files for multifile optimization"); + multifile = NULL; + } + + ret = dwz_one_file (file, outfile); + } + else + { + if (outfile != NULL) + error (1, 0, "-o option not allowed for multiple files"); + + ret = dwz_files (nr_files, files, hardlink); + } + + if (stats_p) + free (stats); + + return ret; +} diff --git a/hashtab.c b/hashtab.c new file mode 100644 index 0000000..a9b9d13 --- /dev/null +++ b/hashtab.c @@ -0,0 +1,628 @@ +/* An expandable hash tables datatype. + Copyright (C) 1999-2016 Free Software Foundation, Inc. + Contributed by Vladimir Makarov (vmakarov@cygnus.com). + +This file is part of the libiberty library. +Libiberty is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +Libiberty 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 +Library General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street - Fifth Floor, +Boston, MA 02110-1301, USA. */ + +/* This package implements basic hash table functionality. It is possible + to search for an entry, create an entry and destroy an entry. + + Elements in the table are generic pointers. + + The size of the table is not fixed; if the occupancy of the table + grows too high the hash table will be expanded. + + The abstract data implementation is based on generalized Algorithm D + from Knuth's book "The art of computer programming". Hash table is + expanded by creation of new hash table and transferring elements from + the old table to the new table. */ + +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include "hashtab.h" + +#include <endian.h> +#if __BYTE_ORDER == __BIG_ENDIAN +# define WORDS_BIGENDIAN 1 +#endif + +/* This macro defines reserved value for empty table entry. */ + +#define EMPTY_ENTRY ((void *) 0) + +/* This macro defines reserved value for table entry which contained + a deleted element. */ + +#define DELETED_ENTRY ((void *) 1) + +static hashval_t hash_pointer (const void *); +static int eq_pointer (const void *, const void *); +static int htab_expand (htab_t); +static void **find_empty_slot_for_expand (htab_t, hashval_t); + +/* At some point, we could make these be NULL, and modify the + hash-table routines to handle NULL specially; that would avoid + function-call overhead for the common case of hashing pointers. */ +htab_hash htab_hash_pointer = hash_pointer; +htab_eq htab_eq_pointer = eq_pointer; + +/* The following function returns a nearest prime number which is + greater than N, and near a power of two. */ + +unsigned long +higher_prime_number (n) + unsigned long n; +{ + /* These are primes that are near, but slightly smaller than, a + power of two. */ + static unsigned long primes[] = { + (unsigned long) 7, + (unsigned long) 13, + (unsigned long) 31, + (unsigned long) 61, + (unsigned long) 127, + (unsigned long) 251, + (unsigned long) 509, + (unsigned long) 1021, + (unsigned long) 2039, + (unsigned long) 4093, + (unsigned long) 8191, + (unsigned long) 16381, + (unsigned long) 32749, + (unsigned long) 65521, + (unsigned long) 131071, + (unsigned long) 262139, + (unsigned long) 524287, + (unsigned long) 1048573, + (unsigned long) 2097143, + (unsigned long) 4194301, + (unsigned long) 8388593, + (unsigned long) 16777213, + (unsigned long) 33554393, + (unsigned long) 67108859, + (unsigned long) 134217689, + (unsigned long) 268435399, + (unsigned long) 536870909, + (unsigned long) 1073741789, + (unsigned long) 2147483647, + /* 4294967291L */ + ((unsigned long) 2147483647) + ((unsigned long) 2147483644), + }; + + unsigned long* low = &primes[0]; + unsigned long* high = &primes[sizeof(primes) / sizeof(primes[0])]; + + while (low != high) + { + unsigned long* mid = low + (high - low) / 2; + if (n > *mid) + low = mid + 1; + else + high = mid; + } + + /* If we've run out of primes, abort. */ + if (n > *low) + { + fprintf (stderr, "Cannot find prime bigger than %lu\n", n); + abort (); + } + + return *low; +} + +/* Returns a hash code for P. */ + +static hashval_t +hash_pointer (p) + const void * p; +{ + return (hashval_t) ((long)p >> 3); +} + +/* Returns non-zero if P1 and P2 are equal. */ + +static int +eq_pointer (p1, p2) + const void * p1; + const void * p2; +{ + return p1 == p2; +} + +/* This function creates table with length slightly longer than given + source length. The created hash table is initiated as empty (all the + hash table entries are EMPTY_ENTRY). The function returns the created + hash table. Memory allocation may fail; it may return NULL. */ + +htab_t +htab_try_create (size, hash_f, eq_f, del_f) + size_t size; + htab_hash hash_f; + htab_eq eq_f; + htab_del del_f; +{ + htab_t result; + + size = higher_prime_number (size); + result = (htab_t) calloc (1, sizeof (struct htab)); + if (result == NULL) + return NULL; + + result->entries = (void **) calloc (size, sizeof (void *)); + if (result->entries == NULL) + { + free (result); + return NULL; + } + + result->size = size; + result->hash_f = hash_f; + result->eq_f = eq_f; + result->del_f = del_f; + result->return_allocation_failure = 1; + return result; +} + +/* This function frees all memory allocated for given hash table. + Naturally the hash table must already exist. */ + +void +htab_delete (htab) + htab_t htab; +{ + int i; + + if (htab->del_f) + for (i = htab->size - 1; i >= 0; i--) + if (htab->entries[i] != EMPTY_ENTRY + && htab->entries[i] != DELETED_ENTRY) + (*htab->del_f) (htab->entries[i]); + + free (htab->entries); + free (htab); +} + +/* This function clears all entries in the given hash table. */ + +void +htab_empty (htab) + htab_t htab; +{ + int i; + + if (htab->del_f) + for (i = htab->size - 1; i >= 0; i--) + if (htab->entries[i] != EMPTY_ENTRY + && htab->entries[i] != DELETED_ENTRY) + (*htab->del_f) (htab->entries[i]); + + memset (htab->entries, 0, htab->size * sizeof (void *)); + htab->n_deleted = 0; + htab->n_elements = 0; +} + +/* Similar to htab_find_slot, but without several unwanted side effects: + - Does not call htab->eq_f when it finds an existing entry. + - Does not change the count of elements/searches/collisions in the + hash table. + This function also assumes there are no deleted entries in the table. + HASH is the hash value for the element to be inserted. */ + +static void ** +find_empty_slot_for_expand (htab, hash) + htab_t htab; + hashval_t hash; +{ + size_t size = htab->size; + unsigned int index = hash % size; + void **slot = htab->entries + index; + hashval_t hash2; + + if (*slot == EMPTY_ENTRY) + return slot; + else if (*slot == DELETED_ENTRY) + abort (); + + hash2 = 1 + hash % (size - 2); + for (;;) + { + index += hash2; + if (index >= size) + index -= size; + + slot = htab->entries + index; + if (*slot == EMPTY_ENTRY) + return slot; + else if (*slot == DELETED_ENTRY) + abort (); + } +} + +/* The following function changes size of memory allocated for the + entries and repeatedly inserts the table elements. The occupancy + of the table after the call will be about 50%. Naturally the hash + table must already exist. Remember also that the place of the + table entries is changed. If memory allocation failures are allowed, + this function will return zero, indicating that the table could not be + expanded. If all goes well, it will return a non-zero value. */ + +static int +htab_expand (htab) + htab_t htab; +{ + void **oentries; + void **olimit; + void **p; + + oentries = htab->entries; + olimit = oentries + htab->size; + + htab->size = higher_prime_number (htab->size * 2); + + if (htab->return_allocation_failure) + { + void **nentries = (void **) calloc (htab->size, sizeof (void **)); + if (nentries == NULL) + return 0; + htab->entries = nentries; + } + + htab->n_elements -= htab->n_deleted; + htab->n_deleted = 0; + + p = oentries; + do + { + void * x = *p; + + if (x != EMPTY_ENTRY && x != DELETED_ENTRY) + { + void **q = find_empty_slot_for_expand (htab, (*htab->hash_f) (x)); + + *q = x; + } + + p++; + } + while (p < olimit); + + free (oentries); + return 1; +} + +/* This function searches for a hash table entry equal to the given + element. It cannot be used to insert or delete an element. */ + +void * +htab_find_with_hash (htab, element, hash) + htab_t htab; + const void * element; + hashval_t hash; +{ + unsigned int index; + hashval_t hash2; + size_t size; + void * entry; + + htab->searches++; + size = htab->size; + index = hash % size; + + entry = htab->entries[index]; + if (entry == EMPTY_ENTRY + || (entry != DELETED_ENTRY && (*htab->eq_f) (entry, element))) + return entry; + + hash2 = 1 + hash % (size - 2); + + for (;;) + { + htab->collisions++; + index += hash2; + if (index >= size) + index -= size; + + entry = htab->entries[index]; + if (entry == EMPTY_ENTRY + || (entry != DELETED_ENTRY && (*htab->eq_f) (entry, element))) + return entry; + } +} + +/* Like htab_find_slot_with_hash, but compute the hash value from the + element. */ + +void * +htab_find (htab, element) + htab_t htab; + const void * element; +{ + return htab_find_with_hash (htab, element, (*htab->hash_f) (element)); +} + +/* This function searches for a hash table slot containing an entry + equal to the given element. To delete an entry, call this with + INSERT = 0, then call htab_clear_slot on the slot returned (possibly + after doing some checks). To insert an entry, call this with + INSERT = 1, then write the value you want into the returned slot. + When inserting an entry, NULL may be returned if memory allocation + fails. */ + +void ** +htab_find_slot_with_hash (htab, element, hash, insert) + htab_t htab; + const void * element; + hashval_t hash; + enum insert_option insert; +{ + void **first_deleted_slot; + unsigned int index; + hashval_t hash2; + size_t size; + void * entry; + + if (insert == INSERT && htab->size * 3 <= htab->n_elements * 4 + && htab_expand (htab) == 0) + return NULL; + + size = htab->size; + index = hash % size; + + htab->searches++; + first_deleted_slot = NULL; + + entry = htab->entries[index]; + if (entry == EMPTY_ENTRY) + goto empty_entry; + else if (entry == DELETED_ENTRY) + first_deleted_slot = &htab->entries[index]; + else if ((*htab->eq_f) (entry, element)) + return &htab->entries[index]; + + hash2 = 1 + hash % (size - 2); + for (;;) + { + htab->collisions++; + index += hash2; + if (index >= size) + index -= size; + + entry = htab->entries[index]; + if (entry == EMPTY_ENTRY) + goto empty_entry; + else if (entry == DELETED_ENTRY) + { + if (!first_deleted_slot) + first_deleted_slot = &htab->entries[index]; + } + else if ((*htab->eq_f) (entry, element)) + return &htab->entries[index]; + } + + empty_entry: + if (insert == NO_INSERT) + return NULL; + + htab->n_elements++; + + if (first_deleted_slot) + { + *first_deleted_slot = EMPTY_ENTRY; + return first_deleted_slot; + } + + return &htab->entries[index]; +} + +/* Like htab_find_slot_with_hash, but compute the hash value from the + element. */ + +void ** +htab_find_slot (htab, element, insert) + htab_t htab; + const void * element; + enum insert_option insert; +{ + return htab_find_slot_with_hash (htab, element, (*htab->hash_f) (element), + insert); +} + +/* This function deletes an element with the given value from hash + table. If there is no matching element in the hash table, this + function does nothing. */ + +void +htab_remove_elt (htab, element) + htab_t htab; + void * element; +{ + void **slot; + + slot = htab_find_slot (htab, element, NO_INSERT); + if (*slot == EMPTY_ENTRY) + return; + + if (htab->del_f) + (*htab->del_f) (*slot); + + *slot = DELETED_ENTRY; + htab->n_deleted++; +} + +/* This function clears a specified slot in a hash table. It is + useful when you've already done the lookup and don't want to do it + again. */ + +void +htab_clear_slot (htab, slot) + htab_t htab; + void **slot; +{ + if (slot < htab->entries || slot >= htab->entries + htab->size + || *slot == EMPTY_ENTRY || *slot == DELETED_ENTRY) + abort (); + + if (htab->del_f) + (*htab->del_f) (*slot); + + *slot = DELETED_ENTRY; + htab->n_deleted++; +} + +/* This function scans over the entire hash table calling + CALLBACK for each live entry. If CALLBACK returns false, + the iteration stops. INFO is passed as CALLBACK's second + argument. */ + +void +htab_traverse (htab, callback, info) + htab_t htab; + htab_trav callback; + void * info; +{ + void **slot = htab->entries; + void **limit = slot + htab->size; + + do + { + void * x = *slot; + + if (x != EMPTY_ENTRY && x != DELETED_ENTRY) + if (!(*callback) (slot, info)) + break; + } + while (++slot < limit); +} + +/* Return the current size of given hash table. */ + +size_t +htab_size (htab) + htab_t htab; +{ + return htab->size; +} + +/* Return the current number of elements in given hash table. */ + +size_t +htab_elements (htab) + htab_t htab; +{ + return htab->n_elements - htab->n_deleted; +} + +/* Return the fraction of fixed collisions during all work with given + hash table. */ + +double +htab_collisions (htab) + htab_t htab; +{ + if (htab->searches == 0) + return 0.0; + + return (double) htab->collisions / (double) htab->searches; +} + +#ifndef NDEBUG +void +htab_dump (htab, name, dumpfn) + htab_t htab; + const char *name; + htab_dumpfn dumpfn; +{ + FILE *f = fopen (name, "w"); + size_t i, j; + + if (f == NULL) + abort (); + fprintf (f, "size %zd n_elements %zd n_deleted %zd\n", + htab->size, htab->n_elements, htab->n_deleted); + for (i = 0; i < htab->size; ++i) + { + if (htab->entries [i] == EMPTY_ENTRY + || htab->entries [i] == DELETED_ENTRY) + { + for (j = i + 1; j < htab->size; ++j) + if (htab->entries [j] != htab->entries [i]) + break; + fprintf (f, "%c%zd\n", + htab->entries [i] == EMPTY_ENTRY ? 'E' : 'D', + j - i); + i = j - 1; + } + else + { + fputc ('V', f); + (*dumpfn) (f, htab->entries [i]); + } + } + fclose (f); +} + +void +htab_restore (htab, name, restorefn) + htab_t htab; + const char *name; + htab_restorefn restorefn; +{ + FILE *f = fopen (name, "r"); + size_t size, n_elements, n_deleted, i, j, k; + int c; + + if (f == NULL) + abort (); + if (fscanf (f, "size %zd n_elements %zd n_deleted %zd\n", + &size, &n_elements, &n_deleted) != 3) + abort (); + htab_empty (htab); + free (htab->entries); + htab->entries = (void **) calloc (size, sizeof (void *)); + if (htab->entries == NULL) + abort (); + htab->size = size; + htab->n_elements = n_elements; + htab->n_deleted = n_deleted; + for (i = 0; i < htab->size; ++i) + { + switch ((c = fgetc (f))) + { + case 'E': + case 'D': + if (fscanf (f, "%zd\n", &j) != 1) + abort (); + if (i + j > htab->size) + abort (); + if (c == 'D') + for (k = i; k < i + j; ++k) + htab->entries [k] = DELETED_ENTRY; + i += j - 1; + break; + case 'V': + htab->entries [i] = (*restorefn) (f); + break; + default: + abort (); + } + } + fclose (f); +} +#endif diff --git a/hashtab.h b/hashtab.h new file mode 100644 index 0000000..5920abc --- /dev/null +++ b/hashtab.h @@ -0,0 +1,160 @@ +/* An expandable hash tables datatype. + Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2009, 2010 + Free Software Foundation, Inc. + Contributed by Vladimir Makarov (vmakarov@cygnus.com). + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +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 +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street - Fifth Floor, +Boston, MA 02110-1301, USA. */ + +/* This package implements basic hash table functionality. It is possible + to search for an entry, create an entry and destroy an entry. + + Elements in the table are generic pointers. + + The size of the table is not fixed; if the occupancy of the table + grows too high the hash table will be expanded. + + The abstract data implementation is based on generalized Algorithm D + from Knuth's book "The art of computer programming". Hash table is + expanded by creation of new hash table and transferring elements from + the old table to the new table. */ + +#ifndef __HASHTAB_H__ +#define __HASHTAB_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* The type for a hash code. */ +typedef unsigned int hashval_t; + +/* Callback function pointer types. */ + +/* Calculate hash of a table entry. */ +typedef hashval_t (*htab_hash) (const void *); + +/* Compare a table entry with a possible entry. The entry already in + the table always comes first, so the second element can be of a + different type (but in this case htab_find and htab_find_slot + cannot be used; instead the variants that accept a hash value + must be used). */ +typedef int (*htab_eq) (const void *, const void *); + +/* Cleanup function called whenever a live element is removed from + the hash table. */ +typedef void (*htab_del) (void *); + +/* Function called by htab_traverse for each live element. The first + arg is the slot of the element (which can be passed to htab_clear_slot + if desired), the second arg is the auxiliary pointer handed to + htab_traverse. Return 1 to continue scan, 0 to stop. */ +typedef int (*htab_trav) (void **, void *); + +/* Hash tables are of the following type. The structure + (implementation) of this type is not needed for using the hash + tables. All work with hash table should be executed only through + functions mentioned below. */ + +struct htab +{ + /* Pointer to hash function. */ + htab_hash hash_f; + + /* Pointer to comparison function. */ + htab_eq eq_f; + + /* Pointer to cleanup function. */ + htab_del del_f; + + /* Table itself. */ + void **entries; + + /* Current size (in entries) of the hash table */ + size_t size; + + /* Current number of elements including also deleted elements */ + size_t n_elements; + + /* Current number of deleted elements in the table */ + size_t n_deleted; + + /* The following member is used for debugging. Its value is number + of all calls of `htab_find_slot' for the hash table. */ + unsigned int searches; + + /* The following member is used for debugging. Its value is number + of collisions fixed for time of work with the hash table. */ + unsigned int collisions; + + /* This is non-zero if we are allowed to return NULL for function calls + that allocate memory. */ + int return_allocation_failure; +}; + +typedef struct htab *htab_t; + +/* An enum saying whether we insert into the hash table or not. */ +enum insert_option {NO_INSERT, INSERT}; + +/* The prototypes of the package functions. */ + +/* This function is like htab_create, but may return NULL if memory + allocation fails, and also signals that htab_find_slot_with_hash and + htab_find_slot are allowed to return NULL when inserting. */ +extern htab_t htab_try_create (size_t, htab_hash, htab_eq, htab_del); +extern void htab_delete (htab_t); +extern void htab_empty (htab_t); + +extern void *htab_find (htab_t, const void *); +extern void **htab_find_slot (htab_t, const void *, enum insert_option); +extern void *htab_find_with_hash (htab_t, const void *, hashval_t); +extern void **htab_find_slot_with_hash (htab_t, const void *, hashval_t, + enum insert_option); +extern void htab_clear_slot (htab_t, void **); +extern void htab_remove_elt (htab_t, void *); + +extern void htab_traverse (htab_t, htab_trav, void *); + +extern size_t htab_size (htab_t); +extern size_t htab_elements (htab_t); +extern double htab_collisions (htab_t); + +/* Utility function. */ +unsigned long higher_prime_number (unsigned long); + +/* A hash function for pointers. */ +extern htab_hash htab_hash_pointer; + +/* An equality function for pointers. */ +extern htab_eq htab_eq_pointer; + +#ifndef NDEBUG + +#include <stdio.h> + +typedef void (*htab_dumpfn) (FILE *, const void *); +typedef void *(*htab_restorefn) (FILE *); + +extern void htab_dump (htab_t, const char *, htab_dumpfn); +extern void htab_restore (htab_t, const char *, htab_restorefn); + +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __HASHTAB_H */ diff --git a/iterators.h b/iterators.h new file mode 100644 index 0000000..8672ccf --- /dev/null +++ b/iterators.h @@ -0,0 +1,126 @@ +/* Various iterators. + + Copyright (C) 2019 SUSE LLC. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + 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 + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. */ + + +/* CU iterators. */ + +#define FOREACH_CU(CU) \ + for (CU = first_cu; CU; CU = CU->cu_next) + +#define FOREACH_CU_PU(CU) \ + for (CU = first_cu; CU && CU->cu_kind == CU_PU; CU = CU->cu_next) + +#define FOREACH_CU_NORMAL(CU) \ + for (CU = first_cu; CU && CU->cu_kind != CU_TYPES; CU = CU->cu_next) \ + if (CU->cu_kind == CU_NORMAL) + +#define FOREACH_CU_TYPES(CU) \ + for (CU = first_cu; CU; CU = CU->cu_next) \ + if (CU->cu_kind == CU_TYPES) \ + +/* Function that describes a depth-first traversal path visiting all dies. */ + +static inline dw_die_ref FORCE_INLINE +next_die (dw_die_ref die) +{ + if (die->die_child != NULL) + return die->die_child; + + while (1) + { + if (die->die_sib != NULL) + return die->die_sib; + + if (die->die_root) + return NULL; + + die = die->die_parent; + } +} + +/* Function that describes a depth-first traversal path visiting all toplevel + dies. */ + +static inline dw_die_ref FORCE_INLINE +next_toplevel_die (dw_die_ref die) +{ + if (die->die_child != NULL && die->die_child->die_toplevel) + return die->die_child; + + while (1) + { + if (die->die_sib != NULL && die->die_sib->die_toplevel) + return die->die_sib; + + if (die->die_root) + return NULL; + + die = die->die_parent; + } +} + +/* DIE_IN_CU iterators. */ + +#define FOREACH_DIE_IN_CU(DIE, CU) \ + for (DIE = CU->cu_die; DIE; DIE = next_die (DIE)) + +#define FOREACH_TOPLEVEL_DIE_IN_CU(DIE, CU) \ + for (DIE = CU->cu_die; DIE; DIE = next_toplevel_die (DIE)) + +#define FOREACH_LOW_TOPLEVEL_DIE_IN_CU(DIE, CU) \ + FOREACH_TOPLEVEL_DIE_IN_CU (DIE, CU) \ + if (!(die->die_root || die->die_named_namespace)) + +/* DIE iterators. */ + +#define FOREACH_DIE(CU, DIE) \ + FOREACH_CU (CU) \ + FOREACH_DIE_IN_CU (DIE, CU) + +#define FOREACH_TOPLEVEL_DIE(CU, DIE) \ + FOREACH_CU (CU) \ + FOREACH_TOPLEVEL_DIE_IN_CU (DIE, CU) + +#define FOREACH_LOW_TOPLEVEL_DIE(CU, DIE) \ + FOREACH_CU (CU) \ + FOREACH_LOW_TOPLEVEL_DIE_IN_CU (DIE, CU) + +#define FOREACH_CU_PU_TOPLEVEL_DIE(CU, DIE) \ + FOREACH_CU_PU (CU) \ + FOREACH_TOPLEVEL_DIE_IN_CU (DIE, CU) + +#define FOREACH_CU_NORMAL_TOPLEVEL_DIE(CU, DIE) \ + FOREACH_CU_NORMAL (CU) \ + FOREACH_TOPLEVEL_DIE_IN_CU (DIE, CU) + +#define FOREACH_CU_TYPES_TOPLEVEL_DIE(CU, DIE) \ + FOREACH_CU_TYPES (CU) \ + FOREACH_TOPLEVEL_DIE_IN_CU (DIE, CU) + +#define FOREACH_CU_PU_LOW_TOPLEVEL_DIE(CU, DIE) \ + FOREACH_CU_PU (CU) \ + FOREACH_LOW_TOPLEVEL_DIE_IN_CU (DIE, CU) + +#define FOREACH_CU_NORMAL_LOW_TOPLEVEL_DIE(CU, DIE) \ + FOREACH_CU_NORMAL (CU) \ + FOREACH_LOW_TOPLEVEL_DIE_IN_CU (DIE, CU) + +#define FOREACH_CU_TYPES_LOW_TOPLEVEL_DIE(CU, DIE) \ + FOREACH_CU_TYPES (CU) \ + FOREACH_LOW_TOPLEVEL_DIE_IN_CU (DIE, CU) @@ -0,0 +1,103 @@ +/* Copyright (C) 2001-2021 Red Hat, Inc. + Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2019-2021 SUSE LLC. + Written by Jakub Jelinek <jakub@redhat.com>, 2012. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + 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 + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* Big pool allocator. obstack isn't efficient, because it aligns everything + too much, and allocates too small chunks. All these objects are only freed + together. */ + +#include <stddef.h> +#include <stdlib.h> +#include <inttypes.h> + +#include "pool.h" + +/* Pointer to the start of the current pool chunk, current first free byte + in the chunk and byte after the end of the current pool chunk. */ + +static unsigned char *pool, *pool_next, *pool_limit; + +extern void dwz_oom (void); + +/* Allocate SIZE bytes with ALIGN bytes alignment from the pool. */ +void * +pool_alloc_1 (unsigned int align, unsigned int size) +{ + void *ret; + if (pool == NULL + || (size_t) (pool_limit - pool_next) < (size_t) align + size) + { + size_t new_size = (size_t) align + size; + unsigned char *new_pool; + new_size += sizeof (void *); + if (new_size < 16384 * 1024 - 64) + new_size = 16384 * 1024 - 64; + new_pool = (unsigned char *) malloc (new_size); + if (new_pool == NULL) + dwz_oom (); + *(unsigned char **) new_pool = pool; + pool_next = new_pool + sizeof (unsigned char *); + pool_limit = new_pool + new_size; + pool = new_pool; + } + pool_next = (unsigned char *) (((uintptr_t) pool_next + align - 1) + & ~(uintptr_t) (align - 1)); + ret = pool_next; + pool_next += size; + return ret; +} + +/* Finalize a pool and return it. */ +unsigned char * +finalize_pool (void) +{ + unsigned char *ret = pool; + pool = NULL; + pool_next = NULL; + pool_limit = NULL; + return ret; +} + +/* Free pool P. */ +static void +pool_destroy_1 (unsigned char *p) +{ + while (p) + { + void *elem = (void *) p; + p = *(unsigned char **) p; + free (elem); + } +} + +/* Free pool P, or the current pool if NULL. */ +void +pool_destroy (unsigned char *p) +{ + if (p != NULL) + { + pool_destroy_1 (p); + return; + } + + pool_destroy_1 (pool); + pool = NULL; + pool_next = NULL; + pool_limit = NULL; +} @@ -0,0 +1,26 @@ +/* Copyright (C) 2001-2021 Red Hat, Inc. + Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2019-2021 SUSE LLC. + Written by Jakub Jelinek <jakub@redhat.com>, 2012. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + 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 + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. */ + +extern void *pool_alloc_1 (unsigned int, unsigned int); +extern unsigned char *finalize_pool (void); +extern void pool_destroy (unsigned char *); + +#define pool_alloc(name, size) \ + (struct name *) pool_alloc_1 (ALIGNOF_STRUCT (name), size) @@ -0,0 +1,340 @@ +/* sha1.c - Functions to compute SHA1 message digest of files or + memory blocks according to the NIST specification FIPS-180-1. + + Copyright (C) 2000, 2001, 2003, 2004, 2005, 2006, 2008 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + 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 + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Scott G. Miller + Credits: + Robert Klep <robert@ilse.nl> -- Expansion function fix +*/ + +#include "sha1.h" + +#include <stddef.h> +#include <string.h> +#include <endian.h> + +#if __BYTE_ORDER == __BIG_ENDIAN +# define SWAP(n) (n) +#else +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#endif + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +/* Take a pointer to a 160 bit block of data (five 32 bit ints) and + initialize it to the start constants of the SHA1 algorithm. This + must be called before using hash in the call to sha1_hash. */ +void +sha1_init_ctx (struct sha1_ctx *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + ctx->E = 0xc3d2e1f0; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + +/* Put result from CTX in first 20 bytes following RESBUF. The result + must be in little endian byte order. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32-bit value. */ +void * +sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf) +{ + ((sha1_uint32 *) resbuf)[0] = SWAP (ctx->A); + ((sha1_uint32 *) resbuf)[1] = SWAP (ctx->B); + ((sha1_uint32 *) resbuf)[2] = SWAP (ctx->C); + ((sha1_uint32 *) resbuf)[3] = SWAP (ctx->D); + ((sha1_uint32 *) resbuf)[4] = SWAP (ctx->E); + + return resbuf; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32-bit value. */ +void * +sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf) +{ + /* Take yet unprocessed bytes into account. */ + sha1_uint32 bytes = ctx->buflen; + size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + ctx->buffer[size - 2] = SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29)); + ctx->buffer[size - 1] = SWAP (ctx->total[0] << 3); + + memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); + + /* Process last bytes. */ + sha1_process_block (ctx->buffer, size * 4, ctx); + + return sha1_read_ctx (ctx, resbuf); +} + +/* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +void * +sha1_buffer (const char *buffer, size_t len, void *resblock) +{ + struct sha1_ctx ctx; + + /* Initialize the computation context. */ + sha1_init_ctx (&ctx); + + /* Process whole buffer but last len % 64 bytes. */ + sha1_process_bytes (buffer, len, &ctx); + + /* Put result in desired memory area. */ + return sha1_finish_ctx (&ctx, resblock); +} + +void +sha1_process_bytes (const void *buffer, size_t len, struct sha1_ctx *ctx) +{ + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) + { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy (&((char *) ctx->buffer)[left_over], buffer, add); + ctx->buflen += add; + + if (ctx->buflen > 64) + { + sha1_process_block (ctx->buffer, ctx->buflen & ~63, ctx); + + ctx->buflen &= 63; + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, + &((char *) ctx->buffer)[(left_over + add) & ~63], + ctx->buflen); + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len >= 64) + { +#if !_STRING_ARCH_unaligned +# define alignof(type) offsetof (struct { char c; type x; }, x) +# define UNALIGNED_P(p) (((size_t) p) % alignof (sha1_uint32) != 0) + if (UNALIGNED_P (buffer)) + while (len > 64) + { + sha1_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); + buffer = (const char *) buffer + 64; + len -= 64; + } + else +#endif + { + sha1_process_block (buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0) + { + size_t left_over = ctx->buflen; + + memcpy (&((char *) ctx->buffer)[left_over], buffer, len); + left_over += len; + if (left_over >= 64) + { + sha1_process_block (ctx->buffer, 64, ctx); + left_over -= 64; + memcpy (ctx->buffer, &ctx->buffer[16], left_over); + } + ctx->buflen = left_over; + } +} + +/* --- Code below is the primary difference between md5.c and sha1.c --- */ + +/* SHA1 round constants */ +#define K1 0x5a827999 +#define K2 0x6ed9eba1 +#define K3 0x8f1bbcdc +#define K4 0xca62c1d6 + +/* Round functions. Note that F2 is the same as F4. */ +#define F1(B,C,D) ( D ^ ( B & ( C ^ D ) ) ) +#define F2(B,C,D) (B ^ C ^ D) +#define F3(B,C,D) ( ( B & C ) | ( D & ( B | C ) ) ) +#define F4(B,C,D) (B ^ C ^ D) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. + Most of this code comes from GnuPG's cipher/sha1.c. */ + +void +sha1_process_block (const void *buffer, size_t len, struct sha1_ctx *ctx) +{ + const sha1_uint32 *words = (const sha1_uint32*) buffer; + size_t nwords = len / sizeof (sha1_uint32); + const sha1_uint32 *endp = words + nwords; + sha1_uint32 x[16]; + sha1_uint32 a = ctx->A; + sha1_uint32 b = ctx->B; + sha1_uint32 c = ctx->C; + sha1_uint32 d = ctx->D; + sha1_uint32 e = ctx->E; + + /* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + +#define rol(x, n) (((x) << (n)) | ((sha1_uint32) (x) >> (32 - (n)))) + +#define M(I) ( tm = x[I&0x0f] ^ x[(I-14)&0x0f] \ + ^ x[(I-8)&0x0f] ^ x[(I-3)&0x0f] \ + , (x[I&0x0f] = rol(tm, 1)) ) + +#define R(A,B,C,D,E,F,K,M) do { E += rol( A, 5 ) \ + + F( B, C, D ) \ + + K \ + + M; \ + B = rol( B, 30 ); \ + } while(0) + + while (words < endp) + { + sha1_uint32 tm; + int t; + for (t = 0; t < 16; t++) + { + x[t] = SWAP (*words); + words++; + } + + R( a, b, c, d, e, F1, K1, x[ 0] ); + R( e, a, b, c, d, F1, K1, x[ 1] ); + R( d, e, a, b, c, F1, K1, x[ 2] ); + R( c, d, e, a, b, F1, K1, x[ 3] ); + R( b, c, d, e, a, F1, K1, x[ 4] ); + R( a, b, c, d, e, F1, K1, x[ 5] ); + R( e, a, b, c, d, F1, K1, x[ 6] ); + R( d, e, a, b, c, F1, K1, x[ 7] ); + R( c, d, e, a, b, F1, K1, x[ 8] ); + R( b, c, d, e, a, F1, K1, x[ 9] ); + R( a, b, c, d, e, F1, K1, x[10] ); + R( e, a, b, c, d, F1, K1, x[11] ); + R( d, e, a, b, c, F1, K1, x[12] ); + R( c, d, e, a, b, F1, K1, x[13] ); + R( b, c, d, e, a, F1, K1, x[14] ); + R( a, b, c, d, e, F1, K1, x[15] ); + R( e, a, b, c, d, F1, K1, M(16) ); + R( d, e, a, b, c, F1, K1, M(17) ); + R( c, d, e, a, b, F1, K1, M(18) ); + R( b, c, d, e, a, F1, K1, M(19) ); + R( a, b, c, d, e, F2, K2, M(20) ); + R( e, a, b, c, d, F2, K2, M(21) ); + R( d, e, a, b, c, F2, K2, M(22) ); + R( c, d, e, a, b, F2, K2, M(23) ); + R( b, c, d, e, a, F2, K2, M(24) ); + R( a, b, c, d, e, F2, K2, M(25) ); + R( e, a, b, c, d, F2, K2, M(26) ); + R( d, e, a, b, c, F2, K2, M(27) ); + R( c, d, e, a, b, F2, K2, M(28) ); + R( b, c, d, e, a, F2, K2, M(29) ); + R( a, b, c, d, e, F2, K2, M(30) ); + R( e, a, b, c, d, F2, K2, M(31) ); + R( d, e, a, b, c, F2, K2, M(32) ); + R( c, d, e, a, b, F2, K2, M(33) ); + R( b, c, d, e, a, F2, K2, M(34) ); + R( a, b, c, d, e, F2, K2, M(35) ); + R( e, a, b, c, d, F2, K2, M(36) ); + R( d, e, a, b, c, F2, K2, M(37) ); + R( c, d, e, a, b, F2, K2, M(38) ); + R( b, c, d, e, a, F2, K2, M(39) ); + R( a, b, c, d, e, F3, K3, M(40) ); + R( e, a, b, c, d, F3, K3, M(41) ); + R( d, e, a, b, c, F3, K3, M(42) ); + R( c, d, e, a, b, F3, K3, M(43) ); + R( b, c, d, e, a, F3, K3, M(44) ); + R( a, b, c, d, e, F3, K3, M(45) ); + R( e, a, b, c, d, F3, K3, M(46) ); + R( d, e, a, b, c, F3, K3, M(47) ); + R( c, d, e, a, b, F3, K3, M(48) ); + R( b, c, d, e, a, F3, K3, M(49) ); + R( a, b, c, d, e, F3, K3, M(50) ); + R( e, a, b, c, d, F3, K3, M(51) ); + R( d, e, a, b, c, F3, K3, M(52) ); + R( c, d, e, a, b, F3, K3, M(53) ); + R( b, c, d, e, a, F3, K3, M(54) ); + R( a, b, c, d, e, F3, K3, M(55) ); + R( e, a, b, c, d, F3, K3, M(56) ); + R( d, e, a, b, c, F3, K3, M(57) ); + R( c, d, e, a, b, F3, K3, M(58) ); + R( b, c, d, e, a, F3, K3, M(59) ); + R( a, b, c, d, e, F4, K4, M(60) ); + R( e, a, b, c, d, F4, K4, M(61) ); + R( d, e, a, b, c, F4, K4, M(62) ); + R( c, d, e, a, b, F4, K4, M(63) ); + R( b, c, d, e, a, F4, K4, M(64) ); + R( a, b, c, d, e, F4, K4, M(65) ); + R( e, a, b, c, d, F4, K4, M(66) ); + R( d, e, a, b, c, F4, K4, M(67) ); + R( c, d, e, a, b, F4, K4, M(68) ); + R( b, c, d, e, a, F4, K4, M(69) ); + R( a, b, c, d, e, F4, K4, M(70) ); + R( e, a, b, c, d, F4, K4, M(71) ); + R( d, e, a, b, c, F4, K4, M(72) ); + R( c, d, e, a, b, F4, K4, M(73) ); + R( b, c, d, e, a, F4, K4, M(74) ); + R( a, b, c, d, e, F4, K4, M(75) ); + R( e, a, b, c, d, F4, K4, M(76) ); + R( d, e, a, b, c, F4, K4, M(77) ); + R( c, d, e, a, b, F4, K4, M(78) ); + R( b, c, d, e, a, F4, K4, M(79) ); + + a = ctx->A += a; + b = ctx->B += b; + c = ctx->C += c; + d = ctx->D += d; + e = ctx->E += e; + } +} @@ -0,0 +1,95 @@ +/* Declarations of functions and data types used for SHA1 sum + library functions. + Copyright (C) 2000, 2001, 2003, 2005, 2006, 2008, 2010 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 3, or (at your option) any + later version. + + 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 + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef SHA1_H +# define SHA1_H 1 + +#include <stdio.h> +#include <limits.h> +#include <stdint.h> + +/* The following contortions are an attempt to use the C preprocessor + to determine an unsigned integral type that is 32 bits wide. An + alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but + doing that would require that the configure script compile and *run* + the resulting executable. Locally running cross-compiled executables + is usually not possible. */ + +#include <sys/types.h> +typedef u_int32_t sha1_uint32; +typedef uintptr_t sha1_uintptr; + +/* Structure to save state of computation between the single steps. */ +struct sha1_ctx +{ + sha1_uint32 A; + sha1_uint32 B; + sha1_uint32 C; + sha1_uint32 D; + sha1_uint32 E; + + sha1_uint32 total[2]; + sha1_uint32 buflen; + sha1_uint32 buffer[32]; +}; + + +/* Initialize structure containing state of computation. */ +extern void sha1_init_ctx (struct sha1_ctx *ctx); + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is necessary that LEN is a multiple of 64!!! */ +extern void sha1_process_block (const void *buffer, size_t len, + struct sha1_ctx *ctx); + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is NOT required that LEN is a multiple of 64. */ +extern void sha1_process_bytes (const void *buffer, size_t len, + struct sha1_ctx *ctx); + +/* Process the remaining bytes in the buffer and put result from CTX + in first 20 bytes following RESBUF. The result is always in little + endian byte order, so that a byte-wise output yields to the wanted + ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF be correctly + aligned for a 32 bits value. */ +extern void *sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf); + + +/* Put result from CTX in first 20 bytes following RESBUF. The result is + always in little endian byte order, so that a byte-wise output yields + to the wanted ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +extern void *sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf); + + +/* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +extern void *sha1_buffer (const char *buffer, size_t len, void *resblock); + +#endif diff --git a/testsuite/config/default.exp b/testsuite/config/default.exp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/testsuite/config/default.exp diff --git a/testsuite/dwz.tests/cycle.c b/testsuite/dwz.tests/cycle.c new file mode 100644 index 0000000..4795a79 --- /dev/null +++ b/testsuite/dwz.tests/cycle.c @@ -0,0 +1,13 @@ +struct s; + +struct s { + struct s *p; +}; + +struct s var; + +int +main (void) +{ + return 0; +} diff --git a/testsuite/dwz.tests/cycle.sh b/testsuite/dwz.tests/cycle.sh new file mode 100644 index 0000000..ce42521 --- /dev/null +++ b/testsuite/dwz.tests/cycle.sh @@ -0,0 +1,36 @@ +readelf_flags="" +if readelf -h 2>&1 | grep -q "\-wN"; then + readelf_flags=-wN +fi + +cp $execs/cycle 1 + +# Using mode 3 in checksum_die_ref. +$execs/dwz-for-test 1 -o 1.z --devel-dump-dies 2> DUMP.1 +rm -f 1.z + +# Skipping mode 3 in checksum_die_ref. +$execs/dwz-for-test 1 -o 1.z --devel-dump-dies --devel-no-checksum-cycle-opt 2> DUMP.2 +rm -f 1.z + +# Verify that mode 3 and mode 4 have different checksums. +grep " s structure_type" DUMP.1 > LINE.1 +grep " s structure_type" DUMP.2 > LINE.2 +! diff -q LINE.1 LINE.2 +rm -f DUMP.1 DUMP.2 LINE.1 LINE.2 + +# Verify that dwz actually works with --devel-no-checksum-cycle-opt. +cp 1 2 +$execs/dwz-for-test -m 3 1 2 --devel-no-checksum-cycle-opt --devel-ignore-size + +cnt=$(readelf -wi 3 | grep -c "DW_AT_name.*: s$") +[ $cnt -eq 1 ] + +# Even with -wN readelf 2.38-15.fc37 follows and prints the contents +# of the alt file. So make sure it cannot do that by removing it. +rm 3 + +cnt=$(readelf -wi $readelf_flags 1 | grep -c "DW_AT_name.*: s$" || true) +[ $cnt -eq 0 ] + +rm -f 1 2 3 diff --git a/testsuite/dwz.tests/decl.cc b/testsuite/dwz.tests/decl.cc new file mode 100644 index 0000000..4953853 --- /dev/null +++ b/testsuite/dwz.tests/decl.cc @@ -0,0 +1,3 @@ +class ao_ref; + +ao_ref *p3; diff --git a/testsuite/dwz.tests/def.cc b/testsuite/dwz.tests/def.cc new file mode 100644 index 0000000..8645b9c --- /dev/null +++ b/testsuite/dwz.tests/def.cc @@ -0,0 +1,11 @@ +#include "def.h" + +ao_ref *p1; + +int +main (void) +{ + ao_ref *p1 = new ao_ref; + return 0; +} + diff --git a/testsuite/dwz.tests/def.h b/testsuite/dwz.tests/def.h new file mode 100644 index 0000000..dc3a602 --- /dev/null +++ b/testsuite/dwz.tests/def.h @@ -0,0 +1,10 @@ +class bla { + int a; + public: + bla() { a = 1; } +}; + +class ao_ref +{ + bla a; +}; diff --git a/testsuite/dwz.tests/def2.cc b/testsuite/dwz.tests/def2.cc new file mode 100644 index 0000000..794933f --- /dev/null +++ b/testsuite/dwz.tests/def2.cc @@ -0,0 +1,9 @@ +#include "def.h" + +ao_ref *p2; + +void +foo (void) +{ + p2 = new ao_ref; +} diff --git a/testsuite/dwz.tests/devel-ignore-locus.sh b/testsuite/dwz.tests/devel-ignore-locus.sh new file mode 100644 index 0000000..a8b2dcd --- /dev/null +++ b/testsuite/dwz.tests/devel-ignore-locus.sh @@ -0,0 +1,27 @@ +cp $execs/two-typedef 1 + +cnt=$(readelf -wi 1 \ + | grep 'DW_AT_name.*: aaa' \ + | wc -l) + +[ $cnt -eq 2 ] + +$execs/dwz-for-test 1 2>/dev/null + +cnt=$(readelf -wi 1 \ + | grep 'DW_AT_name.*: aaa' \ + | wc -l) + +[ $cnt -eq 2 ] + +cp $execs/two-typedef 1 + +$execs/dwz-for-test --devel-ignore-locus --devel-ignore-size 1 + +cnt=$(readelf -wi 1 \ + | grep 'DW_AT_name.*: aaa' \ + | wc -l) + +[ $cnt -eq 1 ] + +rm -f 1 diff --git a/testsuite/dwz.tests/devel-ignore-size.sh b/testsuite/dwz.tests/devel-ignore-size.sh new file mode 100644 index 0000000..a30684f --- /dev/null +++ b/testsuite/dwz.tests/devel-ignore-size.sh @@ -0,0 +1,29 @@ +cp $execs/min 1 + +cnt=$(readelf -wi 1 \ + | grep '(DW_TAG_partial_unit' \ + | wc -l) + +[ $cnt -eq 0 ] + +$execs/dwz-for-test 1 2>/dev/null + +cnt=$(readelf -wi 1 \ + | grep '(DW_TAG_partial_unit' \ + | wc -l) + +if [ $cnt -ne 0 ]; then + exit 77 +fi + +cp $execs/min 1 + +$execs/dwz-for-test --devel-ignore-size 1 + +cnt=$(readelf -wi 1 \ + | grep '(DW_TAG_partial_unit' \ + | wc -l) + +[ $cnt -gt 0 ] + +rm -f 1 diff --git a/testsuite/dwz.tests/devel-trace.sh b/testsuite/dwz.tests/devel-trace.sh new file mode 100644 index 0000000..50c4a3d --- /dev/null +++ b/testsuite/dwz.tests/devel-trace.sh @@ -0,0 +1,5 @@ +cp $execs/hello 1 + +$execs/dwz-for-test --devel-trace 1 2>/dev/null + +rm -f 1 diff --git a/testsuite/dwz.tests/dw2-skip-prologue.S b/testsuite/dwz.tests/dw2-skip-prologue.S new file mode 100644 index 0000000..4d2e198 --- /dev/null +++ b/testsuite/dwz.tests/dw2-skip-prologue.S @@ -0,0 +1,403 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2011-2019 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 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 + along with this program. If not, see <http://www.gnu.org/licenses/>. + + This was copied from the git://sourceware.org/git/binutils-gdb.git + repository, file gdb/testsuite/gdb.dwarf2/dw2-skip-prologue.S. */ + +#if PTRBITS == 64 +# define PTRBYTE .8byte +#elif PTRBITS == 32 +# define PTRBYTE .4byte +#else +# error "Unsupported pointer size" +#endif + + .section .debug_info +.Lcu1_begin: + /* CU header */ + .4byte .Lcu1_end - .Lcu1_start /* Length of Compilation Unit */ +.Lcu1_start: + .2byte 2 /* DWARF Version */ + .4byte .Labbrev1_begin /* Offset into abbrev section */ + .byte PTRBITS / 8 /* Pointer size */ + + /* CU die */ + .uleb128 1 /* Abbrev: DW_TAG_compile_unit */ + .4byte .Lline1_begin /* DW_AT_stmt_list */ + PTRBYTE func_start /* DW_AT_low_pc */ + PTRBYTE func_end /* DW_AT_high_pc */ + .ascii "main.c\0" /* DW_AT_name */ + .ascii "GNU C 4.5.0\0" /* DW_AT_producer must be >= 4.5 */ + .byte 2 /* DW_AT_language (DW_LANG_C) */ + + .uleb128 2 /* Abbrev: DW_TAG_subprogram */ + .byte 1 /* DW_AT_external */ + .ascii "func\0" /* DW_AT_name */ + .4byte .Ltype_int-.Lcu1_begin /* DW_AT_type */ + PTRBYTE func_start /* DW_AT_low_pc */ + PTRBYTE func_end /* DW_AT_high_pc */ + +/* GDB `has_loclist' detection of -O2 -g code needs to see a DW_AT_location + location list. There may exist -O2 -g CUs still not needing/using any such + location list - exactly like this CU. Make one up. */ + + .uleb128 0x7 /* (DIE (0x42) DW_TAG_formal_parameter) */ + .ascii "param\0" /* DW_AT_name */ + .4byte .Ltype_int - .Lcu1_begin /* DW_AT_type */ + .4byte loclist /* DW_AT_location */ + + .uleb128 4 /* Abbrev: DW_TAG_inlined_subroutine */ + .ascii "inlined\0" /* DW_AT_name */ + PTRBYTE func0 /* DW_AT_low_pc */ + PTRBYTE func1 /* DW_AT_high_pc */ + .byte 3 /* DW_AT_inline (DW_INL_declared_inlined) */ + .byte 1 /* DW_AT_call_file */ + .byte 8 /* DW_AT_call_line */ + + .uleb128 4 /* Abbrev: DW_TAG_inlined_subroutine */ + .ascii "inlined2\0" /* DW_AT_name */ + PTRBYTE func2 /* DW_AT_low_pc */ + PTRBYTE func3 /* DW_AT_high_pc */ + .byte 3 /* DW_AT_inline (DW_INL_declared_inlined) */ + .byte 1 /* DW_AT_call_file */ + .byte 11 /* DW_AT_call_line */ + +#ifdef INLINED + .uleb128 4 /* Abbrev: DW_TAG_inlined_subroutine */ + .ascii "otherinline\0" /* DW_AT_name */ + PTRBYTE func3 /* DW_AT_low_pc */ + PTRBYTE func_end /* DW_AT_high_pc */ + .byte 3 /* DW_AT_inline (DW_INL_declared_inlined) */ + .byte 1 /* DW_AT_call_file */ + .byte 9 /* DW_AT_call_line */ +#endif + +#ifdef LEXICAL + .uleb128 5 /* Abbrev: DW_TAG_lexical_block */ + PTRBYTE func3 /* DW_AT_low_pc */ + PTRBYTE func_end /* DW_AT_high_pc */ + + /* GDB would otherwise ignore the DW_TAG_lexical_block. */ + .uleb128 6 /* Abbrev: DW_TAG_variable */ + .ascii "lexicalvar\0" /* DW_AT_name */ + .4byte .Ltype_int-.Lcu1_begin /* DW_AT_type */ + + .byte 0 /* End of children of DW_TAG_lexical_block */ +#endif + + .byte 0 /* End of children of DW_TAG_subprogram */ + +/* Simulate `fund' is also named `func' so that the function name matches and + fund's SAL is not discarded in expand_line_sal_maybe. */ + + .uleb128 2 /* Abbrev: DW_TAG_subprogram */ + .byte 1 /* DW_AT_external */ + .ascii "func\0" /* DW_AT_name */ + .4byte .Ltype_int-.Lcu1_begin /* DW_AT_type */ + PTRBYTE fund_start /* DW_AT_low_pc */ + PTRBYTE fund_end /* DW_AT_high_pc */ + + .byte 0 /* End of children of DW_TAG_subprogram */ + +.Ltype_int: + .uleb128 3 /* Abbrev: DW_TAG_base_type */ + .ascii "int\0" /* DW_AT_name */ + .byte 4 /* DW_AT_byte_size */ + .byte 5 /* DW_AT_encoding */ + + .byte 0 /* End of children of CU */ + +.Lcu1_end: + + .section .debug_loc +loclist: + /* Reset the location list base address first. */ + .4byte -1, 0 + + PTRBYTE func_start, func_end + .2byte 2f-1f +1: .byte 0x50 /* DW_OP_reg0 */ +2: + /* Location list end. */ + .4byte 0, 0 + +/* Abbrev table */ + .section .debug_abbrev +.Labbrev1_begin: + .uleb128 1 /* Abbrev code */ + .uleb128 0x11 /* DW_TAG_compile_unit */ + .byte 1 /* has_children */ + .uleb128 0x10 /* DW_AT_stmt_list */ + .uleb128 0x6 /* DW_FORM_data4 */ + .uleb128 0x11 /* DW_AT_low_pc */ + .uleb128 0x1 /* DW_FORM_addr */ + .uleb128 0x12 /* DW_AT_high_pc */ + .uleb128 0x1 /* DW_FORM_addr */ + .uleb128 0x3 /* DW_AT_name */ + .uleb128 0x8 /* DW_FORM_string */ + .uleb128 0x25 /* DW_AT_producer */ + .uleb128 0x8 /* DW_FORM_string */ + .uleb128 0x13 /* DW_AT_language */ + .uleb128 0xb /* DW_FORM_data1 */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + + .uleb128 2 /* Abbrev code */ + .uleb128 0x2e /* DW_TAG_subprogram */ + .byte 1 /* has_children */ + .uleb128 0x3f /* DW_AT_external */ + .uleb128 0xc /* DW_FORM_flag */ + .uleb128 0x3 /* DW_AT_name */ + .uleb128 0x8 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x11 /* DW_AT_low_pc */ + .uleb128 0x1 /* DW_FORM_addr */ + .uleb128 0x12 /* DW_AT_high_pc */ + .uleb128 0x1 /* DW_FORM_addr */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + + .uleb128 3 /* Abbrev code */ + .uleb128 0x24 /* DW_TAG_base_type */ + .byte 0 /* has_children */ + .uleb128 0x3 /* DW_AT_name */ + .uleb128 0x8 /* DW_FORM_string */ + .uleb128 0xb /* DW_AT_byte_size */ + .uleb128 0xb /* DW_FORM_data1 */ + .uleb128 0x3e /* DW_AT_encoding */ + .uleb128 0xb /* DW_FORM_data1 */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + + .uleb128 4 /* Abbrev code */ + .uleb128 0x1d /* DW_TAG_inlined_subroutine */ + .byte 0 /* has_children */ + .uleb128 0x3 /* DW_AT_name */ + .uleb128 0x8 /* DW_FORM_string */ + .uleb128 0x11 /* DW_AT_low_pc */ + .uleb128 0x1 /* DW_FORM_addr */ + .uleb128 0x12 /* DW_AT_high_pc */ + .uleb128 0x1 /* DW_FORM_addr */ + .uleb128 0x20 /* DW_AT_inline */ + .uleb128 0xb /* DW_FORM_data1 */ + .uleb128 0x58 /* DW_AT_call_file */ + .uleb128 0xb /* DW_FORM_data1 */ + .uleb128 0x59 /* DW_AT_call_line */ + .uleb128 0xb /* DW_FORM_data1 */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + + .uleb128 5 /* Abbrev code */ + .uleb128 0x0b /* DW_TAG_lexical_block */ + .byte 1 /* has_children */ + .uleb128 0x11 /* DW_AT_low_pc */ + .uleb128 0x1 /* DW_FORM_addr */ + .uleb128 0x12 /* DW_AT_high_pc */ + .uleb128 0x1 /* DW_FORM_addr */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + + .uleb128 6 /* Abbrev code */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x3 /* DW_AT_name */ + .uleb128 0x8 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + + .uleb128 0x7 /* (abbrev code) */ + .uleb128 0x5 /* (TAG: DW_TAG_formal_parameter) */ + .byte 0x0 /* DW_children_no */ + .uleb128 0x3 /* DW_AT_name */ + .uleb128 0x8 /* DW_FORM_string */ + .uleb128 0x49 /* (DW_AT_type) */ + .uleb128 0x13 /* (DW_FORM_ref4) */ + .uleb128 0x02 /* (DW_AT_location) */ + .uleb128 0x06 /* (DW_FORM_data4) */ + .byte 0x0 + .byte 0x0 + + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + +/* Line table */ + .section .debug_line +.Lline1_begin: + .4byte .Lline1_end - .Lline1_start /* Initial length */ +.Lline1_start: + .2byte 2 /* Version */ + .4byte .Lline1_lines - .Lline1_hdr /* header_length */ +.Lline1_hdr: + .byte 1 /* Minimum insn length */ + .byte 1 /* default_is_stmt */ + .byte 1 /* line_base */ + .byte 1 /* line_range */ + .byte 0x10 /* opcode_base */ + + /* Standard lengths */ + .byte 0 + .byte 1 + .byte 1 + .byte 1 + .byte 1 + .byte 0 + .byte 0 + .byte 0 + .byte 1 + .byte 0 + .byte 0 + .byte 1 + .byte 0 + .byte 0 + .byte 0 + + /* Include directories */ + .byte 0 + + /* File names */ + .ascii "main.c\0" + .uleb128 0 + .uleb128 0 + .uleb128 0 + + .ascii "other.c\0" + .uleb128 0 + .uleb128 0 + .uleb128 0 + + .byte 0 + +.Lline1_lines: + .byte 0 /* DW_LNE_set_address */ + .uleb128 5 + .byte 2 + PTRBYTE func_start + .byte 3 /* DW_LNS_advance_line */ + .sleb128 4 /* ... to 5 */ + .byte 1 /* DW_LNS_copy */ + + .byte 0 /* DW_LNE_set_address */ + .uleb128 5 + .byte 2 + PTRBYTE func0 + .byte 4 /* DW_LNS_set_file */ + .uleb128 2 + .byte 3 /* DW_LNS_advance_line */ + .sleb128 -4 /* ... to 1 */ + .byte 1 /* DW_LNS_copy */ + + .byte 0 /* DW_LNE_set_address */ + .uleb128 5 + .byte 2 + PTRBYTE func1 + .byte 4 /* DW_LNS_set_file */ + .uleb128 1 + .byte 3 /* DW_LNS_advance_line */ + .sleb128 8 /* ... to 9 */ + .byte 1 /* DW_LNS_copy */ + + .byte 0 /* DW_LNE_set_address */ + .uleb128 5 + .byte 2 + PTRBYTE func2 + .byte 4 /* DW_LNS_set_file */ + .uleb128 2 + .byte 3 /* DW_LNS_advance_line */ + .sleb128 -8 /* ... to 1 */ + .byte 1 /* DW_LNS_copy */ + + .byte 0 /* DW_LNE_set_address */ + .uleb128 5 + .byte 2 + PTRBYTE func3 + .byte 4 /* DW_LNS_set_file */ + .uleb128 1 + .byte 3 /* DW_LNS_advance_line */ + .sleb128 8 /* ... to 9 */ + .byte 1 /* DW_LNS_copy */ + + .byte 0 /* DW_LNE_set_address */ + .uleb128 5 + .byte 2 + PTRBYTE func_end + +/* Equivalent copy but renamed s/func/fund/. */ + + .byte 0 /* DW_LNE_set_address */ + .uleb128 5 + .byte 2 + PTRBYTE fund_start + .byte 3 /* DW_LNS_advance_line */ + .sleb128 -4 /* ... to 5 */ + .byte 1 /* DW_LNS_copy */ + + .byte 0 /* DW_LNE_set_address */ + .uleb128 5 + .byte 2 + PTRBYTE fund0 + .byte 4 /* DW_LNS_set_file */ + .uleb128 2 + .byte 3 /* DW_LNS_advance_line */ + .sleb128 -4 /* ... to 1 */ + .byte 1 /* DW_LNS_copy */ + + .byte 0 /* DW_LNE_set_address */ + .uleb128 5 + .byte 2 + PTRBYTE fund1 + .byte 4 /* DW_LNS_set_file */ + .uleb128 1 + .byte 3 /* DW_LNS_advance_line */ + .sleb128 8 /* ... to 9 */ + .byte 1 /* DW_LNS_copy */ + + .byte 0 /* DW_LNE_set_address */ + .uleb128 5 + .byte 2 + PTRBYTE fund2 + .byte 4 /* DW_LNS_set_file */ + .uleb128 2 + .byte 3 /* DW_LNS_advance_line */ + .sleb128 -8 /* ... to 1 */ + .byte 1 /* DW_LNS_copy */ + + .byte 0 /* DW_LNE_set_address */ + .uleb128 5 + .byte 2 + PTRBYTE fund3 + .byte 4 /* DW_LNS_set_file */ + .uleb128 1 + .byte 3 /* DW_LNS_advance_line */ + .sleb128 8 /* ... to 9 */ + .byte 1 /* DW_LNS_copy */ + + .byte 0 /* DW_LNE_set_address */ + .uleb128 5 + .byte 2 + PTRBYTE fund_end + +/* Line numbering end. */ + + .byte 0 /* DW_LNE_end_of_sequence */ + .uleb128 1 + .byte 1 + +.Lline1_end: + .section .note.GNU-stack,"",@progbits diff --git a/testsuite/dwz.tests/dw2-skip-prologue.c b/testsuite/dwz.tests/dw2-skip-prologue.c new file mode 100644 index 0000000..34b0a7c --- /dev/null +++ b/testsuite/dwz.tests/dw2-skip-prologue.c @@ -0,0 +1,61 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2011-2019 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 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 + along with this program. If not, see <http://www.gnu.org/licenses/>. + + This was copied from the git://sourceware.org/git/binutils-gdb.git + repository, file gdb/testsuite/gdb.dwarf2/dw2-skip-prologue.c. */ + +static volatile int v; + +asm ("func_start: .globl func_start\n"); +static int +func (void) +{ + v++; +asm ("func0: .globl func0\n"); + v++; +asm ("func1: .globl func1\n"); + v++; +asm ("func2: .globl func2\n"); + v++; +asm ("func3: .globl func3\n"); + return v; +} +asm ("func_end: .globl func_end\n"); + +/* Equivalent copy but renamed s/func/fund/. */ + +asm ("fund_start: .globl fund_start\n"); +static int +fund (void) +{ + v++; +asm ("fund0: .globl fund0\n"); + v++; +asm ("fund1: .globl fund1\n"); + v++; +asm ("fund2: .globl fund2\n"); + v++; +asm ("fund3: .globl fund3\n"); + return v; +} +asm ("fund_end: .globl fund_end\n"); + +int +main (void) +{ + return func () + fund (); +} diff --git a/testsuite/dwz.tests/dwz-tests.exp b/testsuite/dwz.tests/dwz-tests.exp new file mode 100644 index 0000000..811767f --- /dev/null +++ b/testsuite/dwz.tests/dwz-tests.exp @@ -0,0 +1,145 @@ +set tests [find $srcdir/$subdir *.sh] + +set pwd [pwd] + +set env(PATH) $srcdir/scripts:$::env(PATH) +set env(execs) $pwd + +exec mkdir -p $pwd/testsuite/dwz.tests/execs +exec $srcdir/scripts/xunzip-dir.sh \ + $srcdir/dwz.tests/execs.xz \ + $pwd/testsuite/dwz.tests/execs + +foreach test $tests { + global runtests + + set basename [exec basename $test] + + if ![runtest_file_p $runtests $basename] { + continue + } + + set required_execs [] + if { $basename == "pr24173.sh" } { + lappend required_execs "py-section-script" + } + if { $basename == "pr24341.sh" } { + if { [catch {exec readelf -wi min | grep DW_AT_name | grep -c / } matches] } { + # Some error occurred in the supported test, f.i. DWARF in min + # unsupported by readelf. + unsupported "$test" + continue + } + if { $matches != 2 } { + # The exec min is used for the regression test for pr24341, but it + # only functions as such if the DWARF only contains the CUs of the + # test-case itself. + unsupported "$test" + continue + } + } + if { $basename == "pr24172.sh" } { + if { ![istarget x86_64-*-*] } { + unsupported "$test" + continue + } + lappend required_execs "dw2-skip-prologue" + } + if { $basename == "pr24170.sh" } { + lappend required_execs "implptr-64bit-d2o4a8r8t0" + } + if { $basename == "gold-gdb-index.sh" } { + lappend required_execs "hello-gold-gdb-index" + } + if { $basename == "pr24747.sh" } { + lappend required_execs "start-gold" + } + if { $basename == "pr24771.sh" } { + lappend required_execs "hello-gnu-pubnames" + } + if { $basename == "pr24823.sh" } { + lappend required_execs "varval" + } + if { ![istarget x86_64-*-*] } { + if { $basename == "pr24468.sh" } { + unsupported "$test" + continue + } + if { $basename == "ld-2.26.1-multifile.sh" } { + unsupported "$test" + continue + } + } + if { $basename == "gdb-add-index.sh" } { + if { [catch { exec which gdb-add-index } ] } { + unsupported "$test" + continue + } + } + if { [istarget mips*-*-*] } { + # elfutils don't have a MIPS backend (PR elfutils/24795), so eu-strip + # doesn't work as expected. + if { $basename == "eu-strip-unstrip.sh" || \ + $basename == "eu-strip-unstrip-multifile.sh" || \ + $basename == "pr24173.sh" } { + unsupported "$test" + continue + } + } + if { $basename == "pr25109.sh" } { + lappend required_execs no-multifile-prop + } + if { $basename == "pr27463.sh" } { + lappend required_execs unavailable-dwarf-piece + } + + set unsupported 0 + foreach required_exec $required_execs { + set exists [file exists $required_exec] + if { $exists == 0 } { + set unsupported 1 + break + } + set size [file size $required_exec] + if { $size == 0 } { + set unsupported 1 + break + } + } + if { $unsupported == 1 } { + unsupported "$test" + continue + } + + set dir $pwd/tmp.$basename + exec rm -Rf $dir + exec mkdir $dir + + cd $dir + if { [catch { exec sh -e $test } msg] } { + if { [lindex $::errorCode 0] == "CHILDSTATUS" && \ + [lindex $::errorCode 2] == 77 } { + unsupported "$test" + exec rm -Rf $dir + } else { + puts "$msg" + fail "$test" + } + } else { + if { [file exists dwz.info ] } { + set info [exec cat dwz.info] + verbose -log "$test:" 1 + verbose -log "$info" 1 + exec rm -f dwz.info + } + set file_list [glob -nocomplain "$dir/*"] + if {[llength $file_list] != 0} { + puts "$dir is not empty" + fail "$test" + } else { + pass "$test" + exec rm -Rf $dir + } + } + cd $pwd +} diff --git a/testsuite/dwz.tests/dwz.sh b/testsuite/dwz.tests/dwz.sh new file mode 100644 index 0000000..1cb16d2 --- /dev/null +++ b/testsuite/dwz.tests/dwz.sh @@ -0,0 +1,7 @@ +cp $execs/dwz-for-test 1 + +dwz 1 + +smaller-than.sh 1 $execs/dwz-for-test + +rm -f 1 diff --git a/testsuite/dwz.tests/eu-strip-unstrip-multifile.sh b/testsuite/dwz.tests/eu-strip-unstrip-multifile.sh new file mode 100644 index 0000000..afe0994 --- /dev/null +++ b/testsuite/dwz.tests/eu-strip-unstrip-multifile.sh @@ -0,0 +1,18 @@ +cp $execs/hello 1 +cp $execs/hello 2 + +eu-strip 1 -o 1.stripped -f 1.debug +eu-strip 2 -o 2.stripped -f 2.debug + +dwz -m 3 1.debug 2.debug + +eu-unstrip 1.stripped 1.debug -o 1.unstripped +eu-unstrip 2.stripped 2.debug -o 2.unstripped + +smaller-than.sh 1.unstripped 1 +smaller-than.sh 2.unstripped 2 + +[ "$(gnu-debugaltlink-name.sh 1.unstripped)" = "3" ] +[ "$(gnu-debugaltlink-name.sh 2.unstripped)" = "3" ] + +rm -f 1 1.debug 1.stripped 1.unstripped 2 2.debug 2.stripped 2.unstripped 3 diff --git a/testsuite/dwz.tests/eu-strip-unstrip.sh b/testsuite/dwz.tests/eu-strip-unstrip.sh new file mode 100644 index 0000000..ce3adad --- /dev/null +++ b/testsuite/dwz.tests/eu-strip-unstrip.sh @@ -0,0 +1,11 @@ +cp $execs/hello 1 + +eu-strip 1 -o 1.stripped -f 1.debug + +dwz 1.debug + +eu-unstrip 1.stripped 1.debug -o 1.unstripped + +smaller-than.sh 1.unstripped 1 + +rm -f 1 1.debug 1.stripped 1.unstripped diff --git a/testsuite/dwz.tests/execs.xz/hello-ld-2.26.1.xz b/testsuite/dwz.tests/execs.xz/hello-ld-2.26.1.xz Binary files differnew file mode 100755 index 0000000..3514195 --- /dev/null +++ b/testsuite/dwz.tests/execs.xz/hello-ld-2.26.1.xz diff --git a/testsuite/dwz.tests/execs.xz/hello-leap-15.0.xz b/testsuite/dwz.tests/execs.xz/hello-leap-15.0.xz Binary files differnew file mode 100755 index 0000000..598c88e --- /dev/null +++ b/testsuite/dwz.tests/execs.xz/hello-leap-15.0.xz diff --git a/testsuite/dwz.tests/gdb-add-index.sh b/testsuite/dwz.tests/gdb-add-index.sh new file mode 100644 index 0000000..3095efb --- /dev/null +++ b/testsuite/dwz.tests/gdb-add-index.sh @@ -0,0 +1,15 @@ +cp $execs/hello 1 + +# Redirect gdb-add-index stderr to stdout. +# https://sourceware.org/bugzilla/show_bug.cgi?id=29316 +gdb-add-index 1 2>&1 + +readelf -S 1 | grep -q '\.gdb_index' + +dwz 1 -o 2 + +readelf -S 2 | grep -q '\.gdb_index' + +smaller-than.sh 2 1 + +rm -f 1 2 diff --git a/testsuite/dwz.tests/gold-gdb-index.sh b/testsuite/dwz.tests/gold-gdb-index.sh new file mode 100644 index 0000000..eb2a164 --- /dev/null +++ b/testsuite/dwz.tests/gold-gdb-index.sh @@ -0,0 +1,7 @@ +cp ../hello-gold-gdb-index 1 + +dwz 1 + +smaller-than.sh 1 ../hello-gold-gdb-index + +rm -f 1 diff --git a/testsuite/dwz.tests/hardlink-multifile.sh b/testsuite/dwz.tests/hardlink-multifile.sh new file mode 100644 index 0000000..160a7ed --- /dev/null +++ b/testsuite/dwz.tests/hardlink-multifile.sh @@ -0,0 +1,15 @@ +cp $execs/hello 1 +ln 1 2 +cp $execs/hello 3 + +dwz -h -m 4 1 2 3 + +hardlinks-p.sh 1 2 + +smaller-than.sh 1 $execs/hello +smaller-than.sh 3 $execs/hello + +[ "$(gnu-debugaltlink-name.sh 1)" = "4" ] +[ "$(gnu-debugaltlink-name.sh 3)" = "4" ] + +rm -f 1 2 3 4 diff --git a/testsuite/dwz.tests/hardlink.sh b/testsuite/dwz.tests/hardlink.sh new file mode 100644 index 0000000..3a956b2 --- /dev/null +++ b/testsuite/dwz.tests/hardlink.sh @@ -0,0 +1,11 @@ +cp $execs/hello 1 +ln 1 2 + +dwz -h 1 2 + +smaller-than.sh 1 $execs/hello +smaller-than.sh 2 $execs/hello + +hardlinks-p.sh 1 2 + +rm -f 1 2 diff --git a/testsuite/dwz.tests/hello.c b/testsuite/dwz.tests/hello.c new file mode 100644 index 0000000..82d070e --- /dev/null +++ b/testsuite/dwz.tests/hello.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +int +main (void) +{ + printf ("hello\n"); + return 0; +} diff --git a/testsuite/dwz.tests/help.sh b/testsuite/dwz.tests/help.sh new file mode 100644 index 0000000..b4d5e28 --- /dev/null +++ b/testsuite/dwz.tests/help.sh @@ -0,0 +1,13 @@ +cp $execs/hello 1 + +if dwz -? 1 2> dwz.err; status=$?; then + true +fi + +[ $status -eq 1 ] + +grep -q "Usage:" dwz.err + +cmp 1 $execs/hello + +rm -f 1 dwz.err diff --git a/testsuite/dwz.tests/illegal-option.sh b/testsuite/dwz.tests/illegal-option.sh new file mode 100644 index 0000000..f767f4c --- /dev/null +++ b/testsuite/dwz.tests/illegal-option.sh @@ -0,0 +1,25 @@ +cp $execs/hello 1 + +if dwz -x 1 2> dwz.err; status=$?; then + true +fi + +[ $status -eq 1 ] + +grep -q ": invalid option -- 'x'" dwz.err +grep -q "Usage:" dwz.err + +cmp 1 $execs/hello + +if dwz --x 1 2> dwz.err; status=$?; then + true +fi + +[ $status -eq 1 ] + +grep -q ": unrecognized option '--x'" dwz.err +grep -q "Usage:" dwz.err + +cmp 1 $execs/hello + +rm -f 1 dwz.err diff --git a/testsuite/dwz.tests/implptr-64bit-d2o4a8r8t0.S b/testsuite/dwz.tests/implptr-64bit-d2o4a8r8t0.S new file mode 100644 index 0000000..08e1563 --- /dev/null +++ b/testsuite/dwz.tests/implptr-64bit-d2o4a8r8t0.S @@ -0,0 +1,157 @@ +/* This was generated using git://sourceware.org/git/binutils-gdb.git + repository, file gdb/testsuite/gdb.dwarf2/implptr-64bit.exp. */ + .section .debug_info +.Lcu1_begin: + .4byte .Lcu1_end - .Lcu1_start +.Lcu1_start: + .2byte 2 /* Version */ + .4byte .Labbrev1_begin /* Abbrevs */ + .byte 8 /* Pointer size */ + .uleb128 2 /* Abbrev (DW_TAG_compile_unit) */ + .ascii "GNU C 4.4.3\0" + .sleb128 0x0001 + .ascii "1.c\0" +.Llabel3: + .uleb128 3 /* Abbrev (DW_TAG_base_type) */ + .sleb128 4 + .sleb128 0x5 + .ascii "int\0" +.Llabel1: + .uleb128 4 /* Abbrev (DW_TAG_structure_type) */ + .ascii "s\0" + .sleb128 4 + .uleb128 5 /* Abbrev (DW_TAG_member) */ + .ascii "f\0" + .4byte .Llabel3 - .Lcu1_begin + .byte 0 + .byte 0x0 /* Terminate children */ +.Llabel4: + .uleb128 6 /* Abbrev (DW_TAG_pointer_type) */ + .sleb128 8 + .4byte .Llabel1 - .Lcu1_begin +.Llabel2: + .uleb128 7 /* Abbrev (DW_TAG_variable) */ + .ascii "v\0" + .uleb128 .Lexpr_end6 - .Lexpr_start5/* expression */ +.Lexpr_start5: + .byte 0x9e /* DW_OP_implicit_value */ + .uleb128 .Lvalue_end8 - .Lvalue_start7 +.Lvalue_start7: + .byte 0x1 + .byte 0x1 + .byte 0x1 + .byte 0x1 +.Lvalue_end8: +.Lexpr_end6: + .8byte .Llabel1 - .Lcu1_begin + .uleb128 8 /* Abbrev (DW_TAG_subprogram) */ + .ascii "main\0" + .8byte main + .8byte main+0x100 + .8byte .Llabel3 + .byte 1 + .uleb128 9 /* Abbrev (DW_TAG_variable) */ + .ascii "p\0" + .uleb128 .Lexpr_end10 - .Lexpr_start9/* expression */ +.Lexpr_start9: + .byte 0xf2 /* DW_OP_GNU_implicit_pointer */ + .8byte .Llabel2 + .sleb128 0 +.Lexpr_end10: + .8byte .Llabel4 - .Lcu1_begin + .byte 0x0 /* Terminate children */ + .byte 0x0 /* Terminate children */ +.Lcu1_end: + .section .debug_abbrev +.Labbrev1_begin: + .uleb128 2 /* Abbrev start */ + .uleb128 0x11 /* DW_TAG_compile_unit */ + .byte 1 /* has_children */ + .uleb128 0x25 /* DW_AT_producer */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x13 /* DW_AT_language */ + .uleb128 0x0d /* DW_FORM_sdata */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 3 /* Abbrev start */ + .uleb128 0x24 /* DW_TAG_base_type */ + .byte 0 /* has_children */ + .uleb128 0x0b /* DW_AT_byte_size */ + .uleb128 0x0d /* DW_FORM_sdata */ + .uleb128 0x3e /* DW_AT_encoding */ + .uleb128 0x0d /* DW_FORM_sdata */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 4 /* Abbrev start */ + .uleb128 0x13 /* DW_TAG_structure_type */ + .byte 1 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x0b /* DW_AT_byte_size */ + .uleb128 0x0d /* DW_FORM_sdata */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 5 /* Abbrev start */ + .uleb128 0x0d /* DW_TAG_member */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x38 /* DW_AT_data_member_location */ + .uleb128 0x0b /* DW_FORM_data1 */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 6 /* Abbrev start */ + .uleb128 0x0f /* DW_TAG_pointer_type */ + .byte 0 /* has_children */ + .uleb128 0x0b /* DW_AT_byte_size */ + .uleb128 0x0d /* DW_FORM_sdata */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 7 /* Abbrev start */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x02 /* DW_AT_location */ + .uleb128 0x09 /* SPECIAL_expr */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x14 /* DW_FORM_ref8 */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 8 /* Abbrev start */ + .uleb128 0x2e /* DW_TAG_subprogram */ + .byte 1 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x11 /* DW_AT_low_pc */ + .uleb128 0x01 /* DW_FORM_addr */ + .uleb128 0x12 /* DW_AT_high_pc */ + .uleb128 0x01 /* DW_FORM_addr */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x10 /* DW_FORM_ref_addr */ + .uleb128 0x3f /* DW_AT_external */ + .uleb128 0x0c /* DW_FORM_flag */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 9 /* Abbrev start */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x02 /* DW_AT_location */ + .uleb128 0x09 /* SPECIAL_expr */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x14 /* DW_FORM_ref8 */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .section .note.GNU-stack,"",@progbits diff --git a/testsuite/dwz.tests/ld-2.26.1-multifile.sh b/testsuite/dwz.tests/ld-2.26.1-multifile.sh new file mode 100644 index 0000000..a63253c --- /dev/null +++ b/testsuite/dwz.tests/ld-2.26.1-multifile.sh @@ -0,0 +1,14 @@ +exec=../testsuite/dwz.tests/execs/hello-ld-2.26.1 + +cp $exec 1 +cp 1 2 + +dwz -m 3 1 2 + +smaller-than.sh 1 $exec +smaller-than.sh 2 $exec + +[ "$(gnu-debugaltlink-name.sh 1)" = "3" ] +[ "$(gnu-debugaltlink-name.sh 2)" = "3" ] + +rm -f 1 2 3 diff --git a/testsuite/dwz.tests/low-mem-die-limit-0.sh b/testsuite/dwz.tests/low-mem-die-limit-0.sh new file mode 100644 index 0000000..7105431 --- /dev/null +++ b/testsuite/dwz.tests/low-mem-die-limit-0.sh @@ -0,0 +1,13 @@ +cp $execs/hello 1 + +$execs/dwz-for-test \ + -l0 \ + --devel-trace \ + 1 \ + 2> dwz.err + +if grep -q "Compressing 1$" dwz.err; then + exit 1 +fi + +rm -f 1 dwz.err diff --git a/testsuite/dwz.tests/low-mem-die-limit-at-limit-multifile.sh b/testsuite/dwz.tests/low-mem-die-limit-at-limit-multifile.sh new file mode 100644 index 0000000..ece4fd5 --- /dev/null +++ b/testsuite/dwz.tests/low-mem-die-limit-at-limit-multifile.sh @@ -0,0 +1,23 @@ +cp $execs/hello 1 +cp 1 2 + +count=$(readelf -wi 1 \ + | grep '(DW_TAG' \ + | wc -l) +limit=$count + +if $execs/dwz-for-test \ + -l$limit \ + --devel-trace \ + -m 3 1 2 \ + 2> dwz.err; status=$?; then + true +fi + +if grep -q "Hit low-mem die-limit" dwz.err; then + exit 1 +fi + +[ $status -eq 0 ] + +rm -f 1 2 3 dwz.err diff --git a/testsuite/dwz.tests/low-mem-die-limit-at-limit.sh b/testsuite/dwz.tests/low-mem-die-limit-at-limit.sh new file mode 100644 index 0000000..03ff273 --- /dev/null +++ b/testsuite/dwz.tests/low-mem-die-limit-at-limit.sh @@ -0,0 +1,18 @@ +cp $execs/hello 1 + +count=$(readelf -wi 1 \ + | grep '(DW_TAG' \ + | wc -l) +limit=$count + +$execs/dwz-for-test \ + -l$limit \ + --devel-trace \ + 1 \ + 2> dwz.err + +if grep -q "Compressing 1 in low-mem mode" dwz.err; then + exit 1 +fi + +rm -f 1 dwz.err diff --git a/testsuite/dwz.tests/low-mem-die-limit-none.sh b/testsuite/dwz.tests/low-mem-die-limit-none.sh new file mode 100644 index 0000000..20b8cad --- /dev/null +++ b/testsuite/dwz.tests/low-mem-die-limit-none.sh @@ -0,0 +1,13 @@ +cp $execs/hello 1 + +$execs/dwz-for-test \ + -lnone \ + --devel-trace \ + 1 \ + 2> dwz.err + +if grep -q "Compressing 1 in low-mem mode" dwz.err; then + exit 1 +fi + +rm -f 1 dwz.err diff --git a/testsuite/dwz.tests/low-mem-die-limit-one-above.sh b/testsuite/dwz.tests/low-mem-die-limit-one-above.sh new file mode 100644 index 0000000..9b9a6ff --- /dev/null +++ b/testsuite/dwz.tests/low-mem-die-limit-one-above.sh @@ -0,0 +1,18 @@ +cp $execs/hello 1 + +count=$(readelf -wi 1 \ + | grep '(DW_TAG' \ + | wc -l) +limit=$((count + 1)) + +$execs/dwz-for-test \ + -l$limit \ + --devel-trace \ + 1 \ + 2> dwz.err + +if grep -q "Compressing 1 in low-mem mode" dwz.err; then + exit 1 +fi + +rm -f 1 dwz.err diff --git a/testsuite/dwz.tests/low-mem-die-limit-one-below.sh b/testsuite/dwz.tests/low-mem-die-limit-one-below.sh new file mode 100644 index 0000000..4136336 --- /dev/null +++ b/testsuite/dwz.tests/low-mem-die-limit-one-below.sh @@ -0,0 +1,18 @@ +cp $execs/hello 1 + +count=$(readelf -wi 1 \ + | grep '(DW_TAG' \ + | wc -l) +limit=$((count - 1)) + +$execs/dwz-for-test \ + -l$limit \ + --devel-trace \ + 1 \ + 2> dwz.err + +if ! grep -q "Compressing 1 in low-mem mode" dwz.err; then + exit 1 +fi + +rm -f 1 dwz.err diff --git a/testsuite/dwz.tests/low-mem.sh b/testsuite/dwz.tests/low-mem.sh new file mode 100644 index 0000000..e75a198 --- /dev/null +++ b/testsuite/dwz.tests/low-mem.sh @@ -0,0 +1,7 @@ +cp $execs/hello 1 + +dwz -l0 1 + +smaller-than.sh 1 $execs/hello + +rm -f 1 diff --git a/testsuite/dwz.tests/main.c b/testsuite/dwz.tests/main.c new file mode 100644 index 0000000..486b018 --- /dev/null +++ b/testsuite/dwz.tests/main.c @@ -0,0 +1,25 @@ +int +main (void) +{ + return 0; +} + +int +foo (int i) +{ + int j; + asm (".global foo_start_lbl\nfoo_start_lbl:"); + j = i *2; + asm (".global foo_end_lbl\nfoo_end_lbl:"); + return j; +} + +int +bar (int i) +{ + int j; + asm (".global bar_start_lbl\nbar_start_lbl:"); + j = i *2; + asm (".global bar_end_lbl\nbar_end_lbl:"); + return j; +} diff --git a/testsuite/dwz.tests/max-die-limit-none.sh b/testsuite/dwz.tests/max-die-limit-none.sh new file mode 100644 index 0000000..f45d566 --- /dev/null +++ b/testsuite/dwz.tests/max-die-limit-none.sh @@ -0,0 +1,7 @@ +cp $execs/hello 1 + +$execs/dwz-for-test \ + -Lnone \ + 1 + +rm -f 1 diff --git a/testsuite/dwz.tests/min-2.c b/testsuite/dwz.tests/min-2.c new file mode 100644 index 0000000..a1b61ce --- /dev/null +++ b/testsuite/dwz.tests/min-2.c @@ -0,0 +1,5 @@ +int +foo (void) +{ + return 0; +} diff --git a/testsuite/dwz.tests/min.c b/testsuite/dwz.tests/min.c new file mode 100644 index 0000000..cd4ef42 --- /dev/null +++ b/testsuite/dwz.tests/min.c @@ -0,0 +1,7 @@ +extern int foo (void); + +int +main (void) +{ + return 0; +} diff --git a/testsuite/dwz.tests/multifile-name.sh b/testsuite/dwz.tests/multifile-name.sh new file mode 100644 index 0000000..31e73f2 --- /dev/null +++ b/testsuite/dwz.tests/multifile-name.sh @@ -0,0 +1,12 @@ +cp $execs/hello 1 +cp $execs/hello 2 + +dwz -m 3 -M /xxx/yyy/3 1 2 + +smaller-than.sh 1 $execs/hello +smaller-than.sh 2 $execs/hello + +[ "$(gnu-debugaltlink-name.sh 1)" = "/xxx/yyy/3" ] +[ "$(gnu-debugaltlink-name.sh 2)" = "/xxx/yyy/3" ] + +rm -f 1 2 3 diff --git a/testsuite/dwz.tests/multifile-relative.sh b/testsuite/dwz.tests/multifile-relative.sh new file mode 100644 index 0000000..7254206 --- /dev/null +++ b/testsuite/dwz.tests/multifile-relative.sh @@ -0,0 +1,12 @@ +cp $execs/hello 1 +cp $execs/hello 2 + +dwz -m $(pwd -P)/3 -r 1 2 + +smaller-than.sh 1 $execs/hello +smaller-than.sh 2 $execs/hello + +[ "$(gnu-debugaltlink-name.sh 1)" = "3" ] +[ "$(gnu-debugaltlink-name.sh 2)" = "3" ] + +rm -f 1 2 3 diff --git a/testsuite/dwz.tests/multifile.sh b/testsuite/dwz.tests/multifile.sh new file mode 100644 index 0000000..b5f6112 --- /dev/null +++ b/testsuite/dwz.tests/multifile.sh @@ -0,0 +1,12 @@ +cp $execs/hello 1 +cp $execs/hello 2 + +dwz -m 3 1 2 + +smaller-than.sh 1 $execs/hello +smaller-than.sh 2 $execs/hello + +[ "$(gnu-debugaltlink-name.sh 1)" = "3" ] +[ "$(gnu-debugaltlink-name.sh 2)" = "3" ] + +rm -f 1 2 3 diff --git a/testsuite/dwz.tests/objcopy-eu-unstrip-multifile.sh b/testsuite/dwz.tests/objcopy-eu-unstrip-multifile.sh new file mode 100644 index 0000000..15ed418 --- /dev/null +++ b/testsuite/dwz.tests/objcopy-eu-unstrip-multifile.sh @@ -0,0 +1,39 @@ +cp ../hello 1 + +objcopy --only-keep-debug 1 1.debug +objcopy --strip-debug 1 1.stripped + +if ! eu-unstrip 1.stripped 1.debug -o 1.unstripped; then + exit 77 +fi +rm 1.unstripped + +cp 1.debug 2.debug + +if dwz -m 3 1.debug 2.debug 2> dwz.err; status=$?; then + true +fi + +rm 2.debug + +if grep -q "dwz: Section overlap detected" dwz.err; then + exit 77 +fi + +[ $status -eq 0 ] + +version=$(eu-unstrip --version | head -n 1 | cut -d ' ' -f3) +major=$(echo $version | sed 's%\..*%%') +minor=$(echo $version | sed 's%.*\.%%') +if [ $major -gt 0 ] || [ $minor -ge 168 ]; then + true +else + exit 77 +fi + +eu-unstrip 1.stripped 1.debug -o 1.unstripped + +# An unstripped exec can end up larger than the original (PR elfutils/24809). +#smaller-than.sh 1.unstripped 1 + +rm -f 1 1.debug 1.stripped 1.unstripped 3 dwz.err diff --git a/testsuite/dwz.tests/objcopy-eu-unstrip.sh b/testsuite/dwz.tests/objcopy-eu-unstrip.sh new file mode 100644 index 0000000..18593d0 --- /dev/null +++ b/testsuite/dwz.tests/objcopy-eu-unstrip.sh @@ -0,0 +1,35 @@ +cp ../hello 1 + +objcopy --only-keep-debug 1 1.debug +objcopy --strip-debug 1 1.stripped + +if ! eu-unstrip 1.stripped 1.debug -o 1.unstripped; then + exit 77 +fi +rm 1.unstripped + +if dwz 1.debug 2> dwz.err; status=$?; then + true +fi + +if grep -q "dwz: Section overlap detected" dwz.err; then + exit 77 +fi + +[ $status -eq 0 ] + +version=$(eu-unstrip --version | head -n 1 | cut -d ' ' -f3) +major=$(echo $version | sed 's%\..*%%') +minor=$(echo $version | sed 's%.*\.%%') +if [ $major -gt 0 ] || [ $minor -ge 168 ]; then + true +else + exit 77 +fi + +eu-unstrip 1.stripped 1.debug -o 1.unstripped + +# An unstripped exec can end up larger than the original (PR elfutils/24809). +#smaller-than.sh 1.unstripped 1 + +rm -f 1 1.debug 1.stripped 1.unstripped dwz.err diff --git a/testsuite/dwz.tests/objcopy-remove-debug-abbrev.sh b/testsuite/dwz.tests/objcopy-remove-debug-abbrev.sh new file mode 100644 index 0000000..98ab12c --- /dev/null +++ b/testsuite/dwz.tests/objcopy-remove-debug-abbrev.sh @@ -0,0 +1,18 @@ +objcopy --remove-section=.debug_abbrev $execs/hello 1 + +cp 1 1.saved + +if dwz 1 2>dwz.err; status=$?; then + true +fi + +if ! grep -q "\.debug_abbrev not present" dwz.err; then + cat dwz.err + exit 1 +fi + +[ $status -eq 1 ] + +cmp 1 1.saved + +rm -f 1 1.saved dwz.err diff --git a/testsuite/dwz.tests/objcopy-strip-debug.sh b/testsuite/dwz.tests/objcopy-strip-debug.sh new file mode 100644 index 0000000..a22ba3a --- /dev/null +++ b/testsuite/dwz.tests/objcopy-strip-debug.sh @@ -0,0 +1,30 @@ +objcopy --strip-debug $execs/hello 1 + +cp 1 1.saved + +if dwz 1 2>dwz.err; status=$?; then + true +fi + +if ! grep -q "\.debug_info section not present" dwz.err; then + cat dwz.err + exit 1 +fi + +[ $status -eq 0 ] + +cmp 1 1.saved + +if dwz 1 -o 2 2>dwz.err; status=$?; then + true +fi + +if ! grep -q "\.debug_info section not present" dwz.err; then + cat dwz.err + exit 1 +fi + +[ $status -eq 1 ] +[ ! -f 2 ] + +rm -f 1 1.saved dwz.err diff --git a/testsuite/dwz.tests/odr-2.cc b/testsuite/dwz.tests/odr-2.cc new file mode 100644 index 0000000..8b3dcd2 --- /dev/null +++ b/testsuite/dwz.tests/odr-2.cc @@ -0,0 +1,20 @@ +#if NAMESPACE +namespace ns { +#endif + KIND bbb + { + int member_four; + }; + KIND ccc; +#if NAMESPACE +} +#endif +#include "odr.h" + +#if NAMESPACE +KIND ns::aaa var2; +KIND ns::bbb var4; +#else +KIND aaa var2; +KIND bbb var4; +#endif diff --git a/testsuite/dwz.tests/odr-class-ns.sh b/testsuite/dwz.tests/odr-class-ns.sh new file mode 100644 index 0000000..4b890a3 --- /dev/null +++ b/testsuite/dwz.tests/odr-class-ns.sh @@ -0,0 +1,29 @@ +if ! $execs/dwz-for-test --odr -v 2>/dev/null; then + exit 77 +fi + +cp $execs/odr-class-ns 1 + +verify-dwarf.sh 1 + +aaa=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*aaa') +[ $aaa -eq 2 ] + +bbb=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*bbb') +[ $bbb -eq 2 ] + +ccc=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*ccc') +[ $ccc -eq 2 ] + +$execs/dwz-for-test --odr 1 + +aaa=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*aaa') +[ $aaa -eq 1 ] + +bbb=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*bbb') +[ $bbb -eq 1 ] + +ccc=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*ccc') +[ $ccc -eq 1 ] + +rm -f 1 diff --git a/testsuite/dwz.tests/odr-class.sh b/testsuite/dwz.tests/odr-class.sh new file mode 100644 index 0000000..872c3ca --- /dev/null +++ b/testsuite/dwz.tests/odr-class.sh @@ -0,0 +1,29 @@ +if ! $execs/dwz-for-test --odr -v 2>/dev/null; then + exit 77 +fi + +cp $execs/odr-class 1 + +verify-dwarf.sh 1 + +aaa=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*aaa') +[ $aaa -eq 2 ] + +bbb=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*bbb') +[ $bbb -eq 2 ] + +ccc=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*ccc') +[ $ccc -eq 2 ] + +$execs/dwz-for-test --odr 1 + +aaa=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*aaa') +[ $aaa -eq 1 ] + +bbb=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*bbb') +[ $bbb -eq 1 ] + +ccc=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*ccc') +[ $ccc -eq 1 ] + +rm -f 1 diff --git a/testsuite/dwz.tests/odr-def-decl.sh b/testsuite/dwz.tests/odr-def-decl.sh new file mode 100644 index 0000000..8905e52 --- /dev/null +++ b/testsuite/dwz.tests/odr-def-decl.sh @@ -0,0 +1,19 @@ +if ! $execs/dwz-for-test --odr -v 2>/dev/null; then + exit 77 +fi + +cp $execs/def-decl 1 + +verify-dwarf.sh 1 + +cnt=$(readelf -wi 1 | grep -c "DW_AT_name.*:.*ao_ref" || true) +[ $cnt -eq 5 ] + +$execs/dwz-for-test --odr 1 --devel-ignore-size + +verify-dwarf.sh 1 + +cnt=$(readelf -wi 1 | grep -c "DW_AT_name.*:.*ao_ref" || true) +[ $cnt -eq 2 ] + +rm -f 1 diff --git a/testsuite/dwz.tests/odr-loc-2.cc b/testsuite/dwz.tests/odr-loc-2.cc new file mode 100644 index 0000000..7cf832b --- /dev/null +++ b/testsuite/dwz.tests/odr-loc-2.cc @@ -0,0 +1,14 @@ +struct bbb +{ + int member_four; +}; +struct ccc; + +struct aaa +{ + struct bbb *member_one; + struct ccc *member_two; +}; + +struct aaa var2; +struct bbb var4; diff --git a/testsuite/dwz.tests/odr-loc.cc b/testsuite/dwz.tests/odr-loc.cc new file mode 100644 index 0000000..953fd66 --- /dev/null +++ b/testsuite/dwz.tests/odr-loc.cc @@ -0,0 +1,19 @@ +struct bbb; +struct ccc { + int member_three; +}; + +struct aaa +{ + struct bbb *member_one; + struct ccc *member_two; +}; + +struct aaa var1; +struct ccc var3; + +int +main (void) +{ + return 0; +} diff --git a/testsuite/dwz.tests/odr-loc.sh b/testsuite/dwz.tests/odr-loc.sh new file mode 100644 index 0000000..a8b3318 --- /dev/null +++ b/testsuite/dwz.tests/odr-loc.sh @@ -0,0 +1,39 @@ +if ! $execs/dwz-for-test --odr -v 2>/dev/null; then + exit 77 +fi + +cp $execs/odr-loc 1 + +for name in aaa bbb ccc; do + cnt=$(readelf -wi 1 | grep -c "DW_AT_name.*:.*$name" || true) + [ $cnt -eq 2 ] +done + +$execs/dwz-for-test --odr 1 --devel-ignore-size + +verify-dwarf.sh 1 + +for name in aaa bbb ccc; do + cnt=$(readelf -wi 1 | grep -c "DW_AT_name.*:.*$name" || true) + case $name in + aaa) + [ $cnt -eq 2 ] + ;; + *) + [ $cnt -eq 1 ] + ;; + esac +done + +cp $execs/odr-loc 1 + +$execs/dwz-for-test --odr --devel-ignore-locus 1 + +verify-dwarf.sh 1 + +for name in aaa bbb ccc; do + cnt=$(readelf -wi 1 | grep -c "DW_AT_name.*:.*$name" || true) + [ $cnt -eq 1 ] +done + +rm -f 1 diff --git a/testsuite/dwz.tests/odr-struct-multifile.sh b/testsuite/dwz.tests/odr-struct-multifile.sh new file mode 100755 index 0000000..5961abf --- /dev/null +++ b/testsuite/dwz.tests/odr-struct-multifile.sh @@ -0,0 +1,61 @@ +if ! $execs/dwz-for-test --odr -v 2>/dev/null; then + exit 77 +fi + +readelf_flags="" +if readelf -h 2>&1 | grep -q "\-wN"; then + readelf_flags=-wN +fi + +cp $execs/odr-struct 1 +cp 1 2 + +for name in aaa bbb ccc; do + cnt=$(readelf -wi 1 | grep -c "DW_AT_name.*:.*$name" || true) + [ $cnt -eq 2 ] +done + +for name in member_one member_two member_three member_four; do + cnt=$(readelf -wi 1 | grep -c "DW_AT_name.*:.*$name" || true) + case $name in + member_one|member_two) + [ $cnt -eq 2 ] + ;; + member_three|member_four) + [ $cnt -eq 1 ] + ;; + esac +done + +decl_cnt=$(readelf -wi 1 | grep -c "DW_AT_declaration" || true) + +$execs/dwz-for-test --odr 1 2 -m 3 + +verify-dwarf.sh 1 +verify-dwarf.sh 3 + +for name in aaa bbb ccc; do + cnt=$(readelf -wi 3 | grep -c "DW_AT_name.*:.*$name" || true) + [ $cnt -eq 1 ] +done + +for name in member_one member_two member_three member_four; do + cnt=$(readelf -wi 3 | grep -c "DW_AT_name.*:.*$name" || true) + [ $cnt -eq 1 ] +done + +# Even with -wN readelf 2.38-15.fc37 follows and prints the contents +# of the alt file. So make sure it cannot do that by removing it. +rm 3 + +for name in aaa bbb ccc; do + cnt=$(readelf -wi $readelf_flags 1 | grep -c "DW_AT_name.*:.*$name" || true) + [ $cnt -eq 0 ] +done + +for name in member_one member_two member_three member_four; do + cnt=$(readelf -wi $readelf_flags 1 | grep -c "DW_AT_name.*:.*$name" || true) + [ $cnt -eq 0 ] +done + +rm -f 1 2 3 diff --git a/testsuite/dwz.tests/odr-struct-ns.sh b/testsuite/dwz.tests/odr-struct-ns.sh new file mode 100644 index 0000000..c6e0f1d --- /dev/null +++ b/testsuite/dwz.tests/odr-struct-ns.sh @@ -0,0 +1,49 @@ +if ! $execs/dwz-for-test --odr -v 2>/dev/null; then + exit 77 +fi + +cp $execs/odr-struct-ns 1 + +for name in aaa bbb ccc; do + cnt=$(readelf -wi 1 | grep -c "DW_AT_name.*:.*$name" || true) + [ $cnt -eq 2 ] +done + +for name in member_one member_two member_three member_four; do + cnt=$(readelf -wi 1 | grep -c "DW_AT_name.*:.*$name" || true) + case $name in + member_one|member_two) + [ $cnt -eq 2 ] + ;; + member_three|member_four) + [ $cnt -eq 1 ] + ;; + esac +done + +# Check that bbb and ccc are present as DW_AT_declaration +readelf -wi 1 | grep -3 DW_AT_declaration > decls +grep bbb decls >/dev/null +grep ccc decls >/dev/null + +$execs/dwz-for-test --odr 1 + +verify-dwarf.sh 1 + +for name in aaa bbb ccc; do + cnt=$(readelf -wi 1 | grep -c "DW_AT_name.*:.*$name" || true) + [ $cnt -eq 1 ] +done + +for name in member_one member_two member_three member_four; do + cnt=$(readelf -wi 1 | grep -c "DW_AT_name.*:.*$name" || true) + [ $cnt -eq 1 ] +done + +# We expect two decls to be removed, for bbb and ccc. +readelf -wi 1 | grep -3 DW_AT_declaration > decls || true + +if grep bbb decls >/dev/null ; then exit 1; fi +if grep ccc decls >/dev/null ; then exit 2; fi + +rm -f 1 decls diff --git a/testsuite/dwz.tests/odr-struct.sh b/testsuite/dwz.tests/odr-struct.sh new file mode 100644 index 0000000..c6793f9 --- /dev/null +++ b/testsuite/dwz.tests/odr-struct.sh @@ -0,0 +1,49 @@ +if ! $execs/dwz-for-test --odr -v 2>/dev/null; then + exit 77 +fi + +cp $execs/odr-struct 1 + +for name in aaa bbb ccc; do + cnt=$(readelf -wi 1 | grep -c "DW_AT_name.*:.*$name" || true) + [ $cnt -eq 2 ] +done + +for name in member_one member_two member_three member_four; do + cnt=$(readelf -wi 1 | grep -c "DW_AT_name.*:.*$name" || true) + case $name in + member_one|member_two) + [ $cnt -eq 2 ] + ;; + member_three|member_four) + [ $cnt -eq 1 ] + ;; + esac +done + +# Check that bbb and ccc are present as DW_AT_declaration +readelf -wi 1 | grep -3 DW_AT_declaration > decls +grep bbb decls >/dev/null +grep ccc decls >/dev/null + +$execs/dwz-for-test --odr 1 + +verify-dwarf.sh 1 + +for name in aaa bbb ccc; do + cnt=$(readelf -wi 1 | grep -c "DW_AT_name.*:.*$name" || true) + [ $cnt -eq 1 ] +done + +for name in member_one member_two member_three member_four; do + cnt=$(readelf -wi 1 | grep -c "DW_AT_name.*:.*$name" || true) + [ $cnt -eq 1 ] +done + +# We expect two decls to be removed, for bbb and ccc. +readelf -wi 1 | grep -3 DW_AT_declaration > decls || true + +if grep bbb decls >/dev/null ; then exit 1; fi +if grep ccc decls >/dev/null ; then exit 2; fi + +rm -f 1 decls diff --git a/testsuite/dwz.tests/odr-union-ns.sh b/testsuite/dwz.tests/odr-union-ns.sh new file mode 100644 index 0000000..7d86dba --- /dev/null +++ b/testsuite/dwz.tests/odr-union-ns.sh @@ -0,0 +1,29 @@ +if ! $execs/dwz-for-test --odr -v 2>/dev/null; then + exit 77 +fi + +cp $execs/odr-union-ns 1 + +aaa=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*aaa') +[ $aaa -eq 2 ] + +bbb=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*bbb') +[ $bbb -eq 2 ] + +ccc=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*ccc') +[ $ccc -eq 2 ] + +$execs/dwz-for-test --odr 1 + +verify-dwarf.sh 1 + +aaa=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*aaa') +[ $aaa -eq 1 ] + +bbb=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*bbb') +[ $bbb -eq 1 ] + +ccc=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*ccc') +[ $ccc -eq 1 ] + +rm -f 1 diff --git a/testsuite/dwz.tests/odr-union.sh b/testsuite/dwz.tests/odr-union.sh new file mode 100644 index 0000000..a32a46c --- /dev/null +++ b/testsuite/dwz.tests/odr-union.sh @@ -0,0 +1,29 @@ +if ! $execs/dwz-for-test --odr -v 2>/dev/null; then + exit 77 +fi + +cp $execs/odr-union 1 + +aaa=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*aaa') +[ $aaa -eq 2 ] + +bbb=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*bbb') +[ $bbb -eq 2 ] + +ccc=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*ccc') +[ $ccc -eq 2 ] + +$execs/dwz-for-test --odr 1 + +verify-dwarf.sh 1 + +aaa=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*aaa') +[ $aaa -eq 1 ] + +bbb=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*bbb') +[ $bbb -eq 1 ] + +ccc=$(readelf -wi 1 | grep -c 'DW_AT_name.*:.*ccc') +[ $ccc -eq 1 ] + +rm -f 1 diff --git a/testsuite/dwz.tests/odr.cc b/testsuite/dwz.tests/odr.cc new file mode 100644 index 0000000..9505cb3 --- /dev/null +++ b/testsuite/dwz.tests/odr.cc @@ -0,0 +1,26 @@ +#if NAMESPACE +namespace ns { +#endif + KIND bbb; + KIND ccc { + int member_three; + }; +#if NAMESPACE +} +#endif + +#include "odr.h" + +#if NAMESPACE +KIND ns::aaa var1; +KIND ns::ccc var3; +#else +KIND aaa var1; +KIND ccc var3; +#endif + +int +main (void) +{ + return 0; +} diff --git a/testsuite/dwz.tests/odr.h b/testsuite/dwz.tests/odr.h new file mode 100644 index 0000000..ae0e07d --- /dev/null +++ b/testsuite/dwz.tests/odr.h @@ -0,0 +1,11 @@ +#if NAMESPACE +namespace ns { +#endif + KIND aaa + { + KIND bbb *member_one; + KIND ccc *member_two; + }; +#if NAMESPACE +} +#endif diff --git a/testsuite/dwz.tests/pointer-size.c b/testsuite/dwz.tests/pointer-size.c new file mode 100644 index 0000000..320633d --- /dev/null +++ b/testsuite/dwz.tests/pointer-size.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +int +main (void) +{ + printf ("%llu\n", (unsigned long long)sizeof (void *)); + return 0; +} diff --git a/testsuite/dwz.tests/pr24170.sh b/testsuite/dwz.tests/pr24170.sh new file mode 100644 index 0000000..ec57a71 --- /dev/null +++ b/testsuite/dwz.tests/pr24170.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +exec=$execs/implptr-64bit-d2o4a8r8t0 + +pointer_sizes=$(readelf -wi $exec \ + | grep "Pointer Size:" \ + | sort -u \ + | wc -l) + +if [ $pointer_sizes -ne 1 ]; then + exit 77 +fi + +cp $exec 1 +cp 1 2 + +dwz -m 3 1 2 + +smaller-than.sh 1 $exec +smaller-than.sh 2 $exec + +[ "$(gnu-debugaltlink-name.sh 1)" = "3" ] +[ "$(gnu-debugaltlink-name.sh 2)" = "3" ] + +rm -f 1 2 3 diff --git a/testsuite/dwz.tests/pr24171.sh b/testsuite/dwz.tests/pr24171.sh new file mode 100644 index 0000000..dd99c05 --- /dev/null +++ b/testsuite/dwz.tests/pr24171.sh @@ -0,0 +1,15 @@ +cp $execs/invalid-dw-at-stmt-list-encoding 1 +cp 1 2 + +if dwz -m 3 1 2 2>dwz.err; status=$?; then + true +fi + +if ! grep -q "DW_AT_stmt_list not DW_FORM_sec_offset or DW_FORM_data4" dwz.err; then + cat dwz.err + exit 1 +fi + +[ $status -eq 1 ] + +rm -f 1 2 dwz.err diff --git a/testsuite/dwz.tests/pr24172.sh b/testsuite/dwz.tests/pr24172.sh new file mode 100644 index 0000000..f455b27 --- /dev/null +++ b/testsuite/dwz.tests/pr24172.sh @@ -0,0 +1,14 @@ +cp $execs/dw2-skip-prologue 1 + +if dwz 1 2>dwz.err; status=$?; then + true +fi + +if ! grep -q "locexpr length .* exceeds .debug_loc section" dwz.err; then + cat dwz.err + exit 1 +fi + +[ $status -eq 1 ] + +rm -f 1 dwz.err diff --git a/testsuite/dwz.tests/pr24173.sh b/testsuite/dwz.tests/pr24173.sh new file mode 100644 index 0000000..908c219 --- /dev/null +++ b/testsuite/dwz.tests/pr24173.sh @@ -0,0 +1,16 @@ +cp $execs/py-section-script 1 + +eu-strip -g -f 1.debug 1 + +if dwz 1.debug 2>dwz.err; status=$?; then + true +fi + +if ! grep -q "Found empty .debug_gdb_scripts section, not attempting dwz compression" dwz.err; then + cat dwz.err + exit 1 +fi + +[ $status -eq 1 ] + +rm -f 1 1.debug dwz.err diff --git a/testsuite/dwz.tests/pr24174.sh b/testsuite/dwz.tests/pr24174.sh new file mode 100644 index 0000000..8d1192c --- /dev/null +++ b/testsuite/dwz.tests/pr24174.sh @@ -0,0 +1,24 @@ +exec=$execs/hello +cp $exec 1 +objcopy --compress-debug-sections 1 +if readelf -S 1 \ + | grep -A1 '\.debug_' \ + | grep -v '\.debug_' \ + | grep -q 'C'; then + true +else + exit 77 +fi + +if dwz 1 2>dwz.err; status=$?; then + true +fi + +if grep -q "DWARF version 0 unhandled" dwz.err; then + cat dwz.err + exit 1 +fi + +[ $status -eq 1 ] + +rm -f 1 dwz.err diff --git a/testsuite/dwz.tests/pr24341.sh b/testsuite/dwz.tests/pr24341.sh new file mode 100644 index 0000000..4f044f6 --- /dev/null +++ b/testsuite/dwz.tests/pr24341.sh @@ -0,0 +1,17 @@ +cp $execs/min 1 +cp 1 2 + +dwz -m 3 1 2 + +cnt=$(readelf -S 3 | grep "\.debug_info" | wc -l) +if [ $cnt -ne 0 ]; then + exit 77 +fi + +smaller-than.sh 1 $execs/min +smaller-than.sh 2 $execs/min + +[ "$(gnu-debugaltlink-name.sh 1)" = "3" ] +[ "$(gnu-debugaltlink-name.sh 2)" = "3" ] + +rm -f 1 2 3 diff --git a/testsuite/dwz.tests/pr24468.sh b/testsuite/dwz.tests/pr24468.sh new file mode 100644 index 0000000..7990e83 --- /dev/null +++ b/testsuite/dwz.tests/pr24468.sh @@ -0,0 +1,19 @@ +exec=$execs/testsuite/dwz.tests/execs/hello-leap-15.0 + +cp $exec 1 +cp 1 2 + +dwz -m 3 1 2 + +readelf -wi 1 > READELF 2>/dev/null + +offsets=$(grep '(DW_TAG_partial_unit' READELF \ + | grep -v "DW_AT_import" \ + | awk '{print $1}' \ + | sed 's/.*<//;s/>.*//') +for off in $offsets; do + imports=$(grep -c "DW_AT_import.*0x$off" READELF || true) + [ $imports -gt 0 ] +done + +rm -f 1 2 3 READELF diff --git a/testsuite/dwz.tests/pr24542.sh b/testsuite/dwz.tests/pr24542.sh new file mode 100644 index 0000000..f797034 --- /dev/null +++ b/testsuite/dwz.tests/pr24542.sh @@ -0,0 +1,15 @@ +cp $execs/start 1 +cp 1 2 + +if dwz -m 3 1 2 2> dwz.err; status=$?; then + true +fi + +if ! grep -q "No suitable DWARF found for multifile optimization" dwz.err; then + cat dwz.err + exit 1 +fi + +[ $status -eq 0 ] + +rm -f 1 2 3 dwz.err diff --git a/testsuite/dwz.tests/pr24747.sh b/testsuite/dwz.tests/pr24747.sh new file mode 100644 index 0000000..7182f72 --- /dev/null +++ b/testsuite/dwz.tests/pr24747.sh @@ -0,0 +1,7 @@ +exec=$execs/start-gold + +cp $exec 1 + +dwz 1 + +rm -f 1 diff --git a/testsuite/dwz.tests/pr24771.sh b/testsuite/dwz.tests/pr24771.sh new file mode 100644 index 0000000..4eb6c02 --- /dev/null +++ b/testsuite/dwz.tests/pr24771.sh @@ -0,0 +1,9 @@ +exec=$execs/hello-gnu-pubnames + +cp $exec 1 + +dwz 1 + +smaller-than.sh 1 $exec + +rm -f 1 diff --git a/testsuite/dwz.tests/pr24823.sh b/testsuite/dwz.tests/pr24823.sh new file mode 100644 index 0000000..8720aec --- /dev/null +++ b/testsuite/dwz.tests/pr24823.sh @@ -0,0 +1,25 @@ +exec=$execs/varval + +pointer_sizes=$(readelf -wi $exec \ + | grep "Pointer Size:" \ + | sort -u \ + | wc -l) + +if [ $pointer_sizes -ne 1 ]; then + exit 77 +fi + +cp $exec 1 +cp 1 2 + +dwz -m 3 1 2 + +if [ -f 3 ]; then + readelf -wi 3 > READELF.3 + if grep -q var_c READELF.3; status=$?; then + true + fi + [ $status -ne 0 ] +fi + +rm -f 1 2 3 READELF.3 diff --git a/testsuite/dwz.tests/pr25109.sh b/testsuite/dwz.tests/pr25109.sh new file mode 100644 index 0000000..6da1f22 --- /dev/null +++ b/testsuite/dwz.tests/pr25109.sh @@ -0,0 +1,6 @@ +cp $execs/no-multifile-prop 1 +cp 1 2 + +$execs/dwz-for-test -m 3 1 2 --devel-ignore-size + +rm -f 1 2 3 diff --git a/testsuite/dwz.tests/pr27463.sh b/testsuite/dwz.tests/pr27463.sh new file mode 100644 index 0000000..cd0238c --- /dev/null +++ b/testsuite/dwz.tests/pr27463.sh @@ -0,0 +1,6 @@ +cp $execs/unavailable-dwarf-piece 1 +cp 1 2 + +dwz -m 3 1 2 + +rm -f 1 2 3 diff --git a/testsuite/dwz.tests/py-section-script.s b/testsuite/dwz.tests/py-section-script.s new file mode 100644 index 0000000..1f5efc7 --- /dev/null +++ b/testsuite/dwz.tests/py-section-script.s @@ -0,0 +1,393 @@ +# Generated using gdb/testsuite/gdb.python/py-section-script.c from repo +# git://sourceware.org/git/binutils-gdb.git. + + .file "py-section-script.c" + .text +.Ltext0: +#APP + .pushsection ".debug_gdb_scripts", "S",%progbits +.byte 1 +.asciz "py-section-script.py" +.popsection + + .pushsection ".debug_gdb_scripts", "S",%progbits +.byte 1 +.asciz "py-section-script.py" +.popsection + + .pushsection ".debug_gdb_scripts", "S",%progbits +.byte 4 +.ascii "gdb.inlined-script\n" +.ascii "class test_cmd (gdb.Command):\n" +.ascii " def __init__ (self):\n" +.ascii " super (test_cmd, self).__init__ (\"test-cmd\", gdb.COMMAND_OBSCURE)\n" +.ascii " def invoke (self, arg, from_tty):\n" +.ascii " print (\"test-cmd output, arg = %s\" % arg)\n" +.ascii "test_cmd ()\n" +.byte 0 +.popsection + + .pushsection ".debug_gdb_scripts", "S",%progbits +.byte 4 +.ascii "gdb.inlined-script\n" +.ascii "class test_cmd (gdb.Command):\n" +.ascii " def __init__ (self):\n" +.ascii " super (test_cmd, self).__init__ (\"test-cmd\", gdb.COMMAND_OBSCURE)\n" +.ascii " def invoke (self, arg, from_tty):\n" +.ascii " print (\"test-cmd output, arg = %s\" % arg)\n" +.ascii "test_cmd ()\n" +.byte 0 +.popsection + +#NO_APP + .globl init_ss + .type init_ss, @function +init_ss: +.LFB0: + .file 1 "py-section-script.c" + # py-section-script.c:76 + .loc 1 76 0 + .cfi_startproc +# BLOCK 2 seq:0 +# PRED: ENTRY (FALLTHRU) + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset 6, -16 + movq %rsp, %rbp + .cfi_def_cfa_register 6 + movq %rdi, -8(%rbp) + movl %esi, -12(%rbp) + movl %edx, -16(%rbp) + # py-section-script.c:77 + .loc 1 77 0 + movq -8(%rbp), %rax + movl -12(%rbp), %edx + movl %edx, (%rax) + # py-section-script.c:78 + .loc 1 78 0 + movq -8(%rbp), %rax + movl -16(%rbp), %edx + movl %edx, 4(%rax) + # py-section-script.c:79 + .loc 1 79 0 + nop + popq %rbp + .cfi_def_cfa 7, 8 +# SUCC: EXIT [100.0%] + ret + .cfi_endproc +.LFE0: + .size init_ss, .-init_ss + .globl main + .type main, @function +main: +.LFB1: + # py-section-script.c:83 + .loc 1 83 0 + .cfi_startproc +# BLOCK 2 seq:0 +# PRED: ENTRY (FALLTHRU) + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset 6, -16 + movq %rsp, %rbp + .cfi_def_cfa_register 6 + subq $16, %rsp + # py-section-script.c:86 + .loc 1 86 0 + leaq -8(%rbp), %rax + movl $2, %edx + movl $1, %esi + movq %rax, %rdi + call init_ss + # py-section-script.c:88 + .loc 1 88 0 + movl $0, %eax + # py-section-script.c:89 + .loc 1 89 0 + leave + .cfi_def_cfa 7, 8 +# SUCC: EXIT [100.0%] + ret + .cfi_endproc +.LFE1: + .size main, .-main +.Letext0: + .section .debug_info,"",@progbits +.Ldebug_info0: + .long 0xc8 # Length of Compilation Unit Info + .value 0x4 # DWARF version number + .long .Ldebug_abbrev0 # Offset Into Abbrev. Section + .byte 0x8 # Pointer Size (in bytes) + .uleb128 0x1 # (DIE (0xb) DW_TAG_compile_unit) + .long .LASF2 # DW_AT_producer: "GNU C11 7.3.1 20180323 [gcc-7-branch revision 258812] -mtune=generic -march=x86-64 -g" + .byte 0xc # DW_AT_language + .long .LASF3 # DW_AT_name: "py-section-script.c" + .long .LASF4 # DW_AT_comp_dir: "/home/vries/gdb_versions/devel" + .quad .Ltext0 # DW_AT_low_pc + .quad .Letext0-.Ltext0 # DW_AT_high_pc + .long .Ldebug_line0 # DW_AT_stmt_list + .uleb128 0x2 # (DIE (0x2d) DW_TAG_structure_type) + .ascii "ss\0" # DW_AT_name + .byte 0x8 # DW_AT_byte_size + .byte 0x1 # DW_AT_decl_file (py-section-script.c) + .byte 0x44 # DW_AT_decl_line + .long 0x4d # DW_AT_sibling + .uleb128 0x3 # (DIE (0x38) DW_TAG_member) + .ascii "a\0" # DW_AT_name + .byte 0x1 # DW_AT_decl_file (py-section-script.c) + .byte 0x46 # DW_AT_decl_line + .long 0x4d # DW_AT_type + .byte 0 # DW_AT_data_member_location + .uleb128 0x3 # (DIE (0x42) DW_TAG_member) + .ascii "b\0" # DW_AT_name + .byte 0x1 # DW_AT_decl_file (py-section-script.c) + .byte 0x47 # DW_AT_decl_line + .long 0x4d # DW_AT_type + .byte 0x4 # DW_AT_data_member_location + .byte 0 # end of children of DIE 0x2d + .uleb128 0x4 # (DIE (0x4d) DW_TAG_base_type) + .byte 0x4 # DW_AT_byte_size + .byte 0x5 # DW_AT_encoding + .ascii "int\0" # DW_AT_name + .uleb128 0x5 # (DIE (0x54) DW_TAG_subprogram) + # DW_AT_external + .long .LASF0 # DW_AT_name: "main" + .byte 0x1 # DW_AT_decl_file (py-section-script.c) + .byte 0x52 # DW_AT_decl_line + .long 0x4d # DW_AT_type + .quad .LFB1 # DW_AT_low_pc + .quad .LFE1-.LFB1 # DW_AT_high_pc + .uleb128 0x1 # DW_AT_frame_base + .byte 0x9c # DW_OP_call_frame_cfa + # DW_AT_GNU_all_tail_call_sites + .long 0x83 # DW_AT_sibling + .uleb128 0x6 # (DIE (0x75) DW_TAG_variable) + .ascii "ss\0" # DW_AT_name + .byte 0x1 # DW_AT_decl_file (py-section-script.c) + .byte 0x54 # DW_AT_decl_line + .long 0x2d # DW_AT_type + .uleb128 0x2 # DW_AT_location + .byte 0x91 # DW_OP_fbreg + .sleb128 -24 + .byte 0 # end of children of DIE 0x54 + .uleb128 0x7 # (DIE (0x83) DW_TAG_subprogram) + # DW_AT_external + .long .LASF1 # DW_AT_name: "init_ss" + .byte 0x1 # DW_AT_decl_file (py-section-script.c) + .byte 0x4b # DW_AT_decl_line + # DW_AT_prototyped + .quad .LFB0 # DW_AT_low_pc + .quad .LFE0-.LFB0 # DW_AT_high_pc + .uleb128 0x1 # DW_AT_frame_base + .byte 0x9c # DW_OP_call_frame_cfa + # DW_AT_GNU_all_call_sites + .long 0xc5 # DW_AT_sibling + .uleb128 0x8 # (DIE (0xa0) DW_TAG_formal_parameter) + .ascii "s\0" # DW_AT_name + .byte 0x1 # DW_AT_decl_file (py-section-script.c) + .byte 0x4b # DW_AT_decl_line + .long 0xc5 # DW_AT_type + .uleb128 0x2 # DW_AT_location + .byte 0x91 # DW_OP_fbreg + .sleb128 -24 + .uleb128 0x8 # (DIE (0xac) DW_TAG_formal_parameter) + .ascii "a\0" # DW_AT_name + .byte 0x1 # DW_AT_decl_file (py-section-script.c) + .byte 0x4b # DW_AT_decl_line + .long 0x4d # DW_AT_type + .uleb128 0x2 # DW_AT_location + .byte 0x91 # DW_OP_fbreg + .sleb128 -28 + .uleb128 0x8 # (DIE (0xb8) DW_TAG_formal_parameter) + .ascii "b\0" # DW_AT_name + .byte 0x1 # DW_AT_decl_file (py-section-script.c) + .byte 0x4b # DW_AT_decl_line + .long 0x4d # DW_AT_type + .uleb128 0x2 # DW_AT_location + .byte 0x91 # DW_OP_fbreg + .sleb128 -32 + .byte 0 # end of children of DIE 0x83 + .uleb128 0x9 # (DIE (0xc5) DW_TAG_pointer_type) + .byte 0x8 # DW_AT_byte_size + .long 0x2d # DW_AT_type + .byte 0 # end of children of DIE 0xb + .section .debug_abbrev,"",@progbits +.Ldebug_abbrev0: + .uleb128 0x1 # (abbrev code) + .uleb128 0x11 # (TAG: DW_TAG_compile_unit) + .byte 0x1 # DW_children_yes + .uleb128 0x25 # (DW_AT_producer) + .uleb128 0xe # (DW_FORM_strp) + .uleb128 0x13 # (DW_AT_language) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x3 # (DW_AT_name) + .uleb128 0xe # (DW_FORM_strp) + .uleb128 0x1b # (DW_AT_comp_dir) + .uleb128 0xe # (DW_FORM_strp) + .uleb128 0x11 # (DW_AT_low_pc) + .uleb128 0x1 # (DW_FORM_addr) + .uleb128 0x12 # (DW_AT_high_pc) + .uleb128 0x7 # (DW_FORM_data8) + .uleb128 0x10 # (DW_AT_stmt_list) + .uleb128 0x17 # (DW_FORM_sec_offset) + .byte 0 + .byte 0 + .uleb128 0x2 # (abbrev code) + .uleb128 0x13 # (TAG: DW_TAG_structure_type) + .byte 0x1 # DW_children_yes + .uleb128 0x3 # (DW_AT_name) + .uleb128 0x8 # (DW_FORM_string) + .uleb128 0xb # (DW_AT_byte_size) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x3a # (DW_AT_decl_file) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x3b # (DW_AT_decl_line) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x1 # (DW_AT_sibling) + .uleb128 0x13 # (DW_FORM_ref4) + .byte 0 + .byte 0 + .uleb128 0x3 # (abbrev code) + .uleb128 0xd # (TAG: DW_TAG_member) + .byte 0 # DW_children_no + .uleb128 0x3 # (DW_AT_name) + .uleb128 0x8 # (DW_FORM_string) + .uleb128 0x3a # (DW_AT_decl_file) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x3b # (DW_AT_decl_line) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x49 # (DW_AT_type) + .uleb128 0x13 # (DW_FORM_ref4) + .uleb128 0x38 # (DW_AT_data_member_location) + .uleb128 0xb # (DW_FORM_data1) + .byte 0 + .byte 0 + .uleb128 0x4 # (abbrev code) + .uleb128 0x24 # (TAG: DW_TAG_base_type) + .byte 0 # DW_children_no + .uleb128 0xb # (DW_AT_byte_size) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x3e # (DW_AT_encoding) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x3 # (DW_AT_name) + .uleb128 0x8 # (DW_FORM_string) + .byte 0 + .byte 0 + .uleb128 0x5 # (abbrev code) + .uleb128 0x2e # (TAG: DW_TAG_subprogram) + .byte 0x1 # DW_children_yes + .uleb128 0x3f # (DW_AT_external) + .uleb128 0x19 # (DW_FORM_flag_present) + .uleb128 0x3 # (DW_AT_name) + .uleb128 0xe # (DW_FORM_strp) + .uleb128 0x3a # (DW_AT_decl_file) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x3b # (DW_AT_decl_line) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x49 # (DW_AT_type) + .uleb128 0x13 # (DW_FORM_ref4) + .uleb128 0x11 # (DW_AT_low_pc) + .uleb128 0x1 # (DW_FORM_addr) + .uleb128 0x12 # (DW_AT_high_pc) + .uleb128 0x7 # (DW_FORM_data8) + .uleb128 0x40 # (DW_AT_frame_base) + .uleb128 0x18 # (DW_FORM_exprloc) + .uleb128 0x2116 # (DW_AT_GNU_all_tail_call_sites) + .uleb128 0x19 # (DW_FORM_flag_present) + .uleb128 0x1 # (DW_AT_sibling) + .uleb128 0x13 # (DW_FORM_ref4) + .byte 0 + .byte 0 + .uleb128 0x6 # (abbrev code) + .uleb128 0x34 # (TAG: DW_TAG_variable) + .byte 0 # DW_children_no + .uleb128 0x3 # (DW_AT_name) + .uleb128 0x8 # (DW_FORM_string) + .uleb128 0x3a # (DW_AT_decl_file) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x3b # (DW_AT_decl_line) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x49 # (DW_AT_type) + .uleb128 0x13 # (DW_FORM_ref4) + .uleb128 0x2 # (DW_AT_location) + .uleb128 0x18 # (DW_FORM_exprloc) + .byte 0 + .byte 0 + .uleb128 0x7 # (abbrev code) + .uleb128 0x2e # (TAG: DW_TAG_subprogram) + .byte 0x1 # DW_children_yes + .uleb128 0x3f # (DW_AT_external) + .uleb128 0x19 # (DW_FORM_flag_present) + .uleb128 0x3 # (DW_AT_name) + .uleb128 0xe # (DW_FORM_strp) + .uleb128 0x3a # (DW_AT_decl_file) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x3b # (DW_AT_decl_line) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x27 # (DW_AT_prototyped) + .uleb128 0x19 # (DW_FORM_flag_present) + .uleb128 0x11 # (DW_AT_low_pc) + .uleb128 0x1 # (DW_FORM_addr) + .uleb128 0x12 # (DW_AT_high_pc) + .uleb128 0x7 # (DW_FORM_data8) + .uleb128 0x40 # (DW_AT_frame_base) + .uleb128 0x18 # (DW_FORM_exprloc) + .uleb128 0x2117 # (DW_AT_GNU_all_call_sites) + .uleb128 0x19 # (DW_FORM_flag_present) + .uleb128 0x1 # (DW_AT_sibling) + .uleb128 0x13 # (DW_FORM_ref4) + .byte 0 + .byte 0 + .uleb128 0x8 # (abbrev code) + .uleb128 0x5 # (TAG: DW_TAG_formal_parameter) + .byte 0 # DW_children_no + .uleb128 0x3 # (DW_AT_name) + .uleb128 0x8 # (DW_FORM_string) + .uleb128 0x3a # (DW_AT_decl_file) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x3b # (DW_AT_decl_line) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x49 # (DW_AT_type) + .uleb128 0x13 # (DW_FORM_ref4) + .uleb128 0x2 # (DW_AT_location) + .uleb128 0x18 # (DW_FORM_exprloc) + .byte 0 + .byte 0 + .uleb128 0x9 # (abbrev code) + .uleb128 0xf # (TAG: DW_TAG_pointer_type) + .byte 0 # DW_children_no + .uleb128 0xb # (DW_AT_byte_size) + .uleb128 0xb # (DW_FORM_data1) + .uleb128 0x49 # (DW_AT_type) + .uleb128 0x13 # (DW_FORM_ref4) + .byte 0 + .byte 0 + .byte 0 + .section .debug_aranges,"",@progbits + .long 0x2c # Length of Address Ranges Info + .value 0x2 # DWARF Version + .long .Ldebug_info0 # Offset of Compilation Unit Info + .byte 0x8 # Size of Address + .byte 0 # Size of Segment Descriptor + .value 0 # Pad to 16 byte boundary + .value 0 + .quad .Ltext0 # Address + .quad .Letext0-.Ltext0 # Length + .quad 0 + .quad 0 + .section .debug_line,"",@progbits +.Ldebug_line0: + .section .debug_str,"MS",@progbits,1 +.LASF2: + .string "GNU C11 7.3.1 20180323 [gcc-7-branch revision 258812] -mtune=generic -march=x86-64 -g" +.LASF3: + .string "py-section-script.c" +.LASF0: + .string "main" +.LASF4: + .string "/home/vries/gdb_versions/devel" +.LASF1: + .string "init_ss" + .ident "GCC: (SUSE Linux) 7.3.1 20180323 [gcc-7-branch revision 258812]" + .section .note.GNU-stack,"",@progbits diff --git a/testsuite/dwz.tests/regular-o.sh b/testsuite/dwz.tests/regular-o.sh new file mode 100644 index 0000000..1416e90 --- /dev/null +++ b/testsuite/dwz.tests/regular-o.sh @@ -0,0 +1,9 @@ +cp $execs/hello 1 + +dwz 1 -o 2 + +cmp 1 $execs/hello + +smaller-than.sh 2 1 + +rm -f 1 2 diff --git a/testsuite/dwz.tests/regular.sh b/testsuite/dwz.tests/regular.sh new file mode 100644 index 0000000..9d33153 --- /dev/null +++ b/testsuite/dwz.tests/regular.sh @@ -0,0 +1,7 @@ +cp $execs/hello 1 + +dwz 1 + +smaller-than.sh 1 $execs/hello + +rm -f 1 diff --git a/testsuite/dwz.tests/save-temps.sh b/testsuite/dwz.tests/save-temps.sh new file mode 100644 index 0000000..bcc1f1b --- /dev/null +++ b/testsuite/dwz.tests/save-temps.sh @@ -0,0 +1,20 @@ +cp $execs/hello 1 +cp $execs/hello 2 + +$execs/dwz-for-test --devel-save-temps -m 3 1 2 + +files=" + dwz.debug_abbrev + dwz.debug_info + dwz.debug_line + dwz.debug_macro + dwz.debug_str + dwz.1 + dwz.2 +" + +for f in $files; do + [ -f $f ] +done + +rm -f 1 2 3 $files diff --git a/testsuite/dwz.tests/start.c b/testsuite/dwz.tests/start.c new file mode 100644 index 0000000..a925613 --- /dev/null +++ b/testsuite/dwz.tests/start.c @@ -0,0 +1,3 @@ +void _start (void) +{ +} diff --git a/testsuite/dwz.tests/too-many-dies.sh b/testsuite/dwz.tests/too-many-dies.sh new file mode 100644 index 0000000..53a0f4b --- /dev/null +++ b/testsuite/dwz.tests/too-many-dies.sh @@ -0,0 +1,16 @@ +cp $execs/hello 1 + +if dwz -L0 1 2>dwz.err; status=?; then + true +fi + +if ! grep -q "Too many DIEs, not optimizing" dwz.err; then + cat dwz.err + exit 1 +fi + +[ $status -eq 1 ] + +cmp 1 $execs/hello + +rm -f 1 dwz.err diff --git a/testsuite/dwz.tests/twice-hardlink.sh b/testsuite/dwz.tests/twice-hardlink.sh new file mode 100644 index 0000000..6bc0794 --- /dev/null +++ b/testsuite/dwz.tests/twice-hardlink.sh @@ -0,0 +1,31 @@ +cp $execs/hello 1 +ln 1 2 + +dwz -h 1 2 + +smaller-than.sh 1 $execs/hello + +hardlinks-p.sh 1 2 + +cp 1 1.saved + +if dwz -h 1 2 2>dwz.err; status=$?; then + true +fi + +if ! grep -q "DWARF compression not beneficial" dwz.err; then + cat dwz.err + exit 1 +fi + +if [ $(grep -qv "DWARF compression not beneficial" dwz.err \ + | wc -l) -gt 0 ]; then + cat dwz.err + exit 1 +fi + +[ $status -eq 0 ] + +cmp 1 1.saved + +rm -f 1 1.saved 2 2.saved dwz.err diff --git a/testsuite/dwz.tests/twice-multifile.sh b/testsuite/dwz.tests/twice-multifile.sh new file mode 100644 index 0000000..1ff0166 --- /dev/null +++ b/testsuite/dwz.tests/twice-multifile.sh @@ -0,0 +1,34 @@ +cp $execs/hello 1 +cp 1 2 + +dwz 1 2 + +smaller-than.sh 1 $execs/hello +smaller-than.sh 2 $execs/hello + +cp 1 1.saved +cp 2 2.saved + +if dwz -m 3 1 2 2>dwz.err; status=$?; then + true +fi + +if ! grep -q "DWARF compression not beneficial" dwz.err; then + cat dwz.err + exit 1 +fi + +if [ $(grep -qv "DWARF compression not beneficial" dwz.err \ + | wc -l) -gt 0 ]; then + cat dwz.err + exit 1 +fi + +if [ $status -ne 0 ]; then + cat dwz.err + exit 1 +fi + +smaller-than.sh 1 1.saved + +rm -f 1 1.saved 2 2.saved dwz.err 3 diff --git a/testsuite/dwz.tests/twice.sh b/testsuite/dwz.tests/twice.sh new file mode 100644 index 0000000..210a966 --- /dev/null +++ b/testsuite/dwz.tests/twice.sh @@ -0,0 +1,28 @@ +cp $execs/hello 1 + +dwz 1 + +smaller-than.sh 1 $execs/hello + +cp 1 1.saved + +if dwz 1 2>dwz.err; status=$?; then + true +fi + +if ! grep -q "DWARF compression not beneficial" dwz.err; then + cat dwz.err + exit 1 +fi + +if [ $(grep -qv "DWARF compression not beneficial" dwz.err \ + | wc -l) -gt 0 ]; then + cat dwz.err + exit 1 +fi + +[ $status -eq 0 ] + +cmp 1 1.saved + +rm -f 1 1.saved dwz.err diff --git a/testsuite/dwz.tests/two-files-low-mem-die-limit-0.sh b/testsuite/dwz.tests/two-files-low-mem-die-limit-0.sh new file mode 100644 index 0000000..ee31359 --- /dev/null +++ b/testsuite/dwz.tests/two-files-low-mem-die-limit-0.sh @@ -0,0 +1,15 @@ +cp $execs/hello 1 +cp 1 2 + +$execs/dwz-for-test \ + -l0 \ + --devel-trace \ + 1 2 \ + -j 1 \ + 2> dwz.err + +if grep -Eq "Compressing (1|2)$" dwz.err; then + exit 1 +fi + +rm -f 1 2 dwz.err diff --git a/testsuite/dwz.tests/two-files-too-many-dies-2.sh b/testsuite/dwz.tests/two-files-too-many-dies-2.sh new file mode 100644 index 0000000..91b5ccc --- /dev/null +++ b/testsuite/dwz.tests/two-files-too-many-dies-2.sh @@ -0,0 +1,22 @@ +cp $execs/hello 1 +cp $execs/dwz-for-test 2 + +limit=$(readelf -wi 2 \ + | grep '(DW_TAG' \ + | wc -l) +limit=$((limit - 1)) + +if dwz -L$limit 2 1 2>dwz.err; status=$?; then + true +fi + +if [ $status -eq 0 ]; then + echo "PR24301 workaround used" > dwz.info +else + [ $status -eq 1 ] +fi + +smaller-than.sh 1 $execs/hello +cmp 2 $execs/dwz-for-test + +rm -f 1 2 dwz.err diff --git a/testsuite/dwz.tests/two-files-too-many-dies.sh b/testsuite/dwz.tests/two-files-too-many-dies.sh new file mode 100644 index 0000000..13950ba --- /dev/null +++ b/testsuite/dwz.tests/two-files-too-many-dies.sh @@ -0,0 +1,17 @@ +cp $execs/hello 1 +cp 1 2 +if dwz -L0 1 2 2>dwz.err; status=$?; then + true +fi + +if ! grep -q "Too many DIEs, not optimizing" dwz.err; then + cat dwz.err + exit 1 +fi + +[ $status -eq 1 ] + +cmp 1 $execs/hello +cmp 2 $execs/hello + +rm -f 1 2 dwz.err diff --git a/testsuite/dwz.tests/two-typedef-2.c b/testsuite/dwz.tests/two-typedef-2.c new file mode 100644 index 0000000..8af8df6 --- /dev/null +++ b/testsuite/dwz.tests/two-typedef-2.c @@ -0,0 +1,6 @@ + +typedef long unsigned int size_t; + +#include "two-typedef.h" + +struct aaa var2; diff --git a/testsuite/dwz.tests/two-typedef.c b/testsuite/dwz.tests/two-typedef.c new file mode 100644 index 0000000..fb1e044 --- /dev/null +++ b/testsuite/dwz.tests/two-typedef.c @@ -0,0 +1,11 @@ +typedef long unsigned int size_t; + +#include "two-typedef.h" + +struct aaa var; + +int +main (void) +{ + return 0; +} diff --git a/testsuite/dwz.tests/two-typedef.h b/testsuite/dwz.tests/two-typedef.h new file mode 100644 index 0000000..1675f39 --- /dev/null +++ b/testsuite/dwz.tests/two-typedef.h @@ -0,0 +1,3 @@ +struct aaa { + size_t size; +}; diff --git a/testsuite/dwz.tests/varval.S b/testsuite/dwz.tests/varval.S new file mode 100644 index 0000000..09f65d9 --- /dev/null +++ b/testsuite/dwz.tests/varval.S @@ -0,0 +1,515 @@ +# Generated using gdb/testsuite/gdb.dwarf2/varval.exp from repo +# git://sourceware.org/git/binutils-gdb.git. + .section .debug_info +.Lcu1_begin: + .4byte .Lcu1_end - .Lcu1_start +.Lcu1_start: + .2byte 4 /* Version */ + .4byte .Labbrev1_begin /* Abbrevs */ + .byte 8 /* Pointer size */ + .uleb128 2 /* Abbrev (DW_TAG_compile_unit) */ + .sleb128 0x0004 +.Llabel1: + .uleb128 3 /* Abbrev (DW_TAG_base_type) */ + .uleb128 4 + .sleb128 0x5 + .ascii "int\0" +.Llabel2: + .uleb128 4 /* Abbrev (DW_TAG_pointer_type) */ + .4byte .Llabel1 - .Lcu1_begin +.Llabel4: + .uleb128 5 /* Abbrev (DW_TAG_variable) */ + .ascii "var_a\0" + .4byte .Llabel1 - .Lcu1_begin + .byte 1 + .uleb128 .Lexpr_end16 - .Lexpr_start15/* expression */ +.Lexpr_start15: + .byte 0x03 /* DW_OP_addr */ + .8byte var_a +.Lexpr_end16: +.Llabel12: + .uleb128 6 /* Abbrev (DW_TAG_variable) */ + .4byte .Llabel1 - .Lcu1_begin + .byte 1 +.Llabel5: + .uleb128 7 /* Abbrev (DW_TAG_variable) */ + .ascii "var_b\0" + .4byte .Llabel1 - .Lcu1_begin + .byte 1 + .uleb128 .Lexpr_end18 - .Lexpr_start17/* expression */ +.Lexpr_start17: + .byte 0x03 /* DW_OP_addr */ + .8byte var_b +.Lexpr_end18: +.Llabel6: + .uleb128 8 /* Abbrev (DW_TAG_variable) */ + .ascii "var_c\0" + .4byte .Llabel1 - .Lcu1_begin + .byte 1 + .sleb128 53 +.Llabel7: + .uleb128 9 /* Abbrev (DW_TAG_variable) */ + .ascii "var_p\0" + .4byte .Llabel2 - .Lcu1_begin + .byte 1 + .uleb128 .Lexpr_end20 - .Lexpr_start19/* expression */ +.Lexpr_start19: + .byte 0x03 /* DW_OP_addr */ + .8byte var_p +.Lexpr_end20: +.Llabel3: + .uleb128 10 /* Abbrev (DW_TAG_structure_type) */ + .sleb128 8*4 + .uleb128 11 /* Abbrev (DW_TAG_member) */ + .ascii "a\0" + .4byte .Llabel1 - .Lcu1_begin + .uleb128 0*4 + .uleb128 12 /* Abbrev (DW_TAG_member) */ + .ascii "b\0" + .4byte .Llabel1 - .Lcu1_begin + .uleb128 1*4 + .uleb128 13 /* Abbrev (DW_TAG_member) */ + .ascii "c\0" + .4byte .Llabel1 - .Lcu1_begin + .uleb128 2*4 + .uleb128 14 /* Abbrev (DW_TAG_member) */ + .ascii "d\0" + .4byte .Llabel1 - .Lcu1_begin + .uleb128 3*4 + .uleb128 15 /* Abbrev (DW_TAG_member) */ + .ascii "e\0" + .4byte .Llabel1 - .Lcu1_begin + .uleb128 4*4 + .uleb128 16 /* Abbrev (DW_TAG_member) */ + .ascii "f\0" + .4byte .Llabel1 - .Lcu1_begin + .uleb128 5*4 + .uleb128 17 /* Abbrev (DW_TAG_member) */ + .ascii "g\0" + .4byte .Llabel1 - .Lcu1_begin + .uleb128 6*4 + .uleb128 18 /* Abbrev (DW_TAG_member) */ + .ascii "h\0" + .4byte .Llabel1 - .Lcu1_begin + .uleb128 7*4 + .byte 0x0 /* Terminate children */ +.Llabel10: + .uleb128 19 /* Abbrev (DW_TAG_variable) */ + .ascii "var_s\0" + .4byte .Llabel3 - .Lcu1_begin + .byte 1 + .uleb128 .Lexpr_end22 - .Lexpr_start21/* expression */ +.Lexpr_start21: + .byte 0x03 /* DW_OP_addr */ + .8byte var_s +.Lexpr_end22: +.Llabel11: + .uleb128 20 /* Abbrev (DW_TAG_variable) */ + .ascii "var_untyped\0" + .byte 1 + .uleb128 .Lexpr_end24 - .Lexpr_start23/* expression */ +.Lexpr_start23: + .byte 0x03 /* DW_OP_addr */ + .8byte var_b +.Lexpr_end24: + .uleb128 21 /* Abbrev (DW_TAG_subprogram) */ + .ascii "main\0" + .8byte main_label - 4 + .8byte main_label - 4 + 11 + .4byte .Llabel1 - .Lcu1_begin + .byte 1 +.Llabel9: + .uleb128 22 /* Abbrev (DW_TAG_variable) */ + .ascii "varval\0" + .4byte .Llabel1 - .Lcu1_begin + .uleb128 .Lexpr_end26 - .Lexpr_start25/* expression */ +.Lexpr_start25: + .byte 0xfd /* DW_OP_GNU_variable_value */ + .4byte .Llabel4 + .byte 0x9f /* DW_OP_stack_value */ +.Lexpr_end26: +.Llabel14: + .uleb128 23 /* Abbrev (DW_TAG_variable) */ + .ascii "varval2\0" + .4byte .Llabel1 - .Lcu1_begin + .uleb128 .Lexpr_end28 - .Lexpr_start27/* expression */ +.Lexpr_start27: + .byte 0xfd /* DW_OP_GNU_variable_value */ + .4byte .Llabel12 + .byte 0x9f /* DW_OP_stack_value */ +.Lexpr_end28: +.Llabel13: + .uleb128 24 /* Abbrev (DW_TAG_variable) */ + .4byte .Llabel12 - .Lcu1_begin + .uleb128 .Lexpr_end30 - .Lexpr_start29/* expression */ +.Lexpr_start29: + .byte 0x03 /* DW_OP_addr */ + .8byte var_a +.Lexpr_end30: + .uleb128 25 /* Abbrev (DW_TAG_variable) */ + .ascii "constval\0" + .4byte .Llabel1 - .Lcu1_begin + .uleb128 .Lexpr_end32 - .Lexpr_start31/* expression */ +.Lexpr_start31: + .byte 0xfd /* DW_OP_GNU_variable_value */ + .4byte .Llabel6 + .byte 0x9f /* DW_OP_stack_value */ +.Lexpr_end32: + .uleb128 26 /* Abbrev (DW_TAG_variable) */ + .ascii "mixedval\0" + .4byte .Llabel1 - .Lcu1_begin + .uleb128 .Lexpr_end34 - .Lexpr_start33/* expression */ +.Lexpr_start33: + .byte 0xfd /* DW_OP_GNU_variable_value */ + .4byte .Llabel6 + .byte 0xfd /* DW_OP_GNU_variable_value */ + .4byte .Llabel5 + .byte 0x1b /* DW_OP_div */ + .byte 0xfd /* DW_OP_GNU_variable_value */ + .4byte .Llabel9 + .byte 0x22 /* DW_OP_plus */ + .byte 0x12 /* DW_OP_dup */ + .byte 0x22 /* DW_OP_plus */ + .byte 0xfd /* DW_OP_GNU_variable_value */ + .4byte .Llabel9 + .byte 0x1c /* DW_OP_minus */ + .byte 0x9f /* DW_OP_stack_value */ +.Lexpr_end34: + .uleb128 27 /* Abbrev (DW_TAG_variable) */ + .ascii "pointerval\0" + .4byte .Llabel2 - .Lcu1_begin + .uleb128 .Lexpr_end36 - .Lexpr_start35/* expression */ +.Lexpr_start35: + .byte 0xfd /* DW_OP_GNU_variable_value */ + .4byte .Llabel7 + .byte 0x9f /* DW_OP_stack_value */ +.Lexpr_end36: + .uleb128 28 /* Abbrev (DW_TAG_variable) */ + .ascii "structval\0" + .4byte .Llabel3 - .Lcu1_begin + .uleb128 .Lexpr_end38 - .Lexpr_start37/* expression */ +.Lexpr_start37: + .byte 0xfd /* DW_OP_GNU_variable_value */ + .4byte .Llabel10 + .byte 0x9f /* DW_OP_stack_value */ +.Lexpr_end38: + .uleb128 29 /* Abbrev (DW_TAG_variable) */ + .ascii "untypedval\0" + .uleb128 .Lexpr_end40 - .Lexpr_start39/* expression */ +.Lexpr_start39: + .byte 0xfd /* DW_OP_GNU_variable_value */ + .4byte .Llabel11 + .byte 0x9f /* DW_OP_stack_value */ +.Lexpr_end40: + .byte 0x0 /* Terminate children */ + .byte 0x0 /* Terminate children */ +.Lcu1_end: + .section .debug_abbrev +.Labbrev1_begin: + .uleb128 2 /* Abbrev start */ + .uleb128 0x11 /* DW_TAG_compile_unit */ + .byte 1 /* has_children */ + .uleb128 0x13 /* DW_AT_language */ + .uleb128 0x0d /* DW_FORM_sdata */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 3 /* Abbrev start */ + .uleb128 0x24 /* DW_TAG_base_type */ + .byte 0 /* has_children */ + .uleb128 0x0b /* DW_AT_byte_size */ + .uleb128 0x0f /* DW_FORM_udata */ + .uleb128 0x3e /* DW_AT_encoding */ + .uleb128 0x0d /* DW_FORM_sdata */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 4 /* Abbrev start */ + .uleb128 0x0f /* DW_TAG_pointer_type */ + .byte 0 /* has_children */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 5 /* Abbrev start */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x3f /* DW_AT_external */ + .uleb128 0x0c /* DW_FORM_flag */ + .uleb128 0x02 /* DW_AT_location */ + .uleb128 0x18 /* DW_FORM_exprloc */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 6 /* Abbrev start */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x3f /* DW_AT_external */ + .uleb128 0x0c /* DW_FORM_flag */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 7 /* Abbrev start */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x3f /* DW_AT_external */ + .uleb128 0x0c /* DW_FORM_flag */ + .uleb128 0x02 /* DW_AT_location */ + .uleb128 0x18 /* DW_FORM_exprloc */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 8 /* Abbrev start */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x3f /* DW_AT_external */ + .uleb128 0x0c /* DW_FORM_flag */ + .uleb128 0x1c /* DW_AT_const_value */ + .uleb128 0x0d /* DW_FORM_sdata */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 9 /* Abbrev start */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x3f /* DW_AT_external */ + .uleb128 0x0c /* DW_FORM_flag */ + .uleb128 0x02 /* DW_AT_location */ + .uleb128 0x18 /* DW_FORM_exprloc */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 10 /* Abbrev start */ + .uleb128 0x13 /* DW_TAG_structure_type */ + .byte 1 /* has_children */ + .uleb128 0x0b /* DW_AT_byte_size */ + .uleb128 0x0d /* DW_FORM_sdata */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 11 /* Abbrev start */ + .uleb128 0x0d /* DW_TAG_member */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x38 /* DW_AT_data_member_location */ + .uleb128 0x0f /* DW_FORM_udata */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 12 /* Abbrev start */ + .uleb128 0x0d /* DW_TAG_member */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x38 /* DW_AT_data_member_location */ + .uleb128 0x0f /* DW_FORM_udata */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 13 /* Abbrev start */ + .uleb128 0x0d /* DW_TAG_member */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x38 /* DW_AT_data_member_location */ + .uleb128 0x0f /* DW_FORM_udata */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 14 /* Abbrev start */ + .uleb128 0x0d /* DW_TAG_member */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x38 /* DW_AT_data_member_location */ + .uleb128 0x0f /* DW_FORM_udata */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 15 /* Abbrev start */ + .uleb128 0x0d /* DW_TAG_member */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x38 /* DW_AT_data_member_location */ + .uleb128 0x0f /* DW_FORM_udata */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 16 /* Abbrev start */ + .uleb128 0x0d /* DW_TAG_member */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x38 /* DW_AT_data_member_location */ + .uleb128 0x0f /* DW_FORM_udata */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 17 /* Abbrev start */ + .uleb128 0x0d /* DW_TAG_member */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x38 /* DW_AT_data_member_location */ + .uleb128 0x0f /* DW_FORM_udata */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 18 /* Abbrev start */ + .uleb128 0x0d /* DW_TAG_member */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x38 /* DW_AT_data_member_location */ + .uleb128 0x0f /* DW_FORM_udata */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 19 /* Abbrev start */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x3f /* DW_AT_external */ + .uleb128 0x0c /* DW_FORM_flag */ + .uleb128 0x02 /* DW_AT_location */ + .uleb128 0x18 /* DW_FORM_exprloc */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 20 /* Abbrev start */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x3f /* DW_AT_external */ + .uleb128 0x0c /* DW_FORM_flag */ + .uleb128 0x02 /* DW_AT_location */ + .uleb128 0x18 /* DW_FORM_exprloc */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 21 /* Abbrev start */ + .uleb128 0x2e /* DW_TAG_subprogram */ + .byte 1 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x11 /* DW_AT_low_pc */ + .uleb128 0x01 /* DW_FORM_addr */ + .uleb128 0x12 /* DW_AT_high_pc */ + .uleb128 0x01 /* DW_FORM_addr */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x3f /* DW_AT_external */ + .uleb128 0x0c /* DW_FORM_flag */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 22 /* Abbrev start */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x02 /* DW_AT_location */ + .uleb128 0x18 /* DW_FORM_exprloc */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 23 /* Abbrev start */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x02 /* DW_AT_location */ + .uleb128 0x18 /* DW_FORM_exprloc */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 24 /* Abbrev start */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x31 /* DW_AT_abstract_origin */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x02 /* DW_AT_location */ + .uleb128 0x18 /* DW_FORM_exprloc */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 25 /* Abbrev start */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x02 /* DW_AT_location */ + .uleb128 0x18 /* DW_FORM_exprloc */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 26 /* Abbrev start */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x02 /* DW_AT_location */ + .uleb128 0x18 /* DW_FORM_exprloc */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 27 /* Abbrev start */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x02 /* DW_AT_location */ + .uleb128 0x18 /* DW_FORM_exprloc */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 28 /* Abbrev start */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x49 /* DW_AT_type */ + .uleb128 0x13 /* DW_FORM_ref4 */ + .uleb128 0x02 /* DW_AT_location */ + .uleb128 0x18 /* DW_FORM_exprloc */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .uleb128 29 /* Abbrev start */ + .uleb128 0x34 /* DW_TAG_variable */ + .byte 0 /* has_children */ + .uleb128 0x03 /* DW_AT_name */ + .uleb128 0x08 /* DW_FORM_string */ + .uleb128 0x02 /* DW_AT_location */ + .uleb128 0x18 /* DW_FORM_exprloc */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .byte 0x0 /* Terminator */ + .section .note.GNU-stack,"",@progbits diff --git a/testsuite/dwz.tests/varval.c b/testsuite/dwz.tests/varval.c new file mode 100644 index 0000000..f76bfa8 --- /dev/null +++ b/testsuite/dwz.tests/varval.c @@ -0,0 +1,33 @@ +/* Copyright (C) 2018-2019 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 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 + along with this program. If not, see <http://www.gnu.org/licenses/>. + + This was copied from the git://sourceware.org/git/binutils-gdb.git + repository, file gdb/testsuite/gdb.dwarf2/varval.c. */ + +/* Test program for DW_OP_GNU_variable_value. */ + +int var_a = 8; +int var_b = 3; +int *var_p = &var_b; +struct { int a, b, c, d, e, f, g, h; } var_s = { 101, 102, 103, 104, 105, 106, 107, 108 }; + +int +main (void) +{ + asm ("main_label: .globl main_label"); + return 0; +} diff --git a/testsuite/dwz.tests/version.sh b/testsuite/dwz.tests/version.sh new file mode 100644 index 0000000..fb000c7 --- /dev/null +++ b/testsuite/dwz.tests/version.sh @@ -0,0 +1,9 @@ +cp $execs/hello 1 + +dwz -v 1 > dwz.out 2> /dev/null + +grep -q "dwz version" dwz.out + +cmp 1 $execs/hello + +rm -f 1 dwz.out diff --git a/testsuite/lib/dwarf-lib.exp b/testsuite/lib/dwarf-lib.exp new file mode 100644 index 0000000..aca710e --- /dev/null +++ b/testsuite/lib/dwarf-lib.exp @@ -0,0 +1,28 @@ +# Copyright 2019 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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 +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Auxiliary stuff to make the dwarf assembler in dwarf.exp work outside of the +# gdb testsuite context. We don't add this to dwarf.exp to keep differences +# between dwz's dwarf.exp and gdb's dwarf.exp minimal. + +proc is_64_target {} { + if { $::env(POINTER_SIZE) == 8 } { + return 1 + } elseif { $::env(POINTER_SIZE) == 4 } { + return 0 + } else { + error "POINTER_SIZE not defined in env" + } +} diff --git a/testsuite/lib/dwarf.exp b/testsuite/lib/dwarf.exp new file mode 100644 index 0000000..4dbbc6c --- /dev/null +++ b/testsuite/lib/dwarf.exp @@ -0,0 +1,1674 @@ +# Copyright 2010-2020 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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 +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# This was copied from the git://sourceware.org/git/binutils-gdb.git +# repository, file gdb/testsuite/lib/dwarf.exp + +# Return true if the target supports DWARF-2 and uses gas. +# For now pick a sampling of likely targets. +proc dwarf2_support {} { + if {[istarget *-*-linux*] + || [istarget *-*-gnu*] + || [istarget *-*-elf*] + || [istarget *-*-openbsd*] + || [istarget arm*-*-eabi*] + || [istarget arm*-*-symbianelf*] + || [istarget powerpc-*-eabi*]} { + return 1 + } + + return 0 +} + +# Build an executable from a fission-based .S file. +# This handles the extra work of splitting the .o into non-dwo and dwo +# pieces, making sure the .dwo is available if we're using cc-with-tweaks.sh +# to build a .dwp file. +# The arguments and results are the same as for build_executable. +# +# Current restrictions: +# - only supports one source file +# - cannot be run on remote hosts + +proc build_executable_from_fission_assembler { testname executable sources options } { + verbose -log "build_executable_from_fission_assembler $testname $executable $sources $options" + if { [llength $sources] != 1 } { + error "Only one source file supported." + } + if [is_remote host] { + error "Remote hosts are not supported." + } + + global srcdir subdir + set source_file ${srcdir}/${subdir}/${sources} + set root_name [file rootname [file tail $source_file]] + set output_base [standard_output_file $root_name] + set object_file ${output_base}.o + set dwo_file ${output_base}.dwo + set object_options "object $options" + set objcopy [gdb_find_objcopy] + + set result [gdb_compile $source_file $object_file object $options] + if { "$result" != "" } { + return -1 + } + + set command "$objcopy --extract-dwo $object_file $dwo_file" + verbose -log "Executing $command" + set result [catch "exec $command" output] + verbose -log "objcopy --extract-dwo output: $output" + if { $result == 1 } { + return -1 + } + + set command "$objcopy --strip-dwo $object_file" + verbose -log "Executing $command" + set result [catch "exec $command" output] + verbose -log "objcopy --strip-dwo output: $output" + if { $result == 1 } { + return -1 + } + + set result [gdb_compile $object_file $executable executable $options] + if { "$result" != "" } { + return -1 + } + + return 0 +} + +# Return a list of expressions about function FUNC's address and length. +# The first expression is the address of function FUNC, and the second +# one is FUNC's length. SRC is the source file having function FUNC. +# An internal label ${func}_label must be defined inside FUNC: +# +# int main (void) +# { +# asm ("main_label: .globl main_label"); +# return 0; +# } +# +# This label is needed to compute the start address of function FUNC. +# If the compiler is gcc, we can do the following to get function start +# and end address too: +# +# asm ("func_start: .globl func_start"); +# static void func (void) {} +# asm ("func_end: .globl func_end"); +# +# however, this isn't portable, because other compilers, such as clang, +# may not guarantee the order of global asms and function. The code +# becomes: +# +# asm ("func_start: .globl func_start"); +# asm ("func_end: .globl func_end"); +# static void func (void) {} +# + +proc function_range { func src {options {debug}} } { + global decimal gdb_prompt + + set exe [standard_temp_file func_addr[pid].x] + + gdb_compile $src $exe executable $options + + gdb_exit + gdb_start + gdb_load "$exe" + + # Compute the label offset, and we can get the function start address + # by "${func}_label - $func_label_offset". + set func_label_offset "" + set test "p ${func}_label - ${func}" + gdb_test_multiple $test $test { + -re ".* = ($decimal)\r\n$gdb_prompt $" { + set func_label_offset $expect_out(1,string) + } + } + + # Compute the function length. + global hex + set func_length "" + set test "disassemble $func" + gdb_test_multiple $test $test { + -re ".*$hex <\\+($decimal)>:\[^\r\n\]+\r\nEnd of assembler dump\.\r\n$gdb_prompt $" { + set func_length $expect_out(1,string) + } + } + + # Compute the size of the last instruction. + if { $func_length == 0 } then { + set func_pattern "$func" + } else { + set func_pattern "$func\\+$func_length" + } + set test "x/2i $func+$func_length" + gdb_test_multiple $test $test { + -re ".*($hex) <$func_pattern>:\[^\r\n\]+\r\n\[ \]+($hex).*\.\r\n$gdb_prompt $" { + set start $expect_out(1,string) + set end $expect_out(2,string) + + set func_length [expr $func_length + $end - $start] + } + } + + return [list "${func}_label - $func_label_offset" $func_length] +} + +# Extract the start, length, and end for function called NAME and +# create suitable variables in the callers scope. +proc get_func_info { name {options {debug}} } { + global srcdir subdir srcfile + + upvar 1 "${name}_start" func_start + upvar 1 "${name}_len" func_len + upvar 1 "${name}_end" func_end + + lassign [function_range ${name} \ + [list ${srcdir}/${subdir}/$srcfile] \ + ${options}] \ + func_start func_len + set func_end "$func_start + $func_len" +} + +# A DWARF assembler. +# +# All the variables in this namespace are private to the +# implementation. Also, any procedure whose name starts with "_" is +# private as well. Do not use these. +# +# Exported functions are documented at their definition. +# +# In addition to the hand-written functions documented below, this +# module automatically generates a function for each DWARF tag. For +# most tags, two forms are made: a full name, and one with the +# "DW_TAG_" prefix stripped. For example, you can use either +# 'DW_TAG_compile_unit' or 'compile_unit' interchangeably. +# +# There are two exceptions to this rule: DW_TAG_variable and +# DW_TAG_namespace. For these, the full name must always be used, +# as the short name conflicts with Tcl builtins. (Should future +# versions of Tcl or DWARF add more conflicts, this list will grow. +# If you want to be safe you should always use the full names.) +# +# Each tag procedure is defined like: +# +# proc DW_TAG_mumble {{attrs {}} {children {}}} { ... } +# +# ATTRS is an optional list of attributes. +# It is run through 'subst' in the caller's context before processing. +# +# Each attribute in the list has one of two forms: +# 1. { NAME VALUE } +# 2. { NAME VALUE FORM } +# +# In each case, NAME is the attribute's name. +# This can either be the full name, like 'DW_AT_name', or a shortened +# name, like 'name'. These are fully equivalent. +# +# Besides DWARF standard attributes, assembler supports 'macro' attribute +# which will be substituted by one or more standard or macro attributes. +# supported macro attributes are: +# +# - MACRO_AT_range { FUNC } +# It is substituted by DW_AT_low_pc and DW_AT_high_pc with the start and +# end address of function FUNC in file $srcdir/$subdir/$srcfile. +# +# - MACRO_AT_func { FUNC } +# It is substituted by DW_AT_name with FUNC and MACRO_AT_range. +# +# If FORM is given, it should name a DW_FORM_ constant. +# This can either be the short form, like 'DW_FORM_addr', or a +# shortened version, like 'addr'. If the form is given, VALUE +# is its value; see below. In some cases, additional processing +# is done; for example, DW_FORM_strp manages the .debug_str +# section automatically. +# +# If FORM is 'SPECIAL_expr', then VALUE is treated as a location +# expression. The effective form is then DW_FORM_block or DW_FORM_exprloc +# for DWARF version >= 4, and VALUE is passed to the (internal) +# '_location' proc to be translated. +# This proc implements a miniature DW_OP_ assembler. +# +# If FORM is not given, it is guessed: +# * If VALUE starts with the "@" character, the rest of VALUE is +# looked up as a DWARF constant, and DW_FORM_sdata is used. For +# example, '@DW_LANG_c89' could be used. +# * If VALUE starts with the ":" character, then it is a label +# reference. The rest of VALUE is taken to be the name of a label, +# and DW_FORM_ref4 is used. See 'new_label' and 'define_label'. +# * If VALUE starts with the "%" character, then it is a label +# reference too, but DW_FORM_ref_addr is used. +# * Otherwise, if the attribute name has a default form (f.i. DW_FORM_addr for +# DW_AT_low_pc), then that one is used. +# * Otherwise, an error is reported. Either specify a form explicitly, or +# add a default for the the attribute name in _default_form. +# +# CHILDREN is just Tcl code that can be used to define child DIEs. It +# is evaluated in the caller's context. +# +# Currently this code is missing nice support for CFA handling, and +# probably other things as well. + +namespace eval Dwarf { + # True if the module has been initialized. + variable _initialized 0 + + # Constants from dwarf2.h. + variable _constants + # DW_AT short names. + variable _AT + # DW_FORM short names. + variable _FORM + # DW_OP short names. + variable _OP + + # The current output file. + variable _output_file + + # Note: The _cu_ values here also apply to type units (TUs). + # Think of a TU as a special kind of CU. + + # Current CU count. + variable _cu_count + + # The current CU's base label. + variable _cu_label + + # The current CU's version. + variable _cu_version + + # The current CU's address size. + variable _cu_addr_size + # The current CU's offset size. + variable _cu_offset_size + + # Label generation number. + variable _label_num + + # The deferred output array. The index is the section name; the + # contents hold the data for that section. + variable _deferred_output + + # If empty, we should write directly to the output file. + # Otherwise, this is the name of a section to write to. + variable _defer + + # The abbrev section. Typically .debug_abbrev but can be .debug_abbrev.dwo + # for Fission. + variable _abbrev_section + + # The next available abbrev number in the current CU's abbrev + # table. + variable _abbrev_num + + # The string table for this assembly. The key is the string; the + # value is the label for that string. + variable _strings + + # Current .debug_line unit count. + variable _line_count + + # Whether a file_name entry was seen. + variable _line_saw_file + + # Whether a line table program has been seen. + variable _line_saw_program + + # A Label for line table header generation. + variable _line_header_end_label + + # The address size for debug ranges section. + variable _debug_ranges_64_bit + + proc _process_one_constant {name value} { + variable _constants + variable _AT + variable _FORM + variable _OP + + set _constants($name) $value + + if {![regexp "^DW_(\[A-Z\]+)_(\[A-Za-z0-9_\]+)$" $name \ + ignore prefix name2]} { + error "non-matching name: $name" + } + + if {$name2 == "lo_user" || $name2 == "hi_user"} { + return + } + + # We only try to shorten some very common things. + # FIXME: CFA? + switch -exact -- $prefix { + TAG { + # Create two procedures for the tag. These call + # _handle_DW_TAG with the full tag name baked in; this + # does all the actual work. + proc $name {{attrs {}} {children {}}} \ + "_handle_DW_TAG $name \$attrs \$children" + + # Filter out ones that are known to clash. + if {$name2 == "variable" || $name2 == "namespace"} { + set name2 "tag_$name2" + } + + if {[info commands $name2] != {}} { + error "duplicate proc name: from $name" + } + + proc $name2 {{attrs {}} {children {}}} \ + "_handle_DW_TAG $name \$attrs \$children" + } + + AT { + set _AT($name2) $name + } + + FORM { + set _FORM($name2) $name + } + + OP { + set _OP($name2) $name + } + + default { + return + } + } + } + + proc _read_constants {} { + global srcdir hex decimal + + # DWARF name-matching regexp. + set dwrx "DW_\[a-zA-Z0-9_\]+" + # Whitespace regexp. + set ws "\[ \t\]+" + + set fd [open [file join $srcdir .. dwarf2.h]] + while {![eof $fd]} { + set line [gets $fd] + if {[regexp -- "^${ws}($dwrx)${ws}=${ws}($hex|$decimal),?$" \ + $line ignore name value ignore2]} { + _process_one_constant $name $value + } + } + close $fd + + set fd [open [file join $srcdir .. dwarf2.def]] + while {![eof $fd]} { + set line [gets $fd] + if {[regexp -- \ + "^DW_\[A-Z_\]+${ws}\\(($dwrx),${ws}($hex|$decimal)\\)$" \ + $line ignore name value ignore2]} { + _process_one_constant $name $value + } + } + close $fd + } + + proc _quote {string} { + # FIXME + return "\"${string}\\0\"" + } + + proc _nz_quote {string} { + # For now, no quoting is done. + return "\"${string}\"" + } + + proc _handle_DW_FORM {form value} { + switch -exact -- $form { + DW_FORM_string { + _op .ascii [_quote $value] + } + + DW_FORM_flag_present { + # We don't need to emit anything. + } + + DW_FORM_data4 - + DW_FORM_ref4 { + _op .4byte $value + } + + DW_FORM_ref_addr { + variable _cu_offset_size + variable _cu_version + variable _cu_addr_size + + if {$_cu_version == 2} { + set size $_cu_addr_size + } else { + set size $_cu_offset_size + } + + _op .${size}byte $value + } + + DW_FORM_sec_offset { + variable _cu_offset_size + _op .${_cu_offset_size}byte $value + } + + DW_FORM_ref1 - + DW_FORM_flag - + DW_FORM_data1 { + _op .byte $value + } + + DW_FORM_sdata { + _op .sleb128 $value + } + + DW_FORM_ref_udata - + DW_FORM_udata { + _op .uleb128 $value + } + + DW_FORM_addr { + variable _cu_addr_size + + _op .${_cu_addr_size}byte $value + } + + DW_FORM_data2 - + DW_FORM_ref2 { + _op .2byte $value + } + + DW_FORM_data8 - + DW_FORM_ref8 - + DW_FORM_ref_sig8 { + _op .8byte $value + } + + DW_FORM_data16 { + _op .8byte $value + } + + DW_FORM_strp { + variable _strings + variable _cu_offset_size + + if {![info exists _strings($value)]} { + set _strings($value) [new_label strp] + _defer_output .debug_string { + define_label $_strings($value) + _op .ascii [_quote $value] + } + } + + _op .${_cu_offset_size}byte $_strings($value) "strp: $value" + } + + SPECIAL_expr { + set l1 [new_label "expr_start"] + set l2 [new_label "expr_end"] + _op .uleb128 "$l2 - $l1" "expression" + define_label $l1 + _location $value + define_label $l2 + } + + DW_FORM_block1 { + set len [string length $value] + if {$len > 255} { + error "DW_FORM_block1 length too long" + } + _op .byte $len + _op .ascii [_nz_quote $value] + } + + DW_FORM_block2 - + DW_FORM_block4 - + + DW_FORM_block - + + DW_FORM_ref2 - + DW_FORM_indirect - + DW_FORM_exprloc - + + DW_FORM_strx - + DW_FORM_strx1 - + DW_FORM_strx2 - + DW_FORM_strx3 - + DW_FORM_strx4 - + + DW_FORM_GNU_addr_index - + DW_FORM_GNU_str_index - + DW_FORM_GNU_ref_alt - + DW_FORM_GNU_strp_alt - + + default { + error "unhandled form $form" + } + } + } + + proc _guess_form {value varname} { + upvar $varname new_value + + switch -exact -- [string range $value 0 0] { + @ { + # Constant reference. + variable _constants + + set new_value $_constants([string range $value 1 end]) + # Just the simplest. + return DW_FORM_sdata + } + + : { + # Label reference. + variable _cu_label + + set new_value "[string range $value 1 end] - $_cu_label" + + return DW_FORM_ref4 + } + + % { + # Label reference, an offset from .debug_info. + set new_value "[string range $value 1 end]" + + return DW_FORM_ref_addr + } + + default { + return "" + } + } + } + + proc _default_form { attr } { + switch -exact -- $attr { + DW_AT_low_pc { + return DW_FORM_addr + } + DW_AT_producer - + DW_AT_comp_dir - + DW_AT_linkage_name - + DW_AT_MIPS_linkage_name - + DW_AT_name { + return DW_FORM_string + } + } + return "" + } + + # Map NAME to its canonical form. + proc _map_name {name ary} { + variable $ary + + if {[info exists ${ary}($name)]} { + set name [set ${ary}($name)] + } + + return $name + } + + proc _handle_attribute { attr_name attr_value attr_form } { + variable _abbrev_section + variable _constants + variable _cu_version + + _handle_DW_FORM $attr_form $attr_value + + _defer_output $_abbrev_section { + if { $attr_form eq "SPECIAL_expr" } { + if { $_cu_version < 4 } { + set attr_form_comment "DW_FORM_block" + } else { + set attr_form_comment "DW_FORM_exprloc" + } + } else { + set attr_form_comment $attr_form + } + _op .uleb128 $_constants($attr_name) $attr_name + _op .uleb128 $_constants($attr_form) $attr_form_comment + } + } + + # Handle macro attribute MACRO_AT_range. + + proc _handle_macro_at_range { attr_value } { + if {[llength $attr_value] != 1} { + error "usage: MACRO_AT_range { func }" + } + + set func [lindex $attr_value 0] + global srcdir subdir srcfile + set src ${srcdir}/${subdir}/${srcfile} + set result [function_range $func $src] + + _handle_attribute DW_AT_low_pc [lindex $result 0] \ + DW_FORM_addr + _handle_attribute DW_AT_high_pc \ + "[lindex $result 0] + [lindex $result 1]" DW_FORM_addr + } + + # Handle macro attribute MACRO_AT_func. + + proc _handle_macro_at_func { attr_value } { + if {[llength $attr_value] != 1} { + error "usage: MACRO_AT_func { func file }" + } + _handle_attribute DW_AT_name [lindex $attr_value 0] DW_FORM_string + _handle_macro_at_range $attr_value + } + + proc _handle_DW_TAG {tag_name {attrs {}} {children {}}} { + variable _abbrev_section + variable _abbrev_num + variable _constants + + set has_children [expr {[string length $children] > 0}] + set my_abbrev [incr _abbrev_num] + + # We somewhat wastefully emit a new abbrev entry for each tag. + # There's no reason for this other than laziness. + _defer_output $_abbrev_section { + _op .uleb128 $my_abbrev "Abbrev start" + _op .uleb128 $_constants($tag_name) $tag_name + _op .byte $has_children "has_children" + } + + _op .uleb128 $my_abbrev "Abbrev ($tag_name)" + + foreach attr $attrs { + set attr_name [_map_name [lindex $attr 0] _AT] + + # When the length of ATTR is greater than 2, the last + # element of the list must be a form. The second through + # the penultimate elements are joined together and + # evaluated using subst. This allows constructs such as + # [gdb_target_symbol foo] to be used. + + if {[llength $attr] > 2} { + set attr_value [uplevel 2 [list subst [join [lrange $attr 1 end-1]]]] + } else { + set attr_value [uplevel 2 [list subst [lindex $attr 1]]] + } + + if { [string equal "MACRO_AT_func" $attr_name] } { + _handle_macro_at_func $attr_value + } elseif { [string equal "MACRO_AT_range" $attr_name] } { + _handle_macro_at_range $attr_value + } else { + if {[llength $attr] > 2} { + set attr_form [uplevel 2 [list subst [lindex $attr end]]] + + if { [string index $attr_value 0] == ":" } { + # It is a label, get its value. + _guess_form $attr_value attr_value + } + } else { + set attr_form [_guess_form $attr_value attr_value] + if { $attr_form eq "" } { + set attr_form [_default_form $attr_name] + } + if { $attr_form eq "" } { + error "No form for $attr_name $attr_value" + } + } + set attr_form [_map_name $attr_form _FORM] + + _handle_attribute $attr_name $attr_value $attr_form + } + } + + _defer_output $_abbrev_section { + # Terminator. + _op .byte 0x0 "DW_AT - Terminator" + _op .byte 0x0 "DW_FORM - Terminator" + } + + if {$has_children} { + uplevel 2 $children + + # Terminate children. + _op .byte 0x0 "Terminate children" + } + } + + proc _emit {string} { + variable _output_file + variable _defer + variable _deferred_output + + if {$_defer == ""} { + puts $_output_file $string + } else { + append _deferred_output($_defer) ${string}\n + } + } + + proc _section {name {flags ""} {type ""}} { + if {$flags == "" && $type == ""} { + _emit " .section $name" + } elseif {$type == ""} { + _emit " .section $name, \"$flags\"" + } else { + _emit " .section $name, \"$flags\", %$type" + } + } + + # SECTION_SPEC is a list of arguments to _section. + proc _defer_output {section_spec body} { + variable _defer + variable _deferred_output + + set old_defer $_defer + set _defer [lindex $section_spec 0] + + if {![info exists _deferred_output($_defer)]} { + set _deferred_output($_defer) "" + eval _section $section_spec + } + + uplevel $body + + set _defer $old_defer + } + + proc _defer_to_string {body} { + variable _defer + variable _deferred_output + + set old_defer $_defer + set _defer temp + + set _deferred_output($_defer) "" + + uplevel $body + + set result $_deferred_output($_defer) + unset _deferred_output($_defer) + + set _defer $old_defer + return $result + } + + proc _write_deferred_output {} { + variable _output_file + variable _deferred_output + + foreach section [array names _deferred_output] { + # The data already has a newline. + puts -nonewline $_output_file $_deferred_output($section) + } + + # Save some memory. + unset _deferred_output + } + + proc _op {name value {comment ""}} { + set text " ${name} ${value}" + if {$comment != ""} { + # Try to make stuff line up nicely. + while {[string length $text] < 40} { + append text " " + } + append text "/* ${comment} */" + } + _emit $text + } + + proc _compute_label {name} { + return ".L${name}" + } + + # Return a name suitable for use as a label. If BASE_NAME is + # specified, it is incorporated into the label name; this is to + # make debugging the generated assembler easier. If BASE_NAME is + # not specified a generic default is used. This proc does not + # define the label; see 'define_label'. 'new_label' attempts to + # ensure that label names are unique. + proc new_label {{base_name label}} { + variable _label_num + + return [_compute_label ${base_name}[incr _label_num]] + } + + # Define a label named NAME. Ordinarily, NAME comes from a call + # to 'new_label', but this is not required. + proc define_label {name} { + _emit "${name}:" + } + + # A higher-level interface to label handling. + # + # ARGS is a list of label descriptors. Each one is either a + # single element, or a list of two elements -- a name and some + # text. For each descriptor, 'new_label' is invoked. If the list + # form is used, the second element in the list is passed as an + # argument. The label name is used to define a variable in the + # enclosing scope; this can be used to refer to the label later. + # The label name is also used to define a new proc whose name is + # the label name plus a trailing ":". This proc takes a body as + # an argument and can be used to define the label at that point; + # then the body, if any, is evaluated in the caller's context. + # + # For example: + # + # declare_labels int_label + # something { ... $int_label } ;# refer to the label + # int_label: constant { ... } ;# define the label + proc declare_labels {args} { + foreach arg $args { + set name [lindex $arg 0] + set text [lindex $arg 1] + + if { $text == "" } { + set text $name + } + + upvar $name label_var + set label_var [new_label $text] + + proc ${name}: {args} [format { + define_label %s + uplevel $args + } $label_var] + } + } + + # This is a miniature assembler for location expressions. It is + # suitable for use in the attributes to a DIE. Its output is + # prefixed with "=" to make it automatically use DW_FORM_block. + # BODY is split by lines, and each line is taken to be a list. + # (FIXME should use 'info complete' here.) + # Each list's first element is the opcode, either short or long + # forms are accepted. + # FIXME argument handling + # FIXME move docs + proc _location {body} { + variable _constants + variable _cu_label + variable _cu_version + variable _cu_addr_size + variable _cu_offset_size + + foreach line [split $body \n] { + # Ignore blank lines, and allow embedded comments. + if {[lindex $line 0] == "" || [regexp -- {^[ \t]*#} $line]} { + continue + } + set opcode [_map_name [lindex $line 0] _OP] + _op .byte $_constants($opcode) $opcode + + switch -exact -- $opcode { + DW_OP_addr { + _op .${_cu_addr_size}byte [lindex $line 1] + } + + DW_OP_regx { + _op .uleb128 [lindex $line 1] + } + + DW_OP_pick - + DW_OP_const1u - + DW_OP_const1s { + _op .byte [lindex $line 1] + } + + DW_OP_const2u - + DW_OP_const2s { + _op .2byte [lindex $line 1] + } + + DW_OP_const4u - + DW_OP_const4s { + _op .4byte [lindex $line 1] + } + + DW_OP_const8u - + DW_OP_const8s { + _op .8byte [lindex $line 1] + } + + DW_OP_constu { + _op .uleb128 [lindex $line 1] + } + DW_OP_consts { + _op .sleb128 [lindex $line 1] + } + + DW_OP_plus_uconst { + _op .uleb128 [lindex $line 1] + } + + DW_OP_piece { + _op .uleb128 [lindex $line 1] + } + + DW_OP_bit_piece { + _op .uleb128 [lindex $line 1] + _op .uleb128 [lindex $line 2] + } + + DW_OP_skip - + DW_OP_bra { + _op .2byte [lindex $line 1] + } + + DW_OP_implicit_value { + set l1 [new_label "value_start"] + set l2 [new_label "value_end"] + _op .uleb128 "$l2 - $l1" + define_label $l1 + foreach value [lrange $line 1 end] { + switch -regexp -- $value { + {^0x[[:xdigit:]]{1,2}$} {_op .byte $value} + {^0x[[:xdigit:]]{4}$} {_op .2byte $value} + {^0x[[:xdigit:]]{8}$} {_op .4byte $value} + {^0x[[:xdigit:]]{16}$} {_op .8byte $value} + default { + error "bad value '$value' in DW_OP_implicit_value" + } + } + } + define_label $l2 + } + + DW_OP_implicit_pointer - + DW_OP_GNU_implicit_pointer { + if {[llength $line] != 3} { + error "usage: $opcode LABEL OFFSET" + } + + # Here label is a section offset. + set label [lindex $line 1] + if { $_cu_version == 2 } { + _op .${_cu_addr_size}byte $label + } else { + _op .${_cu_offset_size}byte $label + } + _op .sleb128 [lindex $line 2] + } + + DW_OP_GNU_variable_value { + if {[llength $line] != 2} { + error "usage: $opcode LABEL" + } + + # Here label is a section offset. + set label [lindex $line 1] + if { $_cu_version == 2 } { + _op .${_cu_addr_size}byte $label + } else { + _op .${_cu_offset_size}byte $label + } + } + + DW_OP_deref_size { + if {[llength $line] != 2} { + error "usage: DW_OP_deref_size SIZE" + } + + _op .byte [lindex $line 1] + } + + DW_OP_bregx { + _op .uleb128 [lindex $line 1] + _op .sleb128 [lindex $line 2] + } + + default { + if {[llength $line] > 1} { + error "Unimplemented: operands in location for $opcode" + } + } + } + } + } + + # Emit a DWARF CU. + # OPTIONS is a list with an even number of elements containing + # option-name and option-value pairs. + # Current options are: + # is_64 0|1 - boolean indicating if you want to emit 64-bit DWARF + # default = 0 (32-bit) + # version n - DWARF version number to emit + # default = 4 + # addr_size n - the size of addresses in bytes: 4, 8, or default + # default = default + # fission 0|1 - boolean indicating if generating Fission debug info + # default = 0 + # BODY is Tcl code that emits the DIEs which make up the body of + # the CU. It is evaluated in the caller's context. + proc cu {options body} { + variable _constants + variable _cu_count + variable _abbrev_section + variable _abbrev_num + variable _cu_label + variable _cu_version + variable _cu_addr_size + variable _cu_offset_size + + # Establish the defaults. + set is_64 0 + set _cu_version 4 + set _cu_addr_size default + set fission 0 + set section ".debug_info" + set _abbrev_section ".debug_abbrev" + + foreach { name value } $options { + set value [uplevel 1 "subst \"$value\""] + switch -exact -- $name { + is_64 { set is_64 $value } + version { set _cu_version $value } + addr_size { set _cu_addr_size $value } + fission { set fission $value } + default { error "unknown option $name" } + } + } + if {$_cu_addr_size == "default"} { + if {[is_64_target]} { + set _cu_addr_size 8 + } else { + set _cu_addr_size 4 + } + } + set _cu_offset_size [expr { $is_64 ? 8 : 4 }] + if { $fission } { + set section ".debug_info.dwo" + set _abbrev_section ".debug_abbrev.dwo" + } + + if {$_cu_version < 4} { + set _constants(SPECIAL_expr) $_constants(DW_FORM_block) + } else { + set _constants(SPECIAL_expr) $_constants(DW_FORM_exprloc) + } + + _section $section + + set cu_num [incr _cu_count] + set my_abbrevs [_compute_label "abbrev${cu_num}_begin"] + set _abbrev_num 1 + + set _cu_label [_compute_label "cu${cu_num}_begin"] + set start_label [_compute_label "cu${cu_num}_start"] + set end_label [_compute_label "cu${cu_num}_end"] + + define_label $_cu_label + if {$is_64} { + _op .4byte 0xffffffff + _op .8byte "$end_label - $start_label" + } else { + _op .4byte "$end_label - $start_label" + } + define_label $start_label + _op .2byte $_cu_version Version + _op .${_cu_offset_size}byte $my_abbrevs Abbrevs + _op .byte $_cu_addr_size "Pointer size" + + _defer_output $_abbrev_section { + define_label $my_abbrevs + } + + uplevel $body + + _defer_output $_abbrev_section { + # Emit the terminator. + _op .byte 0x0 "Abbrev end - Terminator" + } + + define_label $end_label + } + + # Emit a DWARF TU. + # OPTIONS is a list with an even number of elements containing + # option-name and option-value pairs. + # Current options are: + # is_64 0|1 - boolean indicating if you want to emit 64-bit DWARF + # default = 0 (32-bit) + # version n - DWARF version number to emit + # default = 4 + # addr_size n - the size of addresses in bytes: 4, 8, or default + # default = default + # fission 0|1 - boolean indicating if generating Fission debug info + # default = 0 + # SIGNATURE is the 64-bit signature of the type. + # TYPE_LABEL is the label of the type defined by this TU, + # or "" if there is no type (i.e., type stubs in Fission). + # BODY is Tcl code that emits the DIEs which make up the body of + # the TU. It is evaluated in the caller's context. + proc tu {options signature type_label body} { + variable _cu_count + variable _abbrev_section + variable _abbrev_num + variable _cu_label + variable _cu_version + variable _cu_addr_size + variable _cu_offset_size + + # Establish the defaults. + set is_64 0 + set _cu_version 4 + set _cu_addr_size default + set fission 0 + set section ".debug_types" + set _abbrev_section ".debug_abbrev" + + foreach { name value } $options { + switch -exact -- $name { + is_64 { set is_64 $value } + version { set _cu_version $value } + addr_size { set _cu_addr_size $value } + fission { set fission $value } + default { error "unknown option $name" } + } + } + if {$_cu_addr_size == "default"} { + if {[is_64_target]} { + set _cu_addr_size 8 + } else { + set _cu_addr_size 4 + } + } + set _cu_offset_size [expr { $is_64 ? 8 : 4 }] + if { $fission } { + set section ".debug_types.dwo" + set _abbrev_section ".debug_abbrev.dwo" + } + + _section $section + + set cu_num [incr _cu_count] + set my_abbrevs [_compute_label "abbrev${cu_num}_begin"] + set _abbrev_num 1 + + set _cu_label [_compute_label "cu${cu_num}_begin"] + set start_label [_compute_label "cu${cu_num}_start"] + set end_label [_compute_label "cu${cu_num}_end"] + + define_label $_cu_label + if {$is_64} { + _op .4byte 0xffffffff + _op .8byte "$end_label - $start_label" + } else { + _op .4byte "$end_label - $start_label" + } + define_label $start_label + _op .2byte $_cu_version Version + _op .${_cu_offset_size}byte $my_abbrevs Abbrevs + _op .byte $_cu_addr_size "Pointer size" + _op .8byte $signature Signature + if { $type_label != "" } { + uplevel declare_labels $type_label + upvar $type_label my_type_label + if {$is_64} { + _op .8byte "$my_type_label - $_cu_label" + } else { + _op .4byte "$my_type_label - $_cu_label" + } + } else { + if {$is_64} { + _op .8byte 0 + } else { + _op .4byte 0 + } + } + + _defer_output $_abbrev_section { + define_label $my_abbrevs + } + + uplevel $body + + _defer_output $_abbrev_section { + # Emit the terminator. + _op .byte 0x0 "Abbrev end - Terminator" + } + + define_label $end_label + } + + # Emit a DWARF .debug_ranges unit. + # OPTIONS is a list with an even number of elements containing + # option-name and option-value pairs. + # Current options are: + # is_64 0|1 - boolean indicating if you want to emit 64-bit DWARF + # default = 0 (32-bit) + # + # BODY is Tcl code that emits the content of the .debug_ranges + # unit, it is evaluated in the caller's context. + proc ranges {options body} { + variable _debug_ranges_64_bit + + foreach { name value } $options { + switch -exact -- $name { + is_64 { set _debug_ranges_64_bit [subst $value] } + default { error "unknown option $name" } + } + } + + set section ".debug_ranges" + _section $section + + proc sequence { body } { + variable _debug_ranges_64_bit + + # Emit the sequence of addresses. + + proc base { addr } { + variable _debug_ranges_64_bit + + if { $_debug_ranges_64_bit } then { + _op .8byte 0xffffffffffffffff "Base Marker" + _op .8byte $addr "Base Address" + } else { + _op .4byte 0xffffffff "Base Marker" + _op .4byte $addr "Base Address" + } + } + + proc range { start end } { + variable _debug_ranges_64_bit + + if { $_debug_ranges_64_bit } then { + _op .8byte $start "Start Address" + _op .8byte $end "End Address" + } else { + _op .4byte $start "Start Address" + _op .4byte $end "End Address" + } + } + + uplevel $body + + # End of the sequence. + if { $_debug_ranges_64_bit } then { + _op .8byte 0x0 "End of Sequence Marker (Part 1)" + _op .8byte 0x0 "End of Sequence Marker (Part 2)" + } else { + _op .4byte 0x0 "End of Sequence Marker (Part 1)" + _op .4byte 0x0 "End of Sequence Marker (Part 2)" + } + } + + uplevel $body + } + + + # Emit a DWARF .debug_line unit. + # OPTIONS is a list with an even number of elements containing + # option-name and option-value pairs. + # Current options are: + # is_64 0|1 - boolean indicating if you want to emit 64-bit DWARF + # default = 0 (32-bit) + # version n - DWARF version number to emit + # default = 4 + # addr_size n - the size of addresses in bytes: 4, 8, or default + # default = default + # + # LABEL is the label of the current unit (which is probably + # referenced by a DW_AT_stmt_list), or "" if there is no such + # label. + # + # BODY is Tcl code that emits the parts which make up the body of + # the line unit. It is evaluated in the caller's context. The + # following commands are available for the BODY section: + # + # include_dir "dirname" -- adds a new include directory + # + # file_name "file.c" idx -- adds a new file name. IDX is a + # 1-based index referencing an include directory or 0 for + # current directory. + + proc lines {options label body} { + variable _line_count + variable _line_saw_file + variable _line_saw_program + variable _line_header_end_label + + # Establish the defaults. + set is_64 0 + set _unit_version 4 + set _unit_addr_size default + set _line_saw_program 0 + set _line_saw_file 0 + set _default_is_stmt 1 + + foreach { name value } $options { + switch -exact -- $name { + is_64 { set is_64 $value } + version { set _unit_version $value } + addr_size { set _unit_addr_size $value } + default_is_stmt { set _default_is_stmt $value } + default { error "unknown option $name" } + } + } + if {$_unit_addr_size == "default"} { + if {[is_64_target]} { + set _unit_addr_size 8 + } else { + set _unit_addr_size 4 + } + } + + set unit_num [incr _line_count] + + set section ".debug_line" + _section $section + + if { "$label" != "" } { + # Define the user-provided label at this point. + $label: + } + + set unit_len_label [_compute_label "line${_line_count}_start"] + set unit_end_label [_compute_label "line${_line_count}_end"] + set header_len_label [_compute_label "line${_line_count}_header_start"] + set _line_header_end_label [_compute_label "line${_line_count}_header_end"] + + if {$is_64} { + _op .4byte 0xffffffff + _op .8byte "$unit_end_label - $unit_len_label" "unit_length" + } else { + _op .4byte "$unit_end_label - $unit_len_label" "unit_length" + } + + define_label $unit_len_label + + _op .2byte $_unit_version version + + if {$is_64} { + _op .8byte "$_line_header_end_label - $header_len_label" "header_length" + } else { + _op .4byte "$_line_header_end_label - $header_len_label" "header_length" + } + + define_label $header_len_label + + _op .byte 1 "minimum_instruction_length" + _op .byte $_default_is_stmt "default_is_stmt" + _op .byte 1 "line_base" + _op .byte 1 "line_range" + _op .byte 10 "opcode_base" + + # The standard_opcode_lengths table. The number of arguments + # for each of the standard opcodes. Generating 9 entries here + # matches the use of 10 in the opcode_base above. These 9 + # entries match the 9 standard opcodes for DWARF2, making use + # of only 9 should be fine, even if we are generating DWARF3 + # or DWARF4. + _op .byte 0 "standard opcode 1" + _op .byte 1 "standard opcode 2" + _op .byte 1 "standard opcode 3" + _op .byte 1 "standard opcode 4" + _op .byte 1 "standard opcode 5" + _op .byte 0 "standard opcode 6" + _op .byte 0 "standard opcode 7" + _op .byte 0 "standard opcode 8" + _op .byte 1 "standard opcode 9" + + proc include_dir {dirname} { + _op .ascii [_quote $dirname] + } + + proc file_name {filename diridx} { + variable _line_saw_file + if "! $_line_saw_file" { + # Terminate the dir list. + _op .byte 0 "Terminator." + set _line_saw_file 1 + } + + _op .ascii [_quote $filename] + _op .sleb128 $diridx + _op .sleb128 0 "mtime" + _op .sleb128 0 "length" + } + + proc program {statements} { + variable _line_saw_program + variable _line_header_end_label + variable _line + + set _line 1 + + if "! $_line_saw_program" { + # Terminate the file list. + _op .byte 0 "Terminator." + define_label $_line_header_end_label + set _line_saw_program 1 + } + + proc DW_LNE_set_address {addr} { + _op .byte 0 + set start [new_label "set_address_start"] + set end [new_label "set_address_end"] + _op .uleb128 "${end} - ${start}" + define_label ${start} + _op .byte 2 + if {[is_64_target]} { + _op .8byte ${addr} + } else { + _op .4byte ${addr} + } + define_label ${end} + } + + proc DW_LNE_end_sequence {} { + variable _line + _op .byte 0 + _op .uleb128 1 + _op .byte 1 + set _line 1 + } + + proc DW_LNE_user { len opcode } { + set DW_LNE_lo_usr 0x80 + set DW_LNE_hi_usr 0xff + if { $DW_LNE_lo_usr <= $opcode + && $opcode <= $DW_LNE_hi_usr } { + _op .byte 0 + _op .uleb128 $len + _op .byte $opcode + for {set i 1} {$i < $len} {incr i} { + _op .byte 0 + } + } else { + error "unknown vendor specific extended opcode: $opcode" + } + } + + proc DW_LNS_copy {} { + _op .byte 1 + } + + proc DW_LNS_negate_stmt {} { + _op .byte 6 + } + + proc DW_LNS_advance_pc {offset} { + _op .byte 2 + _op .uleb128 ${offset} + } + + proc DW_LNS_advance_line {offset} { + variable _line + _op .byte 3 + _op .sleb128 ${offset} + set _line [expr $_line + $offset] + } + + # A pseudo line number program instruction, that can be used instead + # of DW_LNS_advance_line. Rather than writing: + # {DW_LNS_advance_line [expr $line1 - 1]} + # {DW_LNS_advance_line [expr $line2 - $line1]} + # {DW_LNS_advance_line [expr $line3 - $line2]} + # we can just write: + # {line $line1} + # {line $line2} + # {line $line3} + proc line {line} { + variable _line + set offset [expr $line - $_line] + DW_LNS_advance_line $offset + } + + proc DW_LNS_set_file {num} { + _op .byte 4 + _op .sleb128 ${num} + } + + foreach statement $statements { + uplevel 1 $statement + } + } + + uplevel $body + + rename include_dir "" + rename file_name "" + + # Terminate dir list if we saw no files. + if "! $_line_saw_file" { + _op .byte 0 "Terminator." + } + + # Terminate the file list. + if "! $_line_saw_program" { + _op .byte 0 "Terminator." + define_label $_line_header_end_label + } + + define_label $unit_end_label + } + + proc _empty_array {name} { + upvar $name the_array + + catch {unset the_array} + set the_array(_) {} + unset the_array(_) + } + + # Emit a .gnu_debugaltlink section with the given file name and + # build-id. The buildid should be represented as a hexadecimal + # string, like "ffeeddcc". + proc gnu_debugaltlink {filename buildid} { + _defer_output .gnu_debugaltlink { + _op .ascii [_quote $filename] + foreach {a b} [split $buildid {}] { + _op .byte 0x$a$b + } + } + } + + proc _note {type name hexdata} { + set namelen [expr [string length $name] + 1] + + # Name size. + _op .4byte $namelen + # Data size. + _op .4byte [expr [string length $hexdata] / 2] + # Type. + _op .4byte $type + # The name. + _op .ascii [_quote $name] + # Alignment. + set align 2 + set total [expr {($namelen + (1 << $align) - 1) & -(1 << $align)}] + for {set i $namelen} {$i < $total} {incr i} { + _op .byte 0 + } + # The data. + foreach {a b} [split $hexdata {}] { + _op .byte 0x$a$b + } + } + + # Emit a note section holding the given build-id. + proc build_id {buildid} { + _defer_output {.note.gnu.build-id a note} { + # From elf/common.h. + set NT_GNU_BUILD_ID 3 + + _note $NT_GNU_BUILD_ID GNU $buildid + } + } + + # The top-level interface to the DWARF assembler. + # FILENAME is the name of the file where the generated assembly + # code is written. + # BODY is Tcl code to emit the assembly. It is evaluated via + # "eval" -- not uplevel as you might expect, because it is + # important to run the body in the Dwarf namespace. + # + # A typical invocation is something like: + # Dwarf::assemble $file { + # cu 0 2 8 { + # compile_unit { + # ... + # } + # } + # cu 0 2 8 { + # ... + # } + # } + proc assemble {filename body} { + variable _initialized + variable _output_file + variable _deferred_output + variable _defer + variable _label_num + variable _strings + variable _cu_count + variable _line_count + variable _line_saw_file + variable _line_saw_program + variable _line_header_end_label + variable _debug_ranges_64_bit + + if {!$_initialized} { + _read_constants + set _initialized 1 + } + + set _output_file [open $filename w] + set _cu_count 0 + _empty_array _deferred_output + set _defer "" + set _label_num 0 + _empty_array _strings + + set _line_count 0 + set _line_saw_file 0 + set _line_saw_program 0 + set _debug_ranges_64_bit [is_64_target] + + # Not "uplevel" here, because we want to evaluate in this + # namespace. This is somewhat bad because it means we can't + # readily refer to outer variables. + eval $body + + _write_deferred_output + + _section .note.GNU-stack "" progbits + + catch {close $_output_file} + set _output_file {} + } +} diff --git a/testsuite/lib/dwz.exp b/testsuite/lib/dwz.exp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/testsuite/lib/dwz.exp diff --git a/testsuite/lib/invalid-dw-at-stmt-list-encoding.exp b/testsuite/lib/invalid-dw-at-stmt-list-encoding.exp new file mode 100644 index 0000000..490fca4 --- /dev/null +++ b/testsuite/lib/invalid-dw-at-stmt-list-encoding.exp @@ -0,0 +1,21 @@ +load_lib dwarf-lib.exp +load_lib dwarf.exp + +set asm_file invalid-dw-at-stmt-list-encoding-dw.S + +Dwarf::assemble $asm_file { + cu {} { + DW_TAG_compile_unit { + {DW_AT_language @DW_LANG_C} + {DW_AT_name foo.c} + {DW_AT_comp_dir /bar} + {stmt_list 0 DW_FORM_addr} + } { + DW_TAG_base_type { + {DW_AT_byte_size 4 DW_FORM_data1} + {DW_AT_name integer} + } + } + } + +} diff --git a/testsuite/lib/no-multifile-prop.exp b/testsuite/lib/no-multifile-prop.exp new file mode 100644 index 0000000..bcd9669 --- /dev/null +++ b/testsuite/lib/no-multifile-prop.exp @@ -0,0 +1,132 @@ +# Copyright 2019 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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 +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +load_lib dwarf-lib.exp +load_lib dwarf.exp + +set asm_file no-multifile-prop-dw.S + +Dwarf::assemble $asm_file { + + # CU 1 + cu {} { + DW_TAG_compile_unit { + {DW_AT_language @DW_LANG_C_plus_plus} + {DW_AT_name 1.c} + {DW_AT_comp_dir /tmp} + } { + declare_labels integer_label var_label + + integer_label: DW_TAG_base_type { + {DW_AT_byte_size 4 DW_FORM_data1} + {DW_AT_encoding @DW_ATE_signed} + {DW_AT_name integer} + } + + var_label: DW_TAG_variable { + {DW_AT_name foo1} + {DW_AT_type :$integer_label} + {external 1 flag} + } + + DW_TAG_variable { + {DW_AT_name foo2} + {DW_AT_type :$integer_label} + {DW_AT_location { + DW_OP_GNU_implicit_pointer $var_label 0 + } SPECIAL_expr} + {external 1 flag} + } + } + } + + # CU 2 + cu {} { + DW_TAG_compile_unit { + {DW_AT_language @DW_LANG_C_plus_plus} + {DW_AT_name 2.c} + {DW_AT_comp_dir /tmp} + } { + declare_labels integer_label var_label + + integer_label: DW_TAG_base_type { + {DW_AT_byte_size 4 DW_FORM_data1} + {DW_AT_encoding @DW_ATE_signed} + {DW_AT_name integer} + } + + var_label: DW_TAG_variable { + {DW_AT_name foo1} + {DW_AT_type :$integer_label} + {external 1 flag} + } + + DW_TAG_imported_declaration { + {DW_AT_import :$var_label} + } + } + } + + # CU 3 + cu {} { + DW_TAG_compile_unit { + {DW_AT_language @DW_LANG_C_plus_plus} + {DW_AT_name 3.c} + {DW_AT_comp_dir /tmp} + } { + declare_labels integer_label + + integer_label: DW_TAG_base_type { + {DW_AT_byte_size 4 DW_FORM_data1} + {DW_AT_encoding @DW_ATE_signed} + {DW_AT_name integer} + } + + DW_TAG_variable { + {DW_AT_name foo1} + {DW_AT_type :$integer_label} + {external 1 flag} + } + + } + } + + # CU 4 + cu {} { + DW_TAG_compile_unit { + {DW_AT_language @DW_LANG_C_plus_plus} + {DW_AT_name 4.c} + {DW_AT_comp_dir /tmp} + } { + declare_labels integer_label var_label + + integer_label: DW_TAG_base_type { + {DW_AT_byte_size 4 DW_FORM_data1} + {DW_AT_encoding @DW_ATE_signed} + {DW_AT_name integer} + } + + var_label: DW_TAG_variable { + {DW_AT_name foo1} + {DW_AT_type :$integer_label} + {external 1 flag} + } + + DW_TAG_imported_declaration { + {DW_AT_import :$var_label} + } + } + } +} diff --git a/testsuite/lib/unavailable-dwarf-piece.exp b/testsuite/lib/unavailable-dwarf-piece.exp new file mode 100644 index 0000000..0fa1df9 --- /dev/null +++ b/testsuite/lib/unavailable-dwarf-piece.exp @@ -0,0 +1,272 @@ +# Copyright (C) 2013-2021 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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 +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +load_lib dwarf-lib.exp +load_lib dwarf.exp + +set asm_file unavailable-dwarf-piece-dw.S + +Dwarf::assemble $asm_file { + declare_labels uchar_label struct_s_label foo_label struct_t_label bar_label + + cu {} { + compile_unit {{language @DW_LANG_C}} { + uchar_label: DW_TAG_base_type { + {name "unsigned char"} + {byte_size 1 DW_FORM_sdata} + {encoding @DW_ATE_unsigned_char} + } + + struct_s_label: DW_TAG_structure_type { + {name s} + {byte_size 3 DW_FORM_sdata} + {decl_file 0 DW_FORM_udata} + {decl_line 1 DW_FORM_sdata} + } { + DW_TAG_member { + {name a} + {type :$uchar_label} + {data_member_location { + DW_OP_plus_uconst 0 + } SPECIAL_expr} + } + DW_TAG_member { + {name b} + {type :$uchar_label} + {data_member_location { + DW_OP_plus_uconst 1 + } SPECIAL_expr} + } + DW_TAG_member { + {name c} + {type :$uchar_label} + {data_member_location { + DW_OP_plus_uconst 2 + } SPECIAL_expr} + } + } + + struct_t_label: DW_TAG_structure_type { + {name t} + {byte_size 3 DW_FORM_sdata} + {decl_file 0 DW_FORM_udata} + {decl_line 1 DW_FORM_sdata} + } { + DW_TAG_member { + {name a} + {type :$uchar_label} + {data_member_location { + DW_OP_plus_uconst 0 + } SPECIAL_expr} + } + DW_TAG_member { + {name b} + {type :$uchar_label} + {byte_size 1 DW_FORM_sdata} + {bit_size 1 DW_FORM_sdata} + {bit_offset 7 DW_FORM_sdata} + {data_member_location { + DW_OP_plus_uconst 1 + } SPECIAL_expr} + } + DW_TAG_member { + {name c} + {type :$uchar_label} + {byte_size 1 DW_FORM_sdata} + {bit_size 1 DW_FORM_sdata} + {bit_offset 6 DW_FORM_sdata} + {data_member_location { + DW_OP_plus_uconst 1 + } SPECIAL_expr} + } + DW_TAG_member { + {name d} + {type :$uchar_label} + {byte_size 1 DW_FORM_sdata} + {bit_size 1 DW_FORM_sdata} + {bit_offset 5 DW_FORM_sdata} + {data_member_location { + DW_OP_plus_uconst 1 + } SPECIAL_expr} + } + DW_TAG_member { + {name e} + {type :$uchar_label} + {byte_size 1 DW_FORM_sdata} + {bit_size 1 DW_FORM_sdata} + {bit_offset 4 DW_FORM_sdata} + {data_member_location { + DW_OP_plus_uconst 1 + } SPECIAL_expr} + } + DW_TAG_member { + {name f} + {type :$uchar_label} + {byte_size 1 DW_FORM_sdata} + {bit_size 1 DW_FORM_sdata} + {bit_offset 3 DW_FORM_sdata} + {data_member_location { + DW_OP_plus_uconst 1 + } SPECIAL_expr} + } + DW_TAG_member { + {name g} + {type :$uchar_label} + {byte_size 1 DW_FORM_sdata} + {bit_size 1 DW_FORM_sdata} + {bit_offset 2 DW_FORM_sdata} + {data_member_location { + DW_OP_plus_uconst 1 + } SPECIAL_expr} + } + DW_TAG_member { + {name h} + {type :$uchar_label} + {byte_size 1 DW_FORM_sdata} + {bit_size 1 DW_FORM_sdata} + {bit_offset 1 DW_FORM_sdata} + {data_member_location { + DW_OP_plus_uconst 1 + } SPECIAL_expr} + } + DW_TAG_member { + {name i} + {type :$uchar_label} + {byte_size 1 DW_FORM_sdata} + {bit_size 1 DW_FORM_sdata} + {bit_offset 0 DW_FORM_sdata} + {data_member_location { + DW_OP_plus_uconst 1 + } SPECIAL_expr} + } + DW_TAG_member { + {name j} + {type :$uchar_label} + {data_member_location { + DW_OP_plus_uconst 2 + } SPECIAL_expr} + } + } + + DW_TAG_subprogram { + {name foo} + {decl_file 0 udata} + {low_pc foo_start_lbl addr} + {high_pc foo_end_lbl addr} + } { + DW_TAG_formal_parameter { + {type :$struct_s_label} + {name x} + {location { + DW_OP_lit0 + DW_OP_stack_value + DW_OP_piece 2 + DW_OP_reg0 + DW_OP_piece 1 + } SPECIAL_expr} + } + DW_TAG_formal_parameter { + {type :$struct_s_label} + {name y} + {location { + DW_OP_lit0 + DW_OP_stack_value + DW_OP_piece 1 + DW_OP_reg0 + DW_OP_piece 1 + DW_OP_lit0 + DW_OP_stack_value + DW_OP_piece 1 + } SPECIAL_expr} + } + DW_TAG_formal_parameter { + {type :$struct_s_label} + {name z} + {location { + DW_OP_reg0 + DW_OP_piece 1 + DW_OP_lit0 + DW_OP_stack_value + DW_OP_piece 2 + } SPECIAL_expr} + } + } + + + DW_TAG_subprogram { + {name bar} + {decl_file 0 udata} + {low_pc bar_start_lbl addr} + {high_pc bar_end_lbl addr} + } { + DW_TAG_formal_parameter { + {type :$struct_t_label} + {name x} + {location { + DW_OP_lit0 + DW_OP_stack_value + DW_OP_piece 1 + DW_OP_reg0 + DW_OP_bit_piece 1 0 + DW_OP_lit0 + DW_OP_stack_value + DW_OP_bit_piece 7 0 + DW_OP_lit0 + DW_OP_stack_value + DW_OP_piece 1 + } SPECIAL_expr} + } + DW_TAG_formal_parameter { + {type :$struct_t_label} + {name y} + {location { + DW_OP_lit0 + DW_OP_stack_value + DW_OP_piece 1 + DW_OP_lit0 + DW_OP_stack_value + DW_OP_bit_piece 3 0 + DW_OP_reg0 + DW_OP_bit_piece 1 0 + DW_OP_lit0 + DW_OP_stack_value + DW_OP_bit_piece 4 0 + DW_OP_lit0 + DW_OP_stack_value + DW_OP_piece 1 + } SPECIAL_expr} + } + DW_TAG_formal_parameter { + {type :$struct_t_label} + {name z} + {location { + DW_OP_lit0 + DW_OP_stack_value + DW_OP_piece 1 + DW_OP_lit0 + DW_OP_stack_value + DW_OP_bit_piece 7 0 + DW_OP_reg0 + DW_OP_bit_piece 1 0 + DW_OP_lit0 + DW_OP_stack_value + DW_OP_piece 1 + } SPECIAL_expr} + } + } + + } + } +} diff --git a/testsuite/scripts/gnu-debugaltlink-name.sh b/testsuite/scripts/gnu-debugaltlink-name.sh new file mode 100755 index 0000000..667c5fd --- /dev/null +++ b/testsuite/scripts/gnu-debugaltlink-name.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +if ! readelf -S $1 | grep -q '\.gnu_debugaltlink'; then + exit +fi + +readelf \ + --string-dump=.gnu_debugaltlink \ + $1 \ + | grep -a '\[[ ]*0\]' \ + | sed 's/.*0\] //' diff --git a/testsuite/scripts/hardlinks-p.sh b/testsuite/scripts/hardlinks-p.sh new file mode 100755 index 0000000..a9a3020 --- /dev/null +++ b/testsuite/scripts/hardlinks-p.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +hardlinks=$(find -samefile "$1") + +for f in "$@"; do + found=false + for hl in $hardlinks; do + if [ "$hl" = "./$f" ]; then + found=true + break + fi + done + if ! $found; then + exit 1 + fi +done diff --git a/testsuite/scripts/smaller-than.sh b/testsuite/scripts/smaller-than.sh new file mode 100755 index 0000000..3b452dc --- /dev/null +++ b/testsuite/scripts/smaller-than.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +f1=$1 +f2=$2 + +section_size () +{ + local f="$1" + local section="$2" + + local s + s=$(readelf -S -W $f \ + | grep "\.debug_$section" \ + | sed 's/.*\.debug_//' \ + | awk '{print $5}') + + if [ "$s" = "" ]; then + echo 0 + return + fi + + # Convert hex to decimal. + s=$(printf "%d" $((16#$s))) + + echo $s +} + +size () +{ + local f="$1" + + local total=0 + local section + for section in info abbrev str macro types; do + total=$(($total + $(section_size $f $section))) + done + + echo $total +} + +s1=$(size $f1) +s2=$(size $f2) + +if [ $s1 -ge $s2 ]; then + exit 1 +fi + +exit 0 diff --git a/testsuite/scripts/verify-dwarf.sh b/testsuite/scripts/verify-dwarf.sh new file mode 100755 index 0000000..8e97c3c --- /dev/null +++ b/testsuite/scripts/verify-dwarf.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +exec="$1" + +gdb -batch "$exec" diff --git a/testsuite/scripts/xunzip-dir.sh b/testsuite/scripts/xunzip-dir.sh new file mode 100755 index 0000000..624d525 --- /dev/null +++ b/testsuite/scripts/xunzip-dir.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +src="$1" +dst="$2" + +if [ ! -d $src ]; then + exit 0 +fi + +files=$(cd $src; find -name "*.xz") + +for f in $files; do + df=$(echo $f \ + | sed 's/\.xz$//') + if [ -f $dst/$df ]; then + continue + fi + cp $src/$f $dst/$f + xz -d $dst/$f +done @@ -0,0 +1,63 @@ +/* Copyright (C) 2001-2021 Red Hat, Inc. + Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2019-2021 SUSE LLC. + Written by Jakub Jelinek <jakub@redhat.com>, 2012. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + 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 + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* Utility macros. */ + +#define IMPLIES(A, B) (!((A) && !(B))) + +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) + +#define XSTR(s) STR(s) +#define STR(s) #s + +#ifndef USE_GNUC +#ifdef __GNUC__ +#define USE_GNUC 1 +#else +#define USE_GNUC 0 +#endif +#endif + +#if USE_GNUC && __GNUC__ >= 3 +# define likely(x) __builtin_expect (!!(x), 1) +# define unlikely(x) __builtin_expect (!!(x), 0) +#else +# define likely(x) (x) +# define unlikely(x) (x) +#endif + +#if USE_GNUC +# define FORCE_INLINE __attribute__((always_inline)) +# define UNUSED __attribute__((unused)) +# define USED __attribute__((used)) +#else +# define FORCE_INLINE +# define UNUSED +# define USED +#endif + +#if USE_GNUC +# define ALIGN_STRUCT(name) +# define ALIGNOF_STRUCT(name) __alignof__ (struct name) +#else +# define ALIGN_STRUCT(name) struct align_##name { char c; struct name s; }; +# define ALIGNOF_STRUCT(name) offsetof (struct align_##name, s) +#endif |