summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 17:55:52 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 17:55:52 +0000
commitf7a951d79bc895eb2171c2570add9f4899794a10 (patch)
treecc0c7147f472fecbc93add134f5c0e5c1bb72529
parentInitial commit. (diff)
downloaddwz-f7a951d79bc895eb2171c2570add9f4899794a10.tar.xz
dwz-f7a951d79bc895eb2171c2570add9f4899794a10.zip
Adding upstream version 0.15.upstream/0.15upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--.gitignore8
-rw-r--r--COPYING340
-rw-r--r--COPYING.RUNTIME73
-rw-r--r--COPYING3674
-rw-r--r--COPYRIGHT_YEARS3
-rw-r--r--COVER-LETTER1
-rw-r--r--Makefile201
-rw-r--r--README.release-checklist28
-rw-r--r--VERSION1
-rw-r--r--args.c743
-rw-r--r--args.h97
-rwxr-xr-xconfigure18
-rwxr-xr-xcontrib/bytes-per-die.sh16
-rwxr-xr-xcontrib/gen-dwz-debug-all.sh28
-rw-r--r--contrib/release/copyright-lines.awk27
-rwxr-xr-xcontrib/release/do-release.sh67
-rwxr-xr-xcontrib/release/gen-copyright-years.sh164
-rwxr-xr-xcontrib/release/upload-release.sh67
-rw-r--r--dwarf2.def818
-rw-r--r--dwarf2.h556
-rw-r--r--dwarfnames.c118
-rw-r--r--dwz.1207
-rw-r--r--dwz.c16872
-rw-r--r--hashtab.c628
-rw-r--r--hashtab.h160
-rw-r--r--iterators.h126
-rw-r--r--pool.c103
-rw-r--r--pool.h26
-rw-r--r--sha1.c340
-rw-r--r--sha1.h95
-rw-r--r--testsuite/config/default.exp0
-rw-r--r--testsuite/dwz.tests/cycle.c13
-rw-r--r--testsuite/dwz.tests/cycle.sh36
-rw-r--r--testsuite/dwz.tests/decl.cc3
-rw-r--r--testsuite/dwz.tests/def.cc11
-rw-r--r--testsuite/dwz.tests/def.h10
-rw-r--r--testsuite/dwz.tests/def2.cc9
-rw-r--r--testsuite/dwz.tests/devel-ignore-locus.sh27
-rw-r--r--testsuite/dwz.tests/devel-ignore-size.sh29
-rw-r--r--testsuite/dwz.tests/devel-trace.sh5
-rw-r--r--testsuite/dwz.tests/dw2-skip-prologue.S403
-rw-r--r--testsuite/dwz.tests/dw2-skip-prologue.c61
-rw-r--r--testsuite/dwz.tests/dwz-tests.exp145
-rw-r--r--testsuite/dwz.tests/dwz.sh7
-rw-r--r--testsuite/dwz.tests/eu-strip-unstrip-multifile.sh18
-rw-r--r--testsuite/dwz.tests/eu-strip-unstrip.sh11
-rwxr-xr-xtestsuite/dwz.tests/execs.xz/hello-ld-2.26.1.xzbin0 -> 2380 bytes
-rwxr-xr-xtestsuite/dwz.tests/execs.xz/hello-leap-15.0.xzbin0 -> 3996 bytes
-rw-r--r--testsuite/dwz.tests/gdb-add-index.sh15
-rw-r--r--testsuite/dwz.tests/gold-gdb-index.sh7
-rw-r--r--testsuite/dwz.tests/hardlink-multifile.sh15
-rw-r--r--testsuite/dwz.tests/hardlink.sh11
-rw-r--r--testsuite/dwz.tests/hello.c8
-rw-r--r--testsuite/dwz.tests/help.sh13
-rw-r--r--testsuite/dwz.tests/illegal-option.sh25
-rw-r--r--testsuite/dwz.tests/implptr-64bit-d2o4a8r8t0.S157
-rw-r--r--testsuite/dwz.tests/ld-2.26.1-multifile.sh14
-rw-r--r--testsuite/dwz.tests/low-mem-die-limit-0.sh13
-rw-r--r--testsuite/dwz.tests/low-mem-die-limit-at-limit-multifile.sh23
-rw-r--r--testsuite/dwz.tests/low-mem-die-limit-at-limit.sh18
-rw-r--r--testsuite/dwz.tests/low-mem-die-limit-none.sh13
-rw-r--r--testsuite/dwz.tests/low-mem-die-limit-one-above.sh18
-rw-r--r--testsuite/dwz.tests/low-mem-die-limit-one-below.sh18
-rw-r--r--testsuite/dwz.tests/low-mem.sh7
-rw-r--r--testsuite/dwz.tests/main.c25
-rw-r--r--testsuite/dwz.tests/max-die-limit-none.sh7
-rw-r--r--testsuite/dwz.tests/min-2.c5
-rw-r--r--testsuite/dwz.tests/min.c7
-rw-r--r--testsuite/dwz.tests/multifile-name.sh12
-rw-r--r--testsuite/dwz.tests/multifile-relative.sh12
-rw-r--r--testsuite/dwz.tests/multifile.sh12
-rw-r--r--testsuite/dwz.tests/objcopy-eu-unstrip-multifile.sh39
-rw-r--r--testsuite/dwz.tests/objcopy-eu-unstrip.sh35
-rw-r--r--testsuite/dwz.tests/objcopy-remove-debug-abbrev.sh18
-rw-r--r--testsuite/dwz.tests/objcopy-strip-debug.sh30
-rw-r--r--testsuite/dwz.tests/odr-2.cc20
-rw-r--r--testsuite/dwz.tests/odr-class-ns.sh29
-rw-r--r--testsuite/dwz.tests/odr-class.sh29
-rw-r--r--testsuite/dwz.tests/odr-def-decl.sh19
-rw-r--r--testsuite/dwz.tests/odr-loc-2.cc14
-rw-r--r--testsuite/dwz.tests/odr-loc.cc19
-rw-r--r--testsuite/dwz.tests/odr-loc.sh39
-rwxr-xr-xtestsuite/dwz.tests/odr-struct-multifile.sh61
-rw-r--r--testsuite/dwz.tests/odr-struct-ns.sh49
-rw-r--r--testsuite/dwz.tests/odr-struct.sh49
-rw-r--r--testsuite/dwz.tests/odr-union-ns.sh29
-rw-r--r--testsuite/dwz.tests/odr-union.sh29
-rw-r--r--testsuite/dwz.tests/odr.cc26
-rw-r--r--testsuite/dwz.tests/odr.h11
-rw-r--r--testsuite/dwz.tests/pointer-size.c8
-rw-r--r--testsuite/dwz.tests/pr24170.sh25
-rw-r--r--testsuite/dwz.tests/pr24171.sh15
-rw-r--r--testsuite/dwz.tests/pr24172.sh14
-rw-r--r--testsuite/dwz.tests/pr24173.sh16
-rw-r--r--testsuite/dwz.tests/pr24174.sh24
-rw-r--r--testsuite/dwz.tests/pr24341.sh17
-rw-r--r--testsuite/dwz.tests/pr24468.sh19
-rw-r--r--testsuite/dwz.tests/pr24542.sh15
-rw-r--r--testsuite/dwz.tests/pr24747.sh7
-rw-r--r--testsuite/dwz.tests/pr24771.sh9
-rw-r--r--testsuite/dwz.tests/pr24823.sh25
-rw-r--r--testsuite/dwz.tests/pr25109.sh6
-rw-r--r--testsuite/dwz.tests/pr27463.sh6
-rw-r--r--testsuite/dwz.tests/py-section-script.s393
-rw-r--r--testsuite/dwz.tests/regular-o.sh9
-rw-r--r--testsuite/dwz.tests/regular.sh7
-rw-r--r--testsuite/dwz.tests/save-temps.sh20
-rw-r--r--testsuite/dwz.tests/start.c3
-rw-r--r--testsuite/dwz.tests/too-many-dies.sh16
-rw-r--r--testsuite/dwz.tests/twice-hardlink.sh31
-rw-r--r--testsuite/dwz.tests/twice-multifile.sh34
-rw-r--r--testsuite/dwz.tests/twice.sh28
-rw-r--r--testsuite/dwz.tests/two-files-low-mem-die-limit-0.sh15
-rw-r--r--testsuite/dwz.tests/two-files-too-many-dies-2.sh22
-rw-r--r--testsuite/dwz.tests/two-files-too-many-dies.sh17
-rw-r--r--testsuite/dwz.tests/two-typedef-2.c6
-rw-r--r--testsuite/dwz.tests/two-typedef.c11
-rw-r--r--testsuite/dwz.tests/two-typedef.h3
-rw-r--r--testsuite/dwz.tests/varval.S515
-rw-r--r--testsuite/dwz.tests/varval.c33
-rw-r--r--testsuite/dwz.tests/version.sh9
-rw-r--r--testsuite/lib/dwarf-lib.exp28
-rw-r--r--testsuite/lib/dwarf.exp1674
-rw-r--r--testsuite/lib/dwz.exp0
-rw-r--r--testsuite/lib/invalid-dw-at-stmt-list-encoding.exp21
-rw-r--r--testsuite/lib/no-multifile-prop.exp132
-rw-r--r--testsuite/lib/unavailable-dwarf-piece.exp272
-rwxr-xr-xtestsuite/scripts/gnu-debugaltlink-name.sh11
-rwxr-xr-xtestsuite/scripts/hardlinks-p.sh16
-rwxr-xr-xtestsuite/scripts/smaller-than.sh48
-rwxr-xr-xtestsuite/scripts/verify-dwarf.sh5
-rwxr-xr-xtestsuite/scripts/xunzip-dir.sh20
-rw-r--r--util.h63
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
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..623b625
--- /dev/null
+++ b/COPYING
@@ -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.
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..2856407
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.15
diff --git a/args.c b/args.c
new file mode 100644
index 0000000..e93c24d
--- /dev/null
+++ b/args.c
@@ -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;
+ }
+}
diff --git a/args.h b/args.h
new file mode 100644
index 0000000..0eacbd0
--- /dev/null
+++ b/args.h
@@ -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
diff --git a/dwz.1 b/dwz.1
new file mode 100644
index 0000000..1cff329
--- /dev/null
+++ b/dwz.1
@@ -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> .
diff --git a/dwz.c b/dwz.c
new file mode 100644
index 0000000..3bc6038
--- /dev/null
+++ b/dwz.c
@@ -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 (&current);
+
+ 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)
diff --git a/pool.c b/pool.c
new file mode 100644
index 0000000..c8c7171
--- /dev/null
+++ b/pool.c
@@ -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;
+}
diff --git a/pool.h b/pool.h
new file mode 100644
index 0000000..b32d2bf
--- /dev/null
+++ b/pool.h
@@ -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)
diff --git a/sha1.c b/sha1.c
new file mode 100644
index 0000000..28d1642
--- /dev/null
+++ b/sha1.c
@@ -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;
+ }
+}
diff --git a/sha1.h b/sha1.h
new file mode 100644
index 0000000..b3cb3aa
--- /dev/null
+++ b/sha1.h
@@ -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
new file mode 100755
index 0000000..3514195
--- /dev/null
+++ b/testsuite/dwz.tests/execs.xz/hello-ld-2.26.1.xz
Binary files differ
diff --git a/testsuite/dwz.tests/execs.xz/hello-leap-15.0.xz b/testsuite/dwz.tests/execs.xz/hello-leap-15.0.xz
new file mode 100755
index 0000000..598c88e
--- /dev/null
+++ b/testsuite/dwz.tests/execs.xz/hello-leap-15.0.xz
Binary files differ
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
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..d542942
--- /dev/null
+++ b/util.h
@@ -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