diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 130444ec10d2a40e2b3ede4863ab005aa1f4580c (patch) | |
tree | 435b158b8046565c2275e527e1d5bfedbcb3d3b8 | |
download | elfcopy-130444ec10d2a40e2b3ede4863ab005aa1f4580c.tar.gz |
Initial Contributionandroid-1.0release-1.0cdma-import
-rwxr-xr-x | Android.mk | 52 | ||||
-rw-r--r-- | MODULE_LICENSE_GPL | 0 | ||||
-rw-r--r-- | NOTICE | 340 | ||||
-rw-r--r-- | common.c | 35 | ||||
-rw-r--r-- | common.h | 49 | ||||
-rw-r--r-- | debug.c | 39 | ||||
-rw-r--r-- | debug.h | 94 | ||||
-rw-r--r-- | dwarf.c | 3960 | ||||
-rw-r--r-- | dwarf.h | 122 | ||||
-rw-r--r-- | dwarf2.h | 836 | ||||
-rw-r--r-- | elfcopy.c | 2989 | ||||
-rw-r--r-- | elfcopy.h | 94 | ||||
-rw-r--r-- | fixdwarf.c | 577 | ||||
-rw-r--r-- | fixdwarf.h | 14 | ||||
-rw-r--r-- | hash.c | 76 | ||||
-rw-r--r-- | hash.h | 22 | ||||
-rw-r--r-- | rangesort.c | 335 | ||||
-rw-r--r-- | rangesort.h | 105 |
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 @@ -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*/ @@ -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; +} @@ -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*/ @@ -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; +} @@ -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 @@ -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; +} @@ -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*/ |