summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit130444ec10d2a40e2b3ede4863ab005aa1f4580c (patch)
tree435b158b8046565c2275e527e1d5bfedbcb3d3b8
downloadelfcopy-130444ec10d2a40e2b3ede4863ab005aa1f4580c.tar.gz
-rwxr-xr-xAndroid.mk52
-rw-r--r--MODULE_LICENSE_GPL0
-rw-r--r--NOTICE340
-rw-r--r--common.c35
-rw-r--r--common.h49
-rw-r--r--debug.c39
-rw-r--r--debug.h94
-rw-r--r--dwarf.c3960
-rw-r--r--dwarf.h122
-rw-r--r--dwarf2.h836
-rw-r--r--elfcopy.c2989
-rw-r--r--elfcopy.h94
-rw-r--r--fixdwarf.c577
-rw-r--r--fixdwarf.h14
-rw-r--r--hash.c76
-rw-r--r--hash.h22
-rw-r--r--rangesort.c335
-rw-r--r--rangesort.h105
18 files changed, 9739 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100755
index 0000000..67f92af
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,52 @@
+LOCAL_PATH:= $(call my-dir)
+
+#
+# libelfcopy
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES += \
+ common.c \
+ debug.c \
+ elfcopy.c \
+ hash.c \
+ rangesort.c \
+ fixdwarf.c \
+ dwarf.c
+
+ifeq ($(HOST_OS),linux)
+endif
+ifeq ($(HOST_OS),darwin)
+endif
+
+LOCAL_MODULE:=libelfcopy
+
+#LOCAL_LDLIBS += -ldl
+LOCAL_CFLAGS += -O2 -g
+LOCAL_CFLAGS += -fno-function-sections -fno-data-sections -fno-inline
+LOCAL_CFLAGS += -Wall -Wno-unused-function #-Werror
+LOCAL_CFLAGS += -DBIG_ENDIAN=1
+LOCAL_CFLAGS += -DARM_SPECIFIC_HACKS
+LOCAL_CFLAGS += -DDEBUG
+LOCAL_CFLAGS += -DSTRIP_SECTIONS
+LOCAL_CFLAGS += -DSTRIP_STATIC_SYMBOLS
+LOCAL_CFLAGS += -DMOVE_SECTIONS_IN_RANGES
+#LOCAL_CFLAGS += -DSORT_LOCATION_LIST_OFFSETS
+
+
+# dwarf.c
+LOCAL_CFLAGS += -DATTRIBUTE_UNUSED="__attribute__((unused))"
+LOCAL_CFLAGS += -DTRUE=1
+LOCAL_CFLAGS += -DFALSE=0
+LOCAL_CFLAGS += -Dprogram_name=\"libelfcopy\"
+
+LOCAL_STATIC_LIBRARIES := libelf libebl libebl_arm
+
+LOCAL_C_INCLUDES:= \
+ $(LOCAL_PATH)/ \
+ external/elfutils/lib/ \
+ external/elfutils/libelf/ \
+ external/elfutils/libebl/
+
+include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/MODULE_LICENSE_GPL b/MODULE_LICENSE_GPL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_GPL
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..623b625
--- /dev/null
+++ b/NOTICE
@@ -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/common.c b/common.c
new file mode 100644
index 0000000..b90cf41
--- /dev/null
+++ b/common.c
@@ -0,0 +1,35 @@
+#include <stdlib.h>
+#include <common.h>
+#include <debug.h>
+
+void map_over_sections(Elf *elf,
+ section_match_fn_t match,
+ void *user_data)
+{
+ Elf_Scn* section = NULL;
+ while ((section = elf_nextscn(elf, section)) != NULL) {
+ if (match(elf, section, user_data))
+ return;
+ }
+}
+
+void map_over_segments(Elf *elf,
+ segment_match_fn_t match,
+ void *user_data)
+{
+ Elf32_Ehdr *ehdr;
+ Elf32_Phdr *phdr;
+ int index;
+
+ ehdr = elf32_getehdr(elf);
+ phdr = elf32_getphdr(elf);
+
+ INFO("Scanning over %d program segments...\n",
+ ehdr->e_phnum);
+
+ for (index = ehdr->e_phnum; index; index--) {
+ if (match(elf, phdr++, user_data))
+ return;
+ }
+}
+
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..dacf930
--- /dev/null
+++ b/common.h
@@ -0,0 +1,49 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <libelf.h>
+#include <elf.h>
+
+#define unlikely(expr) __builtin_expect (expr, 0)
+#define likely(expr) __builtin_expect (expr, 1)
+
+#define MIN(a,b) ((a)<(b)?(a):(b)) /* no side effects in arguments allowed! */
+
+typedef int (*section_match_fn_t)(Elf *, Elf_Scn *, void *);
+void map_over_sections(Elf *, section_match_fn_t, void *);
+
+typedef int (*segment_match_fn_t)(Elf *, Elf32_Phdr *, void *);
+void map_over_segments(Elf *, segment_match_fn_t, void *);
+
+typedef struct {
+ Elf_Scn *sect;
+ Elf32_Shdr *hdr;
+ Elf_Data *data;
+ size_t index;
+} section_info_t;
+
+static inline void get_section_info(Elf_Scn *sect, section_info_t *info)
+{
+ info->sect = sect;
+ info->data = elf_getdata(sect, 0);
+ info->hdr = elf32_getshdr(sect);
+ info->index = elf_ndxscn(sect);
+}
+
+static inline int is_host_little(void)
+{
+ short val = 0x10;
+ return ((char *)&val)[0] != 0;
+}
+
+static inline long switch_endianness(long val)
+{
+ long newval;
+ ((char *)&newval)[3] = ((char *)&val)[0];
+ ((char *)&newval)[2] = ((char *)&val)[1];
+ ((char *)&newval)[1] = ((char *)&val)[2];
+ ((char *)&newval)[0] = ((char *)&val)[3];
+ return newval;
+}
+
+#endif/*COMMON_H*/
diff --git a/debug.c b/debug.c
new file mode 100644
index 0000000..e7e16d4
--- /dev/null
+++ b/debug.c
@@ -0,0 +1,39 @@
+#include <debug.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#define NUM_COLS (32)
+
+/* returns the number of non-zero non-printable characters. */
+
+int dump_hex_buffer(FILE *s, void *b, size_t len, size_t elsize) {
+ int num_nonprintable = 0;
+ int i, last;
+ char *pchr = (char *)b;
+ fputc('\n', s);
+ for (i = last = 0; i < len; i++) {
+ if (!elsize) {
+ if (i && !(i % 4)) fprintf(s, " ");
+ if (i && !(i % 8)) fprintf(s, " ");
+ } else {
+ if (i && !(i % elsize)) fprintf(s, " ");
+ }
+
+ if (i && !(i % NUM_COLS)) {
+ while (last < i) {
+ if (isprint(pchr[last]))
+ fputc(pchr[last], s);
+ else {
+ fputc('.', s);
+ if(pchr[last])
+ num_nonprintable++;
+ }
+ last++;
+ }
+ fprintf(s, " (%d)\n", i);
+ }
+ fprintf(s, "%02x", (unsigned char)pchr[i]);
+ }
+ if (i && (i % NUM_COLS)) fputs("\n", s);
+ return num_nonprintable;
+}
diff --git a/debug.h b/debug.h
new file mode 100644
index 0000000..16d627d
--- /dev/null
+++ b/debug.h
@@ -0,0 +1,94 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <common.h>
+
+#ifdef DEBUG
+
+ #define FAILIF(cond, msg...) do { \
+ if (unlikely(cond)) { \
+ fprintf(stderr, "%s(%d): ", __FILE__, __LINE__); \
+ fprintf(stderr, ##msg); \
+ exit(1); \
+ } \
+} while(0)
+
+/* Debug enabled */
+ #define ASSERT(x) do { \
+ if (unlikely(!(x))) { \
+ fprintf(stderr, \
+ "ASSERTION FAILURE %s:%d: [%s]\n", \
+ __FILE__, __LINE__, #x); \
+ exit(1); \
+ } \
+} while(0)
+
+#else
+
+ #define FAILIF(cond, msg...) do { \
+ if (unlikely(cond)) { \
+ fprintf(stderr, ##msg); \
+ exit(1); \
+ } \
+} while(0)
+
+/* No debug */
+ #define ASSERT(x) do { } while(0)
+
+#endif/* DEBUG */
+
+#define FAILIF_LIBELF(cond, function) \
+ FAILIF(cond, "%s(): %s\n", #function, elf_errmsg(elf_errno()));
+
+static inline void *MALLOC(unsigned int size) {
+ void *m = malloc(size);
+ FAILIF(NULL == m, "malloc(%d) failed!\n", size);
+ return m;
+}
+
+static inline void *CALLOC(unsigned int num_entries, unsigned int entry_size) {
+ void *m = calloc(num_entries, entry_size);
+ FAILIF(NULL == m, "calloc(%d, %d) failed!\n", num_entries, entry_size);
+ return m;
+}
+
+static inline void *REALLOC(void *ptr, unsigned int size) {
+ void *m = realloc(ptr, size);
+ FAILIF(NULL == m, "realloc(%p, %d) failed!\n", ptr, size);
+ return m;
+}
+
+static inline void FREE(void *ptr) {
+ free(ptr);
+}
+
+static inline void FREEIF(void *ptr) {
+ if (ptr) FREE(ptr);
+}
+
+#define PRINT(x...) do { \
+ extern int quiet_flag; \
+ if(likely(!quiet_flag)) \
+ fprintf(stdout, ##x); \
+} while(0)
+
+#define ERROR(x...) fprintf(stderr, ##x)
+
+#define INFO(x...) do { \
+ extern int verbose_flag; \
+ if(unlikely(verbose_flag)) \
+ fprintf(stdout, ##x); \
+} while(0)
+
+#define PUTCHAR(c) do { \
+ extern int verbose_flag; \
+ if(unlikely(verbose_flag)) \
+ putc((c), stdout); \
+} while(0)
+
+/* Prints a hex and ASCII dump of the selected buffer to the selected stream. */
+int dump_hex_buffer(FILE *s, void *b, size_t l, size_t elsize);
+
+#endif/*DEBUG_H*/
diff --git a/dwarf.c b/dwarf.c
new file mode 100644
index 0000000..33d7469
--- /dev/null
+++ b/dwarf.c
@@ -0,0 +1,3960 @@
+/* dwarf.c -- display DWARF contents of a BFD binary file
+ Copyright 2005, 2006
+ Free Software Foundation, Inc.
+
+ This file is part of GNU Binutils.
+
+ 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. */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "debug.h"
+
+#define _(val) val
+#define __str(s) #s
+#define printf INFO
+#define putchar PUTCHAR
+
+#include "dwarf.h"
+
+static int have_frame_base;
+static int need_base_address;
+
+static unsigned int last_pointer_size = 0;
+static int warned_about_missing_comp_units = FALSE;
+
+static unsigned int num_debug_info_entries = 0;
+static debug_info *debug_information = NULL;
+
+dwarf_vma eh_addr_size;
+int is_relocatable;
+
+int do_debug_info;
+int do_debug_abbrevs;
+int do_debug_lines;
+int do_debug_pubnames;
+int do_debug_aranges;
+int do_debug_ranges;
+int do_debug_frames;
+int do_debug_frames_interp;
+int do_debug_macinfo;
+int do_debug_str;
+int do_debug_loc;
+
+dwarf_vma (*byte_get) (unsigned char *, int);
+
+static void *xmalloc(size_t sz);
+static void *cmalloc (size_t, size_t);
+static void *xcmalloc (size_t, size_t);
+static void *xcrealloc (void *, size_t, size_t);
+
+static void error (const char *, ...); // ATTRIBUTE_PRINTF_1; //HACK
+static void warn (const char *, ...); // ATTRIBUTE_PRINTF_1; //HACK
+
+
+dwarf_vma
+byte_get_little_endian (unsigned char *field, int size)
+{
+ switch (size)
+ {
+ case 1:
+ return *field;
+
+ case 2:
+ return ((unsigned int) (field[0]))
+ | (((unsigned int) (field[1])) << 8);
+
+ case 4:
+ return ((unsigned long) (field[0]))
+ | (((unsigned long) (field[1])) << 8)
+ | (((unsigned long) (field[2])) << 16)
+ | (((unsigned long) (field[3])) << 24);
+
+ case 8:
+ if (sizeof (dwarf_vma) == 8)
+ return ((dwarf_vma) (field[0]))
+ | (((dwarf_vma) (field[1])) << 8)
+ | (((dwarf_vma) (field[2])) << 16)
+ | (((dwarf_vma) (field[3])) << 24)
+ | (((dwarf_vma) (field[4])) << 32)
+ | (((dwarf_vma) (field[5])) << 40)
+ | (((dwarf_vma) (field[6])) << 48)
+ | (((dwarf_vma) (field[7])) << 56);
+ else if (sizeof (dwarf_vma) == 4)
+ /* We want to extract data from an 8 byte wide field and
+ place it into a 4 byte wide field. Since this is a little
+ endian source we can just use the 4 byte extraction code. */
+ return ((unsigned long) (field[0]))
+ | (((unsigned long) (field[1])) << 8)
+ | (((unsigned long) (field[2])) << 16)
+ | (((unsigned long) (field[3])) << 24);
+
+ default:
+ error (_("Unhandled data length: %d\n"), size);
+ abort ();
+ }
+}
+
+dwarf_vma
+byte_get_big_endian (unsigned char *field, int size)
+{
+ switch (size)
+ {
+ case 1:
+ return *field;
+
+ case 2:
+ return ((unsigned int) (field[1])) | (((int) (field[0])) << 8);
+
+ case 4:
+ return ((unsigned long) (field[3]))
+ | (((unsigned long) (field[2])) << 8)
+ | (((unsigned long) (field[1])) << 16)
+ | (((unsigned long) (field[0])) << 24);
+
+ case 8:
+ if (sizeof (dwarf_vma) == 8)
+ return ((dwarf_vma) (field[7]))
+ | (((dwarf_vma) (field[6])) << 8)
+ | (((dwarf_vma) (field[5])) << 16)
+ | (((dwarf_vma) (field[4])) << 24)
+ | (((dwarf_vma) (field[3])) << 32)
+ | (((dwarf_vma) (field[2])) << 40)
+ | (((dwarf_vma) (field[1])) << 48)
+ | (((dwarf_vma) (field[0])) << 56);
+ else if (sizeof (dwarf_vma) == 4)
+ {
+ /* Although we are extracing data from an 8 byte wide field,
+ we are returning only 4 bytes of data. */
+ field += 4;
+ return ((unsigned long) (field[3]))
+ | (((unsigned long) (field[2])) << 8)
+ | (((unsigned long) (field[1])) << 16)
+ | (((unsigned long) (field[0])) << 24);
+ }
+
+ default:
+ error (_("Unhandled data length: %d\n"), size);
+ abort ();
+ }
+}
+
+static dwarf_vma
+byte_get_signed (unsigned char *field, int size)
+{
+ dwarf_vma x = byte_get (field, size);
+
+ switch (size)
+ {
+ case 1:
+ return (x ^ 0x80) - 0x80;
+ case 2:
+ return (x ^ 0x8000) - 0x8000;
+ case 4:
+ return (x ^ 0x80000000) - 0x80000000;
+ case 8:
+ return x;
+ default:
+ abort ();
+ }
+}
+
+static unsigned long int
+read_leb128 (unsigned char *data, unsigned int *length_return, int sign)
+{
+ unsigned long int result = 0;
+ unsigned int num_read = 0;
+ unsigned int shift = 0;
+ unsigned char byte;
+
+ do
+ {
+ byte = *data++;
+ num_read++;
+
+ result |= ((unsigned long int) (byte & 0x7f)) << shift;
+
+ shift += 7;
+
+ }
+ while (byte & 0x80);
+
+ if (length_return != NULL)
+ *length_return = num_read;
+
+ if (sign && (shift < 8 * sizeof (result)) && (byte & 0x40))
+ result |= -1L << shift;
+
+ return result;
+}
+
+typedef struct State_Machine_Registers
+{
+ unsigned long address;
+ unsigned int file;
+ unsigned int line;
+ unsigned int column;
+ int is_stmt;
+ int basic_block;
+ int end_sequence;
+/* This variable hold the number of the last entry seen
+ in the File Table. */
+ unsigned int last_file_entry;
+} SMR;
+
+static SMR state_machine_regs;
+
+static void
+reset_state_machine (int is_stmt)
+{
+ state_machine_regs.address = 0;
+ state_machine_regs.file = 1;
+ state_machine_regs.line = 1;
+ state_machine_regs.column = 0;
+ state_machine_regs.is_stmt = is_stmt;
+ state_machine_regs.basic_block = 0;
+ state_machine_regs.end_sequence = 0;
+ state_machine_regs.last_file_entry = 0;
+}
+
+/* Handled an extend line op.
+ Returns the number of bytes read. */
+
+static int
+process_extended_line_op (unsigned char *data, int is_stmt)
+{
+ unsigned char op_code;
+ unsigned int bytes_read;
+ unsigned int len;
+ unsigned char *name;
+ unsigned long adr;
+
+ len = read_leb128 (data, & bytes_read, 0);
+ data += bytes_read;
+
+ if (len == 0)
+ {
+ warn (_("badly formed extended line op encountered!\n"));
+ return bytes_read;
+ }
+
+ len += bytes_read;
+ op_code = *data++;
+
+ printf (_(" Extended opcode %d: "), op_code);
+
+ switch (op_code)
+ {
+ case DW_LNE_end_sequence:
+ printf (_("End of Sequence\n\n"));
+ reset_state_machine (is_stmt);
+ break;
+
+ case DW_LNE_set_address:
+ adr = byte_get (data, len - bytes_read - 1);
+ printf (_("set Address to 0x%lx\n"), adr);
+ state_machine_regs.address = adr;
+ value_hook(data, len - bytes_read - 1, adr);
+ break;
+
+ case DW_LNE_define_file:
+ printf (_(" define new File Table entry\n"));
+ printf (_(" Entry\tDir\tTime\tSize\tName\n"));
+
+ ++state_machine_regs.last_file_entry;
+ printf (_(" %d\t"), state_machine_regs.last_file_entry);
+ name = data;
+ data += strlen ((char *) data) + 1;
+ unsigned long val;
+ val = read_leb128 (data, & bytes_read, 0);
+ printf (_("%lu\t"), val);
+ data += bytes_read;
+ val = read_leb128 (data, & bytes_read, 0);
+ printf (_("%lu\t"), val);
+ data += bytes_read;
+ val = read_leb128 (data, & bytes_read, 0);
+ printf (_("%lu\t"), val);
+ printf (_("%s\n\n"), name);
+ break;
+
+ default:
+ printf (_("UNKNOWN: length %d\n"), len - bytes_read);
+ break;
+ }
+
+ return len;
+}
+
+static const char *
+fetch_indirect_string (unsigned long offset)
+{
+ struct dwarf_section *section = &debug_displays [str].section;
+
+ if (section->start == NULL)
+ return _("<no .debug_str section>");
+
+ /* DWARF sections under Mach-O have non-zero addresses. */
+ offset -= section->address;
+ if (offset > section->size)
+ {
+ warn (_("DW_FORM_strp offset too big: %lx\n"), offset);
+ return _("<offset is too big>");
+ }
+
+ return (const char *) section->start + offset;
+}
+
+/* FIXME: There are better and more efficient ways to handle
+ these structures. For now though, I just want something that
+ is simple to implement. */
+typedef struct abbrev_attr
+{
+ unsigned long attribute;
+ unsigned long form;
+ struct abbrev_attr *next;
+}
+abbrev_attr;
+
+typedef struct abbrev_entry
+{
+ unsigned long entry;
+ unsigned long tag;
+ int children;
+ struct abbrev_attr *first_attr;
+ struct abbrev_attr *last_attr;
+ struct abbrev_entry *next;
+}
+abbrev_entry;
+
+static abbrev_entry *first_abbrev = NULL;
+static abbrev_entry *last_abbrev = NULL;
+
+static void
+free_abbrevs (void)
+{
+ abbrev_entry *abbrev;
+
+ for (abbrev = first_abbrev; abbrev;)
+ {
+ abbrev_entry *next = abbrev->next;
+ abbrev_attr *attr;
+
+ for (attr = abbrev->first_attr; attr;)
+ {
+ abbrev_attr *next = attr->next;
+
+ free (attr);
+ attr = next;
+ }
+
+ free (abbrev);
+ abbrev = next;
+ }
+
+ last_abbrev = first_abbrev = NULL;
+}
+
+static void
+add_abbrev (unsigned long number, unsigned long tag, int children)
+{
+ abbrev_entry *entry;
+
+ entry = malloc (sizeof (*entry));
+
+ if (entry == NULL)
+ /* ugg */
+ return;
+
+ entry->entry = number;
+ entry->tag = tag;
+ entry->children = children;
+ entry->first_attr = NULL;
+ entry->last_attr = NULL;
+ entry->next = NULL;
+
+ if (first_abbrev == NULL)
+ first_abbrev = entry;
+ else
+ last_abbrev->next = entry;
+
+ last_abbrev = entry;
+}
+
+static void
+add_abbrev_attr (unsigned long attribute, unsigned long form)
+{
+ abbrev_attr *attr;
+
+ attr = malloc (sizeof (*attr));
+
+ if (attr == NULL)
+ /* ugg */
+ return;
+
+ attr->attribute = attribute;
+ attr->form = form;
+ attr->next = NULL;
+
+ if (last_abbrev->first_attr == NULL)
+ last_abbrev->first_attr = attr;
+ else
+ last_abbrev->last_attr->next = attr;
+
+ last_abbrev->last_attr = attr;
+}
+
+/* Processes the (partial) contents of a .debug_abbrev section.
+ Returns NULL if the end of the section was encountered.
+ Returns the address after the last byte read if the end of
+ an abbreviation set was found. */
+
+static unsigned char *
+process_abbrev_section (unsigned char *start, unsigned char *end)
+{
+ if (first_abbrev != NULL)
+ return NULL;
+
+ while (start < end)
+ {
+ unsigned int bytes_read;
+ unsigned long entry;
+ unsigned long tag;
+ unsigned long attribute;
+ int children;
+
+ entry = read_leb128 (start, & bytes_read, 0);
+ start += bytes_read;
+
+ /* A single zero is supposed to end the section according
+ to the standard. If there's more, then signal that to
+ the caller. */
+ if (entry == 0)
+ return start == end ? NULL : start;
+
+ tag = read_leb128 (start, & bytes_read, 0);
+ start += bytes_read;
+
+ children = *start++;
+
+ add_abbrev (entry, tag, children);
+
+ do
+ {
+ unsigned long form;
+
+ attribute = read_leb128 (start, & bytes_read, 0);
+ start += bytes_read;
+
+ form = read_leb128 (start, & bytes_read, 0);
+ start += bytes_read;
+
+ if (attribute != 0)
+ add_abbrev_attr (attribute, form);
+ }
+ while (attribute != 0);
+ }
+
+ return NULL;
+}
+
+static char *
+get_TAG_name (unsigned long tag)
+{
+ switch (tag)
+ {
+ case DW_TAG_padding: return "DW_TAG_padding";
+ case DW_TAG_array_type: return "DW_TAG_array_type";
+ case DW_TAG_class_type: return "DW_TAG_class_type";
+ case DW_TAG_entry_point: return "DW_TAG_entry_point";
+ case DW_TAG_enumeration_type: return "DW_TAG_enumeration_type";
+ case DW_TAG_formal_parameter: return "DW_TAG_formal_parameter";
+ case DW_TAG_imported_declaration: return "DW_TAG_imported_declaration";
+ case DW_TAG_label: return "DW_TAG_label";
+ case DW_TAG_lexical_block: return "DW_TAG_lexical_block";
+ case DW_TAG_member: return "DW_TAG_member";
+ case DW_TAG_pointer_type: return "DW_TAG_pointer_type";
+ case DW_TAG_reference_type: return "DW_TAG_reference_type";
+ case DW_TAG_compile_unit: return "DW_TAG_compile_unit";
+ case DW_TAG_string_type: return "DW_TAG_string_type";
+ case DW_TAG_structure_type: return "DW_TAG_structure_type";
+ case DW_TAG_subroutine_type: return "DW_TAG_subroutine_type";
+ case DW_TAG_typedef: return "DW_TAG_typedef";
+ case DW_TAG_union_type: return "DW_TAG_union_type";
+ case DW_TAG_unspecified_parameters: return "DW_TAG_unspecified_parameters";
+ case DW_TAG_variant: return "DW_TAG_variant";
+ case DW_TAG_common_block: return "DW_TAG_common_block";
+ case DW_TAG_common_inclusion: return "DW_TAG_common_inclusion";
+ case DW_TAG_inheritance: return "DW_TAG_inheritance";
+ case DW_TAG_inlined_subroutine: return "DW_TAG_inlined_subroutine";
+ case DW_TAG_module: return "DW_TAG_module";
+ case DW_TAG_ptr_to_member_type: return "DW_TAG_ptr_to_member_type";
+ case DW_TAG_set_type: return "DW_TAG_set_type";
+ case DW_TAG_subrange_type: return "DW_TAG_subrange_type";
+ case DW_TAG_with_stmt: return "DW_TAG_with_stmt";
+ case DW_TAG_access_declaration: return "DW_TAG_access_declaration";
+ case DW_TAG_base_type: return "DW_TAG_base_type";
+ case DW_TAG_catch_block: return "DW_TAG_catch_block";
+ case DW_TAG_const_type: return "DW_TAG_const_type";
+ case DW_TAG_constant: return "DW_TAG_constant";
+ case DW_TAG_enumerator: return "DW_TAG_enumerator";
+ case DW_TAG_file_type: return "DW_TAG_file_type";
+ case DW_TAG_friend: return "DW_TAG_friend";
+ case DW_TAG_namelist: return "DW_TAG_namelist";
+ case DW_TAG_namelist_item: return "DW_TAG_namelist_item";
+ case DW_TAG_packed_type: return "DW_TAG_packed_type";
+ case DW_TAG_subprogram: return "DW_TAG_subprogram";
+ case DW_TAG_template_type_param: return "DW_TAG_template_type_param";
+ case DW_TAG_template_value_param: return "DW_TAG_template_value_param";
+ case DW_TAG_thrown_type: return "DW_TAG_thrown_type";
+ case DW_TAG_try_block: return "DW_TAG_try_block";
+ case DW_TAG_variant_part: return "DW_TAG_variant_part";
+ case DW_TAG_variable: return "DW_TAG_variable";
+ case DW_TAG_volatile_type: return "DW_TAG_volatile_type";
+ case DW_TAG_MIPS_loop: return "DW_TAG_MIPS_loop";
+ case DW_TAG_format_label: return "DW_TAG_format_label";
+ case DW_TAG_function_template: return "DW_TAG_function_template";
+ case DW_TAG_class_template: return "DW_TAG_class_template";
+ /* DWARF 2.1 values. */
+ case DW_TAG_dwarf_procedure: return "DW_TAG_dwarf_procedure";
+ case DW_TAG_restrict_type: return "DW_TAG_restrict_type";
+ case DW_TAG_interface_type: return "DW_TAG_interface_type";
+ case DW_TAG_namespace: return "DW_TAG_namespace";
+ case DW_TAG_imported_module: return "DW_TAG_imported_module";
+ case DW_TAG_unspecified_type: return "DW_TAG_unspecified_type";
+ case DW_TAG_partial_unit: return "DW_TAG_partial_unit";
+ case DW_TAG_imported_unit: return "DW_TAG_imported_unit";
+ /* UPC values. */
+ case DW_TAG_upc_shared_type: return "DW_TAG_upc_shared_type";
+ case DW_TAG_upc_strict_type: return "DW_TAG_upc_strict_type";
+ case DW_TAG_upc_relaxed_type: return "DW_TAG_upc_relaxed_type";
+ default:
+ {
+ static char buffer[100];
+
+ snprintf (buffer, sizeof (buffer), _("Unknown TAG value: %lx"), tag);
+ return buffer;
+ }
+ }
+}
+
+static char *
+get_FORM_name (unsigned long form)
+{
+ switch (form)
+ {
+ case DW_FORM_addr: return "DW_FORM_addr";
+ case DW_FORM_block2: return "DW_FORM_block2";
+ case DW_FORM_block4: return "DW_FORM_block4";
+ case DW_FORM_data2: return "DW_FORM_data2";
+ case DW_FORM_data4: return "DW_FORM_data4";
+ case DW_FORM_data8: return "DW_FORM_data8";
+ case DW_FORM_string: return "DW_FORM_string";
+ case DW_FORM_block: return "DW_FORM_block";
+ case DW_FORM_block1: return "DW_FORM_block1";
+ case DW_FORM_data1: return "DW_FORM_data1";
+ case DW_FORM_flag: return "DW_FORM_flag";
+ case DW_FORM_sdata: return "DW_FORM_sdata";
+ case DW_FORM_strp: return "DW_FORM_strp";
+ case DW_FORM_udata: return "DW_FORM_udata";
+ case DW_FORM_ref_addr: return "DW_FORM_ref_addr";
+ case DW_FORM_ref1: return "DW_FORM_ref1";
+ case DW_FORM_ref2: return "DW_FORM_ref2";
+ case DW_FORM_ref4: return "DW_FORM_ref4";
+ case DW_FORM_ref8: return "DW_FORM_ref8";
+ case DW_FORM_ref_udata: return "DW_FORM_ref_udata";
+ case DW_FORM_indirect: return "DW_FORM_indirect";
+ default:
+ {
+ static char buffer[100];
+
+ snprintf (buffer, sizeof (buffer), _("Unknown FORM value: %lx"), form);
+ return buffer;
+ }
+ }
+}
+
+static unsigned char *
+display_block (unsigned char *data, unsigned long length)
+{
+ printf (_(" %lu byte block: "), length);
+
+ unsigned long val;
+ while (length --) {
+ val = (unsigned long) byte_get (data++, 1);
+ printf ("%lx ", val);
+ }
+
+ return data;
+}
+
+static int
+decode_location_expression (unsigned char * data,
+ unsigned int pointer_size,
+ unsigned long length,
+ unsigned long cu_offset)
+{
+ unsigned op;
+ unsigned int bytes_read;
+ unsigned long uvalue;
+ unsigned char *end = data + length;
+ int need_frame_base = 0;
+ unsigned long val = 0, val2;
+
+ while (data < end)
+ {
+ op = *data++;
+
+ switch (op)
+ {
+ case DW_OP_addr:
+ val = (unsigned long) byte_get (data, pointer_size);
+ printf ("DW_OP_addr: %lx",
+ val);
+ value_hook(data, pointer_size, val);
+ data += pointer_size;
+ break;
+ case DW_OP_deref:
+ printf ("DW_OP_deref");
+ break;
+ case DW_OP_const1u:
+ val = byte_get (data++, 1);
+ printf ("DW_OP_const1u: %lu", val);
+ break;
+ case DW_OP_const1s:
+ val = byte_get_signed (data++, 1);
+ printf ("DW_OP_const1s: %ld", (long) val);
+ break;
+ case DW_OP_const2u:
+ val = (unsigned long) byte_get (data, 2);
+ printf ("DW_OP_const2u: %lu", val);
+ data += 2;
+ break;
+ case DW_OP_const2s:
+ val = byte_get_signed (data, 2);
+ printf ("DW_OP_const2s: %ld", (long)val);
+ data += 2;
+ break;
+ case DW_OP_const4u:
+ val = (unsigned long) byte_get (data, 4);
+ printf ("DW_OP_const4u: %lu", val);
+ data += 4;
+ break;
+ case DW_OP_const4s:
+ val = byte_get_signed (data, 4);
+ printf ("DW_OP_const4s: %ld", (long) val);
+ data += 4;
+ break;
+ case DW_OP_const8u:
+ val = (unsigned long) byte_get (data, 4);
+ val2 = (unsigned long) byte_get (data + 4, 4);
+ printf ("DW_OP_const8u: %lu %lu", val, val2);
+ data += 8;
+ break;
+ case DW_OP_const8s:
+ val = byte_get (data, 4);
+ val2 = byte_get (data + 4, 4);
+ printf ("DW_OP_const8s: %ld %ld", (long) val, (long) val2);
+ data += 8;
+ break;
+ case DW_OP_constu:
+ val = read_leb128 (data, &bytes_read, 0);
+ printf ("DW_OP_constu: %lu", val);
+ data += bytes_read;
+ break;
+ case DW_OP_consts:
+ val = read_leb128 (data, &bytes_read, 1);
+ printf ("DW_OP_consts: %ld", val);
+ data += bytes_read;
+ break;
+ case DW_OP_dup:
+ printf ("DW_OP_dup");
+ break;
+ case DW_OP_drop:
+ printf ("DW_OP_drop");
+ break;
+ case DW_OP_over:
+ printf ("DW_OP_over");
+ break;
+ case DW_OP_pick:
+ val = (unsigned long) byte_get (data++, 1);
+ printf ("DW_OP_pick: %ld", val);
+ break;
+ case DW_OP_swap:
+ printf ("DW_OP_swap");
+ break;
+ case DW_OP_rot:
+ printf ("DW_OP_rot");
+ break;
+ case DW_OP_xderef:
+ printf ("DW_OP_xderef");
+ break;
+ case DW_OP_abs:
+ printf ("DW_OP_abs");
+ break;
+ case DW_OP_and:
+ printf ("DW_OP_and");
+ break;
+ case DW_OP_div:
+ printf ("DW_OP_div");
+ break;
+ case DW_OP_minus:
+ printf ("DW_OP_minus");
+ break;
+ case DW_OP_mod:
+ printf ("DW_OP_mod");
+ break;
+ case DW_OP_mul:
+ printf ("DW_OP_mul");
+ break;
+ case DW_OP_neg:
+ printf ("DW_OP_neg");
+ break;
+ case DW_OP_not:
+ printf ("DW_OP_not");
+ break;
+ case DW_OP_or:
+ printf ("DW_OP_or");
+ break;
+ case DW_OP_plus:
+ printf ("DW_OP_plus");
+ break;
+ case DW_OP_plus_uconst:
+ val = read_leb128 (data, &bytes_read, 0);
+ printf ("DW_OP_plus_uconst: %lu", val);
+ data += bytes_read;
+ break;
+ case DW_OP_shl:
+ printf ("DW_OP_shl");
+ break;
+ case DW_OP_shr:
+ printf ("DW_OP_shr");
+ break;
+ case DW_OP_shra:
+ printf ("DW_OP_shra");
+ break;
+ case DW_OP_xor:
+ printf ("DW_OP_xor");
+ break;
+ case DW_OP_bra:
+ val = byte_get_signed (data, 2);
+ printf ("DW_OP_bra: %ld", (long) val);
+ data += 2;
+ break;
+ case DW_OP_eq:
+ printf ("DW_OP_eq");
+ break;
+ case DW_OP_ge:
+ printf ("DW_OP_ge");
+ break;
+ case DW_OP_gt:
+ printf ("DW_OP_gt");
+ break;
+ case DW_OP_le:
+ printf ("DW_OP_le");
+ break;
+ case DW_OP_lt:
+ printf ("DW_OP_lt");
+ break;
+ case DW_OP_ne:
+ printf ("DW_OP_ne");
+ break;
+ case DW_OP_skip:
+ val = byte_get_signed (data, 2);
+ printf ("DW_OP_skip: %ld", (long) val);
+ data += 2;
+ break;
+
+ case DW_OP_lit0:
+ case DW_OP_lit1:
+ case DW_OP_lit2:
+ case DW_OP_lit3:
+ case DW_OP_lit4:
+ case DW_OP_lit5:
+ case DW_OP_lit6:
+ case DW_OP_lit7:
+ case DW_OP_lit8:
+ case DW_OP_lit9:
+ case DW_OP_lit10:
+ case DW_OP_lit11:
+ case DW_OP_lit12:
+ case DW_OP_lit13:
+ case DW_OP_lit14:
+ case DW_OP_lit15:
+ case DW_OP_lit16:
+ case DW_OP_lit17:
+ case DW_OP_lit18:
+ case DW_OP_lit19:
+ case DW_OP_lit20:
+ case DW_OP_lit21:
+ case DW_OP_lit22:
+ case DW_OP_lit23:
+ case DW_OP_lit24:
+ case DW_OP_lit25:
+ case DW_OP_lit26:
+ case DW_OP_lit27:
+ case DW_OP_lit28:
+ case DW_OP_lit29:
+ case DW_OP_lit30:
+ case DW_OP_lit31:
+ printf ("DW_OP_lit%d", op - DW_OP_lit0);
+ break;
+
+ case DW_OP_reg0:
+ case DW_OP_reg1:
+ case DW_OP_reg2:
+ case DW_OP_reg3:
+ case DW_OP_reg4:
+ case DW_OP_reg5:
+ case DW_OP_reg6:
+ case DW_OP_reg7:
+ case DW_OP_reg8:
+ case DW_OP_reg9:
+ case DW_OP_reg10:
+ case DW_OP_reg11:
+ case DW_OP_reg12:
+ case DW_OP_reg13:
+ case DW_OP_reg14:
+ case DW_OP_reg15:
+ case DW_OP_reg16:
+ case DW_OP_reg17:
+ case DW_OP_reg18:
+ case DW_OP_reg19:
+ case DW_OP_reg20:
+ case DW_OP_reg21:
+ case DW_OP_reg22:
+ case DW_OP_reg23:
+ case DW_OP_reg24:
+ case DW_OP_reg25:
+ case DW_OP_reg26:
+ case DW_OP_reg27:
+ case DW_OP_reg28:
+ case DW_OP_reg29:
+ case DW_OP_reg30:
+ case DW_OP_reg31:
+ printf ("DW_OP_reg%d", op - DW_OP_reg0);
+ break;
+
+ case DW_OP_breg0:
+ case DW_OP_breg1:
+ case DW_OP_breg2:
+ case DW_OP_breg3:
+ case DW_OP_breg4:
+ case DW_OP_breg5:
+ case DW_OP_breg6:
+ case DW_OP_breg7:
+ case DW_OP_breg8:
+ case DW_OP_breg9:
+ case DW_OP_breg10:
+ case DW_OP_breg11:
+ case DW_OP_breg12:
+ case DW_OP_breg13:
+ case DW_OP_breg14:
+ case DW_OP_breg15:
+ case DW_OP_breg16:
+ case DW_OP_breg17:
+ case DW_OP_breg18:
+ case DW_OP_breg19:
+ case DW_OP_breg20:
+ case DW_OP_breg21:
+ case DW_OP_breg22:
+ case DW_OP_breg23:
+ case DW_OP_breg24:
+ case DW_OP_breg25:
+ case DW_OP_breg26:
+ case DW_OP_breg27:
+ case DW_OP_breg28:
+ case DW_OP_breg29:
+ case DW_OP_breg30:
+ case DW_OP_breg31:
+ val = read_leb128 (data, &bytes_read, 1);
+ printf ("DW_OP_breg%d: %ld", op - DW_OP_breg0,
+ val);
+ data += bytes_read;
+ break;
+
+ case DW_OP_regx:
+ val = read_leb128 (data, &bytes_read, 0);
+ printf ("DW_OP_regx: %lu", val);
+ data += bytes_read;
+ break;
+ case DW_OP_fbreg:
+ need_frame_base = 1;
+ val = read_leb128 (data, &bytes_read, 1);
+ printf ("DW_OP_fbreg: %ld", val);
+ data += bytes_read;
+ break;
+ case DW_OP_bregx:
+ uvalue = read_leb128 (data, &bytes_read, 0);
+ data += bytes_read;
+ val = read_leb128 (data, &bytes_read, 1);
+ printf ("DW_OP_bregx: %lu %ld", uvalue, val);
+ data += bytes_read;
+ break;
+ case DW_OP_piece:
+ val = read_leb128 (data, &bytes_read, 0);
+ printf ("DW_OP_piece: %lu", val);
+ data += bytes_read;
+ break;
+ case DW_OP_deref_size:
+ val = byte_get (data++, 1);
+ printf ("DW_OP_deref_size: %ld", (long) val);
+ break;
+ case DW_OP_xderef_size:
+ val = byte_get (data++, 1);
+ printf ("DW_OP_xderef_size: %ld", (long) val);
+ break;
+ case DW_OP_nop:
+ printf ("DW_OP_nop");
+ break;
+
+ /* DWARF 3 extensions. */
+ case DW_OP_push_object_address:
+ printf ("DW_OP_push_object_address");
+ break;
+ case DW_OP_call2:
+ /* XXX: Strictly speaking for 64-bit DWARF3 files
+ this ought to be an 8-byte wide computation. */
+ val = (unsigned long)((long) byte_get (data, 2) + cu_offset);
+ printf ("DW_OP_call2: <%lx>", (long) val);
+ data += 2;
+ break;
+ case DW_OP_call4:
+ /* XXX: Strictly speaking for 64-bit DWARF3 files
+ this ought to be an 8-byte wide computation. */
+ val = (unsigned long)((long) byte_get (data, 4) + cu_offset);
+ printf ("DW_OP_call4: <%lx>", (long) val);
+ data += 4;
+ break;
+ case DW_OP_call_ref:
+ printf ("DW_OP_call_ref");
+ break;
+
+ /* GNU extensions. */
+ case DW_OP_GNU_push_tls_address:
+ printf ("DW_OP_GNU_push_tls_address");
+ break;
+
+ default:
+ if (op >= DW_OP_lo_user
+ && op <= DW_OP_hi_user)
+ printf (_("(User defined location op)"));
+ else
+ printf (_("(Unknown location op)"));
+ /* No way to tell where the next op is, so just bail. */
+ return need_frame_base;
+ }
+
+ /* Separate the ops. */
+ if (data < end)
+ printf ("; ");
+ }
+
+ return need_frame_base;
+}
+
+static unsigned char *
+read_and_display_attr_value (unsigned long attribute,
+ unsigned long form,
+ unsigned char *data,
+ unsigned long cu_offset,
+ unsigned long pointer_size,
+ unsigned long offset_size,
+ int dwarf_version,
+ debug_info *debug_info_p,
+ int do_loc)
+{
+ unsigned long uvalue = 0;
+ unsigned char *block_start = NULL;
+ unsigned int bytes_read;
+ unsigned long val;
+
+ switch (form)
+ {
+ default:
+ break;
+
+ case DW_FORM_ref_addr:
+ if (dwarf_version == 2)
+ {
+ uvalue = byte_get (data, pointer_size);
+ data += pointer_size;
+ }
+ else if (dwarf_version == 3)
+ {
+ uvalue = byte_get (data, offset_size);
+ data += offset_size;
+ }
+ else
+ {
+ error (_("Internal error: DWARF version is not 2 or 3.\n"));
+ }
+ break;
+
+ case DW_FORM_addr:
+ uvalue = byte_get (data, pointer_size);
+ if (!do_loc)
+ value_hook(data, pointer_size, uvalue);
+ data += pointer_size;
+ break;
+
+ case DW_FORM_strp:
+ uvalue = byte_get (data, offset_size);
+ data += offset_size;
+ break;
+
+ case DW_FORM_ref1:
+ case DW_FORM_flag:
+ case DW_FORM_data1:
+ uvalue = byte_get (data++, 1);
+ break;
+
+ case DW_FORM_ref2:
+ case DW_FORM_data2:
+ uvalue = byte_get (data, 2);
+ data += 2;
+ break;
+
+ case DW_FORM_ref4:
+ case DW_FORM_data4:
+ uvalue = byte_get (data, 4);
+ data += 4;
+ break;
+
+ case DW_FORM_sdata:
+ uvalue = read_leb128 (data, & bytes_read, 1);
+ data += bytes_read;
+ break;
+
+ case DW_FORM_ref_udata:
+ case DW_FORM_udata:
+ uvalue = read_leb128 (data, & bytes_read, 0);
+ data += bytes_read;
+ break;
+
+ case DW_FORM_indirect:
+ form = read_leb128 (data, & bytes_read, 0);
+ data += bytes_read;
+ if (!do_loc)
+ printf (" %s", get_FORM_name (form));
+ return read_and_display_attr_value (attribute, form, data,
+ cu_offset, pointer_size,
+ offset_size, dwarf_version,
+ debug_info_p, do_loc);
+ }
+
+ switch (form)
+ {
+ case DW_FORM_ref_addr:
+ if (!do_loc)
+ printf (" <#%lx>", uvalue);
+ break;
+
+ case DW_FORM_ref1:
+ case DW_FORM_ref2:
+ case DW_FORM_ref4:
+ case DW_FORM_ref_udata:
+ if (!do_loc)
+ printf (" <%lx>", uvalue + cu_offset);
+ break;
+
+ case DW_FORM_data4:
+ case DW_FORM_addr:
+ if (!do_loc)
+ printf (" %#lx", uvalue);
+ break;
+
+ case DW_FORM_flag:
+ case DW_FORM_data1:
+ case DW_FORM_data2:
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ if (!do_loc)
+ printf (" %ld", uvalue);
+ break;
+
+ case DW_FORM_ref8:
+ case DW_FORM_data8:
+ if (!do_loc)
+ {
+ uvalue = byte_get (data, 4);
+ printf (" %lx", uvalue);
+ val = byte_get (data + 4, 4);
+ printf (" %lx", val);
+ }
+ if ((do_loc || do_debug_loc || do_debug_ranges)
+ && num_debug_info_entries == 0)
+ {
+ if (sizeof (uvalue) == 8)
+ uvalue = byte_get (data, 8);
+ else
+ error (_("DW_FORM_data8 is unsupported when sizeof (unsigned long) != 8\n"));
+ }
+ data += 8;
+ break;
+
+ case DW_FORM_string:
+ if (!do_loc)
+ printf (" %s", data);
+ data += strlen ((char *) data) + 1;
+ break;
+
+ case DW_FORM_block:
+ uvalue = read_leb128 (data, & bytes_read, 0);
+ block_start = data + bytes_read;
+ if (do_loc)
+ data = block_start + uvalue;
+ else
+ data = display_block (block_start, uvalue);
+ break;
+
+ case DW_FORM_block1:
+ uvalue = byte_get (data, 1);
+ block_start = data + 1;
+ if (do_loc)
+ data = block_start + uvalue;
+ else
+ data = display_block (block_start, uvalue);
+ break;
+
+ case DW_FORM_block2:
+ uvalue = byte_get (data, 2);
+ block_start = data + 2;
+ if (do_loc)
+ data = block_start + uvalue;
+ else
+ data = display_block (block_start, uvalue);
+ break;
+
+ case DW_FORM_block4:
+ uvalue = byte_get (data, 4);
+ block_start = data + 4;
+ if (do_loc)
+ data = block_start + uvalue;
+ else
+ data = display_block (block_start, uvalue);
+ break;
+
+ case DW_FORM_strp:
+ if (!do_loc)
+ printf (_(" (indirect string, offset: 0x%lx): %s"),
+ uvalue, fetch_indirect_string (uvalue));
+ break;
+
+ case DW_FORM_indirect:
+ /* Handled above. */
+ break;
+
+ default:
+ warn (_("Unrecognized form: %lu\n"), form);
+ break;
+ }
+
+ /* For some attributes we can display further information. */
+ if ((do_loc || do_debug_loc || do_debug_ranges)
+ && num_debug_info_entries == 0)
+ {
+ switch (attribute)
+ {
+ case DW_AT_frame_base:
+ have_frame_base = 1;
+ case DW_AT_location:
+ case DW_AT_data_member_location:
+ case DW_AT_vtable_elem_location:
+ case DW_AT_allocated:
+ case DW_AT_associated:
+ case DW_AT_data_location:
+ case DW_AT_stride:
+ case DW_AT_upper_bound:
+ case DW_AT_lower_bound:
+ if (form == DW_FORM_data4 || form == DW_FORM_data8)
+ {
+ /* Process location list. */
+ unsigned int max = debug_info_p->max_loc_offsets;
+ unsigned int num = debug_info_p->num_loc_offsets;
+
+ if (max == 0 || num >= max)
+ {
+#ifdef SORT_LOCATION_LIST_OFFSETS
+ if (max == 0)
+ debug_info_p->last_loc_offset = uvalue;
+#endif
+ max += 1024;
+ debug_info_p->loc_offsets
+ = xcrealloc (debug_info_p->loc_offsets,
+ max, sizeof (*debug_info_p->loc_offsets));
+ debug_info_p->have_frame_base
+ = xcrealloc (debug_info_p->have_frame_base,
+ max, sizeof (*debug_info_p->have_frame_base));
+ debug_info_p->max_loc_offsets = max;
+ }
+ debug_info_p->loc_offsets [num] = uvalue;
+ debug_info_p->have_frame_base [num] = have_frame_base;
+ debug_info_p->num_loc_offsets++;
+#ifdef SORT_LOCATION_LIST_OFFSETS
+ if (debug_info_p->last_loc_offset != -1UL &&
+ debug_info_p->last_loc_offset > uvalue)
+ {
+ /* The location offsets are not in ascending order! */
+ debug_info_p->last_loc_offset = -1UL;
+ }
+#endif
+ }
+ break;
+
+ case DW_AT_low_pc:
+ if (need_base_address)
+ debug_info_p->base_address = uvalue;
+ break;
+
+ case DW_AT_ranges:
+ if (form == DW_FORM_data4 || form == DW_FORM_data8)
+ {
+ /* Process range list. */
+ unsigned int max = debug_info_p->max_range_lists;
+ unsigned int num = debug_info_p->num_range_lists;
+
+ if (max == 0 || num >= max)
+ {
+ max += 1024;
+ debug_info_p->range_lists
+ = xcrealloc (debug_info_p->range_lists,
+ max, sizeof (*debug_info_p->range_lists));
+ debug_info_p->max_range_lists = max;
+ }
+ debug_info_p->range_lists [num] = uvalue;
+ debug_info_p->num_range_lists++;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (do_loc)
+ return data;
+
+ printf ("\t");
+
+ switch (attribute)
+ {
+ case DW_AT_inline:
+ switch (uvalue)
+ {
+ case DW_INL_not_inlined:
+ printf (_("(not inlined)"));
+ break;
+ case DW_INL_inlined:
+ printf (_("(inlined)"));
+ break;
+ case DW_INL_declared_not_inlined:
+ printf (_("(declared as inline but ignored)"));
+ break;
+ case DW_INL_declared_inlined:
+ printf (_("(declared as inline and inlined)"));
+ break;
+ default:
+ printf (_(" (Unknown inline attribute value: %lx)"), uvalue);
+ break;
+ }
+ break;
+
+ case DW_AT_language:
+ switch (uvalue)
+ {
+ case DW_LANG_C: printf ("(non-ANSI C)"); break;
+ case DW_LANG_C89: printf ("(ANSI C)"); break;
+ case DW_LANG_C_plus_plus: printf ("(C++)"); break;
+ case DW_LANG_Fortran77: printf ("(FORTRAN 77)"); break;
+ case DW_LANG_Fortran90: printf ("(Fortran 90)"); break;
+ case DW_LANG_Modula2: printf ("(Modula 2)"); break;
+ case DW_LANG_Pascal83: printf ("(ANSI Pascal)"); break;
+ case DW_LANG_Ada83: printf ("(Ada)"); break;
+ case DW_LANG_Cobol74: printf ("(Cobol 74)"); break;
+ case DW_LANG_Cobol85: printf ("(Cobol 85)"); break;
+ /* DWARF 2.1 values. */
+ case DW_LANG_C99: printf ("(ANSI C99)"); break;
+ case DW_LANG_Ada95: printf ("(ADA 95)"); break;
+ case DW_LANG_Fortran95: printf ("(Fortran 95)"); break;
+ /* MIPS extension. */
+ case DW_LANG_Mips_Assembler: printf ("(MIPS assembler)"); break;
+ /* UPC extension. */
+ case DW_LANG_Upc: printf ("(Unified Parallel C)"); break;
+ default:
+ printf ("(Unknown: %lx)", uvalue);
+ break;
+ }
+ break;
+
+ case DW_AT_encoding:
+ switch (uvalue)
+ {
+ case DW_ATE_void: printf ("(void)"); break;
+ case DW_ATE_address: printf ("(machine address)"); break;
+ case DW_ATE_boolean: printf ("(boolean)"); break;
+ case DW_ATE_complex_float: printf ("(complex float)"); break;
+ case DW_ATE_float: printf ("(float)"); break;
+ case DW_ATE_signed: printf ("(signed)"); break;
+ case DW_ATE_signed_char: printf ("(signed char)"); break;
+ case DW_ATE_unsigned: printf ("(unsigned)"); break;
+ case DW_ATE_unsigned_char: printf ("(unsigned char)"); break;
+ /* DWARF 2.1 value. */
+ case DW_ATE_imaginary_float: printf ("(imaginary float)"); break;
+ case DW_ATE_decimal_float: printf ("(decimal float)"); break;
+ default:
+ if (uvalue >= DW_ATE_lo_user
+ && uvalue <= DW_ATE_hi_user)
+ printf ("(user defined type)");
+ else
+ printf ("(unknown type)");
+ break;
+ }
+ break;
+
+ case DW_AT_accessibility:
+ switch (uvalue)
+ {
+ case DW_ACCESS_public: printf ("(public)"); break;
+ case DW_ACCESS_protected: printf ("(protected)"); break;
+ case DW_ACCESS_private: printf ("(private)"); break;
+ default:
+ printf ("(unknown accessibility)");
+ break;
+ }
+ break;
+
+ case DW_AT_visibility:
+ switch (uvalue)
+ {
+ case DW_VIS_local: printf ("(local)"); break;
+ case DW_VIS_exported: printf ("(exported)"); break;
+ case DW_VIS_qualified: printf ("(qualified)"); break;
+ default: printf ("(unknown visibility)"); break;
+ }
+ break;
+
+ case DW_AT_virtuality:
+ switch (uvalue)
+ {
+ case DW_VIRTUALITY_none: printf ("(none)"); break;
+ case DW_VIRTUALITY_virtual: printf ("(virtual)"); break;
+ case DW_VIRTUALITY_pure_virtual:printf ("(pure_virtual)"); break;
+ default: printf ("(unknown virtuality)"); break;
+ }
+ break;
+
+ case DW_AT_identifier_case:
+ switch (uvalue)
+ {
+ case DW_ID_case_sensitive: printf ("(case_sensitive)"); break;
+ case DW_ID_up_case: printf ("(up_case)"); break;
+ case DW_ID_down_case: printf ("(down_case)"); break;
+ case DW_ID_case_insensitive: printf ("(case_insensitive)"); break;
+ default: printf ("(unknown case)"); break;
+ }
+ break;
+
+ case DW_AT_calling_convention:
+ switch (uvalue)
+ {
+ case DW_CC_normal: printf ("(normal)"); break;
+ case DW_CC_program: printf ("(program)"); break;
+ case DW_CC_nocall: printf ("(nocall)"); break;
+ default:
+ if (uvalue >= DW_CC_lo_user
+ && uvalue <= DW_CC_hi_user)
+ printf ("(user defined)");
+ else
+ printf ("(unknown convention)");
+ }
+ break;
+
+ case DW_AT_ordering:
+ switch (uvalue)
+ {
+ case -1: printf ("(undefined)"); break;
+ case 0: printf ("(row major)"); break;
+ case 1: printf ("(column major)"); break;
+ }
+ break;
+
+ case DW_AT_frame_base:
+ have_frame_base = 1;
+ case DW_AT_location:
+ case DW_AT_data_member_location:
+ case DW_AT_vtable_elem_location:
+ case DW_AT_allocated:
+ case DW_AT_associated:
+ case DW_AT_data_location:
+ case DW_AT_stride:
+ case DW_AT_upper_bound:
+ case DW_AT_lower_bound:
+ if (block_start)
+ {
+ int need_frame_base;
+
+ printf ("(");
+ need_frame_base = decode_location_expression (block_start,
+ pointer_size,
+ uvalue,
+ cu_offset);
+ printf (")");
+ if (need_frame_base && !have_frame_base)
+ printf (_(" [without DW_AT_frame_base]"));
+ }
+ else if (form == DW_FORM_data4 || form == DW_FORM_data8)
+ printf (_("(location list)"));
+
+ break;
+
+ default:
+ break;
+ }
+
+ return data;
+}
+
+static char *
+get_AT_name (unsigned long attribute)
+{
+ switch (attribute)
+ {
+ case DW_AT_sibling: return "DW_AT_sibling";
+ case DW_AT_location: return "DW_AT_location";
+ case DW_AT_name: return "DW_AT_name";
+ case DW_AT_ordering: return "DW_AT_ordering";
+ case DW_AT_subscr_data: return "DW_AT_subscr_data";
+ case DW_AT_byte_size: return "DW_AT_byte_size";
+ case DW_AT_bit_offset: return "DW_AT_bit_offset";
+ case DW_AT_bit_size: return "DW_AT_bit_size";
+ case DW_AT_element_list: return "DW_AT_element_list";
+ case DW_AT_stmt_list: return "DW_AT_stmt_list";
+ case DW_AT_low_pc: return "DW_AT_low_pc";
+ case DW_AT_high_pc: return "DW_AT_high_pc";
+ case DW_AT_language: return "DW_AT_language";
+ case DW_AT_member: return "DW_AT_member";
+ case DW_AT_discr: return "DW_AT_discr";
+ case DW_AT_discr_value: return "DW_AT_discr_value";
+ case DW_AT_visibility: return "DW_AT_visibility";
+ case DW_AT_import: return "DW_AT_import";
+ case DW_AT_string_length: return "DW_AT_string_length";
+ case DW_AT_common_reference: return "DW_AT_common_reference";
+ case DW_AT_comp_dir: return "DW_AT_comp_dir";
+ case DW_AT_const_value: return "DW_AT_const_value";
+ case DW_AT_containing_type: return "DW_AT_containing_type";
+ case DW_AT_default_value: return "DW_AT_default_value";
+ case DW_AT_inline: return "DW_AT_inline";
+ case DW_AT_is_optional: return "DW_AT_is_optional";
+ case DW_AT_lower_bound: return "DW_AT_lower_bound";
+ case DW_AT_producer: return "DW_AT_producer";
+ case DW_AT_prototyped: return "DW_AT_prototyped";
+ case DW_AT_return_addr: return "DW_AT_return_addr";
+ case DW_AT_start_scope: return "DW_AT_start_scope";
+ case DW_AT_stride_size: return "DW_AT_stride_size";
+ case DW_AT_upper_bound: return "DW_AT_upper_bound";
+ case DW_AT_abstract_origin: return "DW_AT_abstract_origin";
+ case DW_AT_accessibility: return "DW_AT_accessibility";
+ case DW_AT_address_class: return "DW_AT_address_class";
+ case DW_AT_artificial: return "DW_AT_artificial";
+ case DW_AT_base_types: return "DW_AT_base_types";
+ case DW_AT_calling_convention: return "DW_AT_calling_convention";
+ case DW_AT_count: return "DW_AT_count";
+ case DW_AT_data_member_location: return "DW_AT_data_member_location";
+ case DW_AT_decl_column: return "DW_AT_decl_column";
+ case DW_AT_decl_file: return "DW_AT_decl_file";
+ case DW_AT_decl_line: return "DW_AT_decl_line";
+ case DW_AT_declaration: return "DW_AT_declaration";
+ case DW_AT_discr_list: return "DW_AT_discr_list";
+ case DW_AT_encoding: return "DW_AT_encoding";
+ case DW_AT_external: return "DW_AT_external";
+ case DW_AT_frame_base: return "DW_AT_frame_base";
+ case DW_AT_friend: return "DW_AT_friend";
+ case DW_AT_identifier_case: return "DW_AT_identifier_case";
+ case DW_AT_macro_info: return "DW_AT_macro_info";
+ case DW_AT_namelist_items: return "DW_AT_namelist_items";
+ case DW_AT_priority: return "DW_AT_priority";
+ case DW_AT_segment: return "DW_AT_segment";
+ case DW_AT_specification: return "DW_AT_specification";
+ case DW_AT_static_link: return "DW_AT_static_link";
+ case DW_AT_type: return "DW_AT_type";
+ case DW_AT_use_location: return "DW_AT_use_location";
+ case DW_AT_variable_parameter: return "DW_AT_variable_parameter";
+ case DW_AT_virtuality: return "DW_AT_virtuality";
+ case DW_AT_vtable_elem_location: return "DW_AT_vtable_elem_location";
+ /* DWARF 2.1 values. */
+ case DW_AT_allocated: return "DW_AT_allocated";
+ case DW_AT_associated: return "DW_AT_associated";
+ case DW_AT_data_location: return "DW_AT_data_location";
+ case DW_AT_stride: return "DW_AT_stride";
+ case DW_AT_entry_pc: return "DW_AT_entry_pc";
+ case DW_AT_use_UTF8: return "DW_AT_use_UTF8";
+ case DW_AT_extension: return "DW_AT_extension";
+ case DW_AT_ranges: return "DW_AT_ranges";
+ case DW_AT_trampoline: return "DW_AT_trampoline";
+ case DW_AT_call_column: return "DW_AT_call_column";
+ case DW_AT_call_file: return "DW_AT_call_file";
+ case DW_AT_call_line: return "DW_AT_call_line";
+ /* SGI/MIPS extensions. */
+ case DW_AT_MIPS_fde: return "DW_AT_MIPS_fde";
+ case DW_AT_MIPS_loop_begin: return "DW_AT_MIPS_loop_begin";
+ case DW_AT_MIPS_tail_loop_begin: return "DW_AT_MIPS_tail_loop_begin";
+ case DW_AT_MIPS_epilog_begin: return "DW_AT_MIPS_epilog_begin";
+ case DW_AT_MIPS_loop_unroll_factor: return "DW_AT_MIPS_loop_unroll_factor";
+ case DW_AT_MIPS_software_pipeline_depth:
+ return "DW_AT_MIPS_software_pipeline_depth";
+ case DW_AT_MIPS_linkage_name: return "DW_AT_MIPS_linkage_name";
+ case DW_AT_MIPS_stride: return "DW_AT_MIPS_stride";
+ case DW_AT_MIPS_abstract_name: return "DW_AT_MIPS_abstract_name";
+ case DW_AT_MIPS_clone_origin: return "DW_AT_MIPS_clone_origin";
+ case DW_AT_MIPS_has_inlines: return "DW_AT_MIPS_has_inlines";
+ /* GNU extensions. */
+ case DW_AT_sf_names: return "DW_AT_sf_names";
+ case DW_AT_src_info: return "DW_AT_src_info";
+ case DW_AT_mac_info: return "DW_AT_mac_info";
+ case DW_AT_src_coords: return "DW_AT_src_coords";
+ case DW_AT_body_begin: return "DW_AT_body_begin";
+ case DW_AT_body_end: return "DW_AT_body_end";
+ case DW_AT_GNU_vector: return "DW_AT_GNU_vector";
+ /* UPC extension. */
+ case DW_AT_upc_threads_scaled: return "DW_AT_upc_threads_scaled";
+ default:
+ {
+ static char buffer[100];
+
+ snprintf (buffer, sizeof (buffer), _("Unknown AT value: %lx"),
+ attribute);
+ return buffer;
+ }
+ }
+}
+
+static unsigned char *
+read_and_display_attr (unsigned long attribute,
+ unsigned long form,
+ unsigned char *data,
+ unsigned long cu_offset,
+ unsigned long pointer_size,
+ unsigned long offset_size,
+ int dwarf_version,
+ debug_info *debug_info_p,
+ int do_loc)
+{
+ if (!do_loc)
+ printf (" %-18s:", get_AT_name (attribute));
+ data = read_and_display_attr_value (attribute, form, data, cu_offset,
+ pointer_size, offset_size,
+ dwarf_version, debug_info_p,
+ do_loc);
+ if (!do_loc)
+ printf ("\n");
+ return data;
+}
+
+
+/* Process the contents of a .debug_info section. If do_loc is non-zero
+ then we are scanning for location lists and we do not want to display
+ anything to the user. */
+
+static int
+process_debug_info (struct dwarf_section *section, void *file,
+ int do_loc)
+{
+ unsigned char *start = section->start;
+ unsigned char *end = start + section->size;
+ unsigned char *section_begin;
+ unsigned int unit;
+ unsigned int num_units = 0;
+
+ if ((do_loc || do_debug_loc || do_debug_ranges)
+ && num_debug_info_entries == 0)
+ {
+ unsigned long length;
+
+ /* First scan the section to get the number of comp units. */
+ for (section_begin = start, num_units = 0; section_begin < end;
+ num_units ++)
+ {
+ /* Read the first 4 bytes. For a 32-bit DWARF section, this
+ will be the length. For a 64-bit DWARF section, it'll be
+ the escape code 0xffffffff followed by an 8 byte length. */
+ length = byte_get (section_begin, 4);
+
+ if (length == 0xffffffff)
+ {
+ length = byte_get (section_begin + 4, 8);
+ section_begin += length + 12;
+ }
+ else
+ section_begin += length + 4;
+ }
+
+ if (num_units == 0)
+ {
+ error (_("No comp units in %s section ?"), section->name);
+ return 0;
+ }
+
+ /* Then allocate an array to hold the information. */
+ debug_information = cmalloc (num_units,
+ sizeof (* debug_information));
+ if (debug_information == NULL)
+ {
+ error (_("Not enough memory for a debug info array of %u entries.\n"),
+ num_units);
+ return 0;
+ }
+ }
+
+ if (!do_loc)
+ {
+ printf (_("The section %s contains:\n\n"), section->name);
+
+ load_debug_section (str, file);
+ }
+
+ load_debug_section (abbrev, file);
+ if (debug_displays [abbrev].section.start == NULL)
+ {
+ warn (_("Unable to locate %s section!\n"),
+ debug_displays [abbrev].section.name);
+ return 0;
+ }
+
+ for (section_begin = start, unit = 0; start < end; unit++)
+ {
+ DWARF2_Internal_CompUnit compunit;
+ unsigned char *hdrptr;
+ unsigned char *cu_abbrev_offset_ptr;
+ unsigned char *tags;
+ int level;
+ unsigned long cu_offset;
+ int offset_size;
+ int initial_length_size;
+
+ hdrptr = start;
+
+ compunit.cu_length = byte_get (hdrptr, 4);
+ hdrptr += 4;
+
+ if (compunit.cu_length == 0xffffffff)
+ {
+ compunit.cu_length = byte_get (hdrptr, 8);
+ hdrptr += 8;
+ offset_size = 8;
+ initial_length_size = 12;
+ }
+ else
+ {
+ offset_size = 4;
+ initial_length_size = 4;
+ }
+
+ compunit.cu_version = byte_get (hdrptr, 2);
+ hdrptr += 2;
+
+ cu_offset = start - section_begin;
+ start += compunit.cu_length + initial_length_size;
+
+ cu_abbrev_offset_ptr = hdrptr;
+ compunit.cu_abbrev_offset = byte_get (hdrptr, offset_size);
+ hdrptr += offset_size;
+
+ compunit.cu_pointer_size = byte_get (hdrptr, 1);
+ hdrptr += 1;
+ if ((do_loc || do_debug_loc || do_debug_ranges)
+ && num_debug_info_entries == 0)
+ {
+ debug_information [unit].cu_offset = cu_offset;
+ debug_information [unit].pointer_size
+ = compunit.cu_pointer_size;
+ debug_information [unit].base_address = 0;
+ debug_information [unit].loc_offsets = NULL;
+ debug_information [unit].have_frame_base = NULL;
+ debug_information [unit].max_loc_offsets = 0;
+ debug_information [unit].num_loc_offsets = 0;
+#ifdef SORT_LOCATION_LIST_OFFSETS
+ debug_information [unit].last_loc_offset = 0;
+#endif
+ debug_information [unit].range_lists = NULL;
+ debug_information [unit].max_range_lists= 0;
+ debug_information [unit].num_range_lists = 0;
+ }
+
+ tags = hdrptr;
+
+ if (!do_loc)
+ {
+ printf (_(" Compilation Unit @ offset 0x%lx:\n"), cu_offset);
+ printf (_(" Length: %ld\n"), compunit.cu_length);
+ printf (_(" Version: %d\n"), compunit.cu_version);
+ printf (_(" Abbrev Offset: %ld\n"), compunit.cu_abbrev_offset);
+ printf (_(" Pointer Size: %d\n"), compunit.cu_pointer_size);
+ }
+
+ if (compunit.cu_version != 2 && compunit.cu_version != 3)
+ {
+ warn (_("Only version 2 and 3 DWARF debug information is currently supported.\n"));
+ continue;
+ }
+
+ free_abbrevs ();
+
+ /* Process the abbrevs used by this compilation unit. DWARF
+ sections under Mach-O have non-zero addresses. */
+ process_abbrev_section
+ ((unsigned char *) debug_displays [abbrev].section.start
+ + compunit.cu_abbrev_offset - debug_displays [abbrev].section.address,
+ (unsigned char *) debug_displays [abbrev].section.start
+ + debug_displays [abbrev].section.size);
+
+ level = 0;
+ while (tags < start)
+ {
+ unsigned int bytes_read;
+ unsigned long abbrev_number;
+ abbrev_entry *entry;
+ abbrev_attr *attr;
+
+ abbrev_number = read_leb128 (tags, & bytes_read, 0);
+ tags += bytes_read;
+
+ /* A null DIE marks the end of a list of children. */
+ if (abbrev_number == 0)
+ {
+ --level;
+ continue;
+ }
+
+ /* Scan through the abbreviation list until we reach the
+ correct entry. */
+ for (entry = first_abbrev;
+ entry && entry->entry != abbrev_number;
+ entry = entry->next)
+ continue;
+
+ if (entry == NULL)
+ {
+ warn (_("Unable to locate entry %lu in the abbreviation table\n"),
+ abbrev_number);
+ return 0;
+ }
+
+ if (!do_loc)
+ printf (_(" <%d><%lx>: Abbrev Number: %lu (%s)\n"),
+ level,
+ (unsigned long) (tags - section_begin
+ - bytes_read),
+ abbrev_number,
+ get_TAG_name (entry->tag));
+
+ switch (entry->tag)
+ {
+ default:
+ need_base_address = 0;
+ break;
+ case DW_TAG_compile_unit:
+ need_base_address = 1;
+ break;
+ case DW_TAG_entry_point:
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_subprogram:
+ need_base_address = 0;
+ /* Assuming that there is no DW_AT_frame_base. */
+ have_frame_base = 0;
+ break;
+ }
+
+ for (attr = entry->first_attr; attr; attr = attr->next)
+ tags = read_and_display_attr (attr->attribute,
+ attr->form,
+ tags, cu_offset,
+ compunit.cu_pointer_size,
+ offset_size,
+ compunit.cu_version,
+ &debug_information [unit],
+ do_loc);
+
+ if (entry->children)
+ ++level;
+ }
+ }
+
+ /* Set num_debug_info_entries here so that it can be used to check if
+ we need to process .debug_loc and .debug_ranges sections. */
+ if ((do_loc || do_debug_loc || do_debug_ranges)
+ && num_debug_info_entries == 0)
+ num_debug_info_entries = num_units;
+
+ if (!do_loc)
+ {
+ printf ("\n");
+ }
+
+ return 1;
+}
+
+/* Locate and scan the .debug_info section in the file and record the pointer
+ sizes and offsets for the compilation units in it. Usually an executable
+ will have just one pointer size, but this is not guaranteed, and so we try
+ not to make any assumptions. Returns zero upon failure, or the number of
+ compilation units upon success. */
+
+static unsigned int
+load_debug_info (void * file)
+{
+ /* Reset the last pointer size so that we can issue correct error
+ messages if we are displaying the contents of more than one section. */
+ last_pointer_size = 0;
+ warned_about_missing_comp_units = FALSE;
+
+ /* If we already have the information there is nothing else to do. */
+ if (num_debug_info_entries > 0)
+ return num_debug_info_entries;
+
+ if (load_debug_section (info, file)
+ && process_debug_info (&debug_displays [info].section, file, 1))
+ return num_debug_info_entries;
+ else
+ return 0;
+}
+
+static int
+display_debug_lines (struct dwarf_section *section, void *file)
+{
+ unsigned char *start = section->start;
+ unsigned char *data = start;
+ unsigned char *end = start + section->size;
+ unsigned long val;
+
+ printf (_("\nDump of debug contents of section %s:\n\n"),
+ section->name);
+
+ load_debug_info (file);
+
+ while (data < end)
+ {
+ DWARF2_Internal_LineInfo info;
+ unsigned char *standard_opcodes;
+ unsigned char *end_of_sequence;
+ unsigned char *hdrptr;
+ int initial_length_size;
+ int offset_size;
+ int i;
+
+ hdrptr = data;
+
+ /* Check the length of the block. */
+ info.li_length = byte_get (hdrptr, 4);
+ hdrptr += 4;
+
+ if (info.li_length == 0xffffffff)
+ {
+ /* This section is 64-bit DWARF 3. */
+ info.li_length = byte_get (hdrptr, 8);
+ hdrptr += 8;
+ offset_size = 8;
+ initial_length_size = 12;
+ }
+ else
+ {
+ offset_size = 4;
+ initial_length_size = 4;
+ }
+
+ if (info.li_length + initial_length_size > section->size)
+ {
+ warn
+ (_("The line info appears to be corrupt - the section is too small\n"));
+ return 0;
+ }
+
+ /* Check its version number. */
+ info.li_version = byte_get (hdrptr, 2);
+ hdrptr += 2;
+ if (info.li_version != 2 && info.li_version != 3)
+ {
+ warn (_("Only DWARF version 2 and 3 line info is currently supported.\n"));
+ return 0;
+ }
+
+ info.li_prologue_length = byte_get (hdrptr, offset_size);
+ hdrptr += offset_size;
+ info.li_min_insn_length = byte_get (hdrptr, 1);
+ hdrptr++;
+ info.li_default_is_stmt = byte_get (hdrptr, 1);
+ hdrptr++;
+ info.li_line_base = byte_get (hdrptr, 1);
+ hdrptr++;
+ info.li_line_range = byte_get (hdrptr, 1);
+ hdrptr++;
+ info.li_opcode_base = byte_get (hdrptr, 1);
+ hdrptr++;
+
+ /* Sign extend the line base field. */
+ info.li_line_base <<= 24;
+ info.li_line_base >>= 24;
+
+ printf (_(" Length: %ld\n"), info.li_length);
+ printf (_(" DWARF Version: %d\n"), info.li_version);
+ printf (_(" Prologue Length: %d\n"), info.li_prologue_length);
+ printf (_(" Minimum Instruction Length: %d\n"), info.li_min_insn_length);
+ printf (_(" Initial value of 'is_stmt': %d\n"), info.li_default_is_stmt);
+ printf (_(" Line Base: %d\n"), info.li_line_base);
+ printf (_(" Line Range: %d\n"), info.li_line_range);
+ printf (_(" Opcode Base: %d\n"), info.li_opcode_base);
+
+ end_of_sequence = data + info.li_length + initial_length_size;
+
+ reset_state_machine (info.li_default_is_stmt);
+
+ /* Display the contents of the Opcodes table. */
+ standard_opcodes = hdrptr;
+
+ printf (_("\n Opcodes:\n"));
+
+ for (i = 1; i < info.li_opcode_base; i++)
+ printf (_(" Opcode %d has %d args\n"), i, standard_opcodes[i - 1]);
+
+ /* Display the contents of the Directory table. */
+ data = standard_opcodes + info.li_opcode_base - 1;
+
+ if (*data == 0)
+ printf (_("\n The Directory Table is empty.\n"));
+ else
+ {
+ printf (_("\n The Directory Table:\n"));
+
+ while (*data != 0)
+ {
+ printf (_(" %s\n"), data);
+
+ data += strlen ((char *) data) + 1;
+ }
+ }
+
+ /* Skip the NUL at the end of the table. */
+ data++;
+
+ /* Display the contents of the File Name table. */
+ if (*data == 0)
+ printf (_("\n The File Name Table is empty.\n"));
+ else
+ {
+ printf (_("\n The File Name Table:\n"));
+ printf (_(" Entry\tDir\tTime\tSize\tName\n"));
+
+ while (*data != 0)
+ {
+ unsigned char *name;
+ unsigned int bytes_read;
+
+ ++state_machine_regs.last_file_entry;
+ printf (_(" %d\t"), state_machine_regs.last_file_entry);
+ name = data;
+
+ data += strlen ((char *) data) + 1;
+ val = read_leb128 (data, & bytes_read, 0);
+ printf (_("%lu\t"), val);
+ data += bytes_read;
+ val = read_leb128 (data, & bytes_read, 0);
+ printf (_("%lu\t"), val);
+ data += bytes_read;
+ val = read_leb128 (data, & bytes_read, 0);
+ printf (_("%lu\t"), val);
+ data += bytes_read;
+ printf (_("%s\n"), name);
+ }
+ }
+
+ /* Skip the NUL at the end of the table. */
+ data++;
+
+ /* Now display the statements. */
+ printf (_("\n Line Number Statements:\n"));
+
+ while (data < end_of_sequence)
+ {
+ unsigned char op_code;
+ int adv;
+ unsigned long int uladv;
+ unsigned int bytes_read;
+
+ op_code = *data++;
+
+ if (op_code >= info.li_opcode_base)
+ {
+ op_code -= info.li_opcode_base;
+ uladv = (op_code / info.li_line_range) * info.li_min_insn_length;
+ state_machine_regs.address += uladv;
+ printf (_(" Special opcode %d: advance Address by %lu to 0x%lx"),
+ op_code, uladv, state_machine_regs.address);
+ adv = (op_code % info.li_line_range) + info.li_line_base;
+ state_machine_regs.line += adv;
+ printf (_(" and Line by %d to %d\n"),
+ adv, state_machine_regs.line);
+ }
+ else switch (op_code)
+ {
+ case DW_LNS_extended_op:
+ data += process_extended_line_op (data, info.li_default_is_stmt);
+ break;
+
+ case DW_LNS_copy:
+ printf (_(" Copy\n"));
+ break;
+
+ case DW_LNS_advance_pc:
+ uladv = read_leb128 (data, & bytes_read, 0);
+ uladv *= info.li_min_insn_length;
+ data += bytes_read;
+ state_machine_regs.address += uladv;
+ printf (_(" Advance PC by %lu to 0x%lx\n"), uladv,
+ state_machine_regs.address);
+ break;
+
+ case DW_LNS_advance_line:
+ adv = read_leb128 (data, & bytes_read, 1);
+ data += bytes_read;
+ state_machine_regs.line += adv;
+ printf (_(" Advance Line by %d to %d\n"), adv,
+ state_machine_regs.line);
+ break;
+
+ case DW_LNS_set_file:
+ adv = read_leb128 (data, & bytes_read, 0);
+ data += bytes_read;
+ printf (_(" Set File Name to entry %d in the File Name Table\n"),
+ adv);
+ state_machine_regs.file = adv;
+ break;
+
+ case DW_LNS_set_column:
+ uladv = read_leb128 (data, & bytes_read, 0);
+ data += bytes_read;
+ printf (_(" Set column to %lu\n"), uladv);
+ state_machine_regs.column = uladv;
+ break;
+
+ case DW_LNS_negate_stmt:
+ adv = state_machine_regs.is_stmt;
+ adv = ! adv;
+ printf (_(" Set is_stmt to %d\n"), adv);
+ state_machine_regs.is_stmt = adv;
+ break;
+
+ case DW_LNS_set_basic_block:
+ printf (_(" Set basic block\n"));
+ state_machine_regs.basic_block = 1;
+ break;
+
+ case DW_LNS_const_add_pc:
+ uladv = (((255 - info.li_opcode_base) / info.li_line_range)
+ * info.li_min_insn_length);
+ state_machine_regs.address += uladv;
+ printf (_(" Advance PC by constant %lu to 0x%lx\n"), uladv,
+ state_machine_regs.address);
+ break;
+
+ case DW_LNS_fixed_advance_pc:
+ uladv = byte_get (data, 2);
+ data += 2;
+ state_machine_regs.address += uladv;
+ printf (_(" Advance PC by fixed size amount %lu to 0x%lx\n"),
+ uladv, state_machine_regs.address);
+ break;
+
+ case DW_LNS_set_prologue_end:
+ printf (_(" Set prologue_end to true\n"));
+ break;
+
+ case DW_LNS_set_epilogue_begin:
+ printf (_(" Set epilogue_begin to true\n"));
+ break;
+
+ case DW_LNS_set_isa:
+ uladv = read_leb128 (data, & bytes_read, 0);
+ data += bytes_read;
+ printf (_(" Set ISA to %lu\n"), uladv);
+ break;
+
+ default:
+ printf (_(" Unknown opcode %d with operands: "), op_code);
+
+ for (i = standard_opcodes[op_code - 1]; i > 0 ; --i)
+ {
+ val = read_leb128 (data, &bytes_read, 0);
+ printf ("0x%lx%s", val,
+ i == 1 ? "" : ", ");
+ data += bytes_read;
+ }
+ putchar ('\n');
+ break;
+ }
+ }
+ putchar ('\n');
+ }
+
+ return 1;
+}
+
+static int
+display_debug_pubnames (struct dwarf_section *section,
+ void *file ATTRIBUTE_UNUSED)
+{
+ DWARF2_Internal_PubNames pubnames;
+ unsigned char *start = section->start;
+ unsigned char *end = start + section->size;
+
+ printf (_("Contents of the %s section:\n\n"), section->name);
+
+ while (start < end)
+ {
+ unsigned char *data;
+ unsigned long offset;
+ int offset_size, initial_length_size;
+
+ data = start;
+
+ pubnames.pn_length = byte_get (data, 4);
+ data += 4;
+ if (pubnames.pn_length == 0xffffffff)
+ {
+ pubnames.pn_length = byte_get (data, 8);
+ data += 8;
+ offset_size = 8;
+ initial_length_size = 12;
+ }
+ else
+ {
+ offset_size = 4;
+ initial_length_size = 4;
+ }
+
+ pubnames.pn_version = byte_get (data, 2);
+ data += 2;
+ pubnames.pn_offset = byte_get (data, offset_size);
+ data += offset_size;
+ pubnames.pn_size = byte_get (data, offset_size);
+ data += offset_size;
+
+ start += pubnames.pn_length + initial_length_size;
+
+ if (pubnames.pn_version != 2 && pubnames.pn_version != 3)
+ {
+ static int warned = 0;
+
+ if (! warned)
+ {
+ warn (_("Only DWARF 2 and 3 pubnames are currently supported\n"));
+ warned = 1;
+ }
+
+ continue;
+ }
+
+ printf (_(" Length: %ld\n"),
+ pubnames.pn_length);
+ printf (_(" Version: %d\n"),
+ pubnames.pn_version);
+ printf (_(" Offset into .debug_info section: %ld\n"),
+ pubnames.pn_offset);
+ printf (_(" Size of area in .debug_info section: %ld\n"),
+ pubnames.pn_size);
+
+ printf (_("\n Offset\tName\n"));
+
+ do
+ {
+ offset = byte_get (data, offset_size);
+
+ if (offset != 0)
+ {
+ data += offset_size;
+ printf (" %-6ld\t\t%s\n", offset, data);
+ data += strlen ((char *) data) + 1;
+ }
+ }
+ while (offset != 0);
+ }
+
+ printf ("\n");
+ return 1;
+}
+
+static int
+display_debug_macinfo (struct dwarf_section *section,
+ void *file ATTRIBUTE_UNUSED)
+{
+ unsigned char *start = section->start;
+ unsigned char *end = start + section->size;
+ unsigned char *curr = start;
+ unsigned int bytes_read;
+ enum dwarf_macinfo_record_type op;
+
+ printf (_("Contents of the %s section:\n\n"), section->name);
+
+ while (curr < end)
+ {
+ unsigned int lineno;
+ const char *string;
+
+ op = *curr;
+ curr++;
+
+ switch (op)
+ {
+ case DW_MACINFO_start_file:
+ {
+ unsigned int filenum;
+
+ lineno = read_leb128 (curr, & bytes_read, 0);
+ curr += bytes_read;
+ filenum = read_leb128 (curr, & bytes_read, 0);
+ curr += bytes_read;
+
+ printf (_(" DW_MACINFO_start_file - lineno: %d filenum: %d\n"),
+ lineno, filenum);
+ }
+ break;
+
+ case DW_MACINFO_end_file:
+ printf (_(" DW_MACINFO_end_file\n"));
+ break;
+
+ case DW_MACINFO_define:
+ lineno = read_leb128 (curr, & bytes_read, 0);
+ curr += bytes_read;
+ string = (char *) curr;
+ curr += strlen (string) + 1;
+ printf (_(" DW_MACINFO_define - lineno : %d macro : %s\n"),
+ lineno, string);
+ break;
+
+ case DW_MACINFO_undef:
+ lineno = read_leb128 (curr, & bytes_read, 0);
+ curr += bytes_read;
+ string = (char *) curr;
+ curr += strlen (string) + 1;
+ printf (_(" DW_MACINFO_undef - lineno : %d macro : %s\n"),
+ lineno, string);
+ break;
+
+ case DW_MACINFO_vendor_ext:
+ {
+ unsigned int constant;
+
+ constant = read_leb128 (curr, & bytes_read, 0);
+ curr += bytes_read;
+ string = (char *) curr;
+ curr += strlen (string) + 1;
+ printf (_(" DW_MACINFO_vendor_ext - constant : %d string : %s\n"),
+ constant, string);
+ }
+ break;
+ }
+ }
+
+ return 1;
+}
+
+static int
+display_debug_abbrev (struct dwarf_section *section,
+ void *file ATTRIBUTE_UNUSED)
+{
+ abbrev_entry *entry;
+ unsigned char *start = section->start;
+ unsigned char *end = start + section->size;
+
+ printf (_("Contents of the %s section:\n\n"), section->name);
+
+ do
+ {
+ free_abbrevs ();
+
+ start = process_abbrev_section (start, end);
+
+ if (first_abbrev == NULL)
+ continue;
+
+ printf (_(" Number TAG\n"));
+
+ for (entry = first_abbrev; entry; entry = entry->next)
+ {
+ abbrev_attr *attr;
+
+ printf (_(" %ld %s [%s]\n"),
+ entry->entry,
+ get_TAG_name (entry->tag),
+ entry->children ? _("has children") : _("no children"));
+
+ for (attr = entry->first_attr; attr; attr = attr->next)
+ printf (_(" %-18s %s\n"),
+ get_AT_name (attr->attribute),
+ get_FORM_name (attr->form));
+ }
+ }
+ while (start);
+
+ printf ("\n");
+
+ return 1;
+}
+
+#ifdef SORT_LOCATION_LIST_OFFSETS
+static int compare_loc_offsets(const void *l, const void *r)
+{
+ return *(long *)l - *(long *)r;
+}
+#endif
+
+static int
+display_debug_loc (struct dwarf_section *section, void *file)
+{
+ unsigned char *start = section->start;
+ unsigned char *section_end;
+ unsigned long bytes;
+ unsigned char *section_begin = start;
+ unsigned int first = 0;
+ unsigned int i;
+ unsigned int j;
+ int seen_first_offset = 0;
+ unsigned char *next, *last_overlap;
+
+ bytes = section->size;
+ section_end = start + bytes;
+
+ if (bytes == 0)
+ {
+ printf (_("\nThe %s section is empty.\n"), section->name);
+ return 0;
+ }
+
+ load_debug_info (file);
+
+ {
+ unsigned int num_loc_list = 0;
+ unsigned long last_offset = 0;
+ int use_debug_info = 1;
+ /* Check the order of location list in .debug_info section. If
+ offsets of location lists are in the ascending order, we can
+ use `debug_information' directly. */
+ for (i = 0; i < num_debug_info_entries; i++)
+ {
+ unsigned int num;
+
+ num = debug_information [i].num_loc_offsets;
+ num_loc_list += num;
+ ASSERT(num_loc_list == 0 || section->start != NULL);
+
+ /* Check if we can use `debug_information' directly. */
+ if (use_debug_info && num != 0)
+ {
+ if (!seen_first_offset)
+ {
+ /* This is the first location list. */
+ last_offset = debug_information [i].loc_offsets [0];
+ first = i;
+ seen_first_offset = 1;
+ j = 1;
+ }
+ else
+ j = 0;
+
+ for (; j < num; j++)
+ {
+ unsigned long offset = debug_information [i].loc_offsets [j];
+ if (last_offset > offset)
+ {
+ if (offset < section->size) {
+ use_debug_info = 0;
+ break;
+ }
+ else
+ continue;
+ }
+
+ if (offset < section->size)
+ last_offset = offset;
+ else
+ error(_("Not updating location-list at offset 0x%x, that offset is garbage.\n"),
+ offset);
+ }
+ }
+ }
+
+ if (!use_debug_info)
+ /* FIXME: Should we handle this case? */
+ error (_("Location lists in .debug_info section aren't in ascending order!\n"));
+ }
+
+ if (!seen_first_offset)
+ error (_("No location lists in .debug_info section!\n"));
+
+ /* DWARF sections under Mach-O have non-zero addresses. */
+ if (debug_information [first].loc_offsets [0] != section->address)
+ warn (_("Location lists in %s section start at 0x%lx\n"),
+ section->name, debug_information [first].loc_offsets [0]);
+
+ printf (_("Contents of the %s section:\n\n"), section->name);
+ printf (_(" Offset Begin End Expression\n"));
+
+ seen_first_offset = 0;
+ for (i = first; i < num_debug_info_entries; i++)
+ {
+ unsigned long begin;
+ unsigned long end;
+ unsigned short length;
+ unsigned long offset;
+ unsigned int pointer_size;
+ unsigned long cu_offset;
+ unsigned long base_address;
+ int need_frame_base;
+ int has_frame_base;
+
+
+ pointer_size = debug_information [i].pointer_size;
+ cu_offset = debug_information [i].cu_offset;
+
+#ifdef SORT_LOCATION_LIST_OFFSETS
+ if (debug_information[i].last_loc_offset == -1UL &&
+ debug_information [i].num_loc_offsets) {
+ error (_("Location lists in .debug_info section aren't in ascending order! Sorting %d of them now...\n"),
+ debug_information[i].num_loc_offsets);
+ qsort(debug_information[i].loc_offsets,
+ debug_information[i].num_loc_offsets,
+ sizeof(long),
+ compare_loc_offsets);
+ debug_information[i].last_loc_offset = 0; /* use any non-negative value to indicate that this is sorted now */
+ if (0) {
+ /* Look for repeating offsets. */
+ int cnt;
+ int repeat_idx = 0;
+ for (cnt = 1; cnt < debug_information[i].num_loc_offsets; cnt++) {
+ if (debug_information[i].loc_offsets[repeat_idx] == debug_information[i].loc_offsets[cnt]) {
+ continue;
+ }
+ if (repeat_idx + 1 < cnt) {
+ error(_("VALUE %x REPEATS IN [%d, %d) (%d TIMES)!\n"),
+ debug_information[i].loc_offsets[repeat_idx],
+ repeat_idx,
+ cnt,
+ cnt - repeat_idx);
+ }
+ repeat_idx = cnt;
+ }
+ }
+ }
+#endif
+
+ last_overlap = NULL;
+ for (j = 0; j < debug_information [i].num_loc_offsets; j++)
+ {
+ has_frame_base = debug_information [i].have_frame_base [j];
+ /* DWARF sections under Mach-O have non-zero addresses. */
+ offset = debug_information [i].loc_offsets [j] - section->address;
+ if (offset >= section->size) {
+ error(_("Location offset 0x%x from CU %d is beyond section's end, skipping...\n"),
+ offset, i);
+ continue;
+ }
+ else
+ /* Check to see if the location list is preceded immediately by a
+ NULL entry for the previous location list, or is the first one
+ in the section. If not, continue, because the offset is
+ garbabe (assuming that the debug_loc section is correct.)
+ */
+ if (offset) {
+ if (offset < 2*pointer_size) {
+ error(_("Location offset 0x%x from CU %d is not preceded by a valid location list, skipping...\n"),
+ offset, i);
+ continue;
+ }
+ /* There is a chance that we may hit an incorrect offset that is precede by two zeros which are
+ actually values of a location expression and not the terminating sequence of the location
+ list, so this check can generate a false negative (i.e., we can decide that the location offset
+ is OK when it is not), but this risk is small. Anyway, a well-formed DWARF record should not
+ be corrupt.
+ */
+ if (0 != byte_get (section_begin + offset - pointer_size, pointer_size) ||
+ 0 != byte_get (section_begin + offset - 2*pointer_size, pointer_size)) {
+ error(_("Location offset 0x%x from CU %d is not preceded by the end of a location list, skipping...\n"),
+ offset, i);
+ continue;
+ }
+ }
+
+ next = section_begin + offset;
+ base_address = debug_information [i].base_address;
+
+ if (!seen_first_offset)
+ seen_first_offset = 1;
+ else
+ {
+ if (start < next)
+ warn (_("There is a hole [0x%lx - 0x%lx] in .debug_loc section.\n"),
+ (long)(start - section_begin), (long)(next - section_begin));
+ else if (start > next) {
+ last_overlap = start;
+ warn (_("There is an overlap of %ld bytes [0x%lx - 0x%lx] in .debug_loc section.\n"),
+ (long)(start - next),
+ (long)(start - section_begin), (long)(next - section_begin));
+ }
+ }
+ start = next;
+
+ if (offset >= bytes)
+ {
+ warn (_("Offset 0x%lx is bigger than .debug_loc section size.\n"),
+ offset);
+ continue;
+ }
+
+ while (1)
+ {
+ if (start + 2 * pointer_size > section_end)
+ {
+ warn (_("Location list starting at offset 0x%lx is not terminated.\n"),
+ offset);
+ break;
+ }
+
+ begin = byte_get (start, pointer_size);
+ start += pointer_size;
+ end = byte_get (start, pointer_size);
+ start += pointer_size;
+
+ if (begin == 0 && end == 0)
+ {
+ printf (_(" %8.8lx <End of list>\n"), offset);
+ break;
+ }
+
+ /* Check base address specifiers. */
+ if (begin == -1UL && end != -1UL)
+ {
+ base_address = end;
+ printf (_(" %8.8lx %8.8lx %8.8lx (base address)\n"),
+ offset, begin, end);
+ continue;
+ }
+
+ /* Call hook to adjust location start and end values. */
+ if (start > last_overlap)
+ base_value_pair_hook(
+ start - 2*pointer_size, pointer_size,
+ base_address, begin, end);
+
+ if (start + 2 > section_end)
+ {
+ warn (_("Location list starting at offset 0x%lx is not terminated.\n"),
+ offset);
+ break;
+ }
+
+ length = byte_get (start, 2);
+ start += 2;
+
+ if (start + length > section_end)
+ {
+ warn (_("Location list starting at offset 0x%lx is not terminated.\n"),
+ offset);
+ break;
+ }
+
+ printf (" %8.8lx %8.8lx %8.8lx (",
+ offset, begin + base_address, end + base_address);
+ need_frame_base = decode_location_expression (start,
+ pointer_size,
+ length,
+ cu_offset);
+ putchar (')');
+
+ if (need_frame_base && !has_frame_base)
+ printf (_(" [without DW_AT_frame_base]"));
+
+ if (begin == end)
+ printf (" (start == end)");
+ else if (begin > end)
+ printf(" (start > end)");
+
+ putchar ('\n');
+
+ start += length;
+ }
+ }
+ }
+ return 1;
+}
+
+static int
+display_debug_str (struct dwarf_section *section,
+ void *file ATTRIBUTE_UNUSED)
+{
+ unsigned char *start = section->start;
+ unsigned long bytes = section->size;
+ dwarf_vma addr = section->address;
+
+ if (bytes == 0)
+ {
+ printf (_("\nThe %s section is empty.\n"), section->name);
+ return 0;
+ }
+
+ printf (_("Contents of the %s section:\n\n"), section->name);
+
+ while (bytes)
+ {
+ int j;
+ int k;
+ int lbytes;
+
+ lbytes = (bytes > 16 ? 16 : bytes);
+
+ printf (" 0x%8.8lx ", (unsigned long) addr);
+
+ for (j = 0; j < 16; j++)
+ {
+ if (j < lbytes)
+ printf ("%2.2x", start[j]);
+ else
+ printf (" ");
+
+ if ((j & 3) == 3)
+ printf (" ");
+ }
+
+ for (j = 0; j < lbytes; j++)
+ {
+ k = start[j];
+ if (k >= ' ' && k < 0x80)
+ printf ("%c", k);
+ else
+ printf (".");
+ }
+
+ putchar ('\n');
+
+ start += lbytes;
+ addr += lbytes;
+ bytes -= lbytes;
+ }
+
+ putchar ('\n');
+
+ return 1;
+}
+
+
+static int
+display_debug_info (struct dwarf_section *section, void *file)
+{
+ return process_debug_info (section, file, 0);
+}
+
+
+static int
+display_debug_aranges (struct dwarf_section *section,
+ void *file ATTRIBUTE_UNUSED)
+{
+ unsigned char *start = section->start;
+ unsigned char *end = start + section->size;
+
+ printf (_("The section %s contains:\n\n"), section->name);
+
+ while (start < end)
+ {
+ unsigned char *hdrptr;
+ DWARF2_Internal_ARange arange;
+ unsigned char *ranges;
+ unsigned long length;
+ unsigned long address;
+ int excess;
+ int offset_size;
+ int initial_length_size;
+
+ hdrptr = start;
+
+ arange.ar_length = byte_get (hdrptr, 4);
+ hdrptr += 4;
+
+ if (arange.ar_length == 0xffffffff)
+ {
+ arange.ar_length = byte_get (hdrptr, 8);
+ hdrptr += 8;
+ offset_size = 8;
+ initial_length_size = 12;
+ }
+ else
+ {
+ offset_size = 4;
+ initial_length_size = 4;
+ }
+
+ arange.ar_version = byte_get (hdrptr, 2);
+ hdrptr += 2;
+
+ arange.ar_info_offset = byte_get (hdrptr, offset_size);
+ hdrptr += offset_size;
+
+ arange.ar_pointer_size = byte_get (hdrptr, 1);
+ hdrptr += 1;
+
+ arange.ar_segment_size = byte_get (hdrptr, 1);
+ hdrptr += 1;
+
+ if (arange.ar_version != 2 && arange.ar_version != 3)
+ {
+ warn (_("Only DWARF 2 and 3 aranges are currently supported.\n"));
+ break;
+ }
+
+ printf (_(" Length: %ld\n"), arange.ar_length);
+ printf (_(" Version: %d\n"), arange.ar_version);
+ printf (_(" Offset into .debug_info: %lx\n"), arange.ar_info_offset);
+ printf (_(" Pointer Size: %d\n"), arange.ar_pointer_size);
+ printf (_(" Segment Size: %d\n"), arange.ar_segment_size);
+
+ printf (_("\n Address Length\n"));
+
+ ranges = hdrptr;
+
+ /* Must pad to an alignment boundary that is twice the pointer size. */
+ excess = (hdrptr - start) % (2 * arange.ar_pointer_size);
+ if (excess)
+ ranges += (2 * arange.ar_pointer_size) - excess;
+
+ start += arange.ar_length + initial_length_size;
+
+ while (ranges + 2 * arange.ar_pointer_size <= start)
+ {
+ address = byte_get (ranges, arange.ar_pointer_size);
+ if (address)
+ value_hook(ranges, arange.ar_pointer_size, address);
+
+ ranges += arange.ar_pointer_size;
+
+ length = byte_get (ranges, arange.ar_pointer_size);
+
+ ranges += arange.ar_pointer_size;
+
+ printf (" %8.8lx %lu\n", address, length);
+ }
+ }
+
+ printf ("\n");
+
+ return 1;
+}
+
+static int
+display_debug_ranges (struct dwarf_section *section,
+ void *file ATTRIBUTE_UNUSED)
+{
+ unsigned char *start = section->start;
+ unsigned char *section_end;
+ unsigned long bytes;
+ unsigned char *section_begin = start;
+ unsigned int num_range_list = 0;
+ unsigned long last_offset = 0;
+ unsigned int first = 0;
+ unsigned int i;
+ unsigned int j;
+ int seen_first_offset = 0;
+ int use_debug_info = 1;
+ unsigned char *next;
+
+ bytes = section->size;
+ section_end = start + bytes;
+
+ if (bytes == 0)
+ {
+ printf (_("\nThe %s section is empty.\n"), section->name);
+ return 0;
+ }
+
+ load_debug_info (file);
+
+ /* Check the order of range list in .debug_info section. If
+ offsets of range lists are in the ascending order, we can
+ use `debug_information' directly. */
+ for (i = 0; i < num_debug_info_entries; i++)
+ {
+ unsigned int num;
+
+ num = debug_information [i].num_range_lists;
+ num_range_list += num;
+
+ /* Check if we can use `debug_information' directly. */
+ if (use_debug_info && num != 0)
+ {
+ if (!seen_first_offset)
+ {
+ /* This is the first range list. */
+ last_offset = debug_information [i].range_lists [0];
+ first = i;
+ seen_first_offset = 1;
+ j = 1;
+ }
+ else
+ j = 0;
+
+ for (; j < num; j++)
+ {
+ if (last_offset >
+ debug_information [i].range_lists [j])
+ {
+ use_debug_info = 0;
+ break;
+ }
+ last_offset = debug_information [i].range_lists [j];
+ }
+ }
+ }
+
+ if (!use_debug_info)
+ /* FIXME: Should we handle this case? */
+ error (_("Range lists in .debug_info section aren't in ascending order!\n"));
+
+ if (!seen_first_offset)
+ error (_("No range lists in .debug_info section!\n"));
+
+ /* DWARF sections under Mach-O have non-zero addresses. */
+ if (debug_information [first].range_lists [0] != section->address)
+ warn (_("Range lists in %s section start at 0x%lx\n"),
+ section->name, debug_information [first].range_lists [0]);
+
+ printf (_("Contents of the %s section:\n\n"), section->name);
+ printf (_(" Offset Begin End\n"));
+
+ seen_first_offset = 0;
+ for (i = first; i < num_debug_info_entries; i++)
+ {
+ unsigned long begin;
+ unsigned long end;
+ unsigned long offset;
+ unsigned int pointer_size;
+ unsigned long base_address;
+
+ pointer_size = debug_information [i].pointer_size;
+
+ for (j = 0; j < debug_information [i].num_range_lists; j++)
+ {
+ /* DWARF sections under Mach-O have non-zero addresses. */
+ offset = debug_information [i].range_lists [j] - section->address;
+ next = section_begin + offset;
+ base_address = debug_information [i].base_address;
+
+ if (!seen_first_offset)
+ seen_first_offset = 1;
+ else
+ {
+ if (start < next)
+ warn (_("There is a hole [0x%lx - 0x%lx] in %s section.\n"),
+ (long)(start - section_begin),
+ (long)(next - section_begin), section->name);
+ else if (start > next)
+ warn (_("There is an overlap [0x%lx - 0x%lx] in %s section.\n"),
+ (long)(start - section_begin),
+ (long)(next - section_begin), section->name);
+ }
+ start = next;
+
+ while (1)
+ {
+ begin = byte_get (start, pointer_size);
+ start += pointer_size;
+ end = byte_get (start, pointer_size);
+ start += pointer_size;
+
+ if (begin == 0 && end == 0)
+ {
+ printf (_(" %8.8lx <End of list>\n"), offset);
+ break;
+ }
+
+ /* Check base address specifiers. */
+ if (begin == -1UL && end != -1UL)
+ {
+ base_address = end;
+ printf (_(" %8.8lx %8.8lx %8.8lx (base address)\n"),
+ offset, begin, end);
+ continue;
+ }
+
+ base_value_pair_hook(start - 2*pointer_size, pointer_size,
+ base_address, begin, end);
+
+ printf (_(" %8.8lx %8.8lx %8.8lx"),
+ offset, begin + base_address, end + base_address);
+
+ if (begin == end)
+ printf (_(" (start == end)"));
+ else if (begin > end)
+ printf (_(" (start > end)"));
+
+ putchar ('\n');
+ }
+ }
+ }
+ putchar ('\n');
+ return 1;
+}
+
+typedef struct Frame_Chunk
+{
+ struct Frame_Chunk *next;
+ unsigned char *chunk_start;
+ int ncols;
+ /* DW_CFA_{undefined,same_value,offset,register,unreferenced} */
+ short int *col_type;
+ int *col_offset;
+ char *augmentation;
+ unsigned int code_factor;
+ int data_factor;
+ unsigned long pc_begin;
+ unsigned long pc_range;
+ int cfa_reg;
+ int cfa_offset;
+ int ra;
+ unsigned char fde_encoding;
+ unsigned char cfa_exp;
+}
+Frame_Chunk;
+
+/* A marker for a col_type that means this column was never referenced
+ in the frame info. */
+#define DW_CFA_unreferenced (-1)
+
+static void
+frame_need_space (Frame_Chunk *fc, int reg)
+{
+ int prev = fc->ncols;
+
+ if (reg < fc->ncols)
+ return;
+
+ fc->ncols = reg + 1;
+ fc->col_type = xcrealloc (fc->col_type, fc->ncols, sizeof (short int));
+ fc->col_offset = xcrealloc (fc->col_offset, fc->ncols, sizeof (int));
+
+ while (prev < fc->ncols)
+ {
+ fc->col_type[prev] = DW_CFA_unreferenced;
+ fc->col_offset[prev] = 0;
+ prev++;
+ }
+}
+
+static void
+frame_display_row (Frame_Chunk *fc, int *need_col_headers, int *max_regs)
+{
+ int r;
+ char tmp[100];
+
+ if (*max_regs < fc->ncols)
+ *max_regs = fc->ncols;
+
+ if (*need_col_headers)
+ {
+ *need_col_headers = 0;
+
+ printf (" LOC CFA ");
+
+ for (r = 0; r < *max_regs; r++)
+ if (fc->col_type[r] != DW_CFA_unreferenced)
+ {
+ if (r == fc->ra)
+ printf ("ra ");
+ else
+ printf ("r%-4d", r);
+ }
+
+ printf ("\n");
+ }
+
+ printf ("%08lx ", fc->pc_begin);
+ if (fc->cfa_exp)
+ strcpy (tmp, "exp");
+ else
+ sprintf (tmp, "r%d%+d", fc->cfa_reg, fc->cfa_offset);
+ printf ("%-8s ", tmp);
+
+ for (r = 0; r < fc->ncols; r++)
+ {
+ if (fc->col_type[r] != DW_CFA_unreferenced)
+ {
+ switch (fc->col_type[r])
+ {
+ case DW_CFA_undefined:
+ strcpy (tmp, "u");
+ break;
+ case DW_CFA_same_value:
+ strcpy (tmp, "s");
+ break;
+ case DW_CFA_offset:
+ sprintf (tmp, "c%+d", fc->col_offset[r]);
+ break;
+ case DW_CFA_val_offset:
+ sprintf (tmp, "v%+d", fc->col_offset[r]);
+ break;
+ case DW_CFA_register:
+ sprintf (tmp, "r%d", fc->col_offset[r]);
+ break;
+ case DW_CFA_expression:
+ strcpy (tmp, "exp");
+ break;
+ case DW_CFA_val_expression:
+ strcpy (tmp, "vexp");
+ break;
+ default:
+ strcpy (tmp, "n/a");
+ break;
+ }
+ printf ("%-5s", tmp);
+ }
+ }
+ printf ("\n");
+}
+
+static int
+size_of_encoded_value (int encoding)
+{
+ switch (encoding & 0x7)
+ {
+ default: /* ??? */
+ case 0: return eh_addr_size;
+ case 2: return 2;
+ case 3: return 4;
+ case 4: return 8;
+ }
+}
+
+static dwarf_vma
+get_encoded_value (unsigned char *data, int encoding)
+{
+ int size = size_of_encoded_value (encoding);
+ if (encoding & DW_EH_PE_signed)
+ return byte_get_signed (data, size);
+ else
+ return byte_get (data, size);
+}
+
+#define GET(N) byte_get (start, N); start += N
+#define LEB() read_leb128 (start, & length_return, 0); start += length_return
+#define SLEB() read_leb128 (start, & length_return, 1); start += length_return
+
+static int
+display_debug_frames (struct dwarf_section *section,
+ void *file ATTRIBUTE_UNUSED)
+{
+ unsigned char *start = section->start;
+ unsigned char *end = start + section->size;
+ unsigned char *section_start = start;
+ Frame_Chunk *chunks = 0;
+ Frame_Chunk *remembered_state = 0;
+ Frame_Chunk *rs;
+ int is_eh = strcmp (section->name, ".eh_frame") == 0;
+ unsigned int length_return;
+ int max_regs = 0;
+
+ printf (_("The section %s contains:\n"), section->name);
+
+ while (start < end)
+ {
+ unsigned char *saved_start;
+ unsigned char *block_end;
+ unsigned long length;
+ unsigned long cie_id;
+ Frame_Chunk *fc;
+ Frame_Chunk *cie;
+ int need_col_headers = 1;
+ unsigned char *augmentation_data = NULL;
+ unsigned long augmentation_data_len = 0;
+ int encoded_ptr_size = eh_addr_size;
+ int offset_size;
+ int initial_length_size;
+
+ saved_start = start;
+ length = byte_get (start, 4); start += 4;
+
+ if (length == 0)
+ {
+ printf ("\n%08lx ZERO terminator\n\n",
+ (unsigned long)(saved_start - section_start));
+ return 1;
+ }
+
+ if (length == 0xffffffff)
+ {
+ length = byte_get (start, 8);
+ start += 8;
+ offset_size = 8;
+ initial_length_size = 12;
+ }
+ else
+ {
+ offset_size = 4;
+ initial_length_size = 4;
+ }
+
+ block_end = saved_start + length + initial_length_size;
+ cie_id = byte_get (start, offset_size); start += offset_size;
+
+ if (is_eh ? (cie_id == 0) : (cie_id == DW_CIE_ID))
+ {
+ int version;
+
+ fc = xmalloc (sizeof (Frame_Chunk));
+ memset (fc, 0, sizeof (Frame_Chunk));
+
+ fc->next = chunks;
+ chunks = fc;
+ fc->chunk_start = saved_start;
+ fc->ncols = 0;
+ fc->col_type = xmalloc (sizeof (short int));
+ fc->col_offset = xmalloc (sizeof (int));
+ frame_need_space (fc, max_regs-1);
+
+ version = *start++;
+
+ fc->augmentation = (char *) start;
+ start = (unsigned char *) strchr ((char *) start, '\0') + 1;
+
+ if (fc->augmentation[0] == 'z')
+ {
+ fc->code_factor = LEB ();
+ fc->data_factor = SLEB ();
+ if (version == 1)
+ {
+ fc->ra = GET (1);
+ }
+ else
+ {
+ fc->ra = LEB ();
+ }
+ augmentation_data_len = LEB ();
+ augmentation_data = start;
+ start += augmentation_data_len;
+ }
+ else if (strcmp (fc->augmentation, "eh") == 0)
+ {
+ start += eh_addr_size;
+ fc->code_factor = LEB ();
+ fc->data_factor = SLEB ();
+ if (version == 1)
+ {
+ fc->ra = GET (1);
+ }
+ else
+ {
+ fc->ra = LEB ();
+ }
+ }
+ else
+ {
+ fc->code_factor = LEB ();
+ fc->data_factor = SLEB ();
+ if (version == 1)
+ {
+ fc->ra = GET (1);
+ }
+ else
+ {
+ fc->ra = LEB ();
+ }
+ }
+ cie = fc;
+
+ if (do_debug_frames_interp)
+ printf ("\n%08lx %08lx %08lx CIE \"%s\" cf=%d df=%d ra=%d\n",
+ (unsigned long)(saved_start - section_start), length, cie_id,
+ fc->augmentation, fc->code_factor, fc->data_factor,
+ fc->ra);
+ else
+ {
+ printf ("\n%08lx %08lx %08lx CIE\n",
+ (unsigned long)(saved_start - section_start), length, cie_id);
+ printf (" Version: %d\n", version);
+ printf (" Augmentation: \"%s\"\n", fc->augmentation);
+ printf (" Code alignment factor: %u\n", fc->code_factor);
+ printf (" Data alignment factor: %d\n", fc->data_factor);
+ printf (" Return address column: %d\n", fc->ra);
+
+ if (augmentation_data_len)
+ {
+ unsigned long i;
+ printf (" Augmentation data: ");
+ for (i = 0; i < augmentation_data_len; ++i)
+ printf (" %02x", augmentation_data[i]);
+ putchar ('\n');
+ }
+ putchar ('\n');
+ }
+
+ if (augmentation_data_len)
+ {
+ unsigned char *p, *q;
+ p = (unsigned char *) fc->augmentation + 1;
+ q = augmentation_data;
+
+ while (1)
+ {
+ if (*p == 'L')
+ q++;
+ else if (*p == 'P')
+ q += 1 + size_of_encoded_value (*q);
+ else if (*p == 'R')
+ fc->fde_encoding = *q++;
+ else
+ break;
+ p++;
+ }
+
+ if (fc->fde_encoding)
+ encoded_ptr_size = size_of_encoded_value (fc->fde_encoding);
+ }
+
+ frame_need_space (fc, fc->ra);
+ }
+ else
+ {
+ unsigned char *look_for;
+ static Frame_Chunk fde_fc;
+
+ fc = & fde_fc;
+ memset (fc, 0, sizeof (Frame_Chunk));
+
+ look_for = is_eh ? start - 4 - cie_id : section_start + cie_id;
+
+ for (cie = chunks; cie ; cie = cie->next)
+ if (cie->chunk_start == look_for)
+ break;
+
+ if (!cie)
+ {
+ warn ("Invalid CIE pointer %08lx in FDE at %08lx\n",
+ cie_id, (unsigned long)(saved_start - section_start));
+ start = block_end;
+ fc->ncols = 0;
+ fc->col_type = xmalloc (sizeof (short int));
+ fc->col_offset = xmalloc (sizeof (int));
+ frame_need_space (fc, max_regs - 1);
+ cie = fc;
+ fc->augmentation = "";
+ fc->fde_encoding = 0;
+ }
+ else
+ {
+ fc->ncols = cie->ncols;
+ fc->col_type = xcmalloc (fc->ncols, sizeof (short int));
+ fc->col_offset = xcmalloc (fc->ncols, sizeof (int));
+ memcpy (fc->col_type, cie->col_type, fc->ncols * sizeof (short int));
+ memcpy (fc->col_offset, cie->col_offset, fc->ncols * sizeof (int));
+ fc->augmentation = cie->augmentation;
+ fc->code_factor = cie->code_factor;
+ fc->data_factor = cie->data_factor;
+ fc->cfa_reg = cie->cfa_reg;
+ fc->cfa_offset = cie->cfa_offset;
+ fc->ra = cie->ra;
+ frame_need_space (fc, max_regs-1);
+ fc->fde_encoding = cie->fde_encoding;
+ }
+
+ if (fc->fde_encoding)
+ encoded_ptr_size = size_of_encoded_value (fc->fde_encoding);
+
+ fc->pc_begin = get_encoded_value (start, fc->fde_encoding);
+ if ((fc->fde_encoding & 0x70) == DW_EH_PE_pcrel
+ /* Don't adjust for relocatable file since there's
+ invariably a pcrel reloc here, which we haven't
+ applied. */
+ && !is_relocatable) {
+ fc->pc_begin += section->address + (start - section_start);
+ }
+ signed_value_hook(start,
+ size_of_encoded_value (fc->fde_encoding),
+ fc->fde_encoding & DW_EH_PE_signed,
+ fc->pc_begin);
+ start += encoded_ptr_size;
+ fc->pc_range = byte_get (start, encoded_ptr_size);
+ start += encoded_ptr_size;
+
+ if (cie->augmentation[0] == 'z')
+ {
+ augmentation_data_len = LEB ();
+ augmentation_data = start;
+ start += augmentation_data_len;
+ }
+
+ printf ("\n%08lx %08lx %08lx FDE cie=%08lx pc=%08lx..%08lx\n",
+ (unsigned long)(saved_start - section_start), length, cie_id,
+ (unsigned long)(cie->chunk_start - section_start),
+ fc->pc_begin, fc->pc_begin + fc->pc_range);
+ if (! do_debug_frames_interp && augmentation_data_len)
+ {
+ unsigned long i;
+
+ printf (" Augmentation data: ");
+ for (i = 0; i < augmentation_data_len; ++i)
+ printf (" %02x", augmentation_data[i]);
+ putchar ('\n');
+ putchar ('\n');
+ }
+ }
+
+ /* At this point, fc is the current chunk, cie (if any) is set, and
+ we're about to interpret instructions for the chunk. */
+ /* ??? At present we need to do this always, since this sizes the
+ fc->col_type and fc->col_offset arrays, which we write into always.
+ We should probably split the interpreted and non-interpreted bits
+ into two different routines, since there's so much that doesn't
+ really overlap between them. */
+ if (1 || do_debug_frames_interp)
+ {
+ /* Start by making a pass over the chunk, allocating storage
+ and taking note of what registers are used. */
+ unsigned char *tmp = start;
+
+ while (start < block_end)
+ {
+ unsigned op, opa;
+ unsigned long reg, tmp;
+
+ op = *start++;
+ opa = op & 0x3f;
+ if (op & 0xc0)
+ op &= 0xc0;
+
+ /* Warning: if you add any more cases to this switch, be
+ sure to add them to the corresponding switch below. */
+ switch (op)
+ {
+ case DW_CFA_advance_loc:
+ break;
+ case DW_CFA_offset:
+ LEB ();
+ frame_need_space (fc, opa);
+ fc->col_type[opa] = DW_CFA_undefined;
+ break;
+ case DW_CFA_restore:
+ frame_need_space (fc, opa);
+ fc->col_type[opa] = DW_CFA_undefined;
+ break;
+ case DW_CFA_set_loc:
+ start += encoded_ptr_size;
+ break;
+ case DW_CFA_advance_loc1:
+ start += 1;
+ break;
+ case DW_CFA_advance_loc2:
+ start += 2;
+ break;
+ case DW_CFA_advance_loc4:
+ start += 4;
+ break;
+ case DW_CFA_offset_extended:
+ case DW_CFA_val_offset:
+ reg = LEB (); LEB ();
+ frame_need_space (fc, reg);
+ fc->col_type[reg] = DW_CFA_undefined;
+ break;
+ case DW_CFA_restore_extended:
+ reg = LEB ();
+ frame_need_space (fc, reg);
+ fc->col_type[reg] = DW_CFA_undefined;
+ break;
+ case DW_CFA_undefined:
+ reg = LEB ();
+ frame_need_space (fc, reg);
+ fc->col_type[reg] = DW_CFA_undefined;
+ break;
+ case DW_CFA_same_value:
+ reg = LEB ();
+ frame_need_space (fc, reg);
+ fc->col_type[reg] = DW_CFA_undefined;
+ break;
+ case DW_CFA_register:
+ reg = LEB (); LEB ();
+ frame_need_space (fc, reg);
+ fc->col_type[reg] = DW_CFA_undefined;
+ break;
+ case DW_CFA_def_cfa:
+ LEB (); LEB ();
+ break;
+ case DW_CFA_def_cfa_register:
+ LEB ();
+ break;
+ case DW_CFA_def_cfa_offset:
+ LEB ();
+ break;
+ case DW_CFA_def_cfa_expression:
+ tmp = LEB ();
+ start += tmp;
+ break;
+ case DW_CFA_expression:
+ case DW_CFA_val_expression:
+ reg = LEB ();
+ tmp = LEB ();
+ start += tmp;
+ frame_need_space (fc, reg);
+ fc->col_type[reg] = DW_CFA_undefined;
+ break;
+ case DW_CFA_offset_extended_sf:
+ case DW_CFA_val_offset_sf:
+ reg = LEB (); SLEB ();
+ frame_need_space (fc, reg);
+ fc->col_type[reg] = DW_CFA_undefined;
+ break;
+ case DW_CFA_def_cfa_sf:
+ LEB (); SLEB ();
+ break;
+ case DW_CFA_def_cfa_offset_sf:
+ SLEB ();
+ break;
+ case DW_CFA_MIPS_advance_loc8:
+ start += 8;
+ break;
+ case DW_CFA_GNU_args_size:
+ LEB ();
+ break;
+ case DW_CFA_GNU_negative_offset_extended:
+ reg = LEB (); LEB ();
+ frame_need_space (fc, reg);
+ fc->col_type[reg] = DW_CFA_undefined;
+
+ default:
+ break;
+ }
+ }
+ start = tmp;
+ }
+
+ /* Now we know what registers are used, make a second pass over
+ the chunk, this time actually printing out the info. */
+
+ while (start < block_end)
+ {
+ unsigned op, opa;
+ unsigned long ul, reg, roffs;
+ long l, ofs;
+ dwarf_vma vma;
+
+ op = *start++;
+ opa = op & 0x3f;
+ if (op & 0xc0)
+ op &= 0xc0;
+
+ /* Warning: if you add any more cases to this switch, be
+ sure to add them to the corresponding switch above. */
+ switch (op)
+ {
+ case DW_CFA_advance_loc:
+ if (do_debug_frames_interp)
+ frame_display_row (fc, &need_col_headers, &max_regs);
+ else
+ printf (" DW_CFA_advance_loc: %d to %08lx\n",
+ opa * fc->code_factor,
+ fc->pc_begin + opa * fc->code_factor);
+ fc->pc_begin += opa * fc->code_factor;
+ break;
+
+ case DW_CFA_offset:
+ roffs = LEB ();
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_offset: r%d at cfa%+ld\n",
+ opa, roffs * fc->data_factor);
+ fc->col_type[opa] = DW_CFA_offset;
+ fc->col_offset[opa] = roffs * fc->data_factor;
+ break;
+
+ case DW_CFA_restore:
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_restore: r%d\n", opa);
+ fc->col_type[opa] = cie->col_type[opa];
+ fc->col_offset[opa] = cie->col_offset[opa];
+ break;
+
+ case DW_CFA_set_loc:
+ vma = get_encoded_value (start, fc->fde_encoding);
+ if ((fc->fde_encoding & 0x70) == DW_EH_PE_pcrel
+ && !is_relocatable) {
+ vma += section->address + (start - section_start);
+ }
+ signed_value_hook(start,
+ size_of_encoded_value (fc->fde_encoding),
+ fc->fde_encoding & DW_EH_PE_signed,
+ vma);
+ start += encoded_ptr_size;
+ if (do_debug_frames_interp)
+ frame_display_row (fc, &need_col_headers, &max_regs);
+ else
+ printf (" DW_CFA_set_loc: %08lx\n", (unsigned long)vma);
+ fc->pc_begin = vma;
+ break;
+
+ case DW_CFA_advance_loc1:
+ ofs = byte_get (start, 1); start += 1;
+ if (do_debug_frames_interp)
+ frame_display_row (fc, &need_col_headers, &max_regs);
+ else
+ printf (" DW_CFA_advance_loc1: %ld to %08lx\n",
+ ofs * fc->code_factor,
+ fc->pc_begin + ofs * fc->code_factor);
+ fc->pc_begin += ofs * fc->code_factor;
+ break;
+
+ case DW_CFA_advance_loc2:
+ ofs = byte_get (start, 2); start += 2;
+ if (do_debug_frames_interp)
+ frame_display_row (fc, &need_col_headers, &max_regs);
+ else
+ printf (" DW_CFA_advance_loc2: %ld to %08lx\n",
+ ofs * fc->code_factor,
+ fc->pc_begin + ofs * fc->code_factor);
+ fc->pc_begin += ofs * fc->code_factor;
+ break;
+
+ case DW_CFA_advance_loc4:
+ ofs = byte_get (start, 4); start += 4;
+ if (do_debug_frames_interp)
+ frame_display_row (fc, &need_col_headers, &max_regs);
+ else
+ printf (" DW_CFA_advance_loc4: %ld to %08lx\n",
+ ofs * fc->code_factor,
+ fc->pc_begin + ofs * fc->code_factor);
+ fc->pc_begin += ofs * fc->code_factor;
+ break;
+
+ case DW_CFA_offset_extended:
+ reg = LEB ();
+ roffs = LEB ();
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_offset_extended: r%ld at cfa%+ld\n",
+ reg, roffs * fc->data_factor);
+ fc->col_type[reg] = DW_CFA_offset;
+ fc->col_offset[reg] = roffs * fc->data_factor;
+ break;
+
+ case DW_CFA_val_offset:
+ reg = LEB ();
+ roffs = LEB ();
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_val_offset: r%ld at cfa%+ld\n",
+ reg, roffs * fc->data_factor);
+ fc->col_type[reg] = DW_CFA_val_offset;
+ fc->col_offset[reg] = roffs * fc->data_factor;
+ break;
+
+ case DW_CFA_restore_extended:
+ reg = LEB ();
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_restore_extended: r%ld\n", reg);
+ fc->col_type[reg] = cie->col_type[reg];
+ fc->col_offset[reg] = cie->col_offset[reg];
+ break;
+
+ case DW_CFA_undefined:
+ reg = LEB ();
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_undefined: r%ld\n", reg);
+ fc->col_type[reg] = DW_CFA_undefined;
+ fc->col_offset[reg] = 0;
+ break;
+
+ case DW_CFA_same_value:
+ reg = LEB ();
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_same_value: r%ld\n", reg);
+ fc->col_type[reg] = DW_CFA_same_value;
+ fc->col_offset[reg] = 0;
+ break;
+
+ case DW_CFA_register:
+ reg = LEB ();
+ roffs = LEB ();
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_register: r%ld in r%ld\n", reg, roffs);
+ fc->col_type[reg] = DW_CFA_register;
+ fc->col_offset[reg] = roffs;
+ break;
+
+ case DW_CFA_remember_state:
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_remember_state\n");
+ rs = xmalloc (sizeof (Frame_Chunk));
+ rs->ncols = fc->ncols;
+ rs->col_type = xcmalloc (rs->ncols, sizeof (short int));
+ rs->col_offset = xcmalloc (rs->ncols, sizeof (int));
+ memcpy (rs->col_type, fc->col_type, rs->ncols);
+ memcpy (rs->col_offset, fc->col_offset, rs->ncols * sizeof (int));
+ rs->next = remembered_state;
+ remembered_state = rs;
+ break;
+
+ case DW_CFA_restore_state:
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_restore_state\n");
+ rs = remembered_state;
+ if (rs)
+ {
+ remembered_state = rs->next;
+ frame_need_space (fc, rs->ncols-1);
+ memcpy (fc->col_type, rs->col_type, rs->ncols);
+ memcpy (fc->col_offset, rs->col_offset,
+ rs->ncols * sizeof (int));
+ free (rs->col_type);
+ free (rs->col_offset);
+ free (rs);
+ }
+ else if (do_debug_frames_interp)
+ printf ("Mismatched DW_CFA_restore_state\n");
+ break;
+
+ case DW_CFA_def_cfa:
+ fc->cfa_reg = LEB ();
+ fc->cfa_offset = LEB ();
+ fc->cfa_exp = 0;
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_def_cfa: r%d ofs %d\n",
+ fc->cfa_reg, fc->cfa_offset);
+ break;
+
+ case DW_CFA_def_cfa_register:
+ fc->cfa_reg = LEB ();
+ fc->cfa_exp = 0;
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_def_cfa_reg: r%d\n", fc->cfa_reg);
+ break;
+
+ case DW_CFA_def_cfa_offset:
+ fc->cfa_offset = LEB ();
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_def_cfa_offset: %d\n", fc->cfa_offset);
+ break;
+
+ case DW_CFA_nop:
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_nop\n");
+ break;
+
+ case DW_CFA_def_cfa_expression:
+ ul = LEB ();
+ if (! do_debug_frames_interp)
+ {
+ printf (" DW_CFA_def_cfa_expression (");
+ decode_location_expression (start, eh_addr_size, ul, 0);
+ printf (")\n");
+ }
+ fc->cfa_exp = 1;
+ start += ul;
+ break;
+
+ case DW_CFA_expression:
+ reg = LEB ();
+ ul = LEB ();
+ if (! do_debug_frames_interp)
+ {
+ printf (" DW_CFA_expression: r%ld (", reg);
+ decode_location_expression (start, eh_addr_size, ul, 0);
+ printf (")\n");
+ }
+ fc->col_type[reg] = DW_CFA_expression;
+ start += ul;
+ break;
+
+ case DW_CFA_val_expression:
+ reg = LEB ();
+ ul = LEB ();
+ if (! do_debug_frames_interp)
+ {
+ printf (" DW_CFA_val_expression: r%ld (", reg);
+ decode_location_expression (start, eh_addr_size, ul, 0);
+ printf (")\n");
+ }
+ fc->col_type[reg] = DW_CFA_val_expression;
+ start += ul;
+ break;
+
+ case DW_CFA_offset_extended_sf:
+ reg = LEB ();
+ l = SLEB ();
+ frame_need_space (fc, reg);
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_offset_extended_sf: r%ld at cfa%+ld\n",
+ reg, l * fc->data_factor);
+ fc->col_type[reg] = DW_CFA_offset;
+ fc->col_offset[reg] = l * fc->data_factor;
+ break;
+
+ case DW_CFA_val_offset_sf:
+ reg = LEB ();
+ l = SLEB ();
+ frame_need_space (fc, reg);
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_val_offset_sf: r%ld at cfa%+ld\n",
+ reg, l * fc->data_factor);
+ fc->col_type[reg] = DW_CFA_val_offset;
+ fc->col_offset[reg] = l * fc->data_factor;
+ break;
+
+ case DW_CFA_def_cfa_sf:
+ fc->cfa_reg = LEB ();
+ fc->cfa_offset = SLEB ();
+ fc->cfa_offset = fc->cfa_offset * fc->data_factor;
+ fc->cfa_exp = 0;
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_def_cfa_sf: r%d ofs %d\n",
+ fc->cfa_reg, fc->cfa_offset);
+ break;
+
+ case DW_CFA_def_cfa_offset_sf:
+ fc->cfa_offset = SLEB ();
+ fc->cfa_offset = fc->cfa_offset * fc->data_factor;
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_def_cfa_offset_sf: %d\n", fc->cfa_offset);
+ break;
+
+ case DW_CFA_MIPS_advance_loc8:
+ ofs = byte_get (start, 8); start += 8;
+ if (do_debug_frames_interp)
+ frame_display_row (fc, &need_col_headers, &max_regs);
+ else
+ printf (" DW_CFA_MIPS_advance_loc8: %ld to %08lx\n",
+ ofs * fc->code_factor,
+ fc->pc_begin + ofs * fc->code_factor);
+ fc->pc_begin += ofs * fc->code_factor;
+ break;
+
+ case DW_CFA_GNU_window_save:
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_GNU_window_save\n");
+ break;
+
+ case DW_CFA_GNU_args_size:
+ ul = LEB ();
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_GNU_args_size: %ld\n", ul);
+ break;
+
+ case DW_CFA_GNU_negative_offset_extended:
+ reg = LEB ();
+ l = - LEB ();
+ frame_need_space (fc, reg);
+ if (! do_debug_frames_interp)
+ printf (" DW_CFA_GNU_negative_offset_extended: r%ld at cfa%+ld\n",
+ reg, l * fc->data_factor);
+ fc->col_type[reg] = DW_CFA_offset;
+ fc->col_offset[reg] = l * fc->data_factor;
+ break;
+
+ default:
+ warn (_("unsupported or unknown DW_CFA_%d\n"), op);
+ start = block_end;
+ }
+ }
+
+ if (do_debug_frames_interp)
+ frame_display_row (fc, &need_col_headers, &max_regs);
+
+ start = block_end;
+ }
+
+ printf ("\n");
+
+ return 1;
+}
+
+#undef GET
+#undef LEB
+#undef SLEB
+
+static int
+display_debug_not_supported (struct dwarf_section *section,
+ void *file ATTRIBUTE_UNUSED)
+{
+ printf (_("Displaying the debug contents of section %s is not yet supported.\n"),
+ section->name);
+
+ return 1;
+}
+
+static void *
+cmalloc (size_t nmemb, size_t size)
+{
+ /* Check for overflow. */
+ if (nmemb >= ~(size_t) 0 / size)
+ return NULL;
+ else
+ return malloc (nmemb * size);
+}
+
+static void *
+xmalloc(size_t size)
+{
+ return malloc(size);
+}
+
+static void *
+xrealloc (p, n)
+ void *p;
+ size_t n;
+{
+ return realloc (p, n);
+}
+
+static void *
+xcmalloc (size_t nmemb, size_t size)
+{
+ /* Check for overflow. */
+ if (nmemb >= ~(size_t) 0 / size)
+ return NULL;
+ else
+ return xmalloc (nmemb * size);
+}
+
+static void *
+xcrealloc (void *ptr, size_t nmemb, size_t size)
+{
+ /* Check for overflow. */
+ if (nmemb >= ~(size_t) 0 / size)
+ return NULL;
+ else
+ return xrealloc (ptr, nmemb * size);
+}
+
+static void
+error (const char *message, ...)
+{
+ va_list args;
+
+ va_start (args, message);
+ fprintf (stderr, _("%s: Warning: "), program_name);
+ vfprintf (stderr, message, args);
+ va_end (args);
+}
+
+static void
+warn (const char *message, ...)
+{
+ va_list args;
+
+ va_start (args, message);
+ fprintf (stderr, _("%s: Warning: "), program_name);
+ vfprintf (stderr, message, args);
+ va_end (args);
+}
+
+void
+free_debug_memory (void)
+{
+ enum dwarf_section_display_enum i;
+
+ free_abbrevs ();
+
+ for (i = 0; i < max; i++)
+ free_debug_section (i);
+
+ if (debug_information)
+ {
+ for (i = 0; i < num_debug_info_entries; i++)
+ {
+ if (!debug_information [i].max_loc_offsets)
+ {
+ free (debug_information [i].loc_offsets);
+ free (debug_information [i].have_frame_base);
+ }
+ if (!debug_information [i].max_range_lists)
+ free (debug_information [i].range_lists);
+ }
+ free (debug_information);
+ debug_information = NULL;
+ num_debug_info_entries = 0;
+ }
+
+}
+
+struct dwarf_section_display debug_displays[] =
+{
+ { { ".debug_abbrev", NULL, 0, 0 },
+ display_debug_abbrev, 0, 0 },
+ { { ".debug_aranges", NULL, 0, 0 },
+ display_debug_aranges, 0, 0 },
+ { { ".debug_frame", NULL, 0, 0 },
+ display_debug_frames, 1, 0 },
+ { { ".debug_info", NULL, 0, 0 },
+ display_debug_info, 1, 0 },
+ { { ".debug_line", NULL, 0, 0 },
+ display_debug_lines, 0, 0 },
+ { { ".debug_pubnames", NULL, 0, 0 },
+ display_debug_pubnames, 0, 0 },
+ { { ".eh_frame", NULL, 0, 0 },
+ display_debug_frames, 1, 1 },
+ { { ".debug_macinfo", NULL, 0, 0 },
+ display_debug_macinfo, 0, 0 },
+ { { ".debug_str", NULL, 0, 0 },
+ display_debug_str, 0, 0 },
+ { { ".debug_loc", NULL, 0, 0 },
+ display_debug_loc, 0, 0 },
+ { { ".debug_pubtypes", NULL, 0, 0 },
+ display_debug_pubnames, 0, 0 },
+ { { ".debug_ranges", NULL, 0, 0 },
+ display_debug_ranges, 0, 0 },
+ { { ".debug_static_func", NULL, 0, 0 },
+ display_debug_not_supported, 0, 0 },
+ { { ".debug_static_vars", NULL, 0, 0 },
+ display_debug_not_supported, 0, 0 },
+ { { ".debug_types", NULL, 0, 0 },
+ display_debug_not_supported, 0, 0 },
+ { { ".debug_weaknames", NULL, 0, 0 },
+ display_debug_not_supported, 0, 0 }
+};
+
+/* Reinitializes all static and global variables owned by this module except for
+ debug_display()
+*/
+void init_dwarf_variables(void)
+{
+ have_frame_base = 0;
+ need_base_address = 0;
+ last_pointer_size = 0;
+ warned_about_missing_comp_units = FALSE;
+ num_debug_info_entries = 0;
+ debug_information = NULL;
+ eh_addr_size = 0;
+ is_relocatable = 0;
+
+ do_debug_info = 0;
+ do_debug_abbrevs = 0;
+ do_debug_lines = 0;
+ do_debug_pubnames = 0;
+ do_debug_aranges = 0;
+ do_debug_ranges = 0;
+ do_debug_frames = 0;
+ do_debug_frames_interp = 0;
+ do_debug_macinfo = 0;
+ do_debug_str = 0;
+ do_debug_loc = 0;
+
+ byte_get = NULL;
+
+ memset(&state_machine_regs, 0, sizeof(state_machine_regs));
+
+ first_abbrev = NULL;
+ last_abbrev = NULL;
+}
diff --git a/dwarf.h b/dwarf.h
new file mode 100644
index 0000000..d362eea
--- /dev/null
+++ b/dwarf.h
@@ -0,0 +1,122 @@
+/* dwwrf.h - DWARF support header file
+ Copyright 2005
+ Free Software Foundation, Inc.
+
+This file is part of GNU Binutils.
+
+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. */
+
+#include "dwarf2.h"
+
+typedef unsigned long dwarf_vma;
+typedef unsigned long dwarf_size_type;
+
+struct dwarf_section
+{
+ const char *name;
+ unsigned char *start;
+ dwarf_vma address;
+ dwarf_size_type size;
+};
+
+/* A structure containing the name of a debug section
+ and a pointer to a function that can decode it. */
+struct dwarf_section_display
+{
+ struct dwarf_section section;
+ int (*display) (struct dwarf_section *, void *);
+ unsigned int relocate : 1;
+ unsigned int eh_frame : 1;
+};
+
+enum dwarf_section_display_enum {
+ abbrev = 0,
+ aranges,
+ frame,
+ info,
+ line,
+ pubnames,
+ eh_frame,
+ macinfo,
+ str,
+ loc,
+ pubtypes,
+ ranges,
+ static_func,
+ static_vars,
+ types,
+ weaknames,
+ max
+};
+
+extern struct dwarf_section_display debug_displays [];
+
+/* This structure records the information that
+ we extract from the.debug_info section. */
+typedef struct
+{
+ unsigned int pointer_size;
+ unsigned long cu_offset;
+ unsigned long base_address;
+ /* This is an array of offsets to the location list table. */
+ unsigned long *loc_offsets;
+ int *have_frame_base;
+ unsigned int num_loc_offsets;
+ unsigned int max_loc_offsets;
+#ifdef SORT_LOCATION_LIST_OFFSETS
+ unsigned long last_loc_offset; /* used to determine whether loc offsets are sorted */
+#endif
+ unsigned long *range_lists;
+ unsigned int num_range_lists;
+ unsigned int max_range_lists;
+}
+debug_info;
+
+extern dwarf_vma (*byte_get) (unsigned char *, int);
+extern dwarf_vma byte_get_little_endian (unsigned char *, int);
+extern dwarf_vma byte_get_big_endian (unsigned char *, int);
+
+extern dwarf_vma eh_addr_size;
+extern int is_relocatable;
+
+extern int do_debug_info;
+extern int do_debug_abbrevs;
+extern int do_debug_lines;
+extern int do_debug_pubnames;
+extern int do_debug_aranges;
+extern int do_debug_ranges;
+extern int do_debug_frames;
+extern int do_debug_frames_interp;
+extern int do_debug_macinfo;
+extern int do_debug_str;
+extern int do_debug_loc;
+
+extern int load_debug_section (enum dwarf_section_display_enum,
+ void *);
+extern void free_debug_section (enum dwarf_section_display_enum);
+
+extern void free_debug_memory (void);
+
+extern void base_value_pair_hook(void *data, int size,
+ int base, int begin, int end);
+
+extern void value_hook(void *data, int size, int val);
+
+extern void signed_value_hook(void *data,
+ int pointer_size,
+ int is_signed,
+ int val);
+
+extern void init_dwarf_variables(void);
diff --git a/dwarf2.h b/dwarf2.h
new file mode 100644
index 0000000..264952a
--- /dev/null
+++ b/dwarf2.h
@@ -0,0 +1,836 @@
+/* Declarations and definitions of codes relating to the DWARF2 and
+ DWARF3 symbolic debugging information formats.
+ Copyright (C) 1992, 1993, 1995, 1996, 1997, 1999, 2000, 2001, 2002,
+ 2003, 2004, 2005, 2006 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 2, 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.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* 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. */
+
+/* This file is shared between GCC and GDB, and should not contain
+ prototypes. */
+
+#ifndef _ELF_DWARF2_H
+#define _ELF_DWARF2_H
+
+/* Structure found in the .debug_line section. */
+typedef struct
+{
+ unsigned char li_length [4];
+ unsigned char li_version [2];
+ unsigned char li_prologue_length [4];
+ unsigned char li_min_insn_length [1];
+ unsigned char li_default_is_stmt [1];
+ unsigned char li_line_base [1];
+ unsigned char li_line_range [1];
+ unsigned char li_opcode_base [1];
+}
+DWARF2_External_LineInfo;
+
+typedef struct
+{
+ unsigned long li_length;
+ unsigned short li_version;
+ unsigned int li_prologue_length;
+ unsigned char li_min_insn_length;
+ unsigned char li_default_is_stmt;
+ int li_line_base;
+ unsigned char li_line_range;
+ unsigned char li_opcode_base;
+}
+DWARF2_Internal_LineInfo;
+
+/* Structure found in .debug_pubnames section. */
+typedef struct
+{
+ unsigned char pn_length [4];
+ unsigned char pn_version [2];
+ unsigned char pn_offset [4];
+ unsigned char pn_size [4];
+}
+DWARF2_External_PubNames;
+
+typedef struct
+{
+ unsigned long pn_length;
+ unsigned short pn_version;
+ unsigned long pn_offset;
+ unsigned long pn_size;
+}
+DWARF2_Internal_PubNames;
+
+/* Structure found in .debug_info section. */
+typedef struct
+{
+ unsigned char cu_length [4];
+ unsigned char cu_version [2];
+ unsigned char cu_abbrev_offset [4];
+ unsigned char cu_pointer_size [1];
+}
+DWARF2_External_CompUnit;
+
+typedef struct
+{
+ unsigned long cu_length;
+ unsigned short cu_version;
+ unsigned long cu_abbrev_offset;
+ unsigned char cu_pointer_size;
+}
+DWARF2_Internal_CompUnit;
+
+typedef struct
+{
+ unsigned char ar_length [4];
+ unsigned char ar_version [2];
+ unsigned char ar_info_offset [4];
+ unsigned char ar_pointer_size [1];
+ unsigned char ar_segment_size [1];
+}
+DWARF2_External_ARange;
+
+typedef struct
+{
+ unsigned long ar_length;
+ unsigned short ar_version;
+ unsigned long ar_info_offset;
+ unsigned char ar_pointer_size;
+ unsigned char ar_segment_size;
+}
+DWARF2_Internal_ARange;
+
+
+/* Tag names and codes. */
+enum dwarf_tag
+ {
+ DW_TAG_padding = 0x00,
+ DW_TAG_array_type = 0x01,
+ DW_TAG_class_type = 0x02,
+ DW_TAG_entry_point = 0x03,
+ DW_TAG_enumeration_type = 0x04,
+ DW_TAG_formal_parameter = 0x05,
+ DW_TAG_imported_declaration = 0x08,
+ DW_TAG_label = 0x0a,
+ DW_TAG_lexical_block = 0x0b,
+ DW_TAG_member = 0x0d,
+ DW_TAG_pointer_type = 0x0f,
+ DW_TAG_reference_type = 0x10,
+ DW_TAG_compile_unit = 0x11,
+ DW_TAG_string_type = 0x12,
+ DW_TAG_structure_type = 0x13,
+ DW_TAG_subroutine_type = 0x15,
+ DW_TAG_typedef = 0x16,
+ DW_TAG_union_type = 0x17,
+ DW_TAG_unspecified_parameters = 0x18,
+ DW_TAG_variant = 0x19,
+ DW_TAG_common_block = 0x1a,
+ DW_TAG_common_inclusion = 0x1b,
+ DW_TAG_inheritance = 0x1c,
+ DW_TAG_inlined_subroutine = 0x1d,
+ DW_TAG_module = 0x1e,
+ DW_TAG_ptr_to_member_type = 0x1f,
+ DW_TAG_set_type = 0x20,
+ DW_TAG_subrange_type = 0x21,
+ DW_TAG_with_stmt = 0x22,
+ DW_TAG_access_declaration = 0x23,
+ DW_TAG_base_type = 0x24,
+ DW_TAG_catch_block = 0x25,
+ DW_TAG_const_type = 0x26,
+ DW_TAG_constant = 0x27,
+ DW_TAG_enumerator = 0x28,
+ DW_TAG_file_type = 0x29,
+ DW_TAG_friend = 0x2a,
+ DW_TAG_namelist = 0x2b,
+ DW_TAG_namelist_item = 0x2c,
+ DW_TAG_packed_type = 0x2d,
+ DW_TAG_subprogram = 0x2e,
+ DW_TAG_template_type_param = 0x2f,
+ DW_TAG_template_value_param = 0x30,
+ DW_TAG_thrown_type = 0x31,
+ DW_TAG_try_block = 0x32,
+ DW_TAG_variant_part = 0x33,
+ DW_TAG_variable = 0x34,
+ DW_TAG_volatile_type = 0x35,
+ /* DWARF 3. */
+ DW_TAG_dwarf_procedure = 0x36,
+ DW_TAG_restrict_type = 0x37,
+ DW_TAG_interface_type = 0x38,
+ DW_TAG_namespace = 0x39,
+ DW_TAG_imported_module = 0x3a,
+ DW_TAG_unspecified_type = 0x3b,
+ DW_TAG_partial_unit = 0x3c,
+ DW_TAG_imported_unit = 0x3d,
+ DW_TAG_condition = 0x3f,
+ DW_TAG_shared_type = 0x40,
+ /* SGI/MIPS Extensions. */
+ DW_TAG_MIPS_loop = 0x4081,
+ /* HP extensions. See: ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz . */
+ DW_TAG_HP_array_descriptor = 0x4090,
+ /* GNU extensions. */
+ DW_TAG_format_label = 0x4101, /* For FORTRAN 77 and Fortran 90. */
+ DW_TAG_function_template = 0x4102, /* For C++. */
+ DW_TAG_class_template = 0x4103, /* For C++. */
+ DW_TAG_GNU_BINCL = 0x4104,
+ DW_TAG_GNU_EINCL = 0x4105,
+ /* Extensions for UPC. See: http://upc.gwu.edu/~upc. */
+ DW_TAG_upc_shared_type = 0x8765,
+ DW_TAG_upc_strict_type = 0x8766,
+ DW_TAG_upc_relaxed_type = 0x8767,
+ /* PGI (STMicroelectronics) extensions. No documentation available. */
+ DW_TAG_PGI_kanji_type = 0xA000,
+ DW_TAG_PGI_interface_block = 0xA020
+ };
+
+#define DW_TAG_lo_user 0x4080
+#define DW_TAG_hi_user 0xffff
+
+/* Flag that tells whether entry has a child or not. */
+#define DW_children_no 0
+#define DW_children_yes 1
+
+/* Form names and codes. */
+enum dwarf_form
+ {
+ DW_FORM_addr = 0x01,
+ DW_FORM_block2 = 0x03,
+ DW_FORM_block4 = 0x04,
+ DW_FORM_data2 = 0x05,
+ DW_FORM_data4 = 0x06,
+ DW_FORM_data8 = 0x07,
+ DW_FORM_string = 0x08,
+ DW_FORM_block = 0x09,
+ DW_FORM_block1 = 0x0a,
+ DW_FORM_data1 = 0x0b,
+ DW_FORM_flag = 0x0c,
+ DW_FORM_sdata = 0x0d,
+ DW_FORM_strp = 0x0e,
+ DW_FORM_udata = 0x0f,
+ DW_FORM_ref_addr = 0x10,
+ DW_FORM_ref1 = 0x11,
+ DW_FORM_ref2 = 0x12,
+ DW_FORM_ref4 = 0x13,
+ DW_FORM_ref8 = 0x14,
+ DW_FORM_ref_udata = 0x15,
+ DW_FORM_indirect = 0x16
+ };
+
+/* Attribute names and codes. */
+enum dwarf_attribute
+ {
+ DW_AT_sibling = 0x01,
+ DW_AT_location = 0x02,
+ DW_AT_name = 0x03,
+ DW_AT_ordering = 0x09,
+ DW_AT_subscr_data = 0x0a,
+ DW_AT_byte_size = 0x0b,
+ DW_AT_bit_offset = 0x0c,
+ DW_AT_bit_size = 0x0d,
+ DW_AT_element_list = 0x0f,
+ DW_AT_stmt_list = 0x10,
+ DW_AT_low_pc = 0x11,
+ DW_AT_high_pc = 0x12,
+ DW_AT_language = 0x13,
+ DW_AT_member = 0x14,
+ DW_AT_discr = 0x15,
+ DW_AT_discr_value = 0x16,
+ DW_AT_visibility = 0x17,
+ DW_AT_import = 0x18,
+ DW_AT_string_length = 0x19,
+ DW_AT_common_reference = 0x1a,
+ DW_AT_comp_dir = 0x1b,
+ DW_AT_const_value = 0x1c,
+ DW_AT_containing_type = 0x1d,
+ DW_AT_default_value = 0x1e,
+ DW_AT_inline = 0x20,
+ DW_AT_is_optional = 0x21,
+ DW_AT_lower_bound = 0x22,
+ DW_AT_producer = 0x25,
+ DW_AT_prototyped = 0x27,
+ DW_AT_return_addr = 0x2a,
+ DW_AT_start_scope = 0x2c,
+ DW_AT_stride_size = 0x2e,
+ DW_AT_upper_bound = 0x2f,
+ DW_AT_abstract_origin = 0x31,
+ DW_AT_accessibility = 0x32,
+ DW_AT_address_class = 0x33,
+ DW_AT_artificial = 0x34,
+ DW_AT_base_types = 0x35,
+ DW_AT_calling_convention = 0x36,
+ DW_AT_count = 0x37,
+ DW_AT_data_member_location = 0x38,
+ DW_AT_decl_column = 0x39,
+ DW_AT_decl_file = 0x3a,
+ DW_AT_decl_line = 0x3b,
+ DW_AT_declaration = 0x3c,
+ DW_AT_discr_list = 0x3d,
+ DW_AT_encoding = 0x3e,
+ DW_AT_external = 0x3f,
+ DW_AT_frame_base = 0x40,
+ DW_AT_friend = 0x41,
+ DW_AT_identifier_case = 0x42,
+ DW_AT_macro_info = 0x43,
+ DW_AT_namelist_items = 0x44,
+ DW_AT_priority = 0x45,
+ DW_AT_segment = 0x46,
+ DW_AT_specification = 0x47,
+ DW_AT_static_link = 0x48,
+ DW_AT_type = 0x49,
+ DW_AT_use_location = 0x4a,
+ DW_AT_variable_parameter = 0x4b,
+ DW_AT_virtuality = 0x4c,
+ DW_AT_vtable_elem_location = 0x4d,
+ /* DWARF 3 values. */
+ DW_AT_allocated = 0x4e,
+ DW_AT_associated = 0x4f,
+ DW_AT_data_location = 0x50,
+ DW_AT_stride = 0x51,
+ DW_AT_entry_pc = 0x52,
+ DW_AT_use_UTF8 = 0x53,
+ DW_AT_extension = 0x54,
+ DW_AT_ranges = 0x55,
+ DW_AT_trampoline = 0x56,
+ DW_AT_call_column = 0x57,
+ DW_AT_call_file = 0x58,
+ DW_AT_call_line = 0x59,
+ DW_AT_description = 0x5a,
+ DW_AT_binary_scale = 0x5b,
+ DW_AT_decimal_scale = 0x5c,
+ DW_AT_small = 0x5d,
+ DW_AT_decimal_sign = 0x5e,
+ DW_AT_digit_count = 0x5f,
+ DW_AT_picture_string = 0x60,
+ DW_AT_mutable = 0x61,
+ DW_AT_threads_scaled = 0x62,
+ DW_AT_explicit = 0x63,
+ DW_AT_object_pointer = 0x64,
+ DW_AT_endianity = 0x65,
+ DW_AT_elemental = 0x66,
+ DW_AT_pure = 0x67,
+ DW_AT_recursive = 0x68,
+ /* SGI/MIPS extensions. */
+ DW_AT_MIPS_fde = 0x2001,
+ DW_AT_MIPS_loop_begin = 0x2002,
+ DW_AT_MIPS_tail_loop_begin = 0x2003,
+ DW_AT_MIPS_epilog_begin = 0x2004,
+ DW_AT_MIPS_loop_unroll_factor = 0x2005,
+ DW_AT_MIPS_software_pipeline_depth = 0x2006,
+ DW_AT_MIPS_linkage_name = 0x2007,
+ DW_AT_MIPS_stride = 0x2008,
+ DW_AT_MIPS_abstract_name = 0x2009,
+ DW_AT_MIPS_clone_origin = 0x200a,
+ DW_AT_MIPS_has_inlines = 0x200b,
+ /* HP extensions. */
+ DW_AT_HP_block_index = 0x2000,
+ DW_AT_HP_unmodifiable = 0x2001, /* Same as DW_AT_MIPS_fde. */
+ DW_AT_HP_actuals_stmt_list = 0x2010,
+ DW_AT_HP_proc_per_section = 0x2011,
+ DW_AT_HP_raw_data_ptr = 0x2012,
+ DW_AT_HP_pass_by_reference = 0x2013,
+ DW_AT_HP_opt_level = 0x2014,
+ DW_AT_HP_prof_version_id = 0x2015,
+ DW_AT_HP_opt_flags = 0x2016,
+ DW_AT_HP_cold_region_low_pc = 0x2017,
+ DW_AT_HP_cold_region_high_pc = 0x2018,
+ DW_AT_HP_all_variables_modifiable = 0x2019,
+ DW_AT_HP_linkage_name = 0x201a,
+ DW_AT_HP_prof_flags = 0x201b, /* In comp unit of procs_info for -g. */
+ /* GNU extensions. */
+ DW_AT_sf_names = 0x2101,
+ DW_AT_src_info = 0x2102,
+ DW_AT_mac_info = 0x2103,
+ DW_AT_src_coords = 0x2104,
+ DW_AT_body_begin = 0x2105,
+ DW_AT_body_end = 0x2106,
+ DW_AT_GNU_vector = 0x2107,
+ /* VMS extensions. */
+ DW_AT_VMS_rtnbeg_pd_address = 0x2201,
+ /* UPC extension. */
+ DW_AT_upc_threads_scaled = 0x3210,
+ /* PGI (STMicroelectronics) extensions. */
+ DW_AT_PGI_lbase = 0x3a00,
+ DW_AT_PGI_soffset = 0x3a01,
+ DW_AT_PGI_lstride = 0x3a02
+ };
+
+#define DW_AT_lo_user 0x2000 /* Implementation-defined range start. */
+#define DW_AT_hi_user 0x3ff0 /* Implementation-defined range end. */
+
+/* Location atom names and codes. */
+enum dwarf_location_atom
+ {
+ DW_OP_addr = 0x03,
+ DW_OP_deref = 0x06,
+ DW_OP_const1u = 0x08,
+ DW_OP_const1s = 0x09,
+ DW_OP_const2u = 0x0a,
+ DW_OP_const2s = 0x0b,
+ DW_OP_const4u = 0x0c,
+ DW_OP_const4s = 0x0d,
+ DW_OP_const8u = 0x0e,
+ DW_OP_const8s = 0x0f,
+ DW_OP_constu = 0x10,
+ DW_OP_consts = 0x11,
+ DW_OP_dup = 0x12,
+ DW_OP_drop = 0x13,
+ DW_OP_over = 0x14,
+ DW_OP_pick = 0x15,
+ DW_OP_swap = 0x16,
+ DW_OP_rot = 0x17,
+ DW_OP_xderef = 0x18,
+ DW_OP_abs = 0x19,
+ DW_OP_and = 0x1a,
+ DW_OP_div = 0x1b,
+ DW_OP_minus = 0x1c,
+ DW_OP_mod = 0x1d,
+ DW_OP_mul = 0x1e,
+ DW_OP_neg = 0x1f,
+ DW_OP_not = 0x20,
+ DW_OP_or = 0x21,
+ DW_OP_plus = 0x22,
+ DW_OP_plus_uconst = 0x23,
+ DW_OP_shl = 0x24,
+ DW_OP_shr = 0x25,
+ DW_OP_shra = 0x26,
+ DW_OP_xor = 0x27,
+ DW_OP_bra = 0x28,
+ DW_OP_eq = 0x29,
+ DW_OP_ge = 0x2a,
+ DW_OP_gt = 0x2b,
+ DW_OP_le = 0x2c,
+ DW_OP_lt = 0x2d,
+ DW_OP_ne = 0x2e,
+ DW_OP_skip = 0x2f,
+ DW_OP_lit0 = 0x30,
+ DW_OP_lit1 = 0x31,
+ DW_OP_lit2 = 0x32,
+ DW_OP_lit3 = 0x33,
+ DW_OP_lit4 = 0x34,
+ DW_OP_lit5 = 0x35,
+ DW_OP_lit6 = 0x36,
+ DW_OP_lit7 = 0x37,
+ DW_OP_lit8 = 0x38,
+ DW_OP_lit9 = 0x39,
+ DW_OP_lit10 = 0x3a,
+ DW_OP_lit11 = 0x3b,
+ DW_OP_lit12 = 0x3c,
+ DW_OP_lit13 = 0x3d,
+ DW_OP_lit14 = 0x3e,
+ DW_OP_lit15 = 0x3f,
+ DW_OP_lit16 = 0x40,
+ DW_OP_lit17 = 0x41,
+ DW_OP_lit18 = 0x42,
+ DW_OP_lit19 = 0x43,
+ DW_OP_lit20 = 0x44,
+ DW_OP_lit21 = 0x45,
+ DW_OP_lit22 = 0x46,
+ DW_OP_lit23 = 0x47,
+ DW_OP_lit24 = 0x48,
+ DW_OP_lit25 = 0x49,
+ DW_OP_lit26 = 0x4a,
+ DW_OP_lit27 = 0x4b,
+ DW_OP_lit28 = 0x4c,
+ DW_OP_lit29 = 0x4d,
+ DW_OP_lit30 = 0x4e,
+ DW_OP_lit31 = 0x4f,
+ DW_OP_reg0 = 0x50,
+ DW_OP_reg1 = 0x51,
+ DW_OP_reg2 = 0x52,
+ DW_OP_reg3 = 0x53,
+ DW_OP_reg4 = 0x54,
+ DW_OP_reg5 = 0x55,
+ DW_OP_reg6 = 0x56,
+ DW_OP_reg7 = 0x57,
+ DW_OP_reg8 = 0x58,
+ DW_OP_reg9 = 0x59,
+ DW_OP_reg10 = 0x5a,
+ DW_OP_reg11 = 0x5b,
+ DW_OP_reg12 = 0x5c,
+ DW_OP_reg13 = 0x5d,
+ DW_OP_reg14 = 0x5e,
+ DW_OP_reg15 = 0x5f,
+ DW_OP_reg16 = 0x60,
+ DW_OP_reg17 = 0x61,
+ DW_OP_reg18 = 0x62,
+ DW_OP_reg19 = 0x63,
+ DW_OP_reg20 = 0x64,
+ DW_OP_reg21 = 0x65,
+ DW_OP_reg22 = 0x66,
+ DW_OP_reg23 = 0x67,
+ DW_OP_reg24 = 0x68,
+ DW_OP_reg25 = 0x69,
+ DW_OP_reg26 = 0x6a,
+ DW_OP_reg27 = 0x6b,
+ DW_OP_reg28 = 0x6c,
+ DW_OP_reg29 = 0x6d,
+ DW_OP_reg30 = 0x6e,
+ DW_OP_reg31 = 0x6f,
+ DW_OP_breg0 = 0x70,
+ DW_OP_breg1 = 0x71,
+ DW_OP_breg2 = 0x72,
+ DW_OP_breg3 = 0x73,
+ DW_OP_breg4 = 0x74,
+ DW_OP_breg5 = 0x75,
+ DW_OP_breg6 = 0x76,
+ DW_OP_breg7 = 0x77,
+ DW_OP_breg8 = 0x78,
+ DW_OP_breg9 = 0x79,
+ DW_OP_breg10 = 0x7a,
+ DW_OP_breg11 = 0x7b,
+ DW_OP_breg12 = 0x7c,
+ DW_OP_breg13 = 0x7d,
+ DW_OP_breg14 = 0x7e,
+ DW_OP_breg15 = 0x7f,
+ DW_OP_breg16 = 0x80,
+ DW_OP_breg17 = 0x81,
+ DW_OP_breg18 = 0x82,
+ DW_OP_breg19 = 0x83,
+ DW_OP_breg20 = 0x84,
+ DW_OP_breg21 = 0x85,
+ DW_OP_breg22 = 0x86,
+ DW_OP_breg23 = 0x87,
+ DW_OP_breg24 = 0x88,
+ DW_OP_breg25 = 0x89,
+ DW_OP_breg26 = 0x8a,
+ DW_OP_breg27 = 0x8b,
+ DW_OP_breg28 = 0x8c,
+ DW_OP_breg29 = 0x8d,
+ DW_OP_breg30 = 0x8e,
+ DW_OP_breg31 = 0x8f,
+ DW_OP_regx = 0x90,
+ DW_OP_fbreg = 0x91,
+ DW_OP_bregx = 0x92,
+ DW_OP_piece = 0x93,
+ DW_OP_deref_size = 0x94,
+ DW_OP_xderef_size = 0x95,
+ DW_OP_nop = 0x96,
+ /* DWARF 3 extensions. */
+ DW_OP_push_object_address = 0x97,
+ DW_OP_call2 = 0x98,
+ DW_OP_call4 = 0x99,
+ DW_OP_call_ref = 0x9a,
+ DW_OP_form_tls_address = 0x9b,
+ DW_OP_call_frame_cfa = 0x9c,
+ DW_OP_bit_piece = 0x9d,
+ /* GNU extensions. */
+ DW_OP_GNU_push_tls_address = 0xe0,
+ /* HP extensions. */
+ DW_OP_HP_unknown = 0xe0, /* Ouch, the same as GNU_push_tls_address. */
+ DW_OP_HP_is_value = 0xe1,
+ DW_OP_HP_fltconst4 = 0xe2,
+ DW_OP_HP_fltconst8 = 0xe3,
+ DW_OP_HP_mod_range = 0xe4,
+ DW_OP_HP_unmod_range = 0xe5,
+ DW_OP_HP_tls = 0xe6
+ };
+
+#define DW_OP_lo_user 0xe0 /* Implementation-defined range start. */
+#define DW_OP_hi_user 0xff /* Implementation-defined range end. */
+
+/* Type encodings. */
+enum dwarf_type
+ {
+ DW_ATE_void = 0x0,
+ DW_ATE_address = 0x1,
+ DW_ATE_boolean = 0x2,
+ DW_ATE_complex_float = 0x3,
+ DW_ATE_float = 0x4,
+ DW_ATE_signed = 0x5,
+ DW_ATE_signed_char = 0x6,
+ DW_ATE_unsigned = 0x7,
+ DW_ATE_unsigned_char = 0x8,
+ /* DWARF 3. */
+ DW_ATE_imaginary_float = 0x9,
+ DW_ATE_packed_decimal = 0xa,
+ DW_ATE_numeric_string = 0xb,
+ DW_ATE_edited = 0xc,
+ DW_ATE_signed_fixed = 0xd,
+ DW_ATE_unsigned_fixed = 0xe,
+ DW_ATE_decimal_float = 0xf,
+ /* HP extensions. */
+ DW_ATE_HP_float80 = 0x80, /* Floating-point (80 bit). */
+ DW_ATE_HP_complex_float80 = 0x81, /* Complex floating-point (80 bit). */
+ DW_ATE_HP_float128 = 0x82, /* Floating-point (128 bit). */
+ DW_ATE_HP_complex_float128 = 0x83, /* Complex floating-point (128 bit). */
+ DW_ATE_HP_floathpintel = 0x84, /* Floating-point (82 bit IA64). */
+ DW_ATE_HP_imaginary_float80 = 0x85,
+ DW_ATE_HP_imaginary_float128 = 0x86
+ };
+
+#define DW_ATE_lo_user 0x80
+#define DW_ATE_hi_user 0xff
+
+/* 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
+ };
+
+#define DW_END_lo_user 0x40
+#define 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,
+ DW_CC_GNU_renesas_sh = 0x40
+ };
+
+#define DW_CC_lo_user 0x40
+#define DW_CC_hi_user 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,
+ /* 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
+ };
+
+#define DW_LNE_lo_user 0x80
+#define DW_LNE_hi_user 0xff
+
+/* Call frame information. */
+enum dwarf_call_frame_info
+ {
+ DW_CFA_advance_loc = 0x40,
+ DW_CFA_offset = 0x80,
+ DW_CFA_restore = 0xc0,
+ DW_CFA_nop = 0x00,
+ DW_CFA_set_loc = 0x01,
+ DW_CFA_advance_loc1 = 0x02,
+ DW_CFA_advance_loc2 = 0x03,
+ DW_CFA_advance_loc4 = 0x04,
+ DW_CFA_offset_extended = 0x05,
+ DW_CFA_restore_extended = 0x06,
+ DW_CFA_undefined = 0x07,
+ DW_CFA_same_value = 0x08,
+ DW_CFA_register = 0x09,
+ DW_CFA_remember_state = 0x0a,
+ DW_CFA_restore_state = 0x0b,
+ DW_CFA_def_cfa = 0x0c,
+ DW_CFA_def_cfa_register = 0x0d,
+ DW_CFA_def_cfa_offset = 0x0e,
+ /* DWARF 3. */
+ DW_CFA_def_cfa_expression = 0x0f,
+ DW_CFA_expression = 0x10,
+ DW_CFA_offset_extended_sf = 0x11,
+ DW_CFA_def_cfa_sf = 0x12,
+ DW_CFA_def_cfa_offset_sf = 0x13,
+ DW_CFA_val_offset = 0x14,
+ DW_CFA_val_offset_sf = 0x15,
+ DW_CFA_val_expression = 0x16,
+ /* SGI/MIPS specific. */
+ DW_CFA_MIPS_advance_loc8 = 0x1d,
+ /* GNU extensions. */
+ DW_CFA_GNU_window_save = 0x2d,
+ DW_CFA_GNU_args_size = 0x2e,
+ DW_CFA_GNU_negative_offset_extended = 0x2f
+ };
+
+#define DW_CIE_ID 0xffffffff
+#define DW_CIE_VERSION 1
+
+#define DW_CFA_extended 0
+#define DW_CFA_lo_user 0x1c
+#define DW_CFA_hi_user 0x3f
+
+#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,
+ /* MIPS. */
+ DW_LANG_Mips_Assembler = 0x8001,
+ /* UPC. */
+ DW_LANG_Upc = 0x8765
+ };
+
+#define DW_LANG_lo_user 0x8000 /* Implementation-defined range start. */
+#define DW_LANG_hi_user 0xffff /* Implementation-defined range start. */
+
+/* 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
+ };
+
+/* @@@ 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
+
+#endif /* _ELF_DWARF2_H */
diff --git a/elfcopy.c b/elfcopy.c
new file mode 100644
index 0000000..85d1b84
--- /dev/null
+++ b/elfcopy.c
@@ -0,0 +1,2989 @@
+
+#include <stdio.h>
+#include <common.h>
+#include <debug.h>
+#include <hash.h>
+#include <libelf.h>
+#include <libebl.h>
+#include <libebl_arm.h>
+#include <elf.h>
+#include <gelf.h>
+#include <string.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#ifdef DEBUG
+ #include <rangesort.h>
+#endif
+
+/* static void print_shdr_array(shdr_info_t *, int); */
+
+#include <elfcopy.h>
+
+#define COPY_SECTION_DATA_BUFFER (0)
+
+/* When this macro is set to a nonzero value, we replace calls to elf_strptr()
+ on the target ELF handle with code that extracts the strings directly from
+ the data buffers of that ELF handle. In this case, elf_strptr() does not
+ work as expected, as it tries to read the data buffer of the associated
+ string section directly from the file, and that buffer does not exist yet
+ in the file, since we haven't committed our changes yet.
+*/
+#define ELF_STRPTR_IS_BROKEN (1)
+
+static void update_relocations_section_symbol_references(Elf *newelf, Elf *elf,
+ shdr_info_t *info, int info_len,
+ shdr_info_t *relsect_info,
+ Elf32_Word *newsymidx);
+
+static void update_relocations_section_offsets(Elf *newelf, Elf *elf, Ebl *ebl,
+ shdr_info_t *info,
+ int info_len,
+ shdr_info_t *relsect_info,
+ Elf_Data *data,
+ range_list_t *old_section_ranges);
+
+static void update_hash_table(Elf *newelf, Elf *elf,
+ Elf32_Word hash_scn_idx,
+ shdr_info_t *symtab_info);
+
+static inline
+Elf_Data *create_section_data(shdr_info_t *, Elf_Scn *);
+
+static Elf64_Off section_to_header_mapping(Elf *elf,
+ int phdr_idx,
+ shdr_info_t *shdr_info,
+ int num_shdr_info,
+ Elf64_Off *file_end,
+ Elf64_Off *mem_end);
+
+static void build_dynamic_segment_strings(Elf *elf, Ebl *oldebl,
+ int dynidx, /* index of .dynamic section */
+ int symtabidx, /* index of symbol table section */
+ shdr_info_t *shdr_info,
+ int shdr_info_len);
+
+#ifdef DEBUG
+static void print_dynamic_segment_strings(Elf *elf, Ebl *oldebl,
+ int dynidx, /* index of .dynamic section */
+ int symtabidx, /* index of symbol table section */
+ shdr_info_t *shdr_info,
+ int shdr_info_len);
+#endif
+
+static void adjust_dynamic_segment_offsets(Elf *elf, Ebl *oldebl,
+ Elf *newelf,
+ int idx, /* index of .dynamic section */
+ shdr_info_t *shdr_info,
+ int shdr_info_len);
+
+static void update_symbol_values(Elf *elf, GElf_Ehdr *ehdr,
+ Elf *newelf,
+ shdr_info_t *shdr_info,
+ int num_shdr_info,
+ int shady,
+ int dynamic_idx);
+
+static bool section_belongs_to_header(GElf_Shdr *shdr, GElf_Phdr *phdr);
+
+static range_list_t *
+update_section_offsets(Elf *elf,
+ Elf *newelf,
+ GElf_Phdr *phdr_info,
+ shdr_info_t *shdr_info,
+ int num_shdr_info,
+ range_list_t *section_ranges,
+ bool adjust_alloc_section_offsets);
+
+void handle_range_error(range_error_t err, range_t *left, range_t *right);
+
+#ifdef DEBUG
+static void
+verify_elf(GElf_Ehdr *ehdr, struct shdr_info_t *shdr_info, int shdr_info_len,
+ GElf_Phdr *phdr_info);
+#endif
+
+void adjust_elf(Elf *elf, const char *elf_name,
+ Elf *newelf, const char *newelf_name,
+ Ebl *ebl,
+ GElf_Ehdr *ehdr, /* store ELF header of original library */
+ bool *sym_filter, int num_symbols,
+ struct shdr_info_t *shdr_info, int shdr_info_len,
+ GElf_Phdr *phdr_info,
+ size_t highest_scn_num,
+ size_t shnum,
+ size_t shstrndx,
+ struct Ebl_Strtab *shst,
+ bool sections_dropped_or_rearranged,
+ int dynamic_idx, /* index in shdr_info[] of .dynamic section */
+ int dynsym_idx, /* index in shdr_info[] of dynamic symbol table */
+ int shady,
+ Elf_Data **shstrtab_data,
+ bool adjust_alloc_section_offsets,
+ bool rebuild_shstrtab)
+{
+ int cnt; /* general-purpose counter */
+ Elf_Scn *scn; /* general-purpose section */
+
+ *shstrtab_data = NULL;
+
+ /* When this flag is true, we have dropped some symbols, which caused
+ a change in the order of symbols in the symbol table (all symbols after
+ the removed symbol have shifted forward), and a change in its size as
+ well. When the symbol table changes this way, we need to modify the
+ relocation entries that relocate symbols in this symbol table, and we
+ also need to rebuild the hash table (the hash is outdated).
+
+ Note that it is possible to change the symbols in the symbol table
+ without changing their position (that is, without cutting any symbols
+ out). If a section that a symbol refers to changes (i.e., moves), we
+ need to update that section's index in the symbol entry in the symbol
+ table. Therefore, there are symbol-table changes that can be made and
+ still have symtab_size_changed == false!
+ */
+ bool symtab_size_changed = false;
+
+ /* We allow adjusting of offsets only for files that are shared libraries.
+ We cannot mess with the relative positions of sections for executable
+ files, because we do not have enough information to adjust them. The
+ text section is already linked to fixed addresses.
+ */
+ ASSERT(!adjust_alloc_section_offsets || ehdr->e_type == ET_DYN);
+
+ if (!sections_dropped_or_rearranged)
+ INFO("Note: we aren't dropping or rearranging any sections.\n");
+
+ /* Index of the section header table in the shdr_info array. This is
+ an important variable because it denotes the last section of the old
+ file, as well as the location of the section-strings section of the
+ new one.
+
+ Note: we use this variable only when we are re-creating the section-
+ header-strings table. Otherwise, we keep it as zero.
+ */
+
+ size_t shdridx = shstrndx;
+ if (rebuild_shstrtab) {
+ INFO("Creating new section-strings section...\n");
+
+ shdridx = shnum;
+
+ /* Create the new section-name-strings section */
+ {
+ INFO("\tNew index will be %d (was %d).\n", highest_scn_num, shstrndx);
+
+ /* Add the section header string table section name. */
+ shdr_info[shdridx] = shdr_info[shstrndx];
+ ASSERT(!strcmp(shdr_info[shdridx].name, ".shstrtab"));
+ shdr_info[shdridx].se = ebl_strtabadd (shst, ".shstrtab", 10);
+ ASSERT(shdr_info[shdridx].se != NULL);
+ shdr_info[shdridx].idx = highest_scn_num;
+
+ /* Create the section header. */
+ shdr_info[shdridx].shdr.sh_type = SHT_STRTAB;
+ shdr_info[shdridx].shdr.sh_flags = 0;
+ shdr_info[shdridx].shdr.sh_addr = 0;
+ shdr_info[shdridx].shdr.sh_link = SHN_UNDEF;
+ shdr_info[shdridx].shdr.sh_info = SHN_UNDEF;
+ shdr_info[shdridx].shdr.sh_entsize = 0;
+
+ shdr_info[shdridx].shdr.sh_offset = shdr_info[shdridx].old_shdr.sh_offset;
+ shdr_info[shdridx].shdr.sh_addralign = 1;
+
+ /* Create the section. */
+ FAILIF_LIBELF((shdr_info[shdridx].newscn = elf_newscn(newelf)) == NULL,
+ elf_newscn);
+ ASSERT(elf_ndxscn (shdr_info[shdridx].newscn) == highest_scn_num);
+
+ {
+ /* Finalize the string table and fill in the correct indices in
+ the section headers. */
+ FAILIF_LIBELF((*shstrtab_data =
+ elf_newdata (shdr_info[shdridx].newscn)) == NULL,
+ elf_newdata);
+ ebl_strtabfinalize (shst, *shstrtab_data);
+ /* We have to set the section size. */
+ INFO("\tNew size will be %d.\n", (*shstrtab_data)->d_size);
+ shdr_info[shdridx].shdr.sh_size = (*shstrtab_data)->d_size;
+ /* Setting the data pointer tells the update loop below not to
+ copy the information from the original section. */
+
+ shdr_info[shdridx].data = *shstrtab_data;
+#if COPY_SECTION_DATA_BUFFER
+ shdr_info[shdridx].data->d_buf = MALLOC(shdr_info[shdridx].data->d_size);
+ ASSERT((*shstrtab_data)->d_buf);
+ memcpy(shdr_info[shdridx].data->d_buf, (*shstrtab_data)->d_buf, (*shstrtab_data)->d_size);
+#endif
+ }
+ }
+ } /* if (rebuild_shstrtab) */
+ else {
+ /* When we are not rebuilding shstrtab, we expect the input parameter
+ shstrndx to be the index of .shstrtab BOTH in shdr_info[] and in
+ as a section index in the ELF file.
+ */
+ ASSERT(!strcmp(shdr_info[shdridx].name, ".shstrtab"));
+ }
+
+ INFO("Updating section information...\n");
+ /* Update the section information. */
+
+#ifdef DEBUG
+ /* We use this flag to ASSERT that the symbol tables comes
+ before the .dynamic section in the file. See comments
+ further below.
+ */
+ bool visited_dynsym = false;
+#endif
+
+ for (cnt = 1; cnt < shdr_info_len; ++cnt) {
+ if (shdr_info[cnt].idx > 0) {
+ Elf_Data *newdata;
+
+ INFO("\t%03d: Updating section %s (index %d, address %lld offset %lld, size %lld, alignment %d)...\n",
+ cnt,
+ (shdr_info[cnt].name ?: "(no name)"),
+ shdr_info[cnt].idx,
+ shdr_info[cnt].shdr.sh_addr,
+ shdr_info[cnt].shdr.sh_offset,
+ shdr_info[cnt].shdr.sh_size,
+ shdr_info[cnt].shdr.sh_addralign);
+
+ scn = shdr_info[cnt].newscn;
+ ASSERT(scn != NULL);
+ ASSERT(scn == elf_getscn(newelf, shdr_info[cnt].idx));
+
+ /* Update the name. */
+ if (rebuild_shstrtab) {
+ Elf64_Word new_sh_name = ebl_strtaboffset(shdr_info[cnt].se);
+ INFO("\t\tname offset %d (was %d).\n",
+ new_sh_name,
+ shdr_info[cnt].shdr.sh_name);
+ shdr_info[cnt].shdr.sh_name = new_sh_name;
+ }
+
+ /* Update the section header from the input file. Some fields
+ might be section indices which now have to be adjusted. */
+ if (shdr_info[cnt].shdr.sh_link != 0) {
+ INFO("\t\tsh_link %d (was %d).\n",
+ shdr_info[shdr_info[cnt].shdr.sh_link].idx,
+ shdr_info[cnt].shdr.sh_link);
+
+ shdr_info[cnt].shdr.sh_link =
+ shdr_info[shdr_info[cnt].shdr.sh_link].idx;
+ }
+
+ /* Handle the SHT_REL, SHT_RELA, and SHF_INFO_LINK flag. */
+ if (SH_INFO_LINK_P (&shdr_info[cnt].shdr)) {
+ INFO("\t\tsh_info %d (was %d).\n",
+ shdr_info[shdr_info[cnt].shdr.sh_info].idx,
+ shdr_info[cnt].shdr.sh_info);
+
+ shdr_info[cnt].shdr.sh_info =
+ shdr_info[shdr_info[cnt].shdr.sh_info].idx;
+ }
+
+ /* Get the data from the old file if necessary. We already
+ created the data for the section header string table, which
+ has a section number equal to shnum--hence the ASSERT().
+ */
+ ASSERT(!rebuild_shstrtab || shdr_info[cnt].data || cnt < shnum);
+ newdata = create_section_data(shdr_info + cnt, scn);
+
+ /* We know the size. */
+ shdr_info[cnt].shdr.sh_size = shdr_info[cnt].data->d_size;
+
+ /* We have to adjust symbol tables. Each symbol contains
+ a reference to the section it belongs to. Since we have
+ renumbered the sections (and dropped some), we need to adjust
+ the symbols' section indices as well. Also, if we do not want
+ to keep a symbol, we drop it from the symbol table in this loop.
+
+ When we drop symbols from the dynamic-symbol table, we need to
+ remove the names of the sybmols from the dynamic-symbol-strings
+ table. Changing the dynamic-symbol-strings table means that we
+ also have to rebuild the strings that go into the .dynamic
+ section (such as the DT_NEEDED strings, which lists the libraries
+ that the file depends on), since those strings are kept in the
+ same dynamic-symbol-strings table. That latter statement
+ is an assumption (which we ASSERT against, read on below).
+
+ Note: we process the symbol-table sections only when the user
+ specifies a symbol filter AND that leads to a change in the
+ symbol table, or when section indices change.
+ */
+
+ /* The .dynamic section's strings need not be contained in the
+ same section as the strings of the dynamic symbol table,
+ but we assume that they are (I haven't seen it be otherwise).
+ We assert the validity of our assumption here.
+
+ If this assertion fails, then we *may* need to reorganize
+ this code as follows: we will need to call function
+ build_dynamic_segment_strings() even when sections numbers
+ don't change and there is no filter. Also, if string section
+ containing the .dynamic section strings changes, then we'd
+ need to update the sh_link of the .dynamic section to point
+ to the new section.
+ */
+
+ ASSERT(shdr_info[dynamic_idx].shdr.sh_link ==
+ shdr_info[dynsym_idx].shdr.sh_link);
+
+ if (sections_dropped_or_rearranged || (sym_filter != NULL))
+ {
+ if(shdr_info[cnt].shdr.sh_type == SHT_DYNSYM)
+ {
+ INFO("\t\tupdating a symbol table.\n");
+
+ /* Calculate the size of the external representation of a
+ symbol. */
+ size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, ehdr->e_version);
+
+ /* Check the length of the dynamic-symbol filter. (This is the
+ second of two identical checks, the first one being in
+ the loop that checks for exceptions.)
+
+ NOTE: We narrow this assertion down to the dynamic-symbol
+ table only. Since we expect the symbol filter to
+ be parallel to .dynsym, and .dynsym in general
+ contains fewer symbols than .strtab, we cannot
+ make this assertion for .strtab.
+ */
+ FAILIF(sym_filter != NULL &&
+ num_symbols != shdr_info[cnt].data->d_size / elsize,
+ "Length of dynsym filter (%d) must equal the number"
+ " of dynamic symbols (%d) in section [%s]!\n",
+ num_symbols,
+ shdr_info[cnt].data->d_size / elsize,
+ shdr_info[cnt].name);
+
+ shdr_info[cnt].symse =
+ (struct Ebl_Strent **)MALLOC(
+ (shdr_info[cnt].data->d_size/elsize) *
+ sizeof(struct Ebl_Strent *));
+ shdr_info[cnt].dynsymst = ebl_strtabinit(1);
+ FAILIF_LIBELF(NULL == shdr_info[cnt].dynsymst, ebl_strtabinit);
+
+ /* Allocate an array of Elf32_Word, one for each symbol. This
+ array will hold the new symbol indices.
+ */
+ shdr_info[cnt].newsymidx =
+ (Elf32_Word *)CALLOC(shdr_info[cnt].data->d_size / elsize,
+ sizeof (Elf32_Word));
+
+ bool last_was_local = true;
+ size_t destidx, // index of the symbol in the new symbol table
+ inner, // index of the symbol in the old table
+ last_local_idx = 0;
+ int num_kept_undefined_and_special = 0;
+ int num_kept_global_or_weak = 0;
+ int num_thrown_away = 0;
+
+ unsigned long num_symbols = shdr_info[cnt].data->d_size / elsize;
+ INFO("\t\tsymbol table has %ld symbols.\n", num_symbols);
+
+ /* In the loop below, determine whether to remove or not each
+ symbol.
+ */
+ for (destidx = inner = 1; inner < num_symbols; ++inner)
+ {
+ Elf32_Word sec; /* index of section a symbol refers to */
+ Elf32_Word xshndx; /* extended-section index of symbol */
+ /* Retrieve symbol information and separate section index
+ from the symbol table at the given index. */
+ GElf_Sym sym_mem; /* holds the symbol */
+
+ /* Retrieve symbol information and separate section index
+ from the symbol table at the given index. */
+ GElf_Sym *sym = gelf_getsymshndx (shdr_info[cnt].data,
+ NULL, inner,
+ &sym_mem, &xshndx);
+ ASSERT(sym != NULL);
+
+ FAILIF(sym->st_shndx == SHN_XINDEX,
+ "Can't handle symbol's st_shndx == SHN_XINDEX!\n");
+
+ /* Do not automatically strip the symbol if:
+ -- the symbol filter is NULL or
+ -- the symbol is marked to keep or
+ -- the symbol is neither of:
+ -- imported or refers to a nonstandard section
+ -- global
+ -- weak
+
+ We do not want to strip imported symbols, because then
+ we won't be able to link against them. We do not want
+ to strip global or weak symbols, because then someone
+ else will fail to link against them. Finally, we do
+ not want to strip nonstandard symbols, because we're
+ not sure what they are doing there.
+ */
+
+ char *symname = elf_strptr(elf,
+ shdr_info[cnt].old_shdr.sh_link,
+ sym->st_name);
+
+ if (NULL == sym_filter || /* no symfilter */
+ sym_filter[inner] || /* keep the symbol! */
+ /* don't keep the symbol, but the symbol is undefined
+ or refers to a specific section */
+ sym->st_shndx == SHN_UNDEF || sym->st_shndx >= shnum ||
+ /* don't keep the symbol, which defined and refers to
+ a normal section, but the symbol is neither global
+ nor weak. */
+ (ELF32_ST_BIND(sym->st_info) != STB_GLOBAL &&
+ ELF32_ST_BIND(sym->st_info) != STB_WEAK))
+ {
+ /* Do not remove the symbol. */
+ if (sym->st_shndx == SHN_UNDEF ||
+ sym->st_shndx >= shnum)
+ {
+ /* This symbol has no section index (it is
+ absolute). Leave the symbol alone unless it is
+ moved. */
+ FAILIF_LIBELF(!(destidx == inner ||
+ gelf_update_symshndx(
+ shdr_info[cnt].data,
+ NULL,
+ destidx,
+ sym,
+ xshndx)),
+ gelf_update_symshndx);
+
+ shdr_info[cnt].newsymidx[inner] = destidx;
+ INFO("\t\t\tkeeping %s symbol %d (new index %d), name [%s]\n",
+ (sym->st_shndx == SHN_UNDEF ? "undefined" : "special"),
+ inner,
+ destidx,
+ symname);
+ /* mark the symbol as kept */
+ if (sym_filter) sym_filter[inner] = 1;
+ shdr_info[cnt].symse[destidx] =
+ ebl_strtabadd (shdr_info[cnt].dynsymst,
+ symname, 0);
+ ASSERT(shdr_info[cnt].symse[destidx] != NULL);
+ num_kept_undefined_and_special++;
+ if (GELF_ST_BIND(sym->st_info) == STB_LOCAL)
+ last_local_idx = destidx;
+ destidx++;
+ } else {
+ /* Get the full section index. */
+ sec = shdr_info[sym->st_shndx].idx;
+
+ if (sec) {
+ Elf32_Word nxshndx;
+
+ ASSERT (sec < SHN_LORESERVE);
+ nxshndx = 0;
+
+ /* Update the symbol only if something changed,
+ that is, if either the symbol's position in
+ the symbol table changed (because we deleted
+ some symbols), or because its section moved!
+
+ NOTE: We don't update the symbol's section
+ index, sym->st_shndx here, but in function
+ update_symbol_values() instead. The reason
+ is that if we update the symbol-section index,
+ now, it won't refer anymore to the shdr_info[]
+ entry, which we will need in
+ update_symbol_values().
+ */
+ if (inner != destidx)
+ {
+ FAILIF_LIBELF(0 ==
+ gelf_update_symshndx(
+ shdr_info[cnt].data,
+ NULL,
+ destidx, sym,
+ nxshndx),
+ gelf_update_symshndx);
+ }
+
+ shdr_info[cnt].newsymidx[inner] = destidx;
+
+ /* If we are not filtering out some symbols,
+ there's no point to printing this message
+ for every single symbol. */
+ if (sym_filter) {
+ INFO("\t\t\tkeeping symbol %d (new index %d), name (index %d) [%s]\n",
+ inner,
+ destidx,
+ sym->st_name,
+ symname);
+ /* mark the symbol as kept */
+ sym_filter[inner] = 1;
+ }
+ shdr_info[cnt].symse[destidx] =
+ ebl_strtabadd(shdr_info[cnt].dynsymst,
+ symname, 0);
+ ASSERT(shdr_info[cnt].symse[destidx] != NULL);
+ num_kept_global_or_weak++;
+ if (GELF_ST_BIND(sym->st_info) == STB_LOCAL)
+ last_local_idx = destidx;
+ destidx++;
+ } else {
+ /* I am not sure, there might be other types of
+ symbols that do not refer to any section, but
+ I will handle them case by case when this
+ assertion fails--I want to know if each of them
+ is safe to remove!
+ */
+ ASSERT(GELF_ST_TYPE (sym->st_info) == STT_SECTION ||
+ GELF_ST_TYPE (sym->st_info) == STT_NOTYPE);
+ INFO("\t\t\tignoring %s symbol [%s]"
+ " at index %d refering to section %d\n",
+ (GELF_ST_TYPE(sym->st_info) == STT_SECTION
+ ? "STT_SECTION" : "STT_NOTYPE"),
+ symname,
+ inner,
+ sym->st_shndx);
+ num_thrown_away++;
+ /* mark the symbol as thrown away */
+ if (sym_filter) sym_filter[inner] = 0;
+ }
+ }
+ } /* to strip or not to strip? */
+ else {
+ INFO("\t\t\tremoving symbol [%s]\n", symname);
+ shdr_info[cnt].newsymidx[inner] = (Elf32_Word)-1;
+ num_thrown_away++;
+ /* mark the symbol as thrown away */
+ if (sym_filter) sym_filter[inner] = 0;
+ }
+
+ /* For symbol-table sections, sh_info is one greater than the
+ symbol table index of the last local symbol. This is why,
+ when we find the last local symbol, we update the sh_info
+ field.
+ */
+
+ if (last_was_local) {
+ if (GELF_ST_BIND (sym->st_info) != STB_LOCAL) {
+ last_was_local = false;
+ if (last_local_idx) {
+ INFO("\t\t\tMARKING ONE PAST LAST LOCAL INDEX %d\n",
+ last_local_idx + 1);
+ shdr_info[cnt].shdr.sh_info =
+ last_local_idx + 1;
+ }
+ else shdr_info[cnt].shdr.sh_info = 0;
+
+ }
+ } else FAILIF(0 && GELF_ST_BIND (sym->st_info) == STB_LOCAL,
+ "Internal error in ELF file: symbol table has"
+ " local symbols after first global"
+ " symbol!\n");
+ } /* for each symbol */
+
+ INFO("\t\t%d undefined or special symbols were kept.\n",
+ num_kept_undefined_and_special);
+ INFO("\t\t%d global or weak symbols were kept.\n",
+ num_kept_global_or_weak);
+ INFO("\t\t%d symbols were thrown away.\n",
+ num_thrown_away);
+
+ if (destidx != inner) {
+ /* The symbol table changed. */
+ INFO("\t\t\tthe symbol table has changed.\n");
+ INFO("\t\t\tdestidx = %d, inner = %d.\n", destidx, inner);
+ INFO("\t\t\tnew size %d (was %lld).\n",
+ destidx * elsize,
+ shdr_info[cnt].shdr.sh_size);
+ shdr_info[cnt].shdr.sh_size = newdata->d_size = destidx * elsize;
+ symtab_size_changed = true;
+ } else {
+ /* The symbol table didn't really change. */
+ INFO("\t\t\tthe symbol table did not change.\n");
+ FREE (shdr_info[cnt].newsymidx);
+ shdr_info[cnt].newsymidx = NULL;
+ }
+#ifdef DEBUG
+ visited_dynsym = shdr_info[cnt].shdr.sh_type == SHT_DYNSYM;
+#endif
+ } /* if it's a symbol table... */
+ else if (shdr_info[cnt].shdr.sh_type == SHT_DYNAMIC) {
+ /* We get here either when we drop some sections, or
+ when we are dropping symbols. If we are not dropping
+ symbols, then the dynamic-symbol-table and its strings
+ section won't change, so we won't need to rebuild the
+ symbols for the SHT_DYNAMIC section either.
+
+ NOTE: If ever in the future we add the ability in
+ adjust_elf() to change the strings in the SHT_DYNAMIC
+ section, then we would need to find a way to rebuild
+ the dynamic-symbol-table-strings section.
+ */
+
+ /* symtab_size_changed has a meaningful value only after
+ we've processed the symbol table. If this assertion
+ is ever violated, it will be because the .dynamic section
+ came before the symbol table in the list of section in
+ a file. If that happens, then we have to break up the
+ loop into two: one that finds and processes the symbol
+ tables, and another, after the first one, that finds
+ and handles the .dynamic sectio.
+ */
+ ASSERT(visited_dynsym == true);
+ if (sym_filter != NULL && symtab_size_changed) {
+ /* Walk the old dynamic segment. For each tag that represents
+ a string, build an entry into the dynamic-symbol-table's
+ strings table. */
+ INFO("\t\tbuilding strings for the dynamic section.\n");
+ ASSERT(cnt == dynamic_idx);
+
+ /* NOTE: By passing the the index (in shdr_info[]) of the
+ dynamic-symbol table to build_dynamic_segment_strings(),
+ we are making the assumption that those strings will be
+ kept in that table. While this does not seem to be
+ mandated by the ELF spec, it seems to be always the case.
+ Where else would you put these strings? You already have
+ the dynamic-symbol table and its strings table, and that's
+ guaranteed to be in the file, so why not put it there?
+ */
+ build_dynamic_segment_strings(elf, ebl,
+ dynamic_idx,
+ dynsym_idx,
+ shdr_info,
+ shdr_info_len);
+ }
+ else {
+ INFO("\t\tThe dynamic-symbol table is not changing, so no "
+ "need to rebuild strings for the dynamic section.\n");
+#ifdef DEBUG
+ print_dynamic_segment_strings(elf, ebl,
+ dynamic_idx,
+ dynsym_idx,
+ shdr_info,
+ shdr_info_len);
+#endif
+ }
+ }
+ }
+
+ /* Set the section header in the new file. There cannot be any
+ overflows. */
+ INFO("\t\tupdating section header (size %lld)\n",
+ shdr_info[cnt].shdr.sh_size);
+
+ FAILIF(!gelf_update_shdr (scn, &shdr_info[cnt].shdr),
+ "Could not update section header for section %s!\n",
+ shdr_info[cnt].name);
+ } /* if (shdr_info[cnt].idx > 0) */
+ else INFO("\t%03d: not updating section %s, it will be discarded.\n",
+ cnt,
+ shdr_info[cnt].name);
+ } /* for (cnt = 1; cnt < shdr_info_len; ++cnt) */
+
+ /* Now, if we removed some symbols and thus modified the symbol table,
+ we need to update the hash table, the relocation sections that use these
+ symbols, and the symbol-strings table to cut out the unused symbols.
+ */
+ if (symtab_size_changed) {
+ for (cnt = 1; cnt < shnum; ++cnt) {
+ if (shdr_info[cnt].idx == 0) {
+ /* Ignore sections which are discarded, unless these sections
+ are relocation sections. This case is for use by the
+ prelinker. */
+ if (shdr_info[cnt].shdr.sh_type != SHT_REL &&
+ shdr_info[cnt].shdr.sh_type != SHT_RELA) {
+ continue;
+ }
+ }
+
+ if (shdr_info[cnt].shdr.sh_type == SHT_REL ||
+ shdr_info[cnt].shdr.sh_type == SHT_RELA) {
+ /* shdr_info[cnt].old_shdr.sh_link is index of old symbol-table
+ section that this relocation-table section was relative to.
+ We can access shdr_info[] at that index to get to the
+ symbol-table section.
+ */
+ Elf32_Word *newsymidx =
+ shdr_info[shdr_info[cnt].old_shdr.sh_link].newsymidx;
+
+ /* The referred-to-section must be a symbol table! Note that
+ alrhough shdr_info[cnt].shdr refers to the updated section
+ header, this assertion is still valid, since when updating
+ the section header we never modify the sh_type field.
+ */
+ {
+ Elf64_Word sh_type =
+ shdr_info[shdr_info[cnt].shdr.sh_link].shdr.sh_type;
+ FAILIF(sh_type != SHT_DYNSYM,
+ "Section refered to from relocation section is not"
+ " a dynamic symbol table (sh_type=%d)!\n",
+ sh_type);
+ }
+
+ /* If that symbol table hasn't changed, then its newsymidx
+ field is NULL (see comments to shdr_info_t), so we
+ don't have to update this relocation-table section
+ */
+ if (newsymidx == NULL) continue;
+
+ update_relocations_section_symbol_references(newelf, elf,
+ shdr_info, shnum,
+ shdr_info + cnt,
+ newsymidx);
+
+ } else if (shdr_info[cnt].shdr.sh_type == SHT_HASH) {
+ /* We have to recompute the hash table. A hash table's
+ sh_link field refers to the symbol table for which the hash
+ table is generated.
+ */
+ Elf32_Word symtabidx = shdr_info[cnt].old_shdr.sh_link;
+
+ /* We do not have to recompute the hash table if the symbol
+ table was not changed. */
+ if (shdr_info[symtabidx].newsymidx == NULL)
+ continue;
+
+ FAILIF(shdr_info[cnt].shdr.sh_entsize != sizeof (Elf32_Word),
+ "Can't handle 64-bit ELF files!\n");
+
+ update_hash_table(newelf, /* new ELF */
+ elf, /* old ELF */
+ shdr_info[cnt].idx, /* hash table index */
+ shdr_info + symtabidx);
+ } /* if SHT_REL else if SHT_HASH ... */
+ else if (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM)
+ {
+ /* The symbol table's sh_link field contains the index of the
+ strings table for this symbol table. We want to find the
+ index of the section in the shdr_info[] array. That index
+ corresponds to the index of the section in the original ELF file,
+ which is why we look at shdr_info[cnt].old_shdr and not
+ shdr_info[cnt].shdr.
+ */
+
+ int symstrndx = shdr_info[cnt].old_shdr.sh_link;
+ INFO("Updating [%s] (symbol-strings-section data for [%s]).\n",
+ shdr_info[symstrndx].name,
+ shdr_info[cnt].name);
+ ASSERT(shdr_info[symstrndx].newscn);
+ size_t new_symstrndx = elf_ndxscn(shdr_info[symstrndx].newscn);
+ Elf_Data *newdata = elf_getdata(shdr_info[symstrndx].newscn, NULL);
+ ASSERT(NULL != newdata);
+ INFO("\tbefore update:\n"
+ "\t\tbuffer: %p\n"
+ "\t\tsize: %d\n",
+ newdata->d_buf,
+ newdata->d_size);
+ ASSERT(shdr_info[cnt].dynsymst);
+ ebl_strtabfinalize (shdr_info[cnt].dynsymst, newdata);
+ INFO("\tafter update:\n"
+ "\t\tbuffer: %p\n"
+ "\t\tsize: %d\n",
+ newdata->d_buf,
+ newdata->d_size);
+ FAILIF(new_symstrndx != shdr_info[cnt].shdr.sh_link,
+ "The index of the symbol-strings table according to elf_ndxscn() is %d, "
+ "according to shdr_info[] is %d!\n",
+ new_symstrndx,
+ shdr_info[cnt].shdr.sh_link);
+
+ INFO("%d nonprintable\n",
+ dump_hex_buffer(stdout, newdata->d_buf, newdata->d_size, 0));
+
+ shdr_info[symstrndx].shdr.sh_size = newdata->d_size;
+ FAILIF(!gelf_update_shdr(shdr_info[symstrndx].newscn,
+ &shdr_info[symstrndx].shdr),
+ "Could not update section header for section %s!\n",
+ shdr_info[symstrndx].name);
+
+ /* Now, update the symbol-name offsets. */
+ {
+ size_t i;
+ size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, ehdr->e_version);
+ for (i = 1; i < shdr_info[cnt].shdr.sh_size / elsize; ++i) {
+ Elf32_Word xshndx;
+ GElf_Sym sym_mem;
+ /* retrieve the symbol information; */
+ GElf_Sym *sym = gelf_getsymshndx (shdr_info[cnt].data,
+ NULL, i,
+ &sym_mem, &xshndx);
+ ASSERT(sym != NULL);
+ ASSERT(NULL != shdr_info[cnt].symse[i]);
+ /* calculate the new name offset; */
+ size_t new_st_name =
+ ebl_strtaboffset(shdr_info[cnt].symse[i]);
+#if 1
+ ASSERT(!strcmp(newdata->d_buf + new_st_name,
+ elf_strptr(elf, shdr_info[cnt].old_shdr.sh_link,
+ sym->st_name)));
+#endif
+ if (sym_filter && (sym->st_name != new_st_name)) {
+ /* FIXME: For some reason, elf_strptr() does not return the updated
+ string value here. It looks like ebl_strtabfinalize() doesn't
+ update libelf's internal structures well enough for elf_strptr()
+ to work on an ELF file that's being compose.
+ */
+ INFO("Symbol [%s]'s name (index %d, old value %llx) changes offset: %d -> %d\n",
+#if 0
+ newdata->d_buf + new_st_name,
+#else
+ elf_strptr(elf, shdr_info[cnt].old_shdr.sh_link,
+ sym->st_name),
+#endif
+ i,
+ sym->st_value,
+ sym->st_name,
+ new_st_name);
+ }
+ sym->st_name = new_st_name;
+ /* update the symbol info; */
+ FAILIF_LIBELF(0 ==
+ gelf_update_symshndx(
+ shdr_info[cnt].data,
+ NULL,
+ i, sym,
+ xshndx),
+ gelf_update_symshndx);
+ } /* for each symbol... */
+ }
+ }
+
+ FAILIF(shdr_info[cnt].shdr.sh_type == SHT_GNU_versym,
+ "Can't handle SHT_GNU_versym!\n");
+ FAILIF(shdr_info[cnt].shdr.sh_type == SHT_GROUP,
+ "Can't handle section groups!\n");
+ } /* for (cnt = 1; cnt < shnum; ++cnt) */
+ } /* if (symtab_size_changed) */
+
+
+ range_list_t *old_section_ranges = init_range_list();
+ range_list_t *section_ranges = NULL;
+ /* Analyze gaps in the ranges before we compact the sections. */
+ INFO("Analyzing gaps in ranges before compacting sections...\n");
+ {
+ size_t scnidx;
+ /* Gather the ranges */
+ for (scnidx = 1; scnidx < shdr_info_len; scnidx++) {
+ if (shdr_info[scnidx].idx > 0) {
+ if (/*shdr_info[scnidx].old_shdr.sh_type != SHT_NOBITS &&*/
+ shdr_info[scnidx].old_shdr.sh_flags & SHF_ALLOC) {
+ add_unique_range_nosort(
+ old_section_ranges,
+ shdr_info[scnidx].old_shdr.sh_addr,
+ shdr_info[scnidx].old_shdr.sh_size,
+ shdr_info + scnidx,
+ handle_range_error,
+ NULL);
+ }
+ }
+ }
+ sort_ranges(old_section_ranges);
+#ifdef DEBUG
+ int num_ranges;
+ /* Analyze gaps in the ranges before we compact the sections. */
+ range_t *ranges = get_sorted_ranges(old_section_ranges, &num_ranges);
+ if (ranges) {
+ GElf_Off last_end = ranges->start;
+ int i;
+ for (i = 0; i < num_ranges; i++) {
+ shdr_info_t *curr = (shdr_info_t *)ranges[i].user;
+ ASSERT(ranges[i].start >= last_end);
+ int col_before, col_after;
+ INFO("[%016lld, %016lld] %n[%s]%n",
+ ranges[i].start,
+ ranges[i].start + ranges[i].length,
+ &col_before,
+ curr->name,
+ &col_after);
+ if (ranges[i].start > last_end) {
+ shdr_info_t *prev = (shdr_info_t *)ranges[i-1].user;
+ ASSERT(prev && curr);
+ while (col_after++ - col_before < 20) INFO(" ");
+ INFO(" [GAP: %lld bytes with %s]\n",
+ (ranges[i].start - last_end),
+ prev->name);
+ }
+ else INFO("\n");
+ last_end = ranges[i].start + ranges[i].length;
+ }
+ }
+#endif/*DEBUG*/
+ }
+
+ /* Calculate the final section offsets */
+ INFO("Calculating new section offsets...\n");
+ section_ranges = update_section_offsets(elf,
+ newelf,
+ phdr_info,
+ shdr_info,
+ shdr_info_len,
+ init_range_list(),
+ adjust_alloc_section_offsets);
+
+#ifdef DEBUG
+ {
+ /* Analyze gaps in the ranges after we've compacted the sections. */
+ int num_ranges;
+ range_t *ranges = get_sorted_ranges(section_ranges, &num_ranges);
+ if (ranges) {
+ int last_end = ranges->start;
+ int i;
+ for (i = 0; i < num_ranges; i++) {
+ shdr_info_t *curr = (shdr_info_t *)ranges[i].user;
+ ASSERT(ranges[i].start >= last_end);
+ int col_before, col_after;
+ INFO("[%016lld, %016lld] %n[%s]%n",
+ ranges[i].start,
+ ranges[i].start + ranges[i].length,
+ &col_before,
+ curr->name,
+ &col_after);
+ if (ranges[i].start > last_end) {
+ shdr_info_t *prev = (shdr_info_t *)ranges[i-1].user;
+ ASSERT(prev && curr);
+ while (col_after++ - col_before < 20) INFO(" ");
+ INFO(" [GAP: %lld bytes with %s]\n",
+ (ranges[i].start - last_end),
+ prev->name);
+ }
+ else INFO("\n");
+ last_end = ranges[i].start + ranges[i].length;
+ }
+ }
+ }
+#endif
+
+ {
+ /* Now that we have modified the section offsets, we need to scan the
+ symbol tables once again and update their st_value fields. A symbol's
+ st_value field (in a shared library) contains the virtual address of the
+ symbol. For each symbol we encounter, we look up the section it was in.
+ If that section's virtual address has changed, then we calculate the
+ delta and update the symbol.
+ */
+
+#if 0
+ {
+ /* for debugging: Print out all sections and their data pointers and
+ sizes. */
+ int i = 1;
+ for (; i < shdr_info_len; i++) {
+ PRINT("%8d: %-15s: %2lld %8lld %08lx (%08lx:%8d) %08lx (%08lx:%8d)\n",
+ i,
+ shdr_info[i].name,
+ shdr_info[i].shdr.sh_entsize,
+ shdr_info[i].shdr.sh_addralign,
+ (long)shdr_info[i].data,
+ (long)(shdr_info[i].data ? shdr_info[i].data->d_buf : 0),
+ (shdr_info[i].data ? shdr_info[i].data->d_size : 0),
+ (long)shdr_info[i].newdata,
+ (long)(shdr_info[i].newdata ? shdr_info[i].newdata->d_buf : 0),
+ (shdr_info[i].newdata ? shdr_info[i].newdata->d_size : 0));
+ if (!strcmp(shdr_info[i].name, ".got") /* ||
+ !strcmp(shdr_info[i].name, ".plt") */) {
+ dump_hex_buffer(stdout,
+ shdr_info[i].newdata->d_buf,
+ shdr_info[i].newdata->d_size,
+ shdr_info[i].shdr.sh_entsize);
+ }
+ }
+ }
+#endif
+
+ INFO("Updating symbol values...\n");
+ update_symbol_values(elf, ehdr, newelf, shdr_info, shdr_info_len,
+ shady,
+ dynamic_idx);
+
+ /* If we are not stripping the debug sections, then we need to adjust
+ * them accordingly, so that the new ELF file is actually debuggable.
+ * For that glorios reason, we call update_dwarf(). Note that
+ * update_dwarf() won't do anything if there, in fact, no debug
+ * sections to speak of.
+ */
+
+ INFO("Updating DWARF records...\n");
+ int num_total_dwarf_patches = 0, num_failed_dwarf_patches = 0;
+ update_dwarf_if_necessary(
+ elf, ehdr, newelf,
+ shdr_info, shdr_info_len,
+ &num_total_dwarf_patches, &num_failed_dwarf_patches);
+ INFO("DWARF: %-15s: total %8d failed %8d.\n", elf_name, num_total_dwarf_patches, num_failed_dwarf_patches);
+
+ /* Adjust the program-header table. Since the file offsets of the various
+ sections may have changed, the file offsets of their containing segments
+ must change as well. We update those offsets in the loop below.
+ */
+ {
+ INFO("Adjusting program-header table...\n");
+ int pi; /* program-header index */
+ for (pi = 0; pi < ehdr->e_phnum; ++pi) {
+ /* Print the segment number. */
+ INFO("\t%2.2zu\t", pi);
+ INFO("PT_ header type: %d", phdr_info[pi].p_type);
+ if (phdr_info[pi].p_type == PT_NULL) {
+ INFO(" PT_NULL (skip)\n");
+ }
+ else if (phdr_info[pi].p_type == PT_PHDR) {
+ INFO(" PT_PHDR\n");
+ ASSERT(phdr_info[pi].p_memsz == phdr_info[pi].p_filesz);
+ /* Although adjust_elf() does not remove program-header entries,
+ we perform this update here because I've seen object files
+ whose PHDR table is bigger by one element than it should be.
+ Here we check and correct the size, if necessary.
+ */
+ if (phdr_info[pi].p_memsz != ehdr->e_phentsize * ehdr->e_phnum) {
+ ASSERT(phdr_info[pi].p_memsz > ehdr->e_phentsize * ehdr->e_phnum);
+ INFO("WARNING: PT_PHDR file and memory sizes are incorrect (%ld instead of %ld). Correcting.\n",
+ (long)phdr_info[pi].p_memsz,
+ (long)(ehdr->e_phentsize * ehdr->e_phnum));
+ phdr_info[pi].p_memsz = ehdr->e_phentsize * ehdr->e_phnum;
+ phdr_info[pi].p_filesz = phdr_info[pi].p_memsz;
+ }
+ }
+ else {
+
+ /* Go over the section array and find which section's offset
+ field matches this program header's, and update the program
+ header's offset to refelect the new value.
+ */
+ Elf64_Off file_end, mem_end;
+ Elf64_Off new_phdr_offset =
+ section_to_header_mapping(elf, pi,
+ shdr_info, shdr_info_len,
+ &file_end,
+ &mem_end);
+
+ /* Alignments of 0 and 1 mean nothing. Higher alignments are
+ interpreted as powers of 2. */
+ if (phdr_info[pi].p_align > 1) {
+ INFO("\t\tapplying alignment of 0x%llx to new offset %lld\n",
+ phdr_info[pi].p_align,
+ new_phdr_offset);
+ new_phdr_offset &= ~(phdr_info[pi].p_align - 1);
+ }
+
+ Elf32_Sxword delta = new_phdr_offset - phdr_info[pi].p_offset;
+
+ INFO("\t\tnew offset %lld (was %lld)\n",
+ new_phdr_offset,
+ phdr_info[pi].p_offset);
+
+ phdr_info[pi].p_offset = new_phdr_offset;
+
+ INFO("\t\tnew vaddr 0x%llx (was 0x%llx)\n",
+ phdr_info[pi].p_vaddr + delta,
+ phdr_info[pi].p_vaddr);
+ phdr_info[pi].p_vaddr += delta;
+
+ INFO("\t\tnew paddr 0x%llx (was 0x%llx)\n",
+ phdr_info[pi].p_paddr + delta,
+ phdr_info[pi].p_paddr);
+ phdr_info[pi].p_paddr += delta;
+
+ INFO("\t\tnew mem size %lld (was %lld)\n",
+ mem_end - new_phdr_offset,
+ phdr_info[pi].p_memsz);
+ //phdr_info[pi].p_memsz = mem_end - new_phdr_offset;
+ phdr_info[pi].p_memsz = mem_end - phdr_info[pi].p_vaddr;
+
+ INFO("\t\tnew file size %lld (was %lld)\n",
+ file_end - new_phdr_offset,
+ phdr_info[pi].p_filesz);
+ //phdr_info[pi].p_filesz = file_end - new_phdr_offset;
+ phdr_info[pi].p_filesz = file_end - phdr_info[pi].p_offset;
+ }
+
+ FAILIF_LIBELF(gelf_update_phdr (newelf, pi, &phdr_info[pi]) == 0,
+ gelf_update_phdr);
+ }
+ }
+
+ if (dynamic_idx >= 0) {
+ /* NOTE: dynamic_idx is the index of .dynamic section in the shdr_info[] array, NOT the
+ index of the section in the ELF file!
+ */
+ adjust_dynamic_segment_offsets(elf, ebl,
+ newelf,
+ dynamic_idx,
+ shdr_info,
+ shdr_info_len);
+ }
+ else INFO("There is no dynamic section in this file.\n");
+
+ /* Walk the relocation sections (again). This time, update offsets of the
+ relocation entries. Note that there is an implication here that the
+ offsets are virual addresses, because we are handling a shared library!
+ */
+ for (cnt = 1; cnt < shdr_info_len; cnt++) {
+ /* Note here that we process even those relocation sections that are
+ * marked for removal. Normally, we wouldn't need to do this, but
+ * in the case where we run adjust_elf() after a dry run of
+ * prelink() (see apriori), we still want to update the relocation
+ * offsets because those will be picked up by the second run of
+ * prelink(). If this all seems too cryptic, go yell at Iliyan
+ * Malchev.
+ */
+ if (/* shdr_info[cnt].idx > 0 && */
+ (shdr_info[cnt].shdr.sh_type == SHT_REL ||
+ shdr_info[cnt].shdr.sh_type == SHT_RELA))
+ {
+ int hacked = shdr_info[cnt].idx == 0;
+ Elf_Data *data;
+ if (hacked) {
+ /* This doesn't work! elf_ndxscn(shdr_info[cnt].scn) will return the section number
+ of the new sectin that has moved into this slot. */
+ shdr_info[cnt].idx = elf_ndxscn(shdr_info[cnt].scn);
+ data = elf_getdata (elf_getscn (elf, shdr_info[cnt].idx), NULL);
+ INFO("PRELINKER HACK: Temporarily restoring index of to-be-removed section [%s] to %d.\n",
+ shdr_info[cnt].name,
+ shdr_info[cnt].idx);
+ }
+ else
+ data = elf_getdata (elf_getscn (newelf, shdr_info[cnt].idx), NULL);
+
+ update_relocations_section_offsets(newelf, elf, ebl,
+ shdr_info, shdr_info_len,
+ shdr_info + cnt,
+ data,
+ old_section_ranges);
+ if (hacked) {
+ INFO("PRELINKER HACK: Done with hack, marking section [%s] for removal again.\n",
+ shdr_info[cnt].name);
+ shdr_info[cnt].idx = 0;
+ }
+ }
+ }
+ }
+
+ /* Finally finish the ELF header. Fill in the fields not handled by
+ libelf from the old file. */
+ {
+ GElf_Ehdr *newehdr, newehdr_mem;
+ newehdr = gelf_getehdr (newelf, &newehdr_mem);
+ FAILIF_LIBELF(newehdr == NULL, gelf_getehdr);
+
+ INFO("Updating ELF header.\n");
+
+ memcpy (newehdr->e_ident, ehdr->e_ident, EI_NIDENT);
+ newehdr->e_type = ehdr->e_type;
+ newehdr->e_machine = ehdr->e_machine;
+ newehdr->e_version = ehdr->e_version;
+ newehdr->e_entry = ehdr->e_entry;
+ newehdr->e_flags = ehdr->e_flags;
+ newehdr->e_phoff = ehdr->e_phoff;
+
+ /* We need to position the section header table. */
+ {
+ const size_t offsize = gelf_fsize (elf, ELF_T_OFF, 1, EV_CURRENT);
+ newehdr->e_shoff = get_last_address(section_ranges);
+ newehdr->e_shoff += offsize - 1;
+ newehdr->e_shoff &= ~((GElf_Off) (offsize - 1));
+ newehdr->e_shentsize = gelf_fsize (elf, ELF_T_SHDR, 1, EV_CURRENT);
+ INFO("\tsetting section-header-table offset to %lld\n",
+ newehdr->e_shoff);
+ }
+
+ if (rebuild_shstrtab) {
+ /* If we are rebuilding the section-headers string table, then
+ the new index must not be zero. This is to guard against
+ code breakage resulting from rebuild_shstrtab and shdridx
+ somehow getting out of sync. */
+ ASSERT(shdridx);
+ /* The new section header string table index. */
+ FAILIF(!(shdr_info[shdridx].idx < SHN_HIRESERVE) &&
+ likely (shdr_info[shdridx].idx != SHN_XINDEX),
+ "Can't handle extended section indices!\n");
+ }
+
+ INFO("Index of shstrtab is now %d (was %d).\n",
+ shdr_info[shdridx].idx,
+ ehdr->e_shstrndx);
+ newehdr->e_shstrndx = shdr_info[shdridx].idx;
+
+ FAILIF_LIBELF(gelf_update_ehdr(newelf, newehdr) == 0, gelf_update_ehdr);
+ }
+ if (section_ranges != NULL) destroy_range_list(section_ranges);
+ destroy_range_list(old_section_ranges);
+
+#ifdef DEBUG
+ verify_elf (ehdr, shdr_info, shdr_info_len, phdr_info);
+#endif
+
+}
+
+static void update_hash_table(Elf *newelf, Elf *elf,
+ Elf32_Word hash_scn_idx,
+ shdr_info_t *symtab_info) {
+ GElf_Shdr shdr_mem, *shdr = NULL;
+ Elf32_Word *chain;
+ Elf32_Word nbucket;
+
+ /* The hash table section and data in the new file. */
+ Elf_Scn *hashscn = elf_getscn (newelf, hash_scn_idx);
+ ASSERT(hashscn != NULL);
+ Elf_Data *hashd = elf_getdata (hashscn, NULL);
+ ASSERT (hashd != NULL);
+ Elf32_Word *bucket = (Elf32_Word *) hashd->d_buf; /* Sane arches first. */
+
+ /* The symbol table data. */
+ Elf_Data *symd = elf_getdata (elf_getscn (newelf, symtab_info->idx), NULL);
+ ASSERT (symd != NULL);
+
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ FAILIF_LIBELF(NULL == ehdr, gelf_getehdr);
+ size_t strshndx = symtab_info->old_shdr.sh_link;
+ size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1,
+ ehdr->e_version);
+
+ /* Convert to the correct byte order. */
+ FAILIF_LIBELF(gelf_xlatetom (newelf, hashd, hashd,
+ BYTE_ORDER == LITTLE_ENDIAN
+ ? ELFDATA2LSB : ELFDATA2MSB) == NULL,
+ gelf_xlatetom);
+
+ /* Adjust the nchain value. The symbol table size changed. We keep the
+ same size for the bucket array. */
+ INFO("hash table: buckets: %d (no change).\n", bucket[0]);
+ INFO("hash table: chains: %d (was %d).\n",
+ symd->d_size / elsize,
+ bucket[1]);
+ bucket[1] = symd->d_size / elsize;
+ nbucket = bucket[0];
+ bucket += 2;
+ chain = bucket + nbucket;
+
+ /* New size of the section. */
+ shdr = gelf_getshdr (hashscn, &shdr_mem);
+ ASSERT(shdr->sh_type == SHT_HASH);
+ shdr->sh_size = (2 + symd->d_size / elsize + nbucket) * sizeof (Elf32_Word);
+ INFO("hash table: size %lld (was %d) bytes.\n",
+ shdr->sh_size,
+ hashd->d_size);
+ hashd->d_size = shdr->sh_size;
+ (void)gelf_update_shdr (hashscn, shdr);
+
+ /* Clear the arrays. */
+ memset (bucket, '\0',
+ (symd->d_size / elsize + nbucket)
+ * sizeof (Elf32_Word));
+
+ size_t inner;
+ for (inner = symtab_info->shdr.sh_info;
+ inner < symd->d_size / elsize;
+ ++inner) {
+ const char *name;
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (symd, inner, &sym_mem);
+ ASSERT (sym != NULL);
+
+ name = elf_strptr (elf, strshndx, sym->st_name);
+ ASSERT (name != NULL);
+ size_t hidx = elf_hash (name) % nbucket;
+
+ if (bucket[hidx] == 0)
+ bucket[hidx] = inner;
+ else {
+ hidx = bucket[hidx];
+ while (chain[hidx] != 0)
+ hidx = chain[hidx];
+ chain[hidx] = inner;
+ }
+ }
+
+ /* Convert back to the file byte order. */
+ FAILIF_LIBELF(gelf_xlatetof (newelf, hashd, hashd,
+ BYTE_ORDER == LITTLE_ENDIAN
+ ? ELFDATA2LSB : ELFDATA2MSB) == NULL,
+ gelf_xlatetof);
+}
+
+/* This function updates the symbol indices of relocation entries. It does not
+ update the section offsets of those entries.
+*/
+static void update_relocations_section_symbol_references(
+ Elf *newelf, Elf *elf __attribute__((unused)),
+ shdr_info_t *info,
+ int info_len __attribute__((unused)),
+ shdr_info_t *relsect_info,
+ Elf32_Word *newsymidx)
+{
+ /* Get this relocation section's data */
+ Elf_Data *d = elf_getdata (elf_getscn (newelf, relsect_info->idx), NULL);
+ ASSERT (d != NULL);
+ ASSERT (d->d_size == relsect_info->shdr.sh_size);
+
+ size_t old_nrels =
+ relsect_info->old_shdr.sh_size / relsect_info->old_shdr.sh_entsize;
+ size_t new_nrels =
+ relsect_info->shdr.sh_size / relsect_info->shdr.sh_entsize;
+
+ size_t nrels = new_nrels;
+ if (relsect_info->use_old_shdr_for_relocation_calculations) {
+ nrels = old_nrels;
+ /* Now, we update d->d_size to point to the old size in order to
+ prevent gelf_update_rel() and gelf_update_rela() from returning
+ an error. We restore the value at the end of the function.
+ */
+ d->d_size = old_nrels * relsect_info->shdr.sh_entsize;
+ }
+
+ /* Now, walk the relocations one by one. For each relocation,
+ check to see whether the symbol it refers to has a new
+ index in the symbol table, and if so--update it. We know
+ if a symbol's index has changed when we look up that
+ the newsymidx[] array at the old index. If the value at that
+ location is different from the array index, then the
+ symbol's index has changed; otherwise, it remained the same.
+ */
+ INFO("Scanning %d relocation entries in section [%s] (taken from %s section header (old %d, new %d))...\n",
+ nrels,
+ relsect_info->name,
+ (relsect_info->use_old_shdr_for_relocation_calculations ? "old" : "new"),
+ old_nrels, new_nrels);
+
+ size_t relidx, newidx;
+ if (relsect_info->shdr.sh_type == SHT_REL) {
+ for (newidx = relidx = 0; relidx < nrels; ++relidx) {
+ GElf_Rel rel_mem;
+ FAILIF_LIBELF(gelf_getrel (d, relidx, &rel_mem) == NULL,
+ gelf_getrel);
+ size_t symidx = GELF_R_SYM (rel_mem.r_info);
+ if (newsymidx[symidx] != (Elf32_Word)-1)
+ {
+ rel_mem.r_info = GELF_R_INFO (newsymidx[symidx],
+ GELF_R_TYPE (rel_mem.r_info));
+ FAILIF_LIBELF(gelf_update_rel (d, newidx, &rel_mem) == 0,
+ gelf_update_rel);
+ newidx++;
+ }
+ else {
+ INFO("Discarding REL entry for symbol [%d], section [%d]\n",
+ symidx,
+ relsect_info->shdr.sh_info);
+ }
+ } /* for each rel entry... */
+ } else {
+ for (newidx = relidx = 0; relidx < nrels; ++relidx) {
+ GElf_Rela rel_mem;
+ FAILIF_LIBELF(gelf_getrela (d, relidx, &rel_mem) == NULL,
+ gelf_getrela);
+ size_t symidx = GELF_R_SYM (rel_mem.r_info);
+ if (newsymidx[symidx] != (Elf32_Word)-1)
+ {
+ rel_mem.r_info
+ = GELF_R_INFO (newsymidx[symidx],
+ GELF_R_TYPE (rel_mem.r_info));
+
+ FAILIF_LIBELF(gelf_update_rela (d, newidx, &rel_mem) == 0,
+ gelf_update_rela);
+ newidx++;
+ }
+ else {
+ INFO("Discarding RELA entry for symbol [%d], section [%d]\n",
+ symidx,
+ relsect_info->shdr.sh_info);
+ }
+ } /* for each rela entry... */
+ } /* if rel else rela */
+
+ if (newidx != relidx)
+ {
+ INFO("Shrinking relocation section from %lld to %lld bytes (%d -> %d "
+ "entries).\n",
+ relsect_info->shdr.sh_size,
+ relsect_info->shdr.sh_entsize * newidx,
+ relidx,
+ newidx);
+
+ d->d_size = relsect_info->shdr.sh_size =
+ relsect_info->shdr.sh_entsize * newidx;
+ } else INFO("Relocation section [%s]'s size (relocates: %s(%d), "
+ "symab: %s(%d)) does not change.\n",
+ relsect_info->name,
+ info[relsect_info->shdr.sh_info].name,
+ relsect_info->shdr.sh_info,
+ info[relsect_info->shdr.sh_link].name,
+ relsect_info->shdr.sh_link);
+
+ /* Restore d->d_size if necessary. */
+ if (relsect_info->use_old_shdr_for_relocation_calculations)
+ d->d_size = new_nrels * relsect_info->shdr.sh_entsize;
+}
+
+static void update_relocations_section_offsets(Elf *newelf, Elf *elf __attribute((unused)),
+ Ebl *ebl __attribute__((unused)),
+ shdr_info_t *info,
+ int info_len __attribute__((unused)),
+ shdr_info_t *relsect_info,
+ Elf_Data *d,
+ range_list_t *old_section_ranges)
+{
+ /* Get this relocation section's data */
+ ASSERT (d != NULL);
+ if (d->d_size != relsect_info->shdr.sh_size) {
+ /* This is not necessarily a fatal error. In the case where we call adjust_elf() from apriori
+ (the prelinker), we may call this function for a relocation section that is marked for
+ removal. We still want to process this relocation section because, even though it is marked
+ for removal, its relocatin entries will be used by the prelinker to know what to prelink.
+ Once the prelinker is done, it will call adjust_elf() one more time to actually eliminate the
+ relocation section. */
+ PRINT("WARNING: section size according to section [%s]'s header is %lld, but according to data buffer is %ld.\n",
+ relsect_info->name,
+ relsect_info->shdr.sh_size,
+ d->d_size);
+ ASSERT((relsect_info->shdr.sh_type == SHT_REL || relsect_info->shdr.sh_type == SHT_RELA) &&
+ relsect_info->use_old_shdr_for_relocation_calculations);
+ }
+
+ size_t old_nrels =
+ relsect_info->old_shdr.sh_size / relsect_info->old_shdr.sh_entsize;
+ size_t new_nrels =
+ relsect_info->shdr.sh_size / relsect_info->shdr.sh_entsize;
+
+ size_t nrels = new_nrels;
+ if (relsect_info->use_old_shdr_for_relocation_calculations) {
+ nrels = old_nrels;
+ /* Now, we update d->d_size to point to the old size in order to
+ prevent gelf_update_rel() and gelf_update_rela() from returning
+ an error. We restore the value at the end of the function.
+ */
+ d->d_size = old_nrels * relsect_info->shdr.sh_entsize;
+ }
+
+ /* Now, walk the relocations one by one. For each relocation,
+ check to see whether the symbol it refers to has a new
+ index in the symbol table, and if so--update it. We know
+ if a symbol's index has changed when we look up that
+ the newsymidx[] array at the old index. If the value at that
+ location is different from the array index, then the
+ symbol's index has changed; otherwise, it remained the same.
+ */
+ INFO("Scanning %d relocation entries in section [%s] (taken from %s section header (old %d, new %d))...\n",
+ nrels,
+ relsect_info->name,
+ (relsect_info->use_old_shdr_for_relocation_calculations ? "old" : "new"),
+ old_nrels, new_nrels);
+
+ if (relsect_info->old_shdr.sh_info == 0) {
+ PRINT("WARNING: Relocation section [%s] relocates the NULL section.\n",
+ relsect_info->name);
+ }
+ else {
+ FAILIF(info[relsect_info->old_shdr.sh_info].idx == 0,
+ "Section [%s] relocates section [%s] (index %d), which is being "
+ "removed!\n",
+ relsect_info->name,
+ info[relsect_info->old_shdr.sh_info].name,
+ relsect_info->old_shdr.sh_info);
+ }
+
+ size_t relidx;
+ FAILIF(relsect_info->shdr.sh_type == SHT_RELA,
+ "Can't handle SHT_RELA relocation entries.\n");
+
+ if (relsect_info->shdr.sh_type == SHT_REL) {
+ for (relidx = 0; relidx < nrels; ++relidx) {
+ GElf_Rel rel_mem;
+ FAILIF_LIBELF(gelf_getrel (d, relidx, &rel_mem) == NULL,
+ gelf_getrel);
+
+ range_t *old_range = find_range(old_section_ranges,
+ rel_mem.r_offset);
+#if 1
+ if (NULL == old_range) {
+ GElf_Sym *sym, sym_mem;
+ unsigned sym_idx = GELF_R_SYM(rel_mem.r_info);
+ /* relsect_info->shdr.sh_link is the index of the associated
+ symbol table. */
+ sym = gelf_getsymshndx(info[relsect_info->shdr.sh_link].data,
+ NULL,
+ sym_idx,
+ &sym_mem,
+ NULL);
+ /* info[relsect_info->shdr.sh_link].shdr.sh_link is the index
+ of the string table associated with the symbol table
+ associated with the relocation section rel_sect. */
+ const char *symname = elf_strptr(elf,
+ info[relsect_info->shdr.sh_link].shdr.sh_link,
+ sym->st_name);
+
+ {
+ int i = 0;
+ INFO("ABOUT TO FAIL: old section ranges:\n");
+
+ int num_ranges;
+ range_t *ranges = get_sorted_ranges(old_section_ranges, &num_ranges);
+
+ for (; i < num_ranges; i++) {
+ shdr_info_t *inf = (shdr_info_t *)ranges[i].user;
+ INFO("\t[%8lld, %8lld] (%8lld bytes) [%8lld, %8lld] (%8lld bytes) [%-15s]\n",
+ ranges[i].start,
+ ranges[i].start + ranges[i].length,
+ ranges[i].length,
+ inf->old_shdr.sh_addr,
+ inf->old_shdr.sh_addr + inf->old_shdr.sh_size,
+ inf->old_shdr.sh_size,
+ inf->name);
+ }
+ INFO("\n");
+ }
+
+ FAILIF(1,
+ "No range matches relocation entry value 0x%llx (%d) [%s]!\n",
+ rel_mem.r_offset,
+ rel_mem.r_offset,
+ symname);
+ }
+#else
+ FAILIF(NULL == old_range,
+ "No range matches relocation entry value 0x%llx!\n",
+ rel_mem.r_offset);
+#endif
+ ASSERT(old_range->start <= rel_mem.r_offset &&
+ rel_mem.r_offset < old_range->start + old_range->length);
+ ASSERT(old_range->user);
+ shdr_info_t *old_range_info = (shdr_info_t *)old_range->user;
+ ASSERT(old_range_info->idx > 0);
+ if (relsect_info->old_shdr.sh_info &&
+ old_range_info->idx != relsect_info->old_shdr.sh_info) {
+ PRINT("Relocation offset 0x%llx does not match section [%s] "
+ "but section [%s]!\n",
+ rel_mem.r_offset,
+ info[relsect_info->old_shdr.sh_info].name,
+ old_range_info->name);
+ }
+
+#if 0 /* This is true only for shared libraries, but not for executables */
+ ASSERT(old_range_info->shdr.sh_addr == old_range_info->shdr.sh_offset);
+ ASSERT(old_range_info->old_shdr.sh_addr == old_range_info->old_shdr.sh_offset);
+#endif
+ Elf64_Sxword delta =
+ old_range_info->shdr.sh_addr - old_range_info->old_shdr.sh_addr;
+
+ if (delta) {
+ extern int verbose_flag;
+ /* Print out some info about the relocation entry we are
+ modifying. */
+ if (unlikely(verbose_flag)) {
+ /* Get associated (new) symbol table. */
+ Elf64_Word symtab = relsect_info->shdr.sh_link;
+ /* Get the symbol that is being relocated. */
+ size_t symidx = GELF_R_SYM (rel_mem.r_info);
+ GElf_Sym sym_mem, *sym;
+ /* Since by now we've already updated the symbol index,
+ we need to retrieve the symbol from the new symbol table.
+ */
+ sym = gelf_getsymshndx (elf_getdata(info[symtab].newscn, NULL),
+ NULL,
+ symidx, &sym_mem, NULL);
+ FAILIF_LIBELF(NULL == sym, gelf_getsymshndx);
+ char buf[64];
+ INFO("\t%02d (%-15s) off 0x%llx -> 0x%llx (%lld) (relocates [%s:(%d)%s])\n",
+ (unsigned)GELF_R_TYPE(rel_mem.r_info),
+ ebl_reloc_type_name(ebl,
+ GELF_R_TYPE(rel_mem.r_info),
+ buf,
+ sizeof(buf)),
+ rel_mem.r_offset, rel_mem.r_offset + delta, delta,
+ old_range_info->name,
+ symidx,
+#if ELF_STRPTR_IS_BROKEN
+ /* libelf does not keep track of changes very well.
+ Looks like, if you use elf_strptr() on a file that
+ has not been updated yet, you get bogus results. */
+ ((char *)info[info[symtab].old_shdr.sh_link].
+ newdata->d_buf) + sym->st_name
+#else
+ elf_strptr(newelf,
+ info[symtab].shdr.sh_link,
+ sym->st_name)
+#endif
+ );
+ } /* if (verbose_flag) */
+
+ rel_mem.r_offset += delta;
+ FAILIF_LIBELF(gelf_update_rel (d, relidx, &rel_mem) == 0,
+ gelf_update_rel);
+
+#ifdef ARM_SPECIFIC_HACKS
+ if (GELF_R_TYPE(rel_mem.r_info) == R_ARM_RELATIVE) {
+ FAILIF(GELF_R_SYM(rel_mem.r_info) != 0,
+ "Can't handle relocation!\n");
+ /* From the ARM documentation: "when the symbol is zero,
+ the R_ARM_RELATIVE entry resolves to the difference
+ between the address at which the segment being
+ relocated was loaded and the address at which it
+ was linked."
+ */
+
+ int *ptr =
+ (int *)(((char *)old_range_info->newdata->d_buf) +
+ (rel_mem.r_offset -
+ old_range_info->shdr.sh_addr));
+ *ptr += (int)delta;
+
+ }
+#endif
+ } /* if (delta) */
+ } /* for each rel entry... */
+ }
+
+ /* Restore d->d_size if necessary. */
+ if (relsect_info->use_old_shdr_for_relocation_calculations)
+ d->d_size = new_nrels * relsect_info->shdr.sh_entsize;
+}
+
+static inline
+Elf_Data *create_section_data(shdr_info_t *info, Elf_Scn *scn)
+{
+ Elf_Data *newdata = NULL;
+
+ if (info->data == NULL) {
+ info->data = elf_getdata (info->scn, NULL);
+ FAILIF_LIBELF(NULL == info->data, elf_getdata);
+ INFO("\t\tcopying data from original section (%d bytes).\n",
+ info->data->d_size);
+ /* Set the data. This is done by copying from the old file. */
+ newdata = elf_newdata (scn);
+ FAILIF_LIBELF(newdata == NULL, elf_newdata);
+ /* Copy the structure. Note that the data buffer pointer gets
+ copied, but the buffer itself does not. */
+ *newdata = *info->data;
+#if COPY_SECTION_DATA_BUFFER
+ if (info->data->d_buf != NULL) {
+ newdata->d_buf = MALLOC(newdata->d_size);
+ memcpy(newdata->d_buf, info->data->d_buf, newdata->d_size);
+ }
+#endif
+ } else {
+ INFO("\t\tassigning new data to section (%d bytes).\n",
+ info->data->d_size);
+ newdata = info->data;
+ }
+
+ info->newdata = newdata;
+ return newdata;
+}
+
+#if 0
+static void print_shdr_array(shdr_info_t *info, int num_entries) {
+ extern int verbose_flag;
+ if (verbose_flag) {
+ int i;
+ for (i = 0; i < num_entries; i++) {
+ INFO("%03d:"
+ "\tname [%s]\n"
+ "\tidx [%d]\n",
+ i, info[i].name, info[i].idx);
+ }
+ } /* if (verbose_flag) */
+}
+#endif
+
+static size_t do_update_dyn_entry_address(Elf *elf,
+ GElf_Dyn *dyn,
+ shdr_info_t *shdr_info,
+ int shdr_info_len,
+ int newline)
+{
+ size_t scnidx = 0;
+ INFO("%#0*llx",
+ gelf_getclass (elf) == ELFCLASS32 ? 10 : 18,
+ dyn->d_un.d_val);
+ for (scnidx = 1; scnidx < shdr_info_len; scnidx++) {
+ if (shdr_info[scnidx].old_shdr.sh_addr == dyn->d_un.d_ptr) {
+ if (shdr_info[scnidx].idx > 0) {
+ INFO(" (updating to 0x%08llx per section %d (shdr_info[] index %d): [%s])",
+ shdr_info[scnidx].shdr.sh_addr,
+ shdr_info[scnidx].idx,
+ scnidx,
+ shdr_info[scnidx].name);
+ dyn->d_un.d_ptr = shdr_info[scnidx].shdr.sh_addr;
+ break;
+ }
+ else {
+ /* FIXME: This should be more intelligent. What if there is more than one section that fits the
+ dynamic entry, and just the first such is being removed? We should keep on searching here.
+ */
+ INFO(" (Setting to ZERO per section (shdr_info[] index %d) [%s], which is being removed)",
+ scnidx,
+ shdr_info[scnidx].name);
+ dyn->d_un.d_ptr = 0;
+ break;
+ }
+ }
+ }
+ if (newline) INFO("\n");
+ return scnidx == shdr_info_len ? 0 : scnidx;
+}
+
+static inline size_t update_dyn_entry_address(Elf *elf,
+ GElf_Dyn *dyn,
+ shdr_info_t *shdr_info,
+ int shdr_info_len)
+{
+ return do_update_dyn_entry_address(elf, dyn, shdr_info, shdr_info_len, 1);
+}
+
+static void update_dyn_entry_address_and_size(Elf *elf, Ebl *oldebl,
+ GElf_Dyn *dyn,
+ shdr_info_t *shdr_info,
+ int shdr_info_len,
+ Elf_Data *dyn_data,
+ size_t *dyn_size_entries,
+ int dyn_entry_idx)
+{
+ size_t scnidx = do_update_dyn_entry_address(elf, dyn,
+ shdr_info, shdr_info_len,
+ 0);
+ if (scnidx) {
+ char buf[64];
+ INFO(" (affects tag %s)",
+ ebl_dynamic_tag_name(oldebl, dyn_entry_idx,
+ buf, sizeof (buf)));
+ if (dyn_size_entries[dyn_entry_idx]) {
+ /* We previously encountered this size entry, and because
+ we did not know which section would affect it, we saved its
+ index in the dyn_size_entries[] array so that we can update
+ the entry when we do know. Now we know that the field
+ shdr_info[scnidx].shdr.sh_size contains that new value.
+ */
+ GElf_Dyn *szdyn, szdyn_mem;
+
+ szdyn = gelf_getdyn (dyn_data,
+ dyn_size_entries[dyn_entry_idx],
+ &szdyn_mem);
+ FAILIF_LIBELF(NULL == szdyn, gelf_getdyn);
+ ASSERT(szdyn->d_tag == dyn_entry_idx);
+
+ INFO("\n (!)\t%-17s completing deferred update (%lld -> %lld bytes)"
+ " per section %d [%s]",
+ ebl_dynamic_tag_name (oldebl, szdyn->d_tag,
+ buf, sizeof (buf)),
+ szdyn->d_un.d_val,
+ shdr_info[scnidx].shdr.sh_size,
+ shdr_info[scnidx].idx,
+ shdr_info[scnidx].name);
+
+ szdyn->d_un.d_val = shdr_info[scnidx].shdr.sh_size;
+ FAILIF_LIBELF(0 == gelf_update_dyn(dyn_data,
+ dyn_size_entries[dyn_entry_idx],
+ szdyn),
+ gelf_update_dyn);
+#ifdef DEBUG
+ dyn_size_entries[dyn_entry_idx] = -1;
+#endif
+ }
+ else dyn_size_entries[dyn_entry_idx] = scnidx;
+ } /* if (scnidx) */
+
+ INFO("\n");
+}
+
+static void do_build_dynamic_segment_strings(Elf *elf, Ebl *oldebl,
+ int dynidx, /* index of .dynamic section */
+ int symtabidx, /* index of symbol table section */
+ shdr_info_t *shdr_info,
+ int shdr_info_len __attribute__((unused)),
+ bool print_strings_only)
+{
+ Elf_Scn *dynscn = elf_getscn(elf, dynidx);
+ FAILIF_LIBELF(NULL == dynscn, elf_getscn);
+ Elf_Data *data = elf_getdata (dynscn, NULL);
+ ASSERT(data != NULL);
+
+ size_t cnt;
+
+ if (!print_strings_only) {
+ /* Allocate an array of string-offset structures. */
+ shdr_info[dynidx].symse =
+ (struct Ebl_Strent **)CALLOC(
+ shdr_info[dynidx].shdr.sh_size/shdr_info[dynidx].shdr.sh_entsize,
+ sizeof(struct Ebl_Strent *));
+ }
+
+ for (cnt = 0;
+ cnt < shdr_info[dynidx].shdr.sh_size/shdr_info[dynidx].shdr.sh_entsize;
+ ++cnt)
+ {
+ char buf[64];
+ GElf_Dyn dynmem;
+ GElf_Dyn *dyn;
+
+ dyn = gelf_getdyn (data, cnt, &dynmem);
+ FAILIF_LIBELF(NULL == dyn, gelf_getdyn);
+
+ switch (dyn->d_tag) {
+ case DT_NEEDED:
+ case DT_SONAME:
+ case DT_RPATH:
+ case DT_RUNPATH:
+ {
+ const char *str =
+ elf_strptr (elf,
+ shdr_info[dynidx].shdr.sh_link,
+ dyn->d_un.d_val);
+ ASSERT(str != NULL);
+ INFO("\t\t\t%-17s: ",
+ ebl_dynamic_tag_name (oldebl,
+ dyn->d_tag,
+ buf, sizeof (buf)));
+ INFO("[%s] (offset %ld)\n", str, dyn->d_un.d_val);
+ if (!print_strings_only) {
+ /* We append the strings to the string table belonging to the
+ dynamic-symbol-table section. We keep the dynsymst handle
+ for the strings section in the shdr_info[] entry for the
+ dynamic-sybmol table. Confusing, I know.
+ */
+ ASSERT(shdr_info[symtabidx].dynsymst);
+ /* The string tables for the symbol table and the .dynamic
+ section must be the same.
+ */
+ ASSERT(shdr_info[symtabidx].shdr.sh_link ==
+ shdr_info[dynidx].shdr.sh_link);
+ shdr_info[dynidx].symse[cnt] =
+ ebl_strtabadd(shdr_info[symtabidx].dynsymst, str?:"", 0);
+ ASSERT(shdr_info[dynidx].symse[cnt] != NULL);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ } /* for (...) */
+} /* build_dynamic_segment_strings() */
+
+static void build_dynamic_segment_strings(Elf *elf, Ebl *oldebl,
+ int dynidx, /* index of .dynamic section */
+ int symtabidx, /* index of symbol table section */
+ shdr_info_t *shdr_info,
+ int shdr_info_len __attribute__((unused)))
+{
+ INFO("\t\tbuilding string offsets for dynamic section [%s], index %d\n",
+ shdr_info[dynidx].name,
+ dynidx);
+ do_build_dynamic_segment_strings(elf, oldebl, dynidx, symtabidx,
+ shdr_info, shdr_info_len, false);
+}
+
+#ifdef DEBUG
+static void print_dynamic_segment_strings(Elf *elf, Ebl *oldebl,
+ int dynidx, /* index of .dynamic section */
+ int symtabidx, /* index of symbol table section */
+ shdr_info_t *shdr_info,
+ int shdr_info_len __attribute__((unused)))
+{
+ INFO("\t\tprinting string offsets for dynamic section [%s], index %d\n",
+ shdr_info[dynidx].name,
+ dynidx);
+ do_build_dynamic_segment_strings(elf, oldebl, dynidx, symtabidx,
+ shdr_info, shdr_info_len, true);
+}
+#endif
+
+static void adjust_dynamic_segment_offsets(Elf *elf, Ebl *oldebl,
+ Elf *newelf,
+ int dynidx, /* index of .dynamic section in shdr_info[] */
+ shdr_info_t *shdr_info,
+ int shdr_info_len)
+{
+ Elf_Scn *scn = shdr_info[dynidx].newscn;
+ FAILIF_LIBELF(NULL == scn, elf_getscn);
+ Elf_Data *data = elf_getdata (scn, NULL);
+ ASSERT(data != NULL);
+
+ size_t cnt;
+ INFO("Updating dynamic section [%s], index %d\n",
+ shdr_info[dynidx].name,
+ dynidx);
+
+ size_t *dyn_size_entries = (size_t *)CALLOC(DT_NUM, sizeof(size_t));
+
+ ASSERT(data->d_type == ELF_T_DYN);
+
+ for (cnt = 0; cnt < shdr_info[dynidx].shdr.sh_size / shdr_info[dynidx].shdr.sh_entsize; ++cnt) {
+ char buf[64];
+ GElf_Dyn dynmem;
+ GElf_Dyn *dyn;
+
+ dyn = gelf_getdyn (data, cnt, &dynmem);
+ FAILIF_LIBELF(NULL == dyn, gelf_getdyn);
+
+ INFO("\t%-17s ",
+ ebl_dynamic_tag_name (oldebl, dyn->d_tag, buf, sizeof (buf)));
+
+ switch (dyn->d_tag) {
+ /* Updates to addresses */
+
+ /* We assume that the address entries come before the size entries.
+ */
+
+ case DT_PLTGOT:
+ case DT_HASH:
+ case DT_SYMTAB:
+ (void)update_dyn_entry_address(elf, dyn, shdr_info, shdr_info_len);
+ break;
+ case DT_STRTAB:
+ /* Defer-update DT_STRSZ as well, if not already updated. */
+ update_dyn_entry_address_and_size(elf, oldebl, dyn,
+ shdr_info, shdr_info_len,
+ data,
+ dyn_size_entries,
+ DT_STRSZ);
+ break;
+ case DT_RELA:
+ /* Defer-update DT_RELASZ as well, if not already updated. */
+ update_dyn_entry_address_and_size(elf, oldebl, dyn,
+ shdr_info, shdr_info_len,
+ data,
+ dyn_size_entries,
+ DT_RELASZ);
+ break;
+ case DT_REL:
+ /* Defer-update DT_RELSZ as well, if not already updated. */
+ update_dyn_entry_address_and_size(elf, oldebl, dyn,
+ shdr_info, shdr_info_len,
+ data,
+ dyn_size_entries,
+ DT_RELSZ);
+ break;
+ case DT_JMPREL:
+ /* Defer-update DT_PLTRELSZ as well, if not already updated. */
+ update_dyn_entry_address_and_size(elf, oldebl, dyn,
+ shdr_info, shdr_info_len,
+ data,
+ dyn_size_entries,
+ DT_PLTRELSZ);
+ break;
+ case DT_INIT_ARRAY:
+ case DT_FINI_ARRAY:
+ case DT_PREINIT_ARRAY:
+ case DT_INIT:
+ case DT_FINI:
+ (void)update_dyn_entry_address(elf, dyn, shdr_info, shdr_info_len);
+ break;
+
+ /* Updates to sizes */
+ case DT_PLTRELSZ: /* DT_JMPREL or DT_PLTGOT */
+ case DT_STRSZ: /* DT_STRTAB */
+ case DT_RELSZ: /* DT_REL */
+ case DT_RELASZ: /* DR_RELA */
+ if (dyn_size_entries[dyn->d_tag] == 0) {
+ /* We have not yet found the new size for this entry, so we
+ save the index of the dynamic entry in the dyn_size_entries[]
+ array. When we find the section affecting this field (in
+ code above), we will update the entry.
+ */
+ INFO("(!) (deferring update: new value not known yet)\n");
+ dyn_size_entries[dyn->d_tag] = cnt;
+ }
+ else {
+ ASSERT(dyn_size_entries[dyn->d_tag] < shdr_info_len);
+ INFO("%lld (bytes) (updating to %lld bytes "
+ "per section %d [%s])\n",
+ dyn->d_un.d_val,
+ shdr_info[dyn_size_entries[dyn->d_tag]].shdr.sh_size,
+ shdr_info[dyn_size_entries[dyn->d_tag]].idx,
+ shdr_info[dyn_size_entries[dyn->d_tag]].name);
+ dyn->d_un.d_val =
+ shdr_info[dyn_size_entries[dyn->d_tag]].shdr.sh_size;
+#ifdef DEBUG
+ /* Clear the array so that we know we are done with it. */
+ dyn_size_entries[dyn->d_tag] = (size_t)-1;
+#endif
+ }
+ break;
+ /* End of updates. */
+
+ case DT_NULL:
+ case DT_DEBUG:
+ case DT_BIND_NOW:
+ case DT_TEXTREL:
+ /* No further output. */
+ INFO("\n");
+ break;
+
+ /* String-entry updates. */
+ case DT_NEEDED:
+ case DT_SONAME:
+ case DT_RPATH:
+ case DT_RUNPATH:
+ if (shdr_info[dynidx].symse != NULL)
+ {
+ Elf64_Xword new_offset =
+ ebl_strtaboffset(shdr_info[dynidx].symse[cnt]);
+ INFO("string [%s] offset changes: %lld -> %lld\n",
+ elf_strptr (elf,
+ shdr_info[dynidx].shdr.sh_link,
+ dyn->d_un.d_val),
+ dyn->d_un.d_val,
+ new_offset);
+ dyn->d_un.d_val = new_offset;
+ FAILIF_LIBELF(0 == gelf_update_dyn(data, cnt, dyn),
+ gelf_update_dyn);
+ }
+ else
+ INFO("string [%s] offset has not changed from %lld, not updating\n",
+ elf_strptr (elf,
+ shdr_info[dynidx].shdr.sh_link,
+ dyn->d_un.d_val),
+ dyn->d_un.d_val);
+ break;
+
+ case DT_RELAENT:
+ case DT_SYMENT:
+ case DT_RELENT:
+ case DT_PLTPADSZ:
+ case DT_MOVEENT:
+ case DT_MOVESZ:
+ case DT_INIT_ARRAYSZ:
+ case DT_FINI_ARRAYSZ:
+ case DT_SYMINSZ:
+ case DT_SYMINENT:
+ case DT_GNU_CONFLICTSZ:
+ case DT_GNU_LIBLISTSZ:
+ INFO("%lld (bytes)\n", dyn->d_un.d_val);
+ break;
+
+ case DT_VERDEFNUM:
+ case DT_VERNEEDNUM:
+ case DT_RELACOUNT:
+ case DT_RELCOUNT:
+ INFO("%lld\n", dyn->d_un.d_val);
+ break;
+
+ case DT_PLTREL: /* Specifies whether PLTREL (same as JMPREL) has REL or RELA entries */
+ INFO("%s (%d)\n", ebl_dynamic_tag_name (oldebl, dyn->d_un.d_val, NULL, 0), dyn->d_un.d_val);
+ break;
+
+ default:
+ INFO("%#0*llx\n",
+ gelf_getclass (elf) == ELFCLASS32 ? 10 : 18,
+ dyn->d_un.d_val);
+ break;
+ }
+
+ FAILIF_LIBELF(0 == gelf_update_dyn(data, cnt, dyn),
+ gelf_update_dyn);
+ } /* for (...) */
+
+#ifdef DEBUG
+ if (1) {
+ int i;
+ for (i = 0; i < DT_NUM; i++)
+ ASSERT((ssize_t)dyn_size_entries[i] <= 0);
+ }
+#endif
+
+ FREE(dyn_size_entries);
+} /* adjust_dynamic_segment_offsets() */
+
+static bool section_belongs_to_header(GElf_Shdr *shdr, GElf_Phdr *phdr)
+{
+ if (shdr->sh_size) {
+ /* Compare allocated sections by VMA, unallocated
+ sections by file offset. */
+ if(shdr->sh_flags & SHF_ALLOC) {
+ if(shdr->sh_addr >= phdr->p_vaddr
+ && (shdr->sh_addr + shdr->sh_size
+ <= phdr->p_vaddr + phdr->p_memsz))
+ {
+ return true;
+ }
+ }
+ else {
+ if (shdr->sh_offset >= phdr->p_offset
+ && (shdr->sh_offset + shdr->sh_size
+ <= phdr->p_offset + phdr->p_filesz))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static Elf64_Off section_to_header_mapping(Elf *elf,
+ int phdr_idx,
+ shdr_info_t *shdr_info,
+ int num_shdr_info,
+ Elf64_Off *file_end,
+ Elf64_Off *mem_end)
+{
+ Elf64_Off start;
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (elf, phdr_idx, &phdr_mem);
+ FAILIF_LIBELF(NULL == phdr, gelf_getphdr);
+ size_t inner;
+
+ FAILIF(phdr->p_type == PT_GNU_RELRO,
+ "Can't handle segments of type PT_GNU_RELRO!\n");
+
+ /* Iterate over the sections. */
+ start = (Elf64_Off)-1;
+ *file_end = *mem_end = 0;
+ INFO("\n\t\t");
+ for (inner = 1; inner < num_shdr_info; ++inner)
+ {
+ if (shdr_info[inner].idx > 0) {
+ /* Check to see the section is in the segment. We use the old
+ header because that header contains the old offset and length
+ information about a section.
+ */
+ if (section_belongs_to_header(&shdr_info[inner].old_shdr, phdr))
+ {
+ INFO("%-17s", shdr_info[inner].name);
+#define SECT_MEM_END(s) ((s).sh_addr + (s).sh_size)
+ if ((shdr_info[inner].shdr.sh_flags & SHF_ALLOC)) {
+ if (SECT_MEM_END(shdr_info[inner].shdr) > *mem_end) {
+ INFO("(mem_end 0x%llx --> 0x%llx) ", *mem_end, SECT_MEM_END(shdr_info[inner].shdr));
+ *mem_end = SECT_MEM_END(shdr_info[inner].shdr);
+ }
+#undef SECT_MEM_END
+#define SECT_FILE_END(s) ((s).sh_offset + (s).sh_size)
+ if (shdr_info[inner].shdr.sh_type != SHT_NOBITS) {
+ if (SECT_FILE_END(shdr_info[inner].shdr) > *file_end) {
+ INFO("(file_end 0x%llx --> 0x%llx) ", *file_end, SECT_FILE_END(shdr_info[inner].shdr));
+ *file_end = SECT_FILE_END(shdr_info[inner].shdr);
+ }
+ }
+#undef SECT_FILE_END
+ if (shdr_info[inner].shdr.sh_offset < start) {
+ start = shdr_info[inner].shdr.sh_offset;
+ }
+ } /* if section takes space */
+ INFO("\n\t\t");
+ }
+ else
+ INFO("(!) %-17s does not match\n\t\t", shdr_info[inner].name);
+ }
+ else
+ INFO("(!) %-17s is not considered, it is being removed\n\t\t", shdr_info[inner].name);
+ }
+
+ /* Finish the line. */
+ INFO("start: %lld\n", start);
+ INFO("\t\tends: %lld file, %lld mem\n", *file_end, *mem_end);
+
+ return start;
+}
+
+static void
+update_symbol_values(Elf *elf, GElf_Ehdr *ehdr,
+ Elf *newelf __attribute__((unused)),
+ shdr_info_t *shdr_info,
+ int num_shdr_info,
+ int shady,
+ int dynamic_idx)
+{
+ /* Scan the sections, looking for the symbol table. */
+ size_t i;
+ for (i = 1; i < num_shdr_info; i++) {
+ if (shdr_info[i].idx > 0 &&
+ (shdr_info[i].shdr.sh_type == SHT_SYMTAB ||
+ shdr_info[i].shdr.sh_type == SHT_DYNSYM))
+ {
+ size_t inner;
+ size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, ehdr->e_version);
+ Elf_Data *symdata = shdr_info[i].newdata;
+ /* shdr_info[i].old_shdr.sh_link is the index of the strings table
+ in the old ELF file. This index still points to the same section
+ in the shdr_info[] array. The idx field of that entry is that
+ section's new index. That index must, therefore, be equal to
+ the new value of sh_link. */
+ ASSERT(shdr_info[shdr_info[i].old_shdr.sh_link].idx ==
+ shdr_info[i].shdr.sh_link);
+ ASSERT(shdr_info[shdr_info[i].old_shdr.sh_link].data);
+
+ INFO("\tupdating symbol values for section [%s]...\n",
+ shdr_info[i].name);
+
+#if 1 /* DEBUG */
+ {
+ Elf_Scn *symstrscn = elf_getscn(newelf, shdr_info[i].shdr.sh_link);
+ ASSERT(symstrscn);
+ Elf_Data *symstrdata = elf_getdata(symstrscn, NULL);
+ ASSERT(symstrdata);
+ INFO("%d nonprintable\n",
+ dump_hex_buffer(stdout, symstrdata->d_buf, symstrdata->d_size, 0));
+ }
+#endif
+
+ INFO("\tnumber of symbols to update: %d (%d bytes)\n",
+ symdata->d_size / elsize, symdata->d_size);
+ for (inner = 0; inner < symdata->d_size / elsize; ++inner)
+ {
+ GElf_Sym sym_mem;
+ GElf_Sym *sym;
+ size_t shnum;
+ FAILIF_LIBELF(elf_getshnum (elf, &shnum) < 0, elf_getshnum);
+
+ sym = gelf_getsymshndx (symdata, NULL,
+ inner, &sym_mem, NULL);
+ FAILIF_LIBELF(sym == NULL, gelf_getsymshndx);
+
+#if 0 /* DEBUG */
+ if (shdr_info[i].shdr.sh_type == SHT_SYMTAB) {
+ PRINT("%8d: name %d info %02x other %02x shndx %d size %lld value %lld\n",
+ inner,
+ sym->st_info,
+ sym->st_name,
+ sym->st_other,
+ sym->st_shndx,
+ sym->st_size,
+ sym->st_value);
+ }
+#endif
+
+ size_t scnidx = sym->st_shndx;
+ FAILIF(scnidx == SHN_XINDEX,
+ "Can't handle SHN_XINDEX!\n");
+
+ char *symname = NULL;
+ {
+#if ELF_STRPTR_IS_BROKEN
+ Elf_Scn *symstrscn = elf_getscn(newelf, shdr_info[i].shdr.sh_link);
+ ASSERT(symstrscn);
+ Elf_Data *symstrdata = elf_getdata(symstrscn, NULL);
+ ASSERT(symstrdata);
+ symname = symstrdata->d_buf + sym->st_name;
+#else
+ symname = elf_strptr(newelf,
+ shdr_info[i].shdr.sh_link,
+ sym->st_name);
+#endif
+ }
+
+ extern int verbose_flag;
+ if (unlikely(verbose_flag))
+ {
+ int c, max = 40;
+ INFO("%-8d [", inner);
+ for (c=0; c<max-1; c++) {
+ if (symname[c]) {
+ INFO("%c", symname[c]);
+ }
+ else break;
+ }
+ if (c < max-1) {
+ while (c++ < max) INFO(" ");
+ }
+ else INFO("<");
+ INFO("]");
+ } /* if (unlikely(verbose_flag)) */
+
+ /* Notice that shdr_info[] is an array whose indices correspond
+ to the section indices in the original ELF file. Of those
+ sections, some have been discarded, and one is moved to the
+ end of the file--this is section .shstrtab. Of course, no
+ symbol refers to this section, so it is safe for us to
+ address sections by their original indices in the
+ shdr_info[] array directly.
+ */
+
+ /* Note that we do not skip over the STT_SECTION symbols. Since
+ they contain the addresses of sections, we update their
+ values as well.
+ */
+ if (scnidx == SHN_UNDEF) {
+ INFO(" undefined\n");
+ continue;
+ }
+ if (scnidx >= shnum ||
+ (scnidx >= SHN_LORESERVE &&
+ scnidx <= SHN_HIRESERVE))
+ {
+ INFO(" special (scn %d, value 0x%llx, size %lld)\n",
+ scnidx,
+ sym->st_value,
+ sym->st_size);
+
+ /* We shouldn't be messing with these symbols, but they are
+ often absolute symbols that encode the starting address
+ or the ending address of some section. As a heuristic,
+ we will check to see if the value of the symbol matches
+ the start or the end of any section, and if so, we will
+ update it, but only if --shady is enabled.
+ */
+
+ if (shady && sym->st_value) {
+ size_t scnidx;
+ /* Is it the special symbol _DYNAMIC? */
+ if (!strcmp(symname, "_DYNAMIC")) {
+ /* The _DYNAMIC symbol points to the DYNAMIC
+ segment. It is used by linker to bootstrap
+ itself. */
+ ASSERT(dynamic_idx >= 0);
+ PRINT("*** SHADY *** symbol %s: "
+ "new st_value = %lld (was %lld), "
+ "st_size = %lld (was %lld)\n",
+ symname,
+ shdr_info[dynamic_idx].shdr.sh_addr,
+ sym->st_value,
+ shdr_info[dynamic_idx].shdr.sh_size,
+ sym->st_size);
+ sym->st_value =
+ shdr_info[dynamic_idx].shdr.sh_addr;
+ sym->st_size =
+ shdr_info[dynamic_idx].shdr.sh_size;
+ /* NOTE: We don't update st_shndx, because this is a special
+ symbol. I am not sure if it's necessary though.
+ */
+ FAILIF_LIBELF(gelf_update_symshndx(symdata,
+ NULL,
+ inner,
+ sym,
+ 0) == 0,
+ gelf_update_symshndx);
+ }
+ else {
+ for (scnidx = 1; scnidx < num_shdr_info; scnidx++) {
+ if (sym->st_value ==
+ shdr_info[scnidx].old_shdr.sh_addr) {
+ if (shdr_info[scnidx].shdr.sh_addr !=
+ sym->st_value) {
+ PRINT("*** SHADY *** symbol %s matches old "
+ "start %lld of section %s, updating "
+ "to %lld.\n",
+ symname,
+ shdr_info[scnidx].old_shdr.sh_addr,
+ shdr_info[scnidx].name,
+ shdr_info[scnidx].shdr.sh_addr);
+ sym->st_value = shdr_info[scnidx].shdr.sh_addr;
+ }
+ break;
+ }
+ else {
+ Elf64_Addr oldaddr =
+ shdr_info[scnidx].old_shdr.sh_addr +
+ shdr_info[scnidx].old_shdr.sh_size;
+ if (sym->st_value == oldaddr) {
+ Elf64_Addr newaddr =
+ shdr_info[scnidx].shdr.sh_addr +
+ shdr_info[scnidx].shdr.sh_size;
+ if (newaddr != sym->st_value) {
+ PRINT("*** SHADY *** symbol %s matches old "
+ "end %lld of section %s, updating "
+ "to %lld.\n",
+ symname,
+ oldaddr,
+ shdr_info[scnidx].name,
+ newaddr);
+ sym->st_value = newaddr;
+ }
+ break;
+ }
+ }
+ } /* for each section... */
+ /* NOTE: We don't update st_shndx, because this is a special
+ symbol. I am not sure if it's necessary though.
+ */
+ if (scnidx < num_shdr_info) {
+ FAILIF_LIBELF(gelf_update_symshndx(symdata,
+ NULL,
+ inner,
+ sym,
+ 0) == 0,
+ gelf_update_symshndx);
+ }
+ } /* if symbol is _DYNAMIC else */
+ }
+
+ continue;
+ } /* handle special-section symbols */
+
+ /* The symbol must refer to a section which is not being
+ removed. */
+ if(shdr_info[scnidx].idx == 0)
+ {
+ FAILIF(GELF_ST_TYPE (sym->st_info) != STT_SECTION,
+ "Non-STT_SECTION symbol [%s] refers to section [%s],"
+ " which is being removed.\n",
+ symname,
+ shdr_info[scnidx].name);
+ INFO("STT_SECTION symbol [%s] refers to section [%s], "
+ "which is being removed. Skipping...\n",
+ symname,
+ shdr_info[scnidx].name);
+ continue;
+ }
+
+ INFO(" %8d %-17s ",
+ sym->st_shndx,
+ shdr_info[sym->st_shndx].name);
+
+ /* Has the section's offset (hence its virtual address,
+ because we set that to the same value as the offset) changed?
+ If so, calculate the delta and update the symbol entry.
+ */
+ Elf64_Sxword delta;
+ delta =
+ shdr_info[scnidx].shdr.sh_offset -
+ shdr_info[scnidx].old_shdr.sh_offset;
+
+ Elf64_Sxword vaddr_delta;
+ vaddr_delta =
+ shdr_info[scnidx].shdr.sh_addr -
+ shdr_info[scnidx].old_shdr.sh_addr;
+
+ if (delta || shdr_info[scnidx].idx != scnidx) {
+
+ if (sym->st_value)
+ INFO("0x%llx -> 0x%llx (delta %lld)",
+ sym->st_value,
+ sym->st_value + delta,
+ delta);
+ else {
+ INFO("(value is zero, not adjusting it)",
+ sym->st_value,
+ sym->st_value + delta,
+ delta);
+ /* This might be a bit too paranoid, but symbols with values of
+ zero for which we are not adjusting the value must be in the
+ static-symbol section and refer to a section which is
+ not loaded at run time. If this assertion ever fails, figure
+ out why and also figure out whether the zero value should have
+ been adjusted, after all.
+ */
+ ASSERT(!(shdr_info[sym->st_shndx].shdr.sh_flags & SHF_ALLOC));
+ ASSERT(shdr_info[i].shdr.sh_type == SHT_SYMTAB);
+ }
+
+ /* The section index of the symbol must coincide with
+ the shdr_info[] index of the section that the
+ symbol refers to. Since that section may have been
+ moved, its new setion index, which is stored in
+ the idx field, may have changed. However the index
+ of the original section must match.
+ */
+ ASSERT(scnidx == elf_ndxscn(shdr_info[scnidx].scn));
+
+ if(unlikely(verbose_flag)) {
+ if (shdr_info[scnidx].idx != scnidx) {
+ INFO(" (updating sym->st_shndx = %lld --> %lld)\n",
+ sym->st_shndx,
+ shdr_info[scnidx].idx);
+ }
+ else INFO("(sym->st_shndx remains %lld)\n", sym->st_shndx);
+ }
+
+ sym->st_shndx = shdr_info[scnidx].idx;
+ if (sym->st_value)
+ sym->st_value += delta;
+ FAILIF_LIBELF(gelf_update_symshndx(symdata,
+ NULL,
+ inner,
+ sym,
+ 0) == 0,
+ gelf_update_symshndx);
+ }
+ else {
+ INFO(" (no change)\n");
+ }
+ } /* for each symbol */
+ } /* if it's a symbol table... */
+ } /* for each section... */
+}
+
+static void adjust_section_offset(Elf *newelf,
+ shdr_info_t *shdr_info,
+ Elf64_Sxword delta)
+{
+ Elf_Scn *scn = elf_getscn (newelf, shdr_info->idx);
+ ASSERT(scn != NULL);
+
+ ASSERT(((Elf64_Sxword)shdr_info->shdr.sh_offset) + delta >= 0);
+ shdr_info->shdr.sh_offset += delta;
+ ASSERT(shdr_info->shdr.sh_addralign);
+#ifdef DEBUG
+ /* The assumption is that the delta is calculated so that it will preserve
+ the alignment. Of course, we don't trust ourselves so we verify.
+
+ NOTE: The assertion below need not hold about NOBITS sections (such as
+ the .bss section), for which the offset in the file and the address at
+ which the section is to be loaded may differ.
+ */
+ if (shdr_info->shdr.sh_type != SHT_NOBITS)
+ {
+ Elf64_Off new_offset = shdr_info->shdr.sh_offset;
+ new_offset += shdr_info->shdr.sh_addralign - 1;
+ new_offset &= ~((GElf_Off)(shdr_info->shdr.sh_addralign - 1));
+
+ ASSERT(shdr_info->shdr.sh_offset == new_offset);
+ }
+#endif
+ INFO("\t\t\t\tsection offset %lld -> %lld%s\n",
+ shdr_info->old_shdr.sh_offset,
+ shdr_info->shdr.sh_offset,
+ (shdr_info->old_shdr.sh_offset ==
+ shdr_info->shdr.sh_offset ? " (SAME)" : ""));
+
+ /* If there is a delta for an ALLOC section, then the sections address must match the sections's offset in
+ the file, if that section is not marked SHT_NOBITS. For SHT_NOBITS sections, the two may differ.
+ Note that we compare against the old_shdr.sh_offset because we just modified shdr.sh_offset!
+ */
+
+ ASSERT(!delta ||
+ !(shdr_info->shdr.sh_flags & SHF_ALLOC) ||
+ shdr_info->shdr.sh_type == SHT_NOBITS ||
+ shdr_info->shdr.sh_addr == shdr_info->old_shdr.sh_offset);
+
+ if ((shdr_info->shdr.sh_flags & SHF_ALLOC) == SHF_ALLOC)
+ {
+ ASSERT(shdr_info->shdr.sh_addr);
+ shdr_info->shdr.sh_addr += delta;
+ INFO("\t\t\t\tsection address %lld -> %lld%s\n",
+ shdr_info->old_shdr.sh_addr,
+ shdr_info->shdr.sh_addr,
+ (shdr_info->old_shdr.sh_addr ==
+ shdr_info->shdr.sh_addr ? " (SAME)" : ""));
+ }
+
+ /* Set the section header in the new file. There cannot be any
+ overflows. */
+ INFO("\t\t\t\tupdating section header (size %lld)\n",
+ shdr_info->shdr.sh_size);
+ FAILIF(!gelf_update_shdr (scn, &shdr_info->shdr),
+ "Could not update section header for section %s!\n",
+ shdr_info->name);
+}
+
+#ifdef MOVE_SECTIONS_IN_RANGES
+static int get_end_of_range(shdr_info_t *shdr_info,
+ int num_shdr_info,
+ int start,
+ Elf64_Xword *alignment,
+ Elf32_Word *real_align)
+{
+ int end = start;
+ ASSERT(start < num_shdr_info);
+
+ /* Note that in the loop below we do not check to see if a section is
+ being thrown away. If a section in the middle of a range is thrown
+ away, that will cause the section to be removed, but it will not cause
+ the relative offsets of the sections in the block to be modified.
+ */
+
+ *alignment = real_align[start];
+ while (end < num_shdr_info &&
+ ((shdr_info[end].shdr.sh_flags & SHF_ALLOC) == SHF_ALLOC) &&
+ ((shdr_info[end].shdr.sh_type == SHT_PROGBITS) ||
+ (shdr_info[end].shdr.sh_type == SHT_INIT_ARRAY) ||
+ (shdr_info[end].shdr.sh_type == SHT_FINI_ARRAY) ||
+ (shdr_info[end].shdr.sh_type == SHT_PREINIT_ARRAY) ||
+ /* (shdr_info[end].shdr.sh_type == SHT_NOBITS) || */
+#ifdef ARM_SPECIFIC_HACKS
+ /* SHF_ALLOC sections with with names starting with ".ARM." are
+ part of the ARM EABI extensions to ELF.
+ */
+ !strncmp(shdr_info[end].name, ".ARM.", 5) ||
+#endif
+ (shdr_info[end].shdr.sh_type == SHT_DYNAMIC)))
+ {
+ if (real_align[end] > *alignment) {
+ *alignment = real_align[end];
+ }
+ end++;
+ }
+
+ return end == start ? end + 1 : end;
+}
+#endif/*MOVE_SECTIONS_IN_RANGES*/
+
+static GElf_Off update_last_offset(shdr_info_t *shdr_info,
+ range_list_t *section_ranges,
+ GElf_Off offset)
+{
+ GElf_Off filesz = 0;
+ if (shdr_info->shdr.sh_type != SHT_NOBITS) {
+ /* This function is used as an assertion: if the range we are
+ adding conflicts with another range already in the list,
+ then add_unique_range() will call FAILIF().
+ */
+ add_unique_range_nosort(section_ranges,
+ shdr_info->shdr.sh_offset,
+ shdr_info->shdr.sh_size,
+ shdr_info,
+ handle_range_error,
+ NULL);
+
+ filesz = shdr_info->shdr.sh_size;
+ }
+
+ /* Remember the last section written so far. */
+ if (offset < shdr_info->shdr.sh_offset + filesz) {
+ offset = shdr_info->shdr.sh_offset + filesz;
+ INFO("\t\t\t\tupdated lastoffset to %lld\n", offset);
+ }
+
+ return offset;
+}
+
+static GElf_Off move_sections(Elf *newelf,
+ shdr_info_t *shdr_info,
+ int num_shdr_info,
+ int start,
+ int end,
+ GElf_Off offset,
+ Elf64_Xword alignment,
+ range_list_t *section_ranges,
+ bool adjust_alloc_section_offsets)
+{
+ /* The alignment parameter is expected to contain the largest alignment of
+ all sections in the block. Thus, when we iterate over all sections in
+ the block and apply the same offset to them, we are guaranteed to
+ preserve (a) the relative offsets between the sections in the block and
+ (b) the alignment requirements of each individual section.
+ */
+
+ ASSERT(start < num_shdr_info);
+ ASSERT(end <= num_shdr_info);
+
+ Elf64_Sxword delta = offset - shdr_info[start].shdr.sh_offset;
+ delta += (alignment - 1);
+ delta &= ~(alignment - 1);
+ while (start < end) {
+ if (shdr_info[start].idx > 0) {
+ if (adjust_alloc_section_offsets || (shdr_info[start].shdr.sh_flags & SHF_ALLOC) != SHF_ALLOC) {
+ INFO("\t\t\t%03d:\tAdjusting offset of section %s "
+ "(index %d) from 0x%llx (%lld) to 0x%llx (%lld) (DELTA %lld)...\n",
+ start,
+ (shdr_info[start].name ?: "(no name)"),
+ shdr_info[start].idx,
+ shdr_info[start].old_shdr.sh_offset, shdr_info[start].old_shdr.sh_offset,
+ offset, offset,
+ delta);
+
+ /* Compute the new offset of the section. */
+ adjust_section_offset(newelf, shdr_info + start, delta);
+ }
+ else {
+ INFO("\t\t\t%03d: NOT adjusting offset of section %s (index %d)"
+ ": (not moving SHF_ALLOC sections)...\n",
+ start,
+ (shdr_info[start].name ?: "(no name)"),
+ shdr_info[start].idx);
+ }
+ offset = update_last_offset(shdr_info + start,
+ section_ranges,
+ offset);
+ } /* if (shdr_info[start].idx > 0) */
+ else {
+ INFO("\t\t\t%03d: NOT adjusting offset of section %s (index %d)"
+ " (ignored)...\n",
+ start,
+ (shdr_info[start].name ?: "(no name)"),
+ shdr_info[start].idx);
+ }
+ start++;
+ }
+
+ sort_ranges(section_ranges);
+ return offset;
+}
+
+/* Compute the alignments of sections with consideration of segment
+ alignments. Returns an array of Elf32_Word containing the alignment
+ of sections. Callee is responsible to deallocate the array after use. */
+Elf32_Word *
+get_section_real_align (GElf_Ehdr *ehdr, GElf_Phdr *phdr_info,
+ struct shdr_info_t *shdr_info, int shdr_info_len)
+{
+ size_t max_align_array_size;
+ Elf32_Word *max_align;
+ size_t first_section;
+ bool propagate_p;
+ int si, pi;
+
+ max_align_array_size = sizeof(Elf32_Word) * shdr_info_len;
+ max_align = (Elf32_Word*) malloc (max_align_array_size);
+ FAILIF(!max_align, "malloc(%zu) failed.\n", max_align_array_size);
+
+ /* Initialize alignment array. */
+ max_align[0] = 0;
+ for (si = 1; si < shdr_info_len; si++)
+ max_align[si] = shdr_info[si].shdr.sh_addralign;
+
+ /* Determine which sections need to be aligned with the alignment of
+ containing segments. Becasue the first section in a segment may
+ be deleted, we need to look at all sections and compare their offsets.
+ */
+ for (pi = 0; pi < ehdr->e_phnum; ++pi) {
+ /* Skip null segment. */
+ if (phdr_info[pi].p_type == PT_NULL)
+ continue;
+
+ /* Look for the first non-deleted section of a segment in output.
+ We assume asections are sorted by offsets. Also check to see if
+ a segment starts with a section. We only want to propagate
+ alignment if the segment starts with a section. */
+ propagate_p = false;
+ first_section = 0;
+ for (si = 1; si < shdr_info_len && first_section == 0; si++) {
+ if (shdr_info[si].old_shdr.sh_offset == phdr_info[pi].p_offset)
+ propagate_p = true;
+
+ if (shdr_info[si].idx > 0
+ && section_belongs_to_header(&shdr_info[si].old_shdr,
+ &phdr_info[pi]))
+ first_section = si;
+ }
+
+ if (!propagate_p || first_section == 0)
+ continue;
+
+ /* Adjust alignment of first section. Note that a section can appear
+ in multiple segments. We only need the extra alignment if the
+ section's alignment is smaller than that of the segment. */
+ if (first_section != 0 &&
+ max_align[first_section] < phdr_info[pi].p_align) {
+ max_align[first_section] = phdr_info[pi].p_align;
+ }
+ }
+
+ return max_align;
+}
+
+static range_list_t *
+update_section_offsets(Elf *elf,
+ Elf *newelf,
+ GElf_Phdr *phdr_info,
+ shdr_info_t *shdr_info,
+ int num_shdr_info,
+ range_list_t *section_ranges,
+ bool adjust_alloc_section_offsets)
+{
+ Elf32_Word *real_align;
+
+ ASSERT(section_ranges);
+ INFO("Updating section addresses and offsets...\n");
+ /* The initial value of lastoffset is set to the size of the ELF header
+ plus the size of the program-header table. libelf seems to always
+ place the program-header table for a new file immediately after the
+ ELF header itself... or I could not find any other way to change it
+ otherwise.
+ */
+ GElf_Ehdr ehdr_mem, *ehdr;
+ ehdr = gelf_getehdr (elf, &ehdr_mem);
+ FAILIF_LIBELF(NULL == ehdr, gelf_getehdr);
+ const size_t ehdr_size = gelf_fsize (elf, ELF_T_EHDR, 1, EV_CURRENT);
+ FAILIF(ehdr->e_phoff != ehdr_size,
+ "Expecting the program-header table to follow the ELF header"
+ " immediately!\n");
+
+ GElf_Off lastoffset = 0;
+ lastoffset += ehdr_size;
+ lastoffset += (ehdr->e_phnum + 1) * ehdr->e_phentsize;
+ INFO("Section offsets will start from %lld.\n", lastoffset);
+
+ int start = 1, end = 1;
+ ASSERT(num_shdr_info > 0);
+ real_align = get_section_real_align (ehdr, phdr_info, shdr_info,
+ num_shdr_info);
+ while (end < num_shdr_info) {
+ Elf64_Xword alignment;
+ /* end is the index one past the last section of the block. */
+#ifdef MOVE_SECTIONS_IN_RANGES
+ end = get_end_of_range(shdr_info, num_shdr_info,
+ start, &alignment, real_align);
+#else
+ end = start + 1;
+ alignment = real_align[start];
+#endif
+
+ INFO("\tAdjusting sections [%d - %d) as a group (start offset %lld, alignment %lld)\n",
+ start, end, lastoffset, alignment);
+ lastoffset = move_sections(newelf,
+ shdr_info,
+ num_shdr_info,
+ start, end,
+ lastoffset,
+ alignment,
+ section_ranges,
+ adjust_alloc_section_offsets);
+
+ start = end;
+ }
+
+ ASSERT(lastoffset == get_last_address(section_ranges));
+ free (real_align);
+ return section_ranges;
+}
+
+void handle_range_error(range_error_t err, range_t *left, range_t *right)
+{
+ shdr_info_t *info_l = (shdr_info_t *)left->user;
+ shdr_info_t *info_r = (shdr_info_t *)right->user;
+ ASSERT(info_l);
+ ASSERT(info_r);
+
+ switch (err) {
+ case ERROR_CONTAINS:
+ ERROR("ERROR: section [%s] (%lld, %lld bytes) contains "
+ "section [%s] (%lld, %lld bytes)\n",
+ info_l->name,
+ left->start, left->length,
+ info_r->name,
+ right->start, right->length);
+ break;
+ case ERROR_OVERLAPS:
+ ERROR("ERROR: Section [%s] (%lld, %lld bytes) intersects "
+ "section [%s] (%lld, %lld bytes)\n",
+ info_l->name,
+ left->start, left->length,
+ info_r->name,
+ right->start, right->length);
+ break;
+ default:
+ ASSERT(!"Unknown range error code!");
+ }
+
+ FAILIF(1, "Range error.\n");
+}
+
+#ifdef DEBUG
+
+/* Functions to ELF file is still sane after adjustment. */
+
+static bool
+sections_overlap_p (GElf_Shdr *s1, GElf_Shdr *s2)
+{
+ GElf_Addr a1, a2;
+ GElf_Off o1, o2;
+
+ if ((s1->sh_flags & s2->sh_flags & SHF_ALLOC) != 0) {
+ a1 = (s1->sh_addr > s2->sh_addr)? s1->sh_addr : s2->sh_addr;
+ a2 = ((s1->sh_addr + s1->sh_size < s2->sh_addr + s2->sh_size)?
+ (s1->sh_addr + s1->sh_size) : (s2->sh_addr + s2->sh_size));
+ if (a1 < a2)
+ return true;
+ }
+
+ if (s1->sh_type != SHT_NOBITS && s2->sh_type != SHT_NOBITS) {
+ o1 = (s1->sh_offset > s2->sh_offset)? s1->sh_offset : s2->sh_offset;
+ o2 = ((s1->sh_offset + s1->sh_size < s2->sh_offset + s2->sh_size)?
+ (s1->sh_offset + s1->sh_size) : (s2->sh_offset + s2->sh_size));
+ if (o1 < o2)
+ return true;
+ }
+
+ return false;
+}
+
+/* Return size of the overlapping portion of section S and segment P
+ in memory. */
+
+static GElf_Word
+mem_overlap_size (GElf_Shdr *s, GElf_Phdr *p)
+{
+ GElf_Addr a1, a2;
+
+ if (s->sh_flags & SHF_ALLOC) {
+ a1 = p->p_vaddr > s->sh_addr ? p->p_vaddr : s->sh_addr;
+ a2 = ((p->p_vaddr + p->p_memsz < s->sh_addr + s->sh_size) ?
+ (p->p_vaddr + p->p_memsz) : (s->sh_addr + s->sh_size));
+ if (a1 < a2) {
+ return a2 - a1;
+ }
+ }
+ return 0;
+}
+
+/* Return size of the overlapping portion of section S and segment P
+ in file. */
+
+static GElf_Word
+file_overlap_size (GElf_Shdr *s, GElf_Phdr *p)
+{
+ GElf_Off o1, o2;
+
+ if (s->sh_type != SHT_NOBITS) {
+ o1 = p->p_offset > s->sh_offset ? p->p_offset : s->sh_offset;
+ o2 = ((p->p_offset + p->p_filesz < s->sh_offset + s->sh_size) ?
+ (p->p_offset + p->p_filesz) : (s->sh_offset + s->sh_size));
+ if (o1 < o2) {
+ return o2 - o1;
+ }
+ }
+ return 0;
+}
+
+/* Verify the ELF file is sane. */
+static void
+verify_elf(GElf_Ehdr *ehdr, struct shdr_info_t *shdr_info, int shdr_info_len,
+ GElf_Phdr *phdr_info)
+{
+ int si, sj, pi;
+ GElf_Word addralign;
+ GElf_Word m_size, f_size;
+
+ /* Check all sections */
+ for (si = 1; si < shdr_info_len; si++) {
+ if (shdr_info[si].idx <= 0)
+ continue;
+
+ /* Check alignment */
+ addralign = shdr_info[si].shdr.sh_addralign;
+ if (addralign != 0) {
+ if (shdr_info[si].shdr.sh_flags & SHF_ALLOC) {
+ FAILIF ((addralign - 1) & shdr_info[si].shdr.sh_addr,
+ "Load address %llx of section %s is not "
+ "aligned to multiples of %u\n",
+ (long long unsigned) shdr_info[si].shdr.sh_addr,
+ shdr_info[si].name,
+ addralign);
+ }
+
+ if (shdr_info[si].shdr.sh_type != SHT_NOBITS) {
+ FAILIF ((addralign - 1) & shdr_info[si].shdr.sh_offset,
+ "Offset %lx of section %s is not "
+ "aligned to multiples of %u\n",
+ shdr_info[si].shdr.sh_offset,
+ shdr_info[si].name,
+ addralign);
+ }
+ }
+
+ /* Verify that sections do not overlap. */
+ for (sj = si + 1; sj < shdr_info_len; sj++) {
+ if (shdr_info[sj].idx <= 0)
+ continue;
+
+ FAILIF (sections_overlap_p (&shdr_info[si].shdr,
+ &shdr_info[sj].shdr),
+ "sections %s and %s overlap.\n", shdr_info[si].name,
+ shdr_info[sj].name);
+ }
+
+ /* Verify that section is properly contained in segments. */
+ for (pi = 0; pi < ehdr->e_phnum; pi++) {
+ if (phdr_info[pi].p_type == PT_NULL)
+ continue;
+
+ f_size = file_overlap_size (&shdr_info[si].shdr, &phdr_info[pi]);
+ m_size = mem_overlap_size (&shdr_info[si].shdr, &phdr_info[pi]);
+
+ if (f_size) {
+ FAILIF (shdr_info[si].shdr.sh_size > phdr_info[pi].p_filesz,
+ "Section %s is larger than segment %d\n",
+ shdr_info[si].name, pi);
+ FAILIF (f_size != shdr_info[si].shdr.sh_size,
+ "Section %s partially overlaps segment %d in file.\n",
+ shdr_info[si].name, pi);
+ }
+
+ if (m_size) {
+ FAILIF (shdr_info[si].shdr.sh_size > phdr_info[pi].p_memsz,
+ "Section %s is larger than segment %d\n",
+ shdr_info[si].name, pi);
+ FAILIF (m_size != shdr_info[si].shdr.sh_size,
+ "Section %s partially overlaps segment %d in memory.\n",
+ shdr_info[si].name, pi);
+ }
+
+ }
+ }
+}
+#endif /* DEBUG */
diff --git a/elfcopy.h b/elfcopy.h
new file mode 100644
index 0000000..df448a5
--- /dev/null
+++ b/elfcopy.h
@@ -0,0 +1,94 @@
+#ifndef ELFCOPY_H
+#define ELFCOPY_H
+
+#include <libelf.h>
+#include <libebl.h>
+#include <elf.h>
+#include <gelf.h>
+
+typedef struct shdr_info_t {
+ /* data from original file: */
+ Elf_Scn *scn; /* original section */
+ /* Original-section header. */
+ GElf_Shdr old_shdr;
+ /* Starts out as the original header, but we modify this variable when we
+ compose the new section information. */
+ GElf_Shdr shdr;
+ /* This oddly-named flag causes adjust_elf() to look at the size of the
+ relocation sections before the modification, as opposed to the new
+ size, in order to determine the number of relocation entries. */
+ bool use_old_shdr_for_relocation_calculations;
+ const char *name; /* name of the original section */
+ /* If we do not want to modify a section's data, we set this field to NULL.
+ This will cause clone_elf() to extract the original section's data and
+ copy it over to the new section. If, on the other hand, we do want to
+ change the data, we call elf_newdata() by ourselves and set *data to
+ the return value.
+ */
+ Elf_Data *data;
+ Elf_Data *newdata;
+
+ /* data for new file */
+
+ /* Index in new file. Before we assign numbers to the sections in the
+ new file, the idx field has the following meaning:
+ 0 -- will strip
+ 1 -- present but not yet investigated
+ 2 -- handled (stripped or decided not to stip).
+ */
+ Elf32_Word idx;
+ Elf_Scn *newscn; /* new section handle */
+ struct Ebl_Strent *se; /* contribution to shstr section */
+ /* The following three variables are for symbol-table-sections (SHT_DYNSYM
+ and SHT_SYMTAB).
+
+ newsymidx: contains a mapping between the indices of old symbols and new
+ symbols. If a symbol table has changed, then newsymidx !=
+ NULL; otherwise, it is NULL. Thus newsymidx can be used also
+ as a flag.
+
+ dynsymst: handle to the new symbol-strings section.
+ */
+ Elf32_Word *newsymidx;
+ struct Ebl_Strtab *dynsymst;
+ /* The following variable is used by SHT_DYNSYM, SHT_SYMTAB and SHT_DYNAMIC
+ sections only. For the symbol tables, this is a parallel array to the
+ symbol table that stores the symbol name's index into the symbol-strings
+ table.
+
+ For the dynamic section, this is an array parallel to the array of
+ structures that the dynamic section is; for each structure that
+ represents a string field, the element at the same index into symse
+ contains the offset of that string into the new dynamic-symbol table.
+ */
+ struct Ebl_Strent **symse;
+} shdr_info_t;
+
+/*
+Symbol_filter:
+ On input: symbol_filter[i] indicates whether to keep a symbol (1) or to
+ remove it from the symbol table.
+ On output: symbol_filter[i] indicates whether a symbol was removed (0) or
+ kept (1) in the symbol table.
+*/
+
+void adjust_elf(Elf *elf, const char *elf_name,
+ Elf *newelf, const char *newelf_name,
+ Ebl *ebl,
+ GElf_Ehdr *ehdr, /* store ELF header of original library */
+ bool *sym_filter, int num_symbols,
+ struct shdr_info_t *shdr_info, int shdr_info_len,
+ GElf_Phdr *phdr_info,
+ size_t highest_scn_num,
+ size_t shnum,
+ size_t shstrndx,
+ struct Ebl_Strtab *shst,
+ bool sections_dropped_or_rearranged,
+ int dynamic_idx, /* index in shdr_info[] of .dynamic section */
+ int dynsym_idx, /* index in shdr_info[] of dynamic symbol table */
+ int shady,
+ Elf_Data **shstrtab_data,
+ bool adjust_section_offsets,
+ bool rebuild_shstrtab);
+
+#endif/*ELFCOPY_H*/
diff --git a/fixdwarf.c b/fixdwarf.c
new file mode 100644
index 0000000..5707d5e
--- /dev/null
+++ b/fixdwarf.c
@@ -0,0 +1,577 @@
+#include <fixdwarf.h>
+#include <common.h>
+#include <debug.h>
+#include <hash.h>
+
+#include <libelf.h>
+#include <libebl.h>
+#include <libebl_arm.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/* When this macro is set to a nonzero value, we maintain a BST where we store each address once
+ we update the value at that address, and check to make sure that the address has not been
+ visited before we udpate it. This way we make sure that we do not do multiple updates at any
+ any given address. The feature is disabled by default because it is very expensive. It should
+ be enabled as a first step in debugging problems with the DWARF patches that this code makes.
+*/
+
+#define PARANOIA (0)
+
+#define _str(name) #name
+#define _id(a,b) a ## b
+
+#if PARANOIA
+#define COLLECT_BACKTRACES (0)
+
+#if COLLECT_BACKTRACES
+#include <execinfo.h>
+#endif
+#endif/*PARANOIA*/
+
+#include <dwarf.h>
+
+int load_debug_section (enum dwarf_section_display_enum debug, void *file);
+void free_debug_section (enum dwarf_section_display_enum debug);
+
+static shdr_info_t *s_shdr_info;
+static int s_shdr_info_len;
+static int dwarf_to_shdr[max];
+static shdr_info_t *s_cached_find_section_result = NULL;
+static int s_num_total_patches = 0;
+static int s_num_failed_patches = 0;
+
+static void init_value_free_lists();
+
+#if PARANOIA
+typedef struct value_struct {
+ unsigned long key;
+ struct value_struct *left;
+ struct value_struct *right;
+#if COLLECT_BACKTRACES
+#define BACKTRACE_DEPTH (10)
+ void *backtrace[BACKTRACE_DEPTH];
+ int backtrace_depth;
+#endif/*COLLECT_BACKTRACES*/
+} value_t;
+
+static value_t *s_visited_values; /* BST of visited values */
+#endif/*PARANOIA*/
+
+static void dump_dwarf_section (enum dwarf_section_display_enum dwarf_idx);
+static void byte_set_little_endian (
+ unsigned char *field, int size, dwarf_vma val);
+static void byte_set_big_endian (
+ unsigned char *field, int size, dwarf_vma val);
+static void (*byte_set) (unsigned char *, int, dwarf_vma);
+
+void update_dwarf_if_necessary(Elf *elf __attribute__((unused)),
+ GElf_Ehdr *ehdr,
+ Elf *newelf __attribute__((unused)),
+ shdr_info_t *shdr_info, int num_shdr_info,
+ int *num_total_patches, int *num_failed_patches)
+{
+ /* Find the debug sections */
+
+ int cnt;
+
+ /* Initialize the static variables, which might have been left in
+ nondefault states from a previous call to this function.
+ */
+ s_shdr_info = NULL;
+ s_cached_find_section_result = NULL;
+ s_shdr_info_len = 0;
+ s_num_total_patches = 0;
+ s_num_failed_patches = 0;
+ memset(dwarf_to_shdr, 0, sizeof(dwarf_to_shdr));
+ for(cnt = 0; cnt < max; cnt++)
+ free_debug_section(cnt);
+#if PARANOIA
+ s_visited_values = NULL;
+ init_value_free_lists();
+#endif/*PARANOIA*/
+ init_dwarf_variables();
+
+ cnt = 0;
+
+ /* Locate the .debug_<xxx> sections, and save
+ their indices (in shdr_info) in the respective
+ idx_debug_<xxx> variable. If a section is not
+ prwesent in the file, the variable will have
+ a negative value after this loop.
+ */
+
+#define CHECK_DEBUG_SECTION(sname) \
+ ASSERT(shdr_info[cnt].name != NULL); \
+ if (!strcmp(shdr_info[cnt].name, \
+ ".debug_" _str(sname))) { \
+ FAILIF(dwarf_to_shdr[sname] > 0, \
+ ".debug_" _str(sname) " is already found at index %d!\n", \
+ dwarf_to_shdr[sname]); \
+ INFO("Index of \".debug_" _str(name) " is %d", cnt); \
+ if (shdr_info[cnt].idx > 0) \
+ dwarf_to_shdr[sname] = cnt; \
+ else INFO(", but the section is being removed."); \
+ INFO("\n"); \
+ }
+
+ for(cnt = 1; cnt < num_shdr_info; cnt++) {
+ CHECK_DEBUG_SECTION(aranges);
+ CHECK_DEBUG_SECTION(info);
+ CHECK_DEBUG_SECTION(abbrev);
+ CHECK_DEBUG_SECTION(line);
+ CHECK_DEBUG_SECTION(frame);
+ CHECK_DEBUG_SECTION(loc);
+ CHECK_DEBUG_SECTION(ranges);
+ CHECK_DEBUG_SECTION(pubnames);
+ CHECK_DEBUG_SECTION(str);
+ }
+#undef CHECK_DEBUG_SECTION
+
+ {
+ is_relocatable = (ehdr->e_type == ET_REL);
+ eh_addr_size = 4;
+
+ if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) {
+ byte_get = byte_get_little_endian;
+ byte_set = byte_set_little_endian;
+ }
+ else {
+ ASSERT(ehdr->e_ident[EI_DATA] == ELFDATA2MSB);
+ byte_get = byte_get_big_endian;
+ byte_set = byte_set_big_endian;
+ }
+ }
+
+#define ADJUST_IF_NECESSARY(sname) \
+ do { \
+ if (dwarf_to_shdr[sname] > 0) { \
+ INFO("\nAdjusting for %s.\n", shdr_info[dwarf_to_shdr[sname]].name); \
+ dump_dwarf_section(sname); \
+ } \
+ else { \
+ INFO("\nNot adjusting for %s.\n", shdr_info[dwarf_to_shdr[sname]].name); \
+ } \
+ } while(0)
+
+ s_shdr_info = shdr_info;
+ s_shdr_info_len = num_shdr_info;
+
+ ADJUST_IF_NECESSARY(info);
+ ADJUST_IF_NECESSARY(loc);
+ ADJUST_IF_NECESSARY(aranges);
+ ADJUST_IF_NECESSARY(frame);
+ ADJUST_IF_NECESSARY(ranges);
+ ADJUST_IF_NECESSARY(line);
+ ADJUST_IF_NECESSARY(str);
+ ADJUST_IF_NECESSARY(pubnames);
+ ADJUST_IF_NECESSARY(abbrev);
+
+#undef ADJUST_IF_NECESSRY
+
+ *num_total_patches = s_num_total_patches;
+ *num_failed_patches = s_num_failed_patches;
+}
+
+int
+load_debug_section (enum dwarf_section_display_enum debug,
+ void *file __attribute__((unused)))
+{
+ struct dwarf_section *section = &debug_displays [debug].section;
+ int shdr_idx = dwarf_to_shdr[debug];
+ if (!shdr_idx) {
+ INFO("Could not load section %s: it is not in the file.\n",
+ debug_displays[debug].section.name);
+ return 0;
+ }
+ ASSERT(s_shdr_info);
+
+ INFO("Loading DWARF section type %s index %d (type %d)\n",
+ s_shdr_info[shdr_idx].name,
+ s_shdr_info[shdr_idx].idx,
+ debug);
+
+ /* If it is already loaded, do nothing. */
+ if (section->start != NULL) {
+ INFO("\tAlready loaded DWARF section type %s (type %d)\n", s_shdr_info[shdr_idx].name, debug);
+ return 1;
+ }
+
+ ASSERT(s_shdr_info[shdr_idx].newdata);
+
+ section->address = s_shdr_info[shdr_idx].shdr.sh_addr;
+ section->start = s_shdr_info[shdr_idx].newdata->d_buf;
+ section->size = s_shdr_info[shdr_idx].newdata->d_size;
+ ASSERT(s_shdr_info[shdr_idx].newdata->d_off == 0);
+
+ ASSERT(section->size != 0);
+ ASSERT(s_shdr_info[shdr_idx].shdr.sh_size == s_shdr_info[shdr_idx].newdata->d_size);
+ ASSERT(section->start != NULL);
+
+ return 1;
+}
+
+void
+free_debug_section (enum dwarf_section_display_enum debug)
+{
+ struct dwarf_section *section = &debug_displays [debug].section;
+
+ INFO("Unloading DWARF section type %d\n", debug);
+
+ if (section->start == NULL)
+ return;
+
+ section->start = NULL;
+ section->address = 0;
+ section->size = 0;
+}
+
+static void
+dump_dwarf_section (enum dwarf_section_display_enum dwarf_idx)
+{
+ int shdr_idx = dwarf_to_shdr[dwarf_idx];
+ ASSERT(shdr_idx);
+ ASSERT(s_shdr_info);
+ ASSERT(s_shdr_info[shdr_idx].idx);
+ ASSERT(s_shdr_info[shdr_idx].name);
+
+ ASSERT(!strcmp (debug_displays[dwarf_idx].section.name, s_shdr_info[shdr_idx].name));
+
+ if (!debug_displays[dwarf_idx].eh_frame) {
+ struct dwarf_section *sec = &debug_displays [dwarf_idx].section;
+
+ if (load_debug_section (dwarf_idx, NULL)) {
+ INFO("Dumping DWARF section [%s] (type %d).\n",
+ s_shdr_info[shdr_idx].name,
+ dwarf_idx);
+ debug_displays[dwarf_idx].display (sec, NULL);
+ if (dwarf_idx != info && dwarf_idx != abbrev)
+ free_debug_section (dwarf_idx);
+ }
+ }
+}
+
+static shdr_info_t *find_section(int value)
+{
+ ASSERT(s_shdr_info != NULL);
+ ASSERT(s_shdr_info_len > 0);
+
+#define IN_RANGE(v,s,l) ((s)<=(v) && (v)<((s)+(l)))
+ if (s_cached_find_section_result != NULL &&
+ IN_RANGE((unsigned)value,
+ s_cached_find_section_result->old_shdr.sh_addr,
+ s_cached_find_section_result->old_shdr.sh_size)) {
+ return s_cached_find_section_result;
+ }
+
+ /* Find the section to which the address belongs. */
+ int cnt;
+ for (cnt = 0; cnt < s_shdr_info_len; cnt++) {
+ if (s_shdr_info[cnt].idx > 0 &&
+ (s_shdr_info[cnt].old_shdr.sh_flags & SHF_ALLOC) &&
+ IN_RANGE((unsigned) value,
+ s_shdr_info[cnt].old_shdr.sh_addr,
+ s_shdr_info[cnt].old_shdr.sh_size)) {
+
+ s_cached_find_section_result = s_shdr_info + cnt;
+ return s_cached_find_section_result;
+ }
+ }
+#undef IN_RANGE
+
+ return NULL;
+}
+
+#if PARANOIA
+static value_t **s_value_free_lists;
+static int s_num_free_lists;
+static int s_cur_free_list;
+static int s_alloc_values; /* number of allocated values in the list */
+#define LISTS_INCREMENT (10)
+#define NUM_VALUES_PER_LIST (10000)
+
+static void init_value_free_lists()
+{
+ if (s_value_free_lists) {
+ value_t **trav = s_value_free_lists;
+ while(s_cur_free_list) {
+ FREE(*trav++);
+ s_cur_free_list--;
+ }
+ FREE(s_value_free_lists);
+ s_value_free_lists = NULL;
+ }
+ s_num_free_lists = 0;
+ s_alloc_values = 0;
+}
+
+static value_t *alloc_value()
+{
+ if (s_alloc_values == NUM_VALUES_PER_LIST) {
+ s_cur_free_list++;
+ s_alloc_values = 0;
+ }
+
+ if (s_cur_free_list == s_num_free_lists) {
+ s_num_free_lists += LISTS_INCREMENT;
+ s_value_free_lists = REALLOC(s_value_free_lists,
+ s_num_free_lists * sizeof(value_t *));
+ memset(s_value_free_lists + s_cur_free_list,
+ 0,
+ (s_num_free_lists - s_cur_free_list) * sizeof(value_t *));
+ }
+
+ if (s_value_free_lists[s_cur_free_list] == NULL) {
+ s_value_free_lists[s_cur_free_list] = MALLOC(NUM_VALUES_PER_LIST*sizeof(value_t));
+ }
+
+ return s_value_free_lists[s_cur_free_list] + s_alloc_values++;
+}
+
+static value_t *would_be_parent = NULL;
+static value_t *find_value(unsigned long val)
+{
+ would_be_parent = NULL;
+ value_t *trav = s_visited_values;
+ while(trav) {
+ would_be_parent = trav;
+ if (val < trav->key)
+ trav = trav->left;
+ else if (val > trav->key)
+ trav = trav->right;
+ else if (val == trav->key) {
+ return trav;
+ }
+ }
+ return NULL;
+}
+
+static int value_visited(unsigned long val)
+{
+ value_t *found = find_value(val);
+ if (found != NULL) {
+#if COLLECT_BACKTRACES
+ void *new_bt[BACKTRACE_DEPTH];
+ int new_bt_depth = backtrace(new_bt, BACKTRACE_DEPTH);
+ char **symbols = backtrace_symbols(new_bt, new_bt_depth);
+ PRINT("NEW VISIT AT %x\n", val);
+ if (symbols != NULL) {
+ int cnt = 0;
+ while(cnt < new_bt_depth) {
+ PRINT("\t%s\n", symbols[cnt]);
+ cnt++;
+ }
+ }
+ FREE(symbols);
+ PRINT("OLD VISIT AT %x\n", val);
+ symbols = backtrace_symbols(found->backtrace, found->backtrace_depth);
+ if (symbols != NULL) {
+ int cnt = 0;
+ while(cnt < new_bt_depth) {
+ PRINT("\t%s\n", symbols[cnt]);
+ cnt++;
+ }
+ }
+ FREE(symbols);
+#else
+ ERROR("DWARF: Double update at address 0x%lx!\n", val);
+#endif/*COLLECT_BACKTRACES*/
+ return 1;
+ }
+ found = alloc_value();
+ found->left = found->right = NULL;
+ found->key = val;
+#if COLLECT_BACKTRACES
+ found->backtrace_depth = backtrace(found->backtrace, BACKTRACE_DEPTH);
+#endif/*COLLECT_BACKTRACES*/
+ if (would_be_parent == NULL) {
+ s_visited_values = found;
+ } else {
+ if (val < would_be_parent->key)
+ would_be_parent->left = found;
+ else
+ would_be_parent->right = found;
+ }
+ return 0;
+}
+#else
+static int value_visited(unsigned long val __attribute__((unused)))
+{
+ return 0;
+}
+#endif /*PARANOIA*/
+
+void value_hook(void *data, int size, int val)
+{
+ shdr_info_t *shdr = find_section(val);
+ s_num_total_patches++;
+ if(shdr == NULL) {
+ PRINT("DWARF: cannot map address 0x%x to any section!\n", val);
+ s_num_failed_patches++;
+ return;
+ }
+ long delta = shdr->shdr.sh_addr - shdr->old_shdr.sh_addr;
+ if(delta) {
+ if (!value_visited((unsigned long)data)) {
+ INFO("DWARF: adjusting %d-byte value at %p: 0x%x -> 0x%x (delta %d per section %s)\n",
+ size, data,
+ val, (int)(val + delta), (int)delta,
+ shdr->name);
+ byte_set(data, size, val + delta);
+ }
+ }
+}
+
+void base_value_pair_hook(void *data, int size,
+ int base, int begin, int end)
+{
+ shdr_info_t *shdr = find_section(base + begin);
+ s_num_total_patches++;
+
+ if (begin > end) {
+ PRINT("DWARF: start > end in range 0x%x:[0x%x, 0x%x)!\n",
+ base,
+ begin,
+ end);
+ s_num_failed_patches++;
+ return;
+ }
+
+ if(shdr == NULL) {
+ PRINT("DWARF: cannot map range 0x%x:[0x%x, 0x%x) to any section!\n",
+ base,
+ begin,
+ end);
+ s_num_failed_patches++;
+ return;
+ }
+
+ if (unlikely(begin != end)) {
+ shdr_info_t *end_shdr = find_section(base + end - 1);
+ if (shdr != end_shdr) {
+ printf("DWARF: range 0x%x:[%x, %x) maps to different sections: %s and %s!\n",
+ base,
+ begin, end,
+ shdr->name,
+ (end_shdr ? end_shdr->name : "(none)"));
+ s_num_failed_patches++;
+ return;
+ }
+ }
+
+ long delta = shdr->shdr.sh_addr - shdr->old_shdr.sh_addr;
+ if(delta) {
+ if (!value_visited((unsigned long)data)) {
+ INFO("DWARF: adjusting %d-byte value at %p: 0x%x -> 0x%x (delta %d per section %s)\n",
+ size, data,
+ begin, (int)(begin + delta), (int)delta,
+ shdr->name);
+ byte_set(data, size, begin + delta);
+ byte_set(data + size, size, end + delta);
+ }
+ }
+}
+
+void signed_value_hook(
+ void *data,
+ int pointer_size,
+ int is_signed,
+ int value)
+{
+ INFO("DWARF frame info: initial PC value: %8x (width %d), %ssigned\n",
+ value, pointer_size,
+ (!is_signed ? "un" : ""));
+
+ ASSERT(s_shdr_info != NULL);
+
+ /* Find the section to which the address belongs. */
+ shdr_info_t *shdr = find_section(value);
+ s_num_total_patches++;
+ if(shdr == NULL) {
+ PRINT("DWARF: cannot map address 0x%x to any section!\n", value);
+ s_num_failed_patches++;
+ return;
+ }
+
+ long delta = shdr->shdr.sh_addr - shdr->old_shdr.sh_addr;
+
+ INFO("DWARF frame info: initial PC value: 0x%lx -> 0x%lx (delta %ld per section %s).\n",
+ (long)value,
+ (long)(value + delta),
+ delta,
+ shdr->name);
+
+ if (delta) {
+ if (!value_visited((unsigned long)data)) {
+ value += delta;
+ if (is_signed) {
+ switch (pointer_size) {
+ case 1:
+ value &= 0xFF;
+ value = (value ^ 0x80) - 0x80;
+ break;
+ case 2:
+ value &= 0xFFFF;
+ value = (value ^ 0x8000) - 0x8000;
+ break;
+ case 4:
+ value &= 0xFFFFFFFF;
+ value = (value ^ 0x80000000) - 0x80000000;
+ break;
+ case 8:
+ break;
+ default:
+ FAILIF(1, "Unsupported data size %d!\n", pointer_size);
+ }
+ }
+ byte_set(data, pointer_size, value);
+ }
+ }
+}
+
+static void byte_set_little_endian (unsigned char *field, int size, dwarf_vma val)
+{
+ switch (size) {
+ case 1:
+ FAILIF(val > 0xFF,
+ "Attempting to set value 0x%lx to %d-bit integer!\n",
+ val, size*8);
+ *((uint8_t *)field) = (uint8_t)val;
+ break;
+ case 2:
+ FAILIF(val > 0xFFFF,
+ "Attempting to set value 0x%lx to %d-bit integer!\n",
+ val, size*8);
+ field[1] = (uint8_t)(val >> 8);
+ field[0] = (uint8_t)val;
+ break;
+ case 4:
+#if 0
+ // this will signal false negatives when running on a 64 bit system.
+ FAILIF(val > 0xFFFFFFFF,
+ "Attempting to set value 0x%lx to %d-bit integer!\n",
+ val, size*8);
+#endif
+ field[3] = (uint8_t)(val >> 23);
+ field[2] = (uint8_t)(val >> 16);
+ field[1] = (uint8_t)(val >> 8);
+ field[0] = (uint8_t)val;
+ break;
+ default:
+ FAILIF(1, "Unhandled data length: %d\n", size);
+ }
+}
+
+static void byte_set_big_endian (unsigned char *field __attribute__((unused)),
+ int size __attribute__((unused)),
+ dwarf_vma val __attribute__((unused)))
+{
+ FAILIF(1, "Not implemented.\n");
+}
diff --git a/fixdwarf.h b/fixdwarf.h
new file mode 100644
index 0000000..32f2f48
--- /dev/null
+++ b/fixdwarf.h
@@ -0,0 +1,14 @@
+#ifndef FIXDWARF_H
+#define FIXDWARF_H
+
+#include <elf.h>
+#include <gelf.h>
+#include <elfcopy.h>
+
+extern void update_dwarf_if_necessary(
+ Elf *elf, GElf_Ehdr *ehdr, Elf *newelf,
+ shdr_info_t *shdr_info, int num_shdr_info,
+ int *num_total_patches, int *num_failed_patches);
+
+
+#endif
diff --git a/hash.c b/hash.c
new file mode 100644
index 0000000..e2cffcc
--- /dev/null
+++ b/hash.c
@@ -0,0 +1,76 @@
+#include <common.h>
+#include <debug.h>
+#include <libelf.h>
+#include <hash.h>
+#include <string.h>
+
+void setup_hash(Elf_Data *hash_data,
+ Elf32_Word nbuckets,
+ Elf32_Word nchains)
+{
+ hash_data->d_size = 2;
+ hash_data->d_size += nbuckets;
+ hash_data->d_size += nchains;
+ hash_data->d_buf = CALLOC(hash_data->d_size, sizeof(Elf32_Word));
+ hash_data->d_size *= sizeof(Elf32_Word);
+ ((Elf32_Word *)hash_data->d_buf)[0] = nbuckets;
+ ((Elf32_Word *)hash_data->d_buf)[1] = nchains;
+}
+
+void add_to_hash(Elf_Data *hash_data,
+ const char *symbol,
+ int symindex)
+{
+ Elf32_Word *buckets = (Elf32_Word *)hash_data->d_buf;
+ Elf32_Word nbuckets = *buckets++;
+ Elf32_Word *chains = ++buckets + nbuckets;
+ Elf32_Word last_chain_index;
+ unsigned long bucket = elf_hash(symbol) % nbuckets;
+
+ ASSERT(symindex != STN_UNDEF);
+
+ if (buckets[bucket] == STN_UNDEF) {
+ INFO("Adding [%s] to hash at bucket [%ld] (first add)\n",
+ symbol, bucket);
+ buckets[bucket] = symindex;
+ }
+ else {
+ INFO("Collision on adding [%s] to hash at bucket [%ld]\n",
+ symbol, bucket);
+ last_chain_index = buckets[bucket];
+ while (chains[last_chain_index] != STN_UNDEF) {
+ INFO("\ttrying at chain index [%d]...\n", last_chain_index);
+ last_chain_index = chains[last_chain_index];
+ }
+ INFO("\tsuccess at chain index [%d]...\n", last_chain_index);
+ chains[last_chain_index] = symindex;
+ }
+}
+
+int hash_lookup(Elf *elf,
+ section_info_t *hash_info,
+ section_info_t *symtab_info,
+ const char *symname,
+ GElf_Sym *sym_mem)
+{
+ Elf32_Word *hash_data = (Elf32_Word *)hash_info->data->d_buf;
+ Elf32_Word index;
+ Elf32_Word nbuckets = *hash_data++;
+ Elf32_Word *buckets = ++hash_data;
+ Elf32_Word *chains = hash_data + nbuckets;
+
+ GElf_Sym *sym;
+
+ index = buckets[elf_hash(symname) % nbuckets];
+ while(index != STN_UNDEF)
+ {
+ sym = gelf_getsymshndx (symtab_info->data, NULL, index, sym_mem, NULL);
+ FAILIF_LIBELF(NULL == sym, gelf_getsymshndx);
+ if (!strcmp(symname,
+ elf_strptr(elf, symtab_info->hdr->sh_link, sym->st_name)))
+ break;
+ index = chains[index];
+ }
+
+ return index;
+}
diff --git a/hash.h b/hash.h
new file mode 100644
index 0000000..f2d5806
--- /dev/null
+++ b/hash.h
@@ -0,0 +1,22 @@
+#ifndef HASH_H
+#define HASH_H
+
+#include <common.h>
+#include <libelf.h>
+#include <gelf.h>
+
+void setup_hash(Elf_Data *hash_data,
+ Elf32_Word nbuckets,
+ Elf32_Word nchains);
+
+void add_to_hash(Elf_Data *hash_data,
+ const char *symbol,
+ int symindex);
+
+int hash_lookup(Elf *elf,
+ section_info_t *hash,
+ section_info_t *symtab,
+ const char *symname,
+ GElf_Sym *sym_mem);
+
+#endif/*HASH_H*/
diff --git a/rangesort.c b/rangesort.c
new file mode 100644
index 0000000..fa35325
--- /dev/null
+++ b/rangesort.c
@@ -0,0 +1,335 @@
+#include <common.h>
+#include <debug.h>
+#include <rangesort.h>
+
+#define PARALLEL_ARRAY_SIZE (5)
+
+struct range_list_t {
+ range_t *array;
+#ifdef DEBUG
+ int is_sorted;
+#endif
+ int array_length;
+ int num_ranges;
+};
+
+range_list_t* init_range_list(void) {
+ range_list_t *ranges = (range_list_t *)MALLOC(sizeof(range_list_t));
+
+ ranges->array = (range_t *)MALLOC(PARALLEL_ARRAY_SIZE*sizeof(range_t));
+ ranges->array_length = PARALLEL_ARRAY_SIZE;
+ ranges->num_ranges = 0;
+#ifdef DEBUG
+ ranges->is_sorted = 0;
+#endif
+ return ranges;
+}
+
+void destroy_range_list(range_list_t *ranges) {
+ int idx;
+ for (idx = 0; idx < ranges->num_ranges; idx++) {
+ if (ranges->array[idx].user_dtor) {
+ ASSERT(ranges->array[idx].user);
+ ranges->array[idx].user_dtor(ranges->array[idx].user);
+ }
+ }
+ FREE(ranges->array);
+ FREE(ranges);
+}
+
+static inline int CONTAINS(range_t *container, range_t *contained) {
+ return container->start <= contained->start && contained->length &&
+ (container->start + container->length >
+ contained->start + contained->length);
+}
+
+static inline int IN_RANGE(range_t *range, GElf_Off point) {
+ return
+ range->start <= point &&
+ point < (range->start + range->length);
+}
+
+static inline int INTERSECT(range_t *left, range_t *right) {
+ return
+ (IN_RANGE(left, right->start) &&
+ IN_RANGE(right, left->start + left->length)) ||
+ (IN_RANGE(right, left->start) &&
+ IN_RANGE(left, right->start + right->length));
+}
+
+static int range_cmp_for_search(const void *l, const void *r) {
+ range_t *left = (range_t *)l, *right = (range_t *)r;
+ if (INTERSECT(left, right) ||
+ CONTAINS(left, right) ||
+ CONTAINS(right, left)) {
+ return 0;
+ }
+
+ /* elfcopy.c checks that the start of a section begins at or
+ after end of the previous section in the sorted list. So we need
+ to be careful about empty sections. */
+ if (left->start != right->start)
+ return left->start - right->start;
+ else {
+ ASSERT(left->length == 0 || right->length == 0);
+ return left->length - right->length;
+ }
+}
+
+static inline void run_checks(const void *l, const void *r) {
+ range_t *left = (range_t *)l, *right = (range_t *)r;
+ if (CONTAINS(left, right)) {
+ if (left->err_fn)
+ left->err_fn(ERROR_CONTAINS, left, right);
+ FAILIF(1, "Range sorting error: [%lld, %lld) contains [%lld, %lld)!\n",
+ left->start, left->start + left->length,
+ right->start, right->start + right->length);
+ }
+ if (CONTAINS(right, left)) {
+ if (right->err_fn)
+ right->err_fn(ERROR_CONTAINS, left, right);
+ FAILIF(1, "Range sorting error: [%lld, %lld) contains [%lld, %lld)!\n",
+ right->start, right->start + right->length,
+ left->start, left->start + left->length);
+ }
+ if (INTERSECT(left, right)) {
+ if (left->err_fn)
+ left->err_fn(ERROR_OVERLAPS, left, right);
+ FAILIF(1, "Range sorting error: [%lld, %lld)and [%lld, %lld) intersect!\n",
+ left->start, left->start + left->length,
+ right->start, right->start + right->length);
+ }
+}
+
+static int range_cmp(const void *l, const void *r) {
+ run_checks(l, r);
+ range_t *left = (range_t *)l, *right = (range_t *)r;
+
+ /* elfcopy.c checks that the start of a section begins at or
+ after end of the previous section in the sorted list. So we need
+ to be careful about empty sections. */
+ if (left->start != right->start)
+ return left->start - right->start;
+ else {
+ ASSERT(left->length == 0 || right->length == 0);
+ return left->length - right->length;
+ }
+}
+
+void add_unique_range_nosort(
+ range_list_t *ranges,
+ GElf_Off start,
+ GElf_Off length,
+ void *user,
+ void (*err_fn)(range_error_t, range_t *, range_t *),
+ void (*user_dtor)(void * ))
+{
+ if (ranges->num_ranges == ranges->array_length) {
+ ranges->array_length += PARALLEL_ARRAY_SIZE;
+ ranges->array = REALLOC(ranges->array,
+ ranges->array_length*sizeof(range_t));
+ }
+ ranges->array[ranges->num_ranges].start = start;
+ ranges->array[ranges->num_ranges].length = length;
+ ranges->array[ranges->num_ranges].user = user;
+ ranges->array[ranges->num_ranges].err_fn = err_fn;
+ ranges->array[ranges->num_ranges].user_dtor = user_dtor;
+ ranges->num_ranges++;
+}
+
+range_list_t *sort_ranges(range_list_t *ranges) {
+ if (ranges->num_ranges > 1)
+ qsort(ranges->array, ranges->num_ranges, sizeof(range_t), range_cmp);
+ ranges->is_sorted = 1;
+ return ranges;
+}
+
+range_t *find_range(range_list_t *ranges, GElf_Off value) {
+#if 1
+ int i;
+ for (i = 0; i < ranges->num_ranges; i++) {
+ if (ranges->array[i].start <= value &&
+ value < ranges->array[i].start + ranges->array[i].length)
+ return ranges->array + i;
+ }
+ return NULL;
+#else
+ ASSERT(ranges->is_sorted); /* The range list must be sorted */
+ range_t lookup;
+ lookup.start = value;
+ lookup.length = 0;
+ return
+ (range_t *)bsearch(&lookup,
+ ranges->array, ranges->num_ranges, sizeof(range_t),
+ range_cmp_for_search);
+#endif
+}
+
+int get_num_ranges(const range_list_t *ranges)
+{
+ return ranges->num_ranges;
+}
+
+range_t *get_sorted_ranges(const range_list_t *ranges, int *num_ranges) {
+ ASSERT(ranges->is_sorted); /* The range list must be sorted */
+ if (num_ranges) {
+ *num_ranges = ranges->num_ranges;
+ }
+ return ranges->array;
+}
+
+GElf_Off get_last_address(const range_list_t *ranges) {
+ ASSERT(ranges->num_ranges);
+ return
+ ranges->array[ranges->num_ranges-1].start +
+ ranges->array[ranges->num_ranges-1].length;
+}
+
+static void handle_range_error(range_error_t err,
+ range_t *left, range_t *right) {
+ switch (err) {
+ case ERROR_CONTAINS:
+ ERROR("ERROR: section (%lld, %lld bytes) contains "
+ "section (%lld, %lld bytes)\n",
+ left->start, left->length,
+ right->start, right->length);
+ break;
+ case ERROR_OVERLAPS:
+ ERROR("ERROR: Section (%lld, %lld bytes) intersects "
+ "section (%lld, %lld bytes)\n",
+ left->start, left->length,
+ right->start, right->length);
+ break;
+ default:
+ ASSERT(!"Unknown range error code!");
+ }
+
+ FAILIF(1, "Range error.\n");
+}
+
+static void destroy_contiguous_range_info(void *user) {
+ contiguous_range_info_t *info = (contiguous_range_info_t *)user;
+ FREE(info->ranges);
+ FREE(info);
+}
+
+static void handle_contiguous_range_error(range_error_t err,
+ range_t *left,
+ range_t *right)
+{
+ contiguous_range_info_t *left_data =
+ (contiguous_range_info_t *)left->user;
+ ASSERT(left_data);
+ contiguous_range_info_t *right_data =
+ (contiguous_range_info_t *)right->user;
+ ASSERT(right_data);
+
+ PRINT("Contiguous-range overlap error. Printing contained ranges:\n");
+ int cnt;
+ PRINT("\tLeft ranges:\n");
+ for (cnt = 0; cnt < left_data->num_ranges; cnt++) {
+ PRINT("\t\t[%lld, %lld)\n",
+ left_data->ranges[cnt].start,
+ left_data->ranges[cnt].start + left_data->ranges[cnt].length);
+ }
+ PRINT("\tRight ranges:\n");
+ for (cnt = 0; cnt < right_data->num_ranges; cnt++) {
+ PRINT("\t\t[%lld, %lld)\n",
+ right_data->ranges[cnt].start,
+ right_data->ranges[cnt].start + right_data->ranges[cnt].length);
+ }
+
+ handle_range_error(err, left, right);
+}
+
+range_list_t* get_contiguous_ranges(const range_list_t *input)
+{
+ ASSERT(input);
+ FAILIF(!input->is_sorted,
+ "get_contiguous_ranges(): input range list is not sorted!\n");
+
+ range_list_t* ret = init_range_list();
+ int num_ranges;
+ range_t *ranges = get_sorted_ranges(input, &num_ranges);
+
+ int end_idx = 0;
+ while (end_idx < num_ranges) {
+ int start_idx = end_idx++;
+ int old_end_idx = start_idx;
+ int total_length = ranges[start_idx].length;
+ while (end_idx < num_ranges) {
+ if (ranges[old_end_idx].start + ranges[old_end_idx].length !=
+ ranges[end_idx].start)
+ break;
+ old_end_idx = end_idx++;
+ total_length += ranges[old_end_idx].length;
+ }
+
+ contiguous_range_info_t *user =
+ (contiguous_range_info_t *)MALLOC(sizeof(contiguous_range_info_t));
+ user->num_ranges = end_idx - start_idx;
+ user->ranges = (range_t *)MALLOC(user->num_ranges * sizeof(range_t));
+ int i;
+ for (i = 0; i < end_idx - start_idx; i++)
+ user->ranges[i] = ranges[start_idx + i];
+ add_unique_range_nosort(ret,
+ ranges[start_idx].start,
+ total_length,
+ user,
+ handle_contiguous_range_error,
+ destroy_contiguous_range_info);
+ }
+
+ return ret;
+}
+
+range_list_t* subtract_ranges(const range_list_t *r, const range_list_t *s)
+{
+ ASSERT(r); ASSERT(r->is_sorted);
+ ASSERT(s); ASSERT(s->is_sorted);
+
+ range_list_t *result = init_range_list();
+
+ int r_num_ranges, r_idx;
+ range_t *r_ranges = get_sorted_ranges(r, &r_num_ranges);
+ ASSERT(r_ranges);
+
+ int s_num_ranges, s_idx;
+ range_t *s_ranges = get_sorted_ranges(s, &s_num_ranges);
+ ASSERT(s_ranges);
+
+ s_idx = 0;
+ for (r_idx = 0; r_idx < r_num_ranges; r_idx++) {
+ GElf_Off last_start = r_ranges[r_idx].start;
+ for (; s_idx < s_num_ranges; s_idx++) {
+ if (CONTAINS(&r_ranges[r_idx], &s_ranges[s_idx])) {
+ if (last_start ==
+ r_ranges[r_idx].start + r_ranges[r_idx].length) {
+ break;
+ }
+ if (last_start == s_ranges[s_idx].start) {
+ last_start += s_ranges[s_idx].length;
+ continue;
+ }
+ INFO("Adding subtracted range [%lld, %lld)\n",
+ last_start,
+ s_ranges[s_idx].start);
+ add_unique_range_nosort(
+ result,
+ last_start,
+ s_ranges[s_idx].start - last_start,
+ NULL,
+ NULL,
+ NULL);
+ last_start = s_ranges[s_idx].start + s_ranges[s_idx].length;
+ } else {
+ ASSERT(!INTERSECT(&r_ranges[r_idx], &s_ranges[s_idx]));
+ break;
+ }
+ } /* while (s_idx < s_num_ranges) */
+ } /* for (r_idx = 0; r_idx < r_num_ranges; r_idx++) */
+
+ return result;
+}
+
+
diff --git a/rangesort.h b/rangesort.h
new file mode 100644
index 0000000..21db357
--- /dev/null
+++ b/rangesort.h
@@ -0,0 +1,105 @@
+#ifndef RANGESORT_H
+#define RANGESORT_H
+
+/* This implements a simple sorted list of non-overlapping ranges. */
+
+#include <debug.h>
+#include <common.h>
+#include <gelf.h>
+
+typedef enum range_error_t {
+ ERROR_CONTAINS,
+ ERROR_OVERLAPS
+} range_error_t;
+
+typedef struct range_t range_t;
+struct range_t {
+ GElf_Off start;
+ GElf_Off length;
+ void *user;
+ void (*err_fn)(range_error_t, range_t *, range_t *);
+ void (*user_dtor)(void *);
+};
+
+typedef struct range_list_t range_list_t;
+
+range_list_t* init_range_list();
+void destroy_range_list(range_list_t *);
+
+/* Just adds a range to the list. We won't detect whether the range overlaps
+ other ranges or contains them, or is contained by them, till we call
+ sort_ranges(). */
+void add_unique_range_nosort(range_list_t *ranges,
+ GElf_Off start, GElf_Off length,
+ void *user,
+ void (*err_fn)(range_error_t, range_t *, range_t *),
+ void (*user_dtor)(void * ));
+
+/* Sorts the ranges. If there are overlapping ranges or ranges that contain
+ other ranges, it will cause the program to exit with a FAIL. */
+range_list_t* sort_ranges(range_list_t *ranges);
+/* Find which range value falls in. Return that range or NULL if value does
+ not fall within any range. */
+range_t *find_range(range_list_t *ranges, GElf_Off value);
+int get_num_ranges(const range_list_t *ranges);
+range_t *get_sorted_ranges(const range_list_t *ranges, int *num_ranges);
+GElf_Off get_last_address(const range_list_t *ranges);
+
+/* This returns a range_list_t handle that contains ranges composed of the
+ adjacent ranges of the input range list. The user data of each range in
+ the range list is a structure of the type contiguous_range_info_t.
+ This structure contains an array of pointers to copies of the original
+ range_t structures comprising each new contiguous range, as well as the
+ length of that array.
+
+ NOTE: The input range must be sorted!
+
+ NOTE: destroy_range_list() will take care of releasing the data that it
+ allocates as a result of calling get_contiguous_ranges(). Do not free that
+ data yourself.
+
+ NOTE: the user data of the original range_t structures is simply copied, so
+ be careful handling it. You can destroy the range_list_t with
+ destroy_range_list() as usual. On error, the function does not return--the
+ program terminates.
+
+ NOTE: The returned range is not sorted. You must call sort_ranges() if you
+ need to.
+*/
+
+typedef struct {
+ int num_ranges;
+ range_t *ranges;
+} contiguous_range_info_t;
+
+range_list_t* get_contiguous_ranges(const range_list_t *);
+
+/* The function below takes in two range lists: r and s, and subtracts the
+ ranges in s from those in r. For example, if r and s are as follows:
+
+ r = { [0, 10) }
+ s = { [3, 5), [7, 9) }
+
+ Then r - s is { [0, 3), [5, 7), [9, 10) }
+
+ NOTE: Both range lists must be sorted on input. This is guarded by an
+ assertion.
+
+ NOTE: Range s must contain ranges, which are fully contained by the span of
+ range r (the span being the interval between the start of the lowest
+ range in r, inclusive, and the end of the highest range in r,
+ exclusive).
+
+ NOTE: In addition to the requirement above, range s must contain ranges,
+ each of which is a subrange of one of the ranges of r.
+
+ NOTE: There is no user info associated with the resulting range.
+
+ NOTE: The resulting range is not sorted.
+
+ Ther returned list must be destroyed with destroy_range_list().
+*/
+
+range_list_t* subtract_ranges(const range_list_t *r, const range_list_t *s);
+
+#endif/*RANGESORT_H*/