diff options
author | Ulrich Drepper <[email protected]> | 2005-07-26 05:00:05 +0000 |
---|---|---|
committer | Ulrich Drepper <[email protected]> | 2005-07-26 05:00:05 +0000 |
commit | b08d5a8fb42f4586d756068065186b5af7e48dad (patch) | |
tree | 9f05f86be7877ed461b4dc05f53b29ea4fc0d2a1 /src |
Adjust for monotone.
Diffstat (limited to 'src')
-rw-r--r-- | src/.cvsignore | 1 | ||||
-rw-r--r-- | src/ChangeLog | 413 | ||||
-rw-r--r-- | src/Makefile.am | 144 | ||||
-rw-r--r-- | src/addr2line.c | 372 | ||||
-rw-r--r-- | src/elf32-i386.script | 215 | ||||
-rw-r--r-- | src/elfcmp.c | 611 | ||||
-rw-r--r-- | src/elflint.c | 2617 | ||||
-rw-r--r-- | src/findtextrel.c | 596 | ||||
-rw-r--r-- | src/i386_ld.c | 894 | ||||
-rw-r--r-- | src/ld.c | 1506 | ||||
-rw-r--r-- | src/ld.h | 1074 | ||||
-rw-r--r-- | src/ldgeneric.c | 6376 | ||||
-rw-r--r-- | src/ldlex.l | 349 | ||||
-rw-r--r-- | src/ldscript.y | 795 | ||||
-rw-r--r-- | src/libld_elf_i386.map | 7 | ||||
-rw-r--r-- | src/nm.c | 1282 | ||||
-rw-r--r-- | src/none_ld.c | 1 | ||||
-rw-r--r-- | src/readelf.c | 4997 | ||||
-rw-r--r-- | src/sectionhash.c | 69 | ||||
-rw-r--r-- | src/sectionhash.h | 23 | ||||
-rw-r--r-- | src/size.c | 682 | ||||
-rw-r--r-- | src/strip.c | 1750 | ||||
-rw-r--r-- | src/symbolhash.c | 29 | ||||
-rw-r--r-- | src/symbolhash.h | 24 | ||||
-rw-r--r-- | src/unaligned.h | 98 | ||||
-rw-r--r-- | src/versionhash.c | 28 | ||||
-rw-r--r-- | src/versionhash.h | 22 | ||||
-rw-r--r-- | src/xelf.h | 387 | ||||
-rw-r--r-- | src/ylwrap | 154 |
29 files changed, 25516 insertions, 0 deletions
diff --git a/src/.cvsignore b/src/.cvsignore new file mode 100644 index 00000000..70845e08 --- /dev/null +++ b/src/.cvsignore @@ -0,0 +1 @@ +Makefile.in diff --git a/src/ChangeLog b/src/ChangeLog new file mode 100644 index 00000000..7ebdcc46 --- /dev/null +++ b/src/ChangeLog @@ -0,0 +1,413 @@ +2005-07-24 Ulrich Drepper <[email protected]> + + * elfcmp.c: Implement comparing gaps between sections. + +2005-07-23 Ulrich Drepper <[email protected]> + + * elflint.c: Include libeblP.h instead of libebl.h. + * nm.c: Likewise. + * readelf.c: Likewise. + * elfcmp.c: Likewise. + + * elfcmp.c (main): Compare individual ELF header fields, excluding + e_shoff instead of the whole struct at once. + Use ebl_section_strip_p instead of SECTION_STRIP_P. + * strip.c: Use ebl_section_strip_p instead of SECTION_STRIP_P. + +2005-07-22 Ulrich Drepper <[email protected]> + + * elfcmp.c (main): Take empty section into account when comparing + section content. + + * elflint.c (check_dynamic): Check that d_tag value is >= 0 before + using it. + +2005-07-21 Ulrich Drepper <[email protected]> + + * elfcmp.c: New file. + * Makefile.am (bin_PROGRAMS): Add elfcmp. + (elfcmp_LDADD): Define. + + * elflint.c (check_rela): Check that copy relocations only reference + object symbols or symbols with unknown type. + (check_rel): Likewise. + +2005-06-08 Roland McGrath <[email protected]> + + * readelf.c (print_ops): Add consts. + +2005-05-31 Roland McGrath <[email protected]> + + * readelf.c (print_debug_abbrev_section): Don't bail after first CU's + abbreviations. Print a header line before each CU section. + + * readelf.c (print_debug_loc_section): Fix indentation for larger + address size. + +2005-05-30 Roland McGrath <[email protected]> + + * readelf.c (print_debug_line_section): Print section offset of each + CU's table, so they are easy to find from seeing the stmt_list value. + + * readelf.c (dwarf_attr_string): Add all attributes in <dwarf.h>. + (attr_callback): Grok DW_AT_ranges and print offset in hex. + + * readelf.c (attr_callback): Add 2 to addrsize * 2 for %#0* format. + (print_debug_ranges_section, print_debug_loc_section): Likewise. + + * readelf.c (print_ops): Take different args for indentation control. + (attr_callback): Caller updated. + Grok several more block-form attributes as being location expressions. + For those same attributes with udata forms, format output differently + for location list offset. + (print_debug_loc_section): Implement it for real. + + * readelf.c (options): Mention ranges for --debug-dump. + (enum section_e): Add section_ranges. + (parse_opt): Grok "ranges" for -w/--debug-dump. + (print_debug_ranges_section): New function. + (print_debug): Handle .debug_ranges section. + +2005-05-30 Ulrich Drepper <[email protected]> + + * readelf.c (handle_notes): At least x86-64 need not have the note + section values aligned to 8 bytes. + +2005-05-18 Ulrich Drepper <[email protected]> + + * readelf.c (dwarf_tag_string): Add new tags. + +2005-05-08 Roland McGrath <[email protected]> + + * strip.c (handle_elf): Don't translate hash and versym data formats, + elf_getdata already did it for us. + +2005-05-07 Ulrich Drepper <[email protected]> + + * Makefile.am (findtextrel_LDADD): Add $(libmudflap). + (addr2line_LDADD): Likewise. + +2005-05-03 Roland McGrath <[email protected]> + + * strip.c (handle_elf): Apply symbol table fixups to discarded + relocation sections when they are being saved in the debug file. + + * strip.c (handle_elf): Pass EHDR->e_ident[EI_DATA] to gelf_xlatetom + and gelf_xlatetof, not the native byte order. + + * strip.c (parse_opt): Give error if -f or -o is repeated. + (main): Exit if argp_parse returns nonzero. + + * strip.c (debug_fname_embed): New variable. + (options, parse_opt): New option -F to set it. + +2005-05-07 Ulrich Drepper <[email protected]> + + * readelf.c (parse_opt): Make any_control_option variable + local. Simplify some tests. + +2005-05-03 Roland McGrath <[email protected]> + + * strip.c (crc32_file): Function removed (now in ../lib). + +2005-05-03 Roland McGrath <[email protected]> + + * elflint.c (is_debuginfo): New variable. + (options, parse_opt): New option --debuginfo/-d to set it. + (check_sections): If is_debuginfo, don't complain about SHT_NOBITS. + (check_note): If is_debuginfo, don't try to get note contents. + +2005-04-24 Ulrich Drepper <[email protected]> + + * readelf.c (print_debug_abbrev_section): Don't print error when end of + section reached. + +2005-04-14 Ulrich Drepper <[email protected]> + + * readelf.c (dwarf_encoding_string): New function. + (dwarf_inline_string): New function. + (dwarf_access_string): New function. + (dwarf_visibility_string): New function. + (dwarf_virtuality_string): New function. + (dwarf_identifier_case_string): New function. + (dwarf_calling_convention_string): New function. + (dwarf_ordering_string): New function. + (dwarf_discr_list_string): New function. + (attr_callback): Decode man more attribute values. + +2005-04-01 Ulrich Drepper <[email protected]> + + * addr2line.c: Finish implementation of -f option. + +2005-03-29 Ulrich Drepper <[email protected]> + + * addr2line.c: New file. + * Makefile.am (bin_PROGRAMS): Add addr2line. + Define addr2line_LDADD. + + * findtextrel.c: Use new dwarf_addrdie function. + + * findtextrel.c: Fix usage message and re-add accidentally removed + line. + +2005-03-28 Ulrich Drepper <[email protected]> + + * findtextrel.c: New file. + * Makefile: Add rules to build findtextrel. + +2005-02-15 Ulrich Drepper <[email protected]> + + * ldlex.l: Provide ECHO definition to avoid warning. + + * elflint.c (check_program_header): Fix typo in RELRO test. + + * Makefile.am (AM_CFLAGS): Add more warning options. + * elflint.c: Fix warnings introduced by the new warning options. + * i386_ld.c: Likewise. + * ld.c: Likewise. + * ld.h: Likewise. + * ldgeneric.c: Likewise. + * nm.c: Likewise. + * readelf.c: Likewise. + * sectionhash.c: Likewise. + * size.c: Likewise. + * string.c: Likewise. + +2005-02-05 Ulrich Drepper <[email protected]> + + * Makefile.am: Check for text relocations in constructed DSOs. + + * Makefile.am [MUDFLAP] (AM_CFLAGS): Add -fmudflap. Link all apps + with -lmudflap. + + * ldscript.y: Add as_needed handling. + * ldlex.l: Recognize AS_NEEDED token. + * ld.h (struct filename_list): Add as_needed flag. + +2005-02-04 Ulrich Drepper <[email protected]> + + * elflint.c (check_symtab): Correctly determine size of GOT section. + +2005-01-19 Ulrich Drepper <[email protected]> + + * ld.c: Remove unnecessary more_help function. Print bug report + address using argp. + * strip.c: Likewise. + * size.c: Likewise. + * nm.c: Likewise. + * readelf.c: Likewise. + * elflint.c: Likewise. + + * elflint.c (main): Don't check for parameter problems here. + (parse_opt): Do it here, where we get informed about some of them + anyway. + + * readelf.c (main): Don't check for parameter problems here. + (parse_opt): Do it here, where we get informed about some of them + anyway. + +2005-01-11 Ulrich Drepper <[email protected]> + + * strip.c: Update copyright year. + * readelf.c: Likewise. + * size.c: Likewise. + * nm.c: Likewise. + * ld.c: Likewise. + * elflint.c: Likewise. + + * elflint.c (check_symtab): Don't warn about wrong size for + _DYNAMIC and __GLOBAL_OFFSET_TABLE__ for --gnu-ld. + +2004-10-05 Ulrich Drepper <[email protected]> + + * readelf.c (print_phdr): In section mapping, also indicate + sections in read-only segments. + +2004-09-25 Ulrich Drepper <[email protected]> + + * readelf.c: Make compile with gcc 4.0. + * strip.c: Likewise. + +2004-08-16 Ulrich Drepper <[email protected]> + + * strip.c (handle_elf): Rewrite dynamic memory handling to use of + allocate to work around gcc 3.4 bug. + +2004-01-25 Ulrich Drepper <[email protected]> + + * ldlex.l (invalid_char): Better error message. + +2004-01-23 Ulrich Drepper <[email protected]> + + * readelf.c: Print SHT_GNU_LIBLIST sections. + + * none_ld.c: New file. + +2004-01-21 Ulrich Drepper <[email protected]> + + * Makefile.am: Enable building of machine specific linker. + +2004-01-20 Ulrich Drepper <[email protected]> + + * Makefile.am: Support building with mudflap. + + * i386_ld.c: Fix warnings gcc 3.4 spits out. + * ldgeneric.c: Likewise. + * ldscript.y: Likewise. + * readelf.c: Likewise. + * strip.c: Likewise. + + * readelf.c (print_debug_line_section): Determine address size + correctly. + +2004-01-19 Ulrich Drepper <[email protected]> + + * readelf.c (print_phdr): Show which sections are covered by the + PT_GNU_RELRO entry. + + * elflint.c (check_program_header): Check PT_GNU_RELRO entry. + + * readelf.c (print_debug_macinfo_section): Implement. + +2004-01-18 Ulrich Drepper <[email protected]> + + * readelf.c (print_debug_line_section): Implement. + +2004-01-17 Ulrich Drepper <[email protected]> + + * src/elflint.c: Use PACKAGE_NAME instead of PACKAGE. + * src/ld.c: Likewise. + * src/nm.c: Likewise. + * src/readelf.c: Likewise. + * src/size.c: Likewise. + * src/strip.c: Likewise. + + * strip.c: Add a few more unlikely. Reduce scope of some variables. + + * Makefile.am: Support building with mudflap. + +2004-01-16 Ulrich Drepper <[email protected]> + + * readelf.c (print_debug_info_section): Free dies memory. + + * readelf.c: Print .debug_info section content. + +2004-01-13 Ulrich Drepper <[email protected]> + + * readelf.c (print_shdr): Add support for SHF_ORDERED and SHF_EXCLUDE. + +2004-01-12 Ulrich Drepper <[email protected]> + + * readelf.c (print_debug_aranges): Implement using libdw. + +2004-01-11 Ulrich Drepper <[email protected]> + + * nm.c: Adjust for Dwarf_Files type and dwarf_lineno interface change. + + * readelf.c: Use libdw instead of libdwarf. Not all of the old + behavior is available yet. + * Makefile.am: Link readelf with libdw. Remove libdwarf include path. + +2004-01-09 Ulrich Drepper <[email protected]> + + * nm.c (get_local_names): Adjust call to dwarf_nextcu. + + * nm.c: Implement getting information about local variables. + +2004-01-07 Ulrich Drepper <[email protected]> + + * nm.c: Read also debug information for local symbols. + +2004-01-05 Ulrich Drepper <[email protected]> + + * nm.c: Shuffle dwarf handling code around so the maximum column + width can be computed ahead of printing. Avoid collection symbols + which are not printed anyway. + + * nm.c: Rewrite dwarf handling to use libdw. + * Makefile.am (AM_CFLAGS): Add -std parameter. + (INCLUDES): Find header in libdw subdir. + (nm_LDADD): Replace libdwarf with libdw. + + * elflint.c: Update copyright year. + * readelf.c: Likewise. + * size.c: Likewise. + * strip.c: Likewise. + * nm.c: Likewise. + +2003-12-31 Ulrich Drepper <[email protected]> + + * strip.c (process_file): Close file before returning. + +2003-11-19 Ulrich Drepper <[email protected]> + + * readelf.c (handle_dynamic): Make column for tag name wider. + +2003-09-29 Ulrich Drepper <[email protected]> + + * readelf.c (handle_dynamic): Always terminate tag name with a space. + +2003-09-25 Ulrich Drepper <[email protected]> + + * strip.c (process_file): Don't mmap the input file, we modify the + data structures and don't want the change end up on disk. + +2003-09-23 Jakub Jelinek <[email protected]> + + * unaligned.h (union u_2ubyte_unaligned, + union u_4ubyte_unaligned, union u_8ubyte_unaligned): Add + packed attribute. + (add_2ubyte_unaligned, add_4ubyte_unaligned, + add_8ubyte_unaligned): Avoid nesting bswap_NN macros. + Read/store value through _ptr->u instead of *_ptr. + +2003-09-22 Ulrich Drepper <[email protected]> + + * size.c (show_sysv): Change type of maxlen to int. + + * strip.c (handle_elf): Handle the 64-bit archs which is 64-bit + buckets. + + * i386_ld.c: Many many fixes and extensions. + * ld.c: Likewise. + * ldgeneric.c: Likewise. + +2003-08-16 Ulrich Drepper <[email protected]> + + * ldgeneric.c (check_definition): Don't add symbol on dso_list if + the reference is from another DSO. + +2003-08-15 Ulrich Drepper <[email protected]> + + * ldgeneric.c (find_entry_point): It is no fatal error if no entry + point is found when creating a DSO. + +2003-08-14 Ulrich Drepper <[email protected]> + + * ld.c (main): Always call FLAG_UNRESOLVED. + * ldgeneric.c (ld_generic_flag_unresolved): Only complain about + undefined symbols if not creating DSO or ld_state.nodefs is not set. + +2003-08-13 Ulrich Drepper <[email protected]> + + * Makefile.in: Depend on libebl.a, not libebl.so. + + * ld.c (main): Mark stream for linker script as locked by caller. + (read_version_script): Likewise. + * ldlex.c: Define fread and fwrite to _unlocked variant. + + * i386_ld.c (elf_i386_finalize_plt): Replace #ifdefs with uses of + target_bswap_32. + * unaligned.h: Define target_bswap_16, target_bswap_32, and + target_bswap_64. + (store_2ubyte_unaligned, store_4ubyte_unaligned, + store_8ubyte_unaligned): Define using new macros. + +2003-08-12 Ulrich Drepper <[email protected]> + + * i386_ld.c (elf_i386_finalize_plt): Use packed structs to access + possibly unaligned memory. Support use of big endian machines. + +2003-08-11 Ulrich Drepper <[email protected]> + + * Moved to CVS archive. diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..ccb6f96c --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,144 @@ +## Process this file with automake to create Makefile.in +## +## Copyright (C) 1996-2002, 2003, 2004, 2005 Red Hat, Inc. +## +## This program is Open Source software; you can redistribute it and/or +## modify it under the terms of the Open Software License version 1.0 as +## published by the Open Source Initiative. +## +## You should have received a copy of the Open Software License along +## with this program; if not, you may obtain a copy of the Open Software +## License version 1.0 from http://www.opensource.org/licenses/osl.php or +## by writing the Open Source Initiative c/o Lawrence Rosen, Esq., +## 3001 King Ranch Road, Ukiah, CA 95482. +## +DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H $(YYDEBUG) \ + -DSRCDIR=\"$(shell cd $(srcdir);pwd)\" -DOBJDIR=\"$(shell pwd)\" +if MUDFLAP +AM_CFLAGS = -Wall -Wshadow -Wunused -Wextra -std=gnu99 -fmudflap \ + $(native_ld_cflags) $(if $($(*F)_no_Wunused),,-Wunused) \ + $(if $($(*F)_no_Wformat),,-Wformat=2) +else +AM_CFLAGS = -Wall -Wshadow -std=gnu99 $(native_ld_cflags) \ + $(if $($(*F)_no_Werror),,-Werror) \ + $(if $($(*F)_no_Wunused),,-Wunused -Wextra) \ + $(if $($(*F)_no_Wformat),,-Wformat=2) +endif +if MUDFLAP +libmudflap = -lmudflap +endif +INCLUDES = -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl -I$(srcdir)/../lib -I$(srcdir)/../libdw -I.. + +YACC = @YACC@ -d +AM_YFLAGS = -pld +AM_LFLAGS = -Pld -olex.yy.c +## Uncomment to enable debugging of linker script parser +##YYDEBUG = -DYYDEBUG=1 + +native_ld = @native_ld@ +base_cpu = @base_cpu@ + +bin_PROGRAMS = readelf nm size strip ld elflint findtextrel addr2line \ + elfcmp + + +ld_dsos = libld_elf_i386_pic.a +if NATIVE_LD +noinst_LIBRARIES = libld_elf.a +native_ld_cflags = -DBASE_ELF_NAME=elf_$(base_cpu) +else +noinst_LIBRARIES = libld_elf.a $(ld_dsos) +noinst_PROGRAMS = $(ld_dsos:_pic.a=.so) +endif + +textrel_check = if readelf -d $@ | fgrep -q TEXTREL; then exit 1; fi + + +ld_SOURCES = ld.c ldgeneric.c ldlex.l ldscript.y symbolhash.c sectionhash.c \ + versionhash.c + +noinst_HEADERS = ld.h symbolhash.h sectionhash.h versionhash.h \ + ldscript.h xelf.h unaligned.h + +EXTRA_DIST = elf32-i386.script libld_elf_i386.map $(ld_modules) +ld_modules = i386_ld.c + +if MUDFLAP +libdw = ../libdw/libdw.a +libelf = ../libelf/libelf.a +else +libdw = ../libdw/libdw.so +libelf = ../libelf/libelf.so +endif +libebl = ../libebl/libebl.a +libeu = ../lib/libeu.a + +nm_no_Wformat = yes +size_no_Wformat = yes +# XXX While the file is not finished, don't warn about this +ldgeneric_no_Wunused = yes + +readelf_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +nm_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +size_LDADD = $(libelf) $(libeu) $(libmudflap) +strip_LDADD = $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +ld_LDADD = $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +if NATIVE_LD +ld_LDADD += libld_elf.a +endif +ld_LDFLAGS = -rdynamic +elflint_LDADD = $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +findtextrel_LDADD = $(libdw) $(libelf) $(libmudflap) +addr2line_LDADD = $(libdw) $(libelf) $(libmudflap) +elfcmp_LDADD = $(libebl) $(libelf) $(libmudflap) -ldl + +ldlex.o: ldscript.c +ldlex_no_Werror = yes +ldscript.h: ldscript.c + +# Machine-specific linker code. +libld_elf_a_SOURCES = $(base_cpu)_ld.c + +libld_elf_i386_pic_a_SOURCES = +am_libld_elf_i386_pic_a_OBJECTS = i386_ld.os + +libld_elf_i386_so_SOURCES = +libld_elf_i386.so: libld_elf_i386_pic.a libld_elf_i386.map + $(CC) -shared -o $@ -Wl,--whole-archive,$<,--no-whole-archive \ + $(libelf) $(libeu) \ + -Wl,--version-script,$(srcdir)/libld_elf_i386.map + $(textrel_check) + + +%.os: %.c %.o + if $(filter-out -fmudflap,$(COMPILE)) -c -o $@ -fpic -DPIC -DSHARED \ + -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" \ + `test -f '$<' || echo '$(srcdir)/'`$<; \ + then cat "$(DEPDIR)/$*.Tpo" >> "$(DEPDIR)/$*.Po"; \ + rm -f "$(DEPDIR)/$*.Tpo"; \ + else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; \ + fi + +# Special rule to make it possible to define libld_elf_a_SOURCES as we do. +# Otherwise make would complain. +.deps/none_ld.Po: none_ld.os + -: + + +installcheck-binPROGRAMS: $(bin_PROGRAMS) + bad=0; pid=$$$$; list="$(bin_PROGRAMS)"; for p in $$list; do \ + case ' $(AM_INSTALLCHECK_STD_OPTIONS_EXEMPT) ' in \ + *" $$p "* | *" $(srcdir)/$$p "*) continue;; \ + esac; \ + f=`echo "$$p" | \ + sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + for opt in --help --version; do \ + if LD_LIBRARY_PATH=$(DESTDIR)$(libdir) \ + $(DESTDIR)$(bindir)/$$f $$opt > c$${pid}_.out 2> c$${pid}_.err \ + && test -n "`cat c$${pid}_.out`" \ + && test -z "`cat c$${pid}_.err`"; then :; \ + else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \ + done; \ + done; rm -f c$${pid}_.???; exit $$bad + +CLEANFILES = none_ld.os $(ld_modules:.c=.os) diff --git a/src/addr2line.c b/src/addr2line.c new file mode 100644 index 00000000..60fcdf63 --- /dev/null +++ b/src/addr2line.c @@ -0,0 +1,372 @@ +/* Locate source files and line information for given addresses + Copyright (C) 2005 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2005. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <argp.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <inttypes.h> +#include <libdw.h> +#include <libintl.h> +#include <locale.h> +#include <mcheck.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + + +/* Values for the parameters which have no short form. */ +#define OPT_DEMANGLER 0x100 + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Input Selection:"), 0 }, + { "exe", 'e', "FILE", 0, N_("Find addresses in FILE"), 0 }, + + { NULL, 0, NULL, 0, N_("Output Selection:"), 0 }, + { "basenames", 's', NULL, 0, N_("Show only base names of source files"), 0 }, + { "functions", 'f', NULL, 0, N_("Additional show function names"), 0 }, + + { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 }, + /* Unsupported options. */ + { "target", 'b', "ARG", OPTION_HIDDEN, NULL, 0 }, + { "demangle", 'C', "ARG", OPTION_HIDDEN | OPTION_ARG_OPTIONAL, NULL, 0 }, + { "demangler", OPT_DEMANGLER, "ARG", OPTION_HIDDEN, NULL, 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("\ +Locate source files and line information for ADDRs (in a.out by default)."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("[ADDR...]"); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, NULL, NULL, NULL +}; + + +/* Handle ADDR. */ +static void handle_address (GElf_Addr addr, Elf *elf, Dwarf *dw); + + +/* Name of the executable. */ +static const char *executable = "a.out"; + +/* True if only base names of files should be shown. */ +static bool only_basenames; + +/* True if function names should be shown. */ +static bool show_functions; + + +int +main (int argc, char *argv[]) +{ + int remaining; + int result = 0; + + /* Make memory leak detection possible. */ + mtrace (); + + /* We use no threads here which can interfere with handling a stream. */ + (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER); + + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + (void) bindtextdomain (PACKAGE, LOCALEDIR); + + /* Initialize the message catalog. */ + (void) textdomain (PACKAGE); + + /* Parse and process arguments. */ + (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + /* Tell the library which version we are expecting. */ + elf_version (EV_CURRENT); + + /* Open the file. */ + int fd = open64 (executable, O_RDONLY); + if (fd == -1) + error (1, errno, gettext ("cannot open '%s'"), executable); + + /* Create the ELF descriptor. */ + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (elf == NULL) + { + close (fd); + error (1, 0, gettext ("cannot create ELF descriptor: %s"), + elf_errmsg (-1)); + } + + /* Try to get a DWARF descriptor. If it fails, we try to locate the + debuginfo file. */ + Dwarf *dw = dwarf_begin_elf (elf, DWARF_C_READ, NULL); + int fd2 = -1; + Elf *elf2 = NULL; + if (dw == NULL) + { + char *canon = canonicalize_file_name (executable); + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); + + if (canon != NULL && ehdr != NULL) + { + const char *debuginfo_dir; + if (ehdr->e_ident[EI_CLASS] == ELFCLASS32 + || ehdr->e_machine == EM_IA_64 || ehdr->e_machine == EM_ALPHA) + debuginfo_dir = "/usr/lib/debug"; + else + debuginfo_dir = "/usr/lib64/debug"; + + char *difname = alloca (strlen (debuginfo_dir) + strlen (canon) + 1); + strcpy (stpcpy (difname, debuginfo_dir), canon); + fd2 = open64 (difname, O_RDONLY); + if (fd2 != -1) + dw = dwarf_begin_elf (elf2, DWARF_C_READ, NULL); + } + + free (canon); + } + + /* Now handle the addresses. In case none are given on the command + line, read from stdin. */ + if (remaining == argc) + { + /* We use no threads here which can interfere with handling a stream. */ + (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER); + + char *buf = NULL; + size_t len = 0; + while (!feof_unlocked (stdin)) + { + if (getline (&buf, &len, stdin) < 0) + break; + + char *endp; + uintmax_t addr = strtoumax (buf, &endp, 0); + if (endp != buf) + handle_address (addr, elf2 ?: elf, dw); + else + result = 1; + } + + free (buf); + } + else + { + do + { + char *endp; + uintmax_t addr = strtoumax (argv[remaining], &endp, 0); + if (endp != argv[remaining]) + handle_address (addr, elf2 ?: elf, dw); + else + result = 1; + } + while (++remaining < argc); + } + + return result; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "addr2line (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2005"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case 'b': + case 'C': + case OPT_DEMANGLER: + /* Ignored for compatibility. */ + break; + + case 'e': + executable = arg; + break; + + case 's': + only_basenames = true; + break; + + case 'f': + show_functions = true; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +struct func_arg +{ + GElf_Addr addr; + const char *name; +}; + + +static int +match_func (Dwarf_Func *func, void *arg) +{ + struct func_arg *func_arg = (struct func_arg *) arg; + Dwarf_Addr addr; + + if (dwarf_func_lowpc (func, &addr) == 0 && addr <= func_arg->addr + && dwarf_func_highpc (func, &addr) == 0 && func_arg->addr < addr) + { + func_arg->name = dwarf_func_name (func); + return DWARF_CB_ABORT; + } + + return DWARF_CB_OK; +} + + +static const char * +elf_getname (GElf_Addr addr, Elf *elf) +{ + /* The DWARF information is not available. Use the ELF + symbol table. */ + Elf_Scn *scn = NULL; + Elf_Scn *dynscn = NULL; + + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr != NULL) + { + if (shdr->sh_type == SHT_SYMTAB) + break; + if (shdr->sh_type == SHT_DYNSYM) + dynscn = scn; + } + } + if (scn == NULL) + scn = dynscn; + + if (scn != NULL) + { + /* Look through the symbol table for a matching symbol. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + assert (shdr != NULL); + + Elf_Data *data = elf_getdata (scn, NULL); + if (data != NULL) + for (int cnt = 1; cnt < (int) (shdr->sh_size / shdr->sh_entsize); + ++cnt) + { + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (data, cnt, &sym_mem); + if (sym != NULL + && sym->st_value <= addr + && addr < sym->st_value + sym->st_size) + return elf_strptr (elf, shdr->sh_link, sym->st_name); + } + } + + return NULL; +} + + +static void +handle_address (GElf_Addr addr, Elf *elf, Dwarf *dw) +{ + Dwarf_Die die_mem; + Dwarf_Die *die = dwarf_addrdie (dw, addr, &die_mem); + + if (show_functions) + { + /* First determine the function name. Use the DWARF information if + possible. */ + struct func_arg arg; + arg.addr = addr; + arg.name = NULL; + + if (dwarf_getfuncs (die, match_func, &arg, 0) <= 0) + arg.name = elf_getname (addr, elf); + + puts (arg.name ?: "??"); + } + + + Dwarf_Line *line; + const char *src; + if ((line = dwarf_getsrc_die (die, addr)) != NULL + && (src = dwarf_linesrc (line, NULL, NULL)) != NULL) + { + if (only_basenames) + src = basename (src); + + int lineno; + if (dwarf_lineno (line, &lineno) != -1) + { + int linecol; + if (dwarf_linecol (line, &linecol) != -1 && linecol != 0) + printf ("%s:%d:%d\n", src, lineno, linecol); + else + printf ("%s:%d\n", src, lineno); + } + else + printf ("%s:0\n", src); + } + else + puts ("??:0"); +} diff --git a/src/elf32-i386.script b/src/elf32-i386.script new file mode 100644 index 00000000..d62333ac --- /dev/null +++ b/src/elf32-i386.script @@ -0,0 +1,215 @@ +ENTRY(_start); + +SEARCH_DIR(/lib); +SEARCH_DIR(/usr/lib); +SEARCH_DIR(/usr/local/lib); +SEARCH_DIR(/usr/i686-pc-linux-gnu/lib); + +INTERP(/lib/ld-linux.so.2); + +PAGESIZE(4k); + +SEGMENT [RX] +{ +#ifdef SHARED + . = SIZEOF_HEADERS; +#else + . = 0x08048000 + SIZEOF_HEADERS; +#endif + + .interp; + .hash; + .dynsym; + .dynstr; + .gnu.version; + .gnu.version_d; + .gnu.version_r; + .rel.dyn; + .rel.plt; + .init { KEEP (*(.init)) } + .plt; + .text + { + *(.text) + *(.text.*) + *(.stub) + *(.gnu.warning) + *(.gnu.linkonce.t.*) + } + .fini { KEEP (*(.fini)) } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + .rodata + { + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + } + .rodata1; + . = ALIGN(32 / 8); + PROVIDE (__preinit_array_start = .); + .preinit_array + { + *(.preinit_array) + } + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array + { + *(.init_array) + } + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array + { + *(.fini_array) + } + PROVIDE (__fini_array_end = .); +} + +SEGMENT [RW] +{ + .sdata2 + { + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + } + .sbss2 + { + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = ALIGN(PAGESIZE) + (. & (PAGESIZE - 1)); + .data + { + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + } + .data1; + .eh_frame + { + KEEP (*(.eh_frame)) + } + .gcc_except_table; + .ctors + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + /* We don't want to include the .ctor section from + from the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr; + .got + { + *(.got.plt) + *(.got) + } + .dynamic; + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata + { + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + .sbss + { + PROVIDE (__sbss_start = .); + PROVIDE (___sbss_start = .); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + PROVIDE (__sbss_end = .); + PROVIDE (___sbss_end = .); + } + .bss + { + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. */ + . = ALIGN(32 / 8); + } + . = ALIGN(32 / 8); + _end = .; + PROVIDE (end = .); +} + +SEGMENT [] +{ + /* Stabs debugging sections. */ + .stab; + .stabstr; + .stab.excl; + .stab.exclstr; + .stab.index; + .stab.indexstr; + .comment; + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug; + .line; + /* GNU DWARF 1 extensions */ + .debug_srcinfo; + .debug_sfnames; + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges; + .debug_pubnames; + /* DWARF 2 */ + .debug_info + { + *(.debug_info) + *(.gnu.linkonce.wi.*) + } + .debug_abbrev; + .debug_line; + .debug_frame; + .debug_str; + .debug_loc; + .debug_macinfo; + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames; + .debug_funcnames; + .debug_typenames; + .debug_varnames; + /* These must appear regardless of . */ +} diff --git a/src/elfcmp.c b/src/elfcmp.c new file mode 100644 index 00000000..6bf7e749 --- /dev/null +++ b/src/elfcmp.c @@ -0,0 +1,611 @@ +/* Compare relevant content of two ELF files. + Copyright (C) 2005 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2005. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <argp.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <libintl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "../libelf/elf-knowledge.h" +#include "../libebl/libeblP.h" + + +/* Prototypes of local functions. */ +static Elf *open_file (const char *fname, int *fdp, Ebl **eblp); +static bool search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx); +static int regioncompare (const void *p1, const void *p2); + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + +/* Values for the parameters which have no short form. */ +#define OPT_GAPS 0x100 + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Control options:"), 0 }, + { "gaps", OPT_GAPS, "ACTION", 0, N_("Control treatment of gaps in loadable segments [ignore|match] (default: ignore)"), 0 }, + { "quiet", 'q', NULL, 0, N_("Output nothing; yield exit status only"), 0 }, + + { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("\ +Compare relevant parts of two ELF files for equality."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("FILE1 FILE2"); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, NULL, NULL, NULL +}; + + +/* How to treat gaps in loadable segments. */ +static enum + { + gaps_ignore = 0, + gaps_match + } + gaps; + +/* Structure to hold information about used regions. */ +struct region +{ + GElf_Addr from; + GElf_Addr to; + struct region *next; +}; + +/* Nonzero if only exit status is wanted. */ +static bool quiet; + + +int +main (int argc, char *argv[]) +{ + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + (void) bindtextdomain (PACKAGE, LOCALEDIR); + + /* Initialize the message catalog. */ + (void) textdomain (PACKAGE); + + /* Parse and process arguments. */ + int remaining; + (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + /* We expect exactly two non-option parameters. */ + if (remaining + 2 != argc) + { + fputs (gettext ("Invalid number of parameters.\n"), stderr); + argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name); + exit (1); + } + + /* Comparing the files is done in two phases: + 1. compare all sections. Sections which are irrelevant (i.e., if + strip would remove them) are ignored. Some section types are + handled special. + 2. all parts of the loadable segments which are not parts of any + section is compared according to the rules of the --gaps option. + */ + int result = 0; + elf_version (EV_CURRENT); + + const char *const fname1 = argv[remaining]; + int fd1; + Ebl *ebl1; + Elf *elf1 = open_file (fname1, &fd1, &ebl1); + + const char *const fname2 = argv[remaining + 1]; + int fd2; + Ebl *ebl2; + Elf *elf2 = open_file (fname2, &fd2, &ebl2); + + GElf_Ehdr ehdr1_mem; + GElf_Ehdr *ehdr1 = gelf_getehdr (elf1, &ehdr1_mem); + if (ehdr1 == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot get ELF header of \"%s\": %s"), + fname1, elf_errmsg (-1)); + GElf_Ehdr ehdr2_mem; + GElf_Ehdr *ehdr2 = gelf_getehdr (elf2, &ehdr2_mem); + if (ehdr2 == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot get ELF header of \"%s\": %s"), + fname2, elf_errmsg (-1)); + + /* Compare the ELF headers. */ + if (memcmp (ehdr1->e_ident, ehdr2->e_ident, EI_NIDENT) != 0 + || ehdr1->e_type != ehdr2->e_type + || ehdr1->e_machine != ehdr2->e_machine + || ehdr1->e_version != ehdr2->e_version + || ehdr1->e_entry != ehdr2->e_entry + || ehdr1->e_phoff != ehdr2->e_phoff + || ehdr1->e_flags != ehdr2->e_flags + || ehdr1->e_ehsize != ehdr2->e_ehsize + || ehdr1->e_phentsize != ehdr2->e_phentsize + || ehdr1->e_phnum != ehdr2->e_phnum + || ehdr1->e_shentsize != ehdr2->e_shentsize + || ehdr1->e_shnum != ehdr2->e_shnum + || ehdr1->e_shstrndx != ehdr2->e_shstrndx) + { + if (! quiet) + error (0, 0, gettext ("%s %s diff: ELF header"), fname1, fname2); + result = 1; + goto out; + } + + /* Iterate over all sections. We expect the sections in the two + files to match exactly. */ + Elf_Scn *scn1 = NULL; + Elf_Scn *scn2 = NULL; + struct region *regions = NULL; + size_t nregions = 0; + while (1) + { + GElf_Shdr shdr1_mem; + GElf_Shdr *shdr1; + const char *sname1 = NULL; + do + { + scn1 = elf_nextscn (elf1, scn1); + shdr1 = gelf_getshdr (scn1, &shdr1_mem); + if (shdr1 != NULL) + sname1 = elf_strptr (elf1, ehdr1->e_shstrndx, shdr1->sh_name); + } + while (scn1 != NULL + && ebl_section_strip_p (ebl1, ehdr1, shdr1, sname1, true, false)); + + GElf_Shdr shdr2_mem; + GElf_Shdr *shdr2; + const char *sname2 = NULL; + do + { + scn2 = elf_nextscn (elf2, scn2); + shdr2 = gelf_getshdr (scn2, &shdr2_mem); + if (shdr2 != NULL) + sname2 = elf_strptr (elf2, ehdr2->e_shstrndx, shdr2->sh_name); + } + while (scn2 != NULL + && ebl_section_strip_p (ebl2, ehdr2, shdr2, sname2, true, false)); + + if (scn1 == NULL || scn2 == NULL) + break; + + if (gaps != gaps_ignore && (shdr1->sh_flags & SHF_ALLOC) != 0) + { + struct region *newp = (struct region *) alloca (sizeof (*newp)); + newp->from = shdr1->sh_offset; + newp->to = shdr1->sh_offset + shdr1->sh_size; + newp->next = regions; + regions = newp; + + ++nregions; + } + + /* Compare the headers. We allow the name to be at a different + location. */ + if (strcmp (sname1, sname2) != 0) + { + header_mismatch: + error (0, 0, gettext ("%s %s differ: section header"), + fname1, fname2); + result = 1; + goto out; + } + + /* We ignore certain sections. */ + if (strcmp (sname1, ".gnu_debuglink") == 0 + || strcmp (sname1, ".gnu.prelink_undo") == 0) + continue; + + if (shdr1->sh_type != shdr2->sh_type + // XXX Any flags which should be ignored? + || shdr1->sh_flags != shdr2->sh_flags + || shdr1->sh_addr != shdr2->sh_addr + || shdr1->sh_offset != shdr2->sh_offset + || shdr1->sh_size != shdr2->sh_size + || shdr1->sh_link != shdr2->sh_link + || shdr1->sh_info != shdr2->sh_info + || shdr1->sh_addralign != shdr2->sh_addralign + || shdr1->sh_entsize != shdr2->sh_entsize) + goto header_mismatch; + + Elf_Data *data1 = elf_getdata (scn1, NULL); + if (data1 == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get content of section %zu in \"%s\": %s"), + elf_ndxscn (scn1), fname1, elf_errmsg (-1)); + + Elf_Data *data2 = elf_getdata (scn2, NULL); + if (data2 == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get content of section %zu in \"%s\": %s"), + elf_ndxscn (scn2), fname2, elf_errmsg (-1)); + + switch (shdr1->sh_type) + { + case SHT_DYNSYM: + case SHT_SYMTAB: + /* Iterate over the symbol table. We ignore the st_size + value of undefined symbols. */ + for (int ndx = 0; ndx < (int) (shdr1->sh_size / shdr1->sh_entsize); + ++ndx) + { + GElf_Sym sym1_mem; + GElf_Sym *sym1 = gelf_getsym (data1, ndx, &sym1_mem); + if (sym1 == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get symbol in \"%s\": %s"), + fname1, elf_errmsg (-1)); + GElf_Sym sym2_mem; + GElf_Sym *sym2 = gelf_getsym (data2, ndx, &sym2_mem); + if (sym2 == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get symbol in \"%s\": %s"), + fname2, elf_errmsg (-1)); + + const char *name1 = elf_strptr (elf1, shdr1->sh_link, + sym1->st_name); + const char *name2 = elf_strptr (elf2, shdr2->sh_link, + sym2->st_name); + if (strcmp (name1, name2) != 0 + || sym1->st_value != sym2->st_value + || (sym1->st_size != sym2->st_size + && sym1->st_shndx != SHN_UNDEF) + || sym1->st_info != sym2->st_info + || sym1->st_other != sym2->st_other + || sym1->st_shndx != sym1->st_shndx) + { + // XXX Do we want to allow reordered symbol tables? + if (! quiet) + error (0, 0, gettext ("%s %s differ: symbol table"), + fname1, fname2); + result = 1; + goto out; + } + + if (sym1->st_shndx == SHN_UNDEF + && sym1->st_size != sym2->st_size) + { + /* The size of the symbol in the object defining it + might have changed. That is OK unless the symbol + is used in a copy relocation. Look over the + sections in both files and determine which + relocation section uses this symbol table + section. Then look through the relocations to + see whether any copy relocation references this + symbol. */ + if (search_for_copy_reloc (ebl1, elf_ndxscn (scn1), ndx) + || search_for_copy_reloc (ebl2, elf_ndxscn (scn2), ndx)) + { + if (! quiet) + error (0, 0, gettext ("%s %s differ: symbol table"), + fname1, fname2); + result = 1; + goto out; + } + } + } + break; + + default: + /* Compare the section content byte for byte. */ + assert (shdr1->sh_type == SHT_NOBITS + || (data1->d_buf != NULL || data1->d_size == 0)); + assert (shdr2->sh_type == SHT_NOBITS + || (data2->d_buf != NULL || data1->d_size == 0)); + + if (data1->d_size != data2->d_size + || (shdr1->sh_type != SHT_NOBITS + && memcmp (data1->d_buf, data2->d_buf, data1->d_size) != 0)) + { + if (! quiet) + error (0, 0, gettext ("%s %s differ: section content"), + fname1, fname2); + result = 1; + goto out; + } + break; + } + } + + if (scn1 != scn2) + { + if (! quiet) + error (0, 0, + gettext ("%s %s differ: unequal amount of important sections"), + fname1, fname2); + result = 1; + goto out; + } + + /* We we look at gaps, create artificial ones for the parts of the + program which we are not in sections. */ + struct region ehdr_region; + struct region phdr_region; + if (gaps != gaps_ignore) + { + ehdr_region.from = 0; + ehdr_region.to = ehdr1->e_ehsize; + ehdr_region.next = &phdr_region; + + phdr_region.from = ehdr1->e_phoff; + phdr_region.to = ehdr1->e_phoff + ehdr1->e_phnum * ehdr1->e_phentsize; + phdr_region.next = regions; + + regions = &ehdr_region; + nregions += 2; + } + + /* If we need to look at the gaps we need access to the file data. */ + char *raw1 = NULL; + size_t size1 = 0; + char *raw2 = NULL; + size_t size2 = 0; + struct region *regionsarr = alloca (nregions * sizeof (struct region)); + if (gaps != gaps_ignore) + { + raw1 = elf_rawfile (elf1, &size1); + if (raw1 == NULL ) + error (EXIT_FAILURE, 0, gettext ("cannot load data of \"%s\": %s"), + fname1, elf_errmsg (-1)); + + raw2 = elf_rawfile (elf2, &size2); + if (raw2 == NULL ) + error (EXIT_FAILURE, 0, gettext ("cannot load data of \"%s\": %s"), + fname2, elf_errmsg (-1)); + + for (size_t cnt = 0; cnt < nregions; ++cnt) + { + regionsarr[cnt] = *regions; + regions = regions->next; + } + + qsort (regionsarr, nregions, sizeof (regionsarr[0]), regioncompare); + } + + /* Compare the program header tables. */ + for (int ndx = 0; ndx < ehdr1->e_phnum; ++ndx) + { + GElf_Phdr phdr1_mem; + GElf_Phdr *phdr1 = gelf_getphdr (elf1, ndx, &phdr1_mem); + if (ehdr1 == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get program header entry %d of \"%s\": %s"), + ndx, fname1, elf_errmsg (-1)); + GElf_Phdr phdr2_mem; + GElf_Phdr *phdr2 = gelf_getphdr (elf2, ndx, &phdr2_mem); + if (ehdr2 == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get program header entry %d of \"%s\": %s"), + ndx, fname2, elf_errmsg (-1)); + + if (memcmp (phdr1, phdr2, sizeof (GElf_Phdr)) != 0) + { + if (! quiet) + error (0, 0, gettext ("%s %s differ: program header %d"), + fname1, fname2, ndx); + result = 1; + goto out; + } + + if (gaps != gaps_ignore && phdr1->p_type == PT_LOAD) + { + size_t cnt = 0; + while (cnt < nregions && regionsarr[cnt].to < phdr1->p_offset) + ++cnt; + + GElf_Off last = phdr1->p_offset; + GElf_Off end = phdr1->p_offset + phdr1->p_filesz; + while (cnt < nregions && regionsarr[cnt].from < end) + { + if (last < regionsarr[cnt].from) + { + /* Compare the [LAST,FROM) region. */ + assert (gaps == gaps_match); + if (memcmp (raw1 + last, raw2 + last, + regionsarr[cnt].from - last) != 0) + { + gapmismatch: + if (!quiet) + error (0, 0, gettext ("%s %s differ: gap"), + fname1, fname2); + result = 1; + goto out; + } + + } + last = regionsarr[cnt].to; + ++cnt; + } + + if (cnt == nregions && last < end) + goto gapmismatch; + } + } + + out: + elf_end (elf1); + elf_end (elf2); + close (fd1); + close (fd2); + + return result; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "elfcmp (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2005"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case 'q': + quiet = true; + break; + + case OPT_GAPS: + if (strcasecmp (arg, "ignore") == 0) + gaps = gaps_ignore; + else if (strcasecmp (arg, "match") == 0) + gaps = gaps_match; + else + { + fprintf (stderr, + gettext ("Invalid value \"%s\" for --gaps parameter."), + arg); + argp_help (&argp, stderr, ARGP_HELP_SEE, + program_invocation_short_name); + exit (1); + } + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +static Elf * +open_file (const char *fname, int *fdp, Ebl **eblp) +{ + int fd = open (fname, O_RDONLY); + if (unlikely (fd == -1)) + error (EXIT_FAILURE, errno, gettext ("cannot open \"%s\""), fname); + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (elf == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create ELF descriptor for \"%s\": %s"), + fname, elf_errmsg (-1)); + Ebl *ebl = ebl_openbackend (elf); + if (ebl == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create EBL descriptor for \"%s\""), fname); + + *fdp = fd; + *eblp = ebl; + return elf; +} + + +static bool +search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx) +{ + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header of section %zu: %s"), + elf_ndxscn (scn), elf_errmsg (-1)); + + if ((shdr->sh_type != SHT_REL && shdr->sh_type != SHT_RELA) + || shdr->sh_link != scnndx) + continue; + + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get content of section %zu: %s"), + elf_ndxscn (scn), elf_errmsg (-1)); + + if (shdr->sh_type == SHT_REL) + for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize); + ++ndx) + { + GElf_Rel rel_mem; + GElf_Rel *rel = gelf_getrel (data, ndx, &rel_mem); + if (rel == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot get relocation: %s"), + elf_errmsg (-1)); + + if ((int) GELF_R_SYM (rel->r_info) == symndx + && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rel->r_info))) + return true; + } + else + for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize); + ++ndx) + { + GElf_Rela rela_mem; + GElf_Rela *rela = gelf_getrela (data, ndx, &rela_mem); + if (rela == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot get relocation: %s"), + elf_errmsg (-1)); + + if ((int) GELF_R_SYM (rela->r_info) == symndx + && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rela->r_info))) + return true; + } + } + + return false; +} + + +static int +regioncompare (const void *p1, const void *p2) +{ + const struct region *r1 = (const struct region *) p1; + const struct region *r2 = (const struct region *) p2; + + if (r1->from < r2->from) + return -1; + return 1; +} diff --git a/src/elflint.c b/src/elflint.c new file mode 100644 index 00000000..03572377 --- /dev/null +++ b/src/elflint.c @@ -0,0 +1,2617 @@ +/* Pedantic checking of ELF files compliance with gABI/psABI spec. + Copyright (C) 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2001. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <argp.h> +#include <assert.h> +#include <byteswap.h> +#include <endian.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <inttypes.h> +#include <libintl.h> +#include <locale.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/param.h> + +#include <elf-knowledge.h> +#include <system.h> +#include "../libebl/libeblP.h" + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + +#define ARGP_strict 300 +#define ARGP_gnuld 301 + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + + { "strict", ARGP_strict, NULL, 0, + N_("Be extremely strict, flag level 2 features."), 0 }, + { "quiet", 'q', NULL, 0, N_("Do not print anything if successful"), 0 }, + { "debuginfo", 'd', NULL, 0, N_("Binary is a separate debuginfo file"), 0 }, + { "gnu-ld", ARGP_gnuld, NULL, 0, + N_("Binary has been created with GNU ld and is therefore known to be \ +broken in certain ways"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("\ +Pedantic checking of ELF files compliance with gABI/psABI spec."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("FILE..."); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, NULL, NULL, NULL +}; + + +/* Declarations of local functions. */ +static void process_file (int fd, Elf *elf, const char *prefix, + const char *suffix, const char *fname, size_t size, + bool only_one); +static void process_elf_file (Elf *elf, const char *prefix, const char *suffix, + const char *fname, size_t size, bool only_one); + +/* Report an error. */ +#define ERROR(str, args...) \ + do { \ + printf (str, ##args); \ + ++error_count; \ + } while (0) +static unsigned int error_count; + +/* True if we should perform very strict testing. */ +static bool be_strict; + +/* True if no message is to be printed if the run is succesful. */ +static bool be_quiet; + +/* True if binary is from strip -f, not a normal ELF file. */ +static bool is_debuginfo; + +/* True if binary is assumed to be generated with GNU ld. */ +static bool gnuld; + +/* Index of section header string table. */ +static uint32_t shstrndx; + +/* Array to count references in section groups. */ +static int *scnref; + + +int +main (int argc, char *argv[]) +{ + /* Set locale. */ + setlocale (LC_ALL, ""); + + /* Initialize the message catalog. */ + textdomain (PACKAGE); + + /* Parse and process arguments. */ + int remaining; + argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + /* Before we start tell the ELF library which version we are using. */ + elf_version (EV_CURRENT); + + /* Now process all the files given at the command line. */ + bool only_one = remaining + 1 == argc; + do + { + /* Open the file. */ + int fd = open (argv[remaining], O_RDONLY); + if (fd == -1) + { + error (0, errno, gettext ("cannot open input file")); + continue; + } + + /* Create an `Elf' descriptor. */ + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (elf == NULL) + ERROR (gettext ("cannot generate Elf descriptor: %s\n"), + elf_errmsg (-1)); + else + { + unsigned int prev_error_count = error_count; + struct stat64 st; + + if (fstat64 (fd, &st) != 0) + { + printf ("cannot stat '%s': %m\n", argv[remaining]); + close (fd); + continue; + } + + process_file (fd, elf, NULL, NULL, argv[remaining], st.st_size, + only_one); + + /* Now we can close the descriptor. */ + if (elf_end (elf) != 0) + ERROR (gettext ("error while closing Elf descriptor: %s\n"), + elf_errmsg (-1)); + + if (prev_error_count == error_count && !be_quiet) + puts (gettext ("No errors")); + } + + close (fd); + } + while (++remaining < argc); + + return error_count != 0; +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg __attribute__ ((unused)), + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case ARGP_strict: + be_strict = true; + break; + + case 'q': + be_quiet = true; + break; + + case 'd': + is_debuginfo = true; + + case ARGP_gnuld: + gnuld = true; + break; + + case ARGP_KEY_NO_ARGS: + fputs (gettext ("Missing file name.\n"), stderr); + argp_help (&argp, stderr, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR, + program_invocation_short_name); + exit (1); + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "elflint (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2005"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Process one file. */ +static void +process_file (int fd, Elf *elf, const char *prefix, const char *suffix, + const char *fname, size_t size, bool only_one) +{ + /* We can handle two types of files: ELF files and archives. */ + Elf_Kind kind = elf_kind (elf); + + switch (kind) + { + case ELF_K_ELF: + /* Yes! It's an ELF file. */ + process_elf_file (elf, prefix, suffix, fname, size, only_one); + break; + + case ELF_K_AR: + { + Elf *subelf; + Elf_Cmd cmd = ELF_C_READ_MMAP; + size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); + size_t fname_len = strlen (fname) + 1; + char new_prefix[prefix_len + 1 + fname_len]; + char new_suffix[(suffix == NULL ? 0 : strlen (suffix)) + 2]; + char *cp = new_prefix; + + /* Create the full name of the file. */ + if (prefix != NULL) + { + cp = mempcpy (cp, prefix, prefix_len); + *cp++ = '('; + strcpy (stpcpy (new_suffix, suffix), ")"); + } + else + new_suffix[0] = '\0'; + memcpy (cp, fname, fname_len); + + /* It's an archive. We process each file in it. */ + while ((subelf = elf_begin (fd, cmd, elf)) != NULL) + { + kind = elf_kind (subelf); + + /* Call this function recursively. */ + if (kind == ELF_K_ELF || kind == ELF_K_AR) + { + Elf_Arhdr *arhdr = elf_getarhdr (subelf); + assert (arhdr != NULL); + + process_file (fd, subelf, new_prefix, new_suffix, + arhdr->ar_name, arhdr->ar_size, false); + } + + /* Get next archive element. */ + cmd = elf_next (subelf); + if (elf_end (subelf) != 0) + ERROR (gettext (" error while freeing sub-ELF descriptor: %s\n"), + elf_errmsg (-1)); + } + } + break; + + default: + /* We cannot do anything. */ + ERROR (gettext ("\ +Not an ELF file - it has the wrong magic bytes at the start")); + break; + } +} + + +static const char * +section_name (Ebl *ebl, int idx) +{ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr; + + shdr = gelf_getshdr (elf_getscn (ebl->elf, idx), &shdr_mem); + + return elf_strptr (ebl->elf, shstrndx, shdr->sh_name); +} + + +static const int valid_e_machine[] = + { + EM_M32, EM_SPARC, EM_386, EM_68K, EM_88K, EM_860, EM_MIPS, EM_S370, + EM_MIPS_RS3_LE, EM_PARISC, EM_VPP500, EM_SPARC32PLUS, EM_960, EM_PPC, + EM_PPC64, EM_S390, EM_V800, EM_FR20, EM_RH32, EM_RCE, EM_ARM, + EM_FAKE_ALPHA, EM_SH, EM_SPARCV9, EM_TRICORE, EM_ARC, EM_H8_300, + EM_H8_300H, EM_H8S, EM_H8_500, EM_IA_64, EM_MIPS_X, EM_COLDFIRE, + EM_68HC12, EM_MMA, EM_PCP, EM_NCPU, EM_NDR1, EM_STARCORE, EM_ME16, + EM_ST100, EM_TINYJ, EM_X86_64, EM_PDSP, EM_FX66, EM_ST9PLUS, EM_ST7, + EM_68HC16, EM_68HC11, EM_68HC08, EM_68HC05, EM_SVX, EM_ST19, EM_VAX, + EM_CRIS, EM_JAVELIN, EM_FIREPATH, EM_ZSP, EM_MMIX, EM_HUANY, EM_PRISM, + EM_AVR, EM_FR30, EM_D10V, EM_D30V, EM_V850, EM_M32R, EM_MN10300, + EM_MN10200, EM_PJ, EM_OPENRISC, EM_ARC_A5, EM_XTENSA + }; +#define nvalid_e_machine \ + (sizeof (valid_e_machine) / sizeof (valid_e_machine[0])) + + +/* Number of sections. */ +static unsigned int shnum; + + +static void +check_elf_header (Ebl *ebl, GElf_Ehdr *ehdr, size_t size) +{ + char buf[512]; + size_t cnt; + + /* Check e_ident field. */ + if (ehdr->e_ident[EI_MAG0] != ELFMAG0) + ERROR ("e_ident[%d] != '%c'\n", EI_MAG0, ELFMAG0); + if (ehdr->e_ident[EI_MAG1] != ELFMAG1) + ERROR ("e_ident[%d] != '%c'\n", EI_MAG1, ELFMAG1); + if (ehdr->e_ident[EI_MAG2] != ELFMAG2) + ERROR ("e_ident[%d] != '%c'\n", EI_MAG2, ELFMAG2); + if (ehdr->e_ident[EI_MAG3] != ELFMAG3) + ERROR ("e_ident[%d] != '%c'\n", EI_MAG3, ELFMAG3); + + if (ehdr->e_ident[EI_CLASS] != ELFCLASS32 + && ehdr->e_ident[EI_CLASS] != ELFCLASS64) + ERROR (gettext ("e_ident[%d] == %d is no known class\n"), + EI_CLASS, ehdr->e_ident[EI_CLASS]); + + if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB + && ehdr->e_ident[EI_DATA] != ELFDATA2MSB) + ERROR (gettext ("e_ident[%d] == %d is no known data encoding\n"), + EI_DATA, ehdr->e_ident[EI_DATA]); + + if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) + ERROR (gettext ("unknown ELF header version number e_ident[%d] == %d\n"), + EI_VERSION, ehdr->e_ident[EI_VERSION]); + + /* We currently don't handle any OS ABIs. */ + if (ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE) + ERROR (gettext ("unsupported OS ABI e_ident[%d] == \"%s\"\n"), + EI_OSABI, + ebl_osabi_name (ebl, ehdr->e_ident[EI_OSABI], buf, sizeof (buf))); + + /* No ABI versions other than zero supported either. */ + if (ehdr->e_ident[EI_ABIVERSION] != 0) + ERROR (gettext ("unsupport ABI version e_ident[%d] == %d\n"), + EI_ABIVERSION, ehdr->e_ident[EI_ABIVERSION]); + + for (cnt = EI_PAD; cnt < EI_NIDENT; ++cnt) + if (ehdr->e_ident[cnt] != 0) + ERROR (gettext ("e_ident[%zu] is not zero\n"), cnt); + + /* Check the e_type field. */ + if (ehdr->e_type != ET_REL && ehdr->e_type != ET_EXEC + && ehdr->e_type != ET_DYN && ehdr->e_type != ET_CORE) + ERROR (gettext ("unknown object file type %d\n"), ehdr->e_type); + + /* Check the e_machine field. */ + for (cnt = 0; cnt < nvalid_e_machine; ++cnt) + if (valid_e_machine[cnt] == ehdr->e_machine) + break; + if (cnt == nvalid_e_machine) + ERROR (gettext ("unknown machine type %d\n"), ehdr->e_machine); + + /* Check the e_version field. */ + if (ehdr->e_version != EV_CURRENT) + ERROR (gettext ("unknown object file version\n")); + + /* Check the e_phoff and e_phnum fields. */ + if (ehdr->e_phoff == 0) + { + if (ehdr->e_phnum != 0) + ERROR (gettext ("invalid program header offset\n")); + else if (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN) + ERROR (gettext ("\ +executables and DSOs cannot have zero program header offset\n")); + } + else if (ehdr->e_phnum == 0) + ERROR (gettext ("invalid number of program header entries\n")); + + /* Check the e_shoff field. */ + shnum = ehdr->e_shnum; + shstrndx = ehdr->e_shstrndx; + if (ehdr->e_shoff == 0) + { + if (ehdr->e_shnum != 0) + ERROR (gettext ("invalid section header table offset\n")); + else if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN + && ehdr->e_type != ET_CORE) + ERROR (gettext ("section header table must be present\n")); + } + else + { + if (ehdr->e_shnum == 0) + { + /* Get the header of the zeroth section. The sh_size field + might contain the section number. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr; + + shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); + if (shdr != NULL) + { + /* The error will be reported later. */ + if (shdr->sh_size == 0) + ERROR (gettext ("\ +invalid number of section header table entries\n")); + else + shnum = shdr->sh_size; + } + } + + if (ehdr->e_shstrndx == SHN_XINDEX) + { + /* Get the header of the zeroth section. The sh_size field + might contain the section number. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr; + + shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); + if (shdr != NULL) + { + /* The error will be reported later. */ + if (shdr->sh_link >= shnum) + ERROR (gettext ("invalid section header index\n")); + else + shstrndx = shdr->sh_link; + } + } + else if (shstrndx >= shnum) + ERROR (gettext ("invalid section header index\n")); + } + + /* Check the e_flags field. */ + if (!ebl_machine_flag_check (ebl, ehdr->e_flags)) + ERROR (gettext ("invalid machine flags: %s\n"), + ebl_machine_flag_name (ebl, ehdr->e_flags, buf, sizeof (buf))); + + /* Check e_ehsize, e_phentsize, and e_shentsize fields. */ + if (gelf_getclass (ebl->elf) == ELFCLASS32) + { + if (ehdr->e_ehsize != 0 && ehdr->e_ehsize != sizeof (Elf32_Ehdr)) + ERROR (gettext ("invalid ELF header size: %hd\n"), ehdr->e_ehsize); + + if (ehdr->e_phentsize != 0 && ehdr->e_phentsize != sizeof (Elf32_Phdr)) + ERROR (gettext ("invalid program header size: %hd\n"), + ehdr->e_phentsize); + else if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > size) + ERROR (gettext ("invalid program header position or size\n")); + + if (ehdr->e_shentsize != 0 && ehdr->e_shentsize != sizeof (Elf32_Shdr)) + ERROR (gettext ("invalid section header size: %hd\n"), + ehdr->e_shentsize); + else if (ehdr->e_shoff + ehdr->e_shnum * ehdr->e_shentsize > size) + ERROR (gettext ("invalid section header position or size\n")); + } + else if (gelf_getclass (ebl->elf) == ELFCLASS64) + { + if (ehdr->e_ehsize != 0 && ehdr->e_ehsize != sizeof (Elf64_Ehdr)) + ERROR (gettext ("invalid ELF header size: %hd\n"), ehdr->e_ehsize); + + if (ehdr->e_phentsize != 0 && ehdr->e_phentsize != sizeof (Elf64_Phdr)) + ERROR (gettext ("invalid program header size: %hd\n"), + ehdr->e_phentsize); + else if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > size) + ERROR (gettext ("invalid program header position or size\n")); + + if (ehdr->e_shentsize != 0 && ehdr->e_shentsize != sizeof (Elf64_Shdr)) + ERROR (gettext ("invalid section header size: %hd\n"), + ehdr->e_shentsize); + else if (ehdr->e_shoff + ehdr->e_shnum * ehdr->e_shentsize > size) + ERROR (gettext ("invalid section header position or size\n")); + } +} + + +/* Check that there is a section group section with index < IDX which + contains section IDX and that there is exactly one. */ +static void +check_scn_group (Ebl *ebl, int idx) +{ + if (scnref[idx] == 0) + { + /* No reference so far. Search following sections, maybe the + order is wrong. */ + size_t cnt; + + for (cnt = idx + 1; cnt < shnum; ++cnt) + { + Elf_Scn *scn; + GElf_Shdr shdr_mem; + GElf_Shdr *shdr; + Elf_Data *data; + Elf32_Word *grpdata; + size_t inner; + + scn = elf_getscn (ebl->elf, cnt); + shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + /* We cannot get the section header so we cannot check it. + The error to get the section header will be shown + somewhere else. */ + continue; + + if (shdr->sh_type != SHT_GROUP) + continue; + + data = elf_getdata (scn, NULL); + if (data == NULL || data->d_size < sizeof (Elf32_Word)) + /* Cannot check the section. */ + continue; + + grpdata = (Elf32_Word *) data->d_buf; + for (inner = 1; inner < data->d_size / sizeof (Elf32_Word); ++inner) + if (grpdata[inner] == (Elf32_Word) idx) + goto out; + } + + out: + if (cnt == shnum) + ERROR (gettext ("\ +section [%2d] '%s': section with SHF_GROUP flag set not part of a section group\n"), + idx, section_name (ebl, idx)); + else + ERROR (gettext ("\ +section [%2d] '%s': section group [%2zu] '%s' does not preceed group member\n"), + idx, section_name (ebl, idx), + cnt, section_name (ebl, cnt)); + } +} + + +static void +check_symtab (Ebl *ebl, GElf_Ehdr *ehdr, int idx) +{ + bool no_xndx_warned = false; + int no_pt_tls = 0; + + Elf_Scn *scn = elf_getscn (ebl->elf, idx); + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + GElf_Shdr strshdr_mem; + GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &strshdr_mem); + if (shdr == NULL || strshdr == NULL) + return; + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + { + ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), + idx, section_name (ebl, idx)); + return; + } + + if (strshdr->sh_type != SHT_STRTAB) + ERROR (gettext ("section [%2d] '%s': referenced as string table for section [%2d] '%s' but type is not SHT_STRTAB\n"), + shdr->sh_link, section_name (ebl, shdr->sh_link), + idx, section_name (ebl, idx)); + + /* Search for an extended section index table section. */ + size_t cnt; + GElf_Shdr xndxshdr_mem; + GElf_Shdr *xndxshdr = NULL; + Elf_Data *xndxdata = NULL; + Elf32_Word xndxscnidx = 0; + for (cnt = 1; cnt < shnum; ++cnt) + if (cnt != (size_t) idx) + { + Elf_Scn *xndxscn = elf_getscn (ebl->elf, cnt); + xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem); + xndxdata = elf_getdata (xndxscn, NULL); + xndxscnidx = elf_ndxscn (xndxscn); + + if (xndxshdr == NULL || xndxdata == NULL) + continue; + + if (xndxshdr->sh_type == SHT_SYMTAB_SHNDX + && xndxshdr->sh_link == (GElf_Word) idx) + break; + } + if (cnt == shnum) + { + xndxshdr = NULL; + xndxdata = NULL; + } + + if (shdr->sh_entsize != gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT)) + ERROR (gettext ("\ +section [%2zu] '%s': entry size is does not match ElfXX_Sym\n"), + cnt, section_name (ebl, cnt)); + + /* Test the zeroth entry. */ + GElf_Sym sym_mem; + Elf32_Word xndx; + GElf_Sym *sym = gelf_getsymshndx (data, xndxdata, 0, &sym_mem, &xndx); + if (sym == NULL) + ERROR (gettext ("section [%2d] '%s': cannot get symbol %d: %s\n"), + idx, section_name (ebl, idx), 0, elf_errmsg (-1)); + else + { + if (sym->st_name != 0) + ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), + idx, section_name (ebl, idx), "st_name"); + if (sym->st_value != 0) + ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), + idx, section_name (ebl, idx), "st_value"); + if (sym->st_size != 0) + ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), + idx, section_name (ebl, idx), "st_size"); + if (sym->st_info != 0) + ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), + idx, section_name (ebl, idx), "st_info"); + if (sym->st_other != 0) + ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), + idx, section_name (ebl, idx), "st_other"); + if (sym->st_shndx != 0) + ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"), + idx, section_name (ebl, idx), "st_shndx"); + if (xndxdata != NULL && xndx != 0) + ERROR (gettext ("\ +section [%2d] '%s': XINDEX for zeroth entry not zero\n"), + xndxscnidx, section_name (ebl, xndxscnidx)); + } + + for (cnt = 1; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) + { + sym = gelf_getsymshndx (data, xndxdata, cnt, &sym_mem, &xndx); + if (sym == NULL) + { + ERROR (gettext ("section [%2d] '%s': cannot get symbol %zu: %s\n"), + idx, section_name (ebl, idx), cnt, elf_errmsg (-1)); + continue; + } + + const char *name = NULL; + if (sym->st_name >= strshdr->sh_size) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: invalid name value\n"), + idx, section_name (ebl, idx), cnt); + else + { + name = elf_strptr (ebl->elf, shdr->sh_link, sym->st_name); + assert (name != NULL); + } + + if (sym->st_shndx == SHN_XINDEX) + { + if (xndxdata == NULL) + { + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: too large section index but no extended section index section\n"), + idx, section_name (ebl, idx), cnt); + no_xndx_warned = true; + } + else if (xndx < SHN_LORESERVE) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: XINDEX used for index which would fit in st_shndx (%" PRIu32 ")\n"), + xndxscnidx, section_name (ebl, xndxscnidx), cnt, + xndx); + } + else if ((sym->st_shndx >= SHN_LORESERVE + // && sym->st_shndx <= SHN_HIRESERVE always true + && sym->st_shndx != SHN_ABS + && sym->st_shndx != SHN_COMMON) + || (sym->st_shndx >= shnum + && (sym->st_shndx < SHN_LORESERVE + /* || sym->st_shndx > SHN_HIRESERVE always false */))) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: invalid section index\n"), + idx, section_name (ebl, idx), cnt); + else + xndx = sym->st_shndx; + + if (GELF_ST_TYPE (sym->st_info) >= STT_NUM) + ERROR (gettext ("section [%2d] '%s': symbol %zu: unknown type\n"), + idx, section_name (ebl, idx), cnt); + + if (GELF_ST_BIND (sym->st_info) >= STB_NUM) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: unknown symbol binding\n"), + idx, section_name (ebl, idx), cnt); + + if (xndx == SHN_COMMON) + { + /* Common symbols can only appear in relocatable files. */ + if (ehdr->e_type != ET_REL) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: COMMON only allowed in relocatable files\n"), + idx, section_name (ebl, idx), cnt); + if (cnt < shdr->sh_info) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: local COMMON symbols are nonsense\n"), + idx, section_name (ebl, idx), cnt); + if (GELF_R_TYPE (sym->st_info) == STT_FUNC) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: function in COMMON section is nonsense\n"), + idx, section_name (ebl, idx), cnt); + } + else if (xndx > 0 && xndx < shnum) + { + GElf_Shdr destshdr_mem; + GElf_Shdr *destshdr; + + destshdr = gelf_getshdr (elf_getscn (ebl->elf, xndx), &destshdr_mem); + if (destshdr != NULL) + { + if (GELF_ST_TYPE (sym->st_info) != STT_TLS) + { + if ((sym->st_value - destshdr->sh_addr) > destshdr->sh_size) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: st_value out of bounds\n"), + idx, section_name (ebl, idx), cnt); + else if ((sym->st_value - destshdr->sh_addr + sym->st_size) + > destshdr->sh_size) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2d] '%s'\n"), + idx, section_name (ebl, idx), cnt, + (int) xndx, section_name (ebl, xndx)); + } + else + { + if ((destshdr->sh_flags & SHF_TLS) == 0) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: referenced section [%2d] '%s' does not have SHF_TLS flag set\n"), + idx, section_name (ebl, idx), cnt, + (int) xndx, section_name (ebl, xndx)); + + if (ehdr->e_type == ET_REL) + { + /* For object files the symbol value must fall + into the section. */ + if (sym->st_value > destshdr->sh_size) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: st_value out of bounds of referenced section [%2d] '%s'\n"), + idx, section_name (ebl, idx), cnt, + (int) xndx, section_name (ebl, xndx)); + else if (sym->st_value + sym->st_size + > destshdr->sh_size) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2d] '%s'\n"), + idx, section_name (ebl, idx), cnt, + (int) xndx, section_name (ebl, xndx)); + } + else + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = NULL; + int pcnt; + + for (pcnt = 0; pcnt < ehdr->e_phnum; ++pcnt) + { + phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem); + if (phdr != NULL && phdr->p_type == PT_TLS) + break; + } + + if (pcnt == ehdr->e_phnum) + { + if (no_pt_tls++ == 0) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: TLS symbol but no TLS program header entry\n"), + idx, section_name (ebl, idx), cnt); + } + else + { + if (sym->st_value + < destshdr->sh_offset - phdr->p_offset) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: st_value short of referenced section [%2d] '%s'\n"), + idx, section_name (ebl, idx), cnt, + (int) xndx, section_name (ebl, xndx)); + else if (sym->st_value + > (destshdr->sh_offset - phdr->p_offset + + destshdr->sh_size)) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: st_value out of bounds of referenced section [%2d] '%s'\n"), + idx, section_name (ebl, idx), cnt, + (int) xndx, section_name (ebl, xndx)); + else if (sym->st_value + sym->st_size + > (destshdr->sh_offset - phdr->p_offset + + destshdr->sh_size)) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2d] '%s'\n"), + idx, section_name (ebl, idx), cnt, + (int) xndx, section_name (ebl, xndx)); + } + } + } + } + } + + if (GELF_ST_BIND (sym->st_info) == STB_LOCAL) + { + if (cnt >= shdr->sh_info) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: local symbol outside range described in sh_info\n"), + idx, section_name (ebl, idx), cnt); + } + else + { + if (cnt < shdr->sh_info) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: non-local symbol outside range described in sh_info\n"), + idx, section_name (ebl, idx), cnt); + } + + if (GELF_ST_TYPE (sym->st_info) == STT_SECTION + && GELF_ST_BIND (sym->st_info) != STB_LOCAL) + ERROR (gettext ("\ +section [%2d] '%s': symbol %zu: non-local section symbol\n"), + idx, section_name (ebl, idx), cnt); + + if (name != NULL) + { + if (strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0) + { + /* Check that address and size match the global offset + table. We have to locate the GOT by searching for a + section named ".got". */ + Elf_Scn *gscn = NULL; + GElf_Addr addr = 0; + GElf_Xword size = 0; + bool found = false; + + while ((gscn = elf_nextscn (ebl->elf, gscn)) != NULL) + { + GElf_Shdr gshdr_mem; + GElf_Shdr *gshdr = gelf_getshdr (gscn, &gshdr_mem); + assert (gshdr != NULL); + + const char *sname = elf_strptr (ebl->elf, ehdr->e_shstrndx, + gshdr->sh_name); + if (sname != NULL) + { + if (strcmp (sname, ".got.plt") == 0) + { + addr = gshdr->sh_addr; + size = gshdr->sh_size; + found = true; + break; + } + if (strcmp (sname, ".got") == 0) + { + addr = gshdr->sh_addr; + size = gshdr->sh_size; + found = true; + /* Do not stop looking. There might be a + .got.plt section. */ + } + } + } + + if (found) + { + /* Found it. */ + if (sym->st_value != addr) + /* This test is more strict than the psABIs which + usually allow the symbol to be in the middle of + the .got section, allowing negative offsets. */ + ERROR (gettext ("\ +section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol value %#" PRIx64 " does not match .got section address %#" PRIx64 "\n"), + idx, section_name (ebl, idx), + (uint64_t) sym->st_value, (uint64_t) addr); + + if (!gnuld && sym->st_size != size) + ERROR (gettext ("\ +section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol size %" PRIu64 " does not match .got section size %" PRIu64 "\n"), + idx, section_name (ebl, idx), + (uint64_t) sym->st_size, (uint64_t) size); + } + else + ERROR (gettext ("\ +section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol present, but no .got section\n"), + idx, section_name (ebl, idx)); + } + else if (strcmp (name, "_DYNAMIC") == 0) + { + /* Check that address and size match the dynamic + section. We locate the dynamic section via the + program header entry. */ + int pcnt; + + for (pcnt = 0; pcnt < ehdr->e_phnum; ++pcnt) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem); + + if (phdr != NULL && phdr->p_type == PT_DYNAMIC) + { + if (sym->st_value != phdr->p_vaddr) + ERROR (gettext ("\ +section [%2d] '%s': _DYNAMIC_ symbol value %#" PRIx64 " does not match dynamic segment address %#" PRIx64 "\n"), + idx, section_name (ebl, idx), + (uint64_t) sym->st_value, + (uint64_t) phdr->p_vaddr); + + if (!gnuld && sym->st_size != phdr->p_memsz) + ERROR (gettext ("\ +section [%2d] '%s': _DYNAMIC symbol size %" PRIu64 " does not match dynamic segment size %" PRIu64 "\n"), + idx, section_name (ebl, idx), + (uint64_t) sym->st_size, + (uint64_t) phdr->p_memsz); + + break; + } + } + } + } + } +} + + +static bool +is_rel_dyn (Ebl *ebl, GElf_Ehdr *ehdr, int idx, GElf_Shdr *shdr, bool rela) +{ + /* If this is no executable or DSO it cannot be a .rel.dyn section. */ + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) + return false; + + /* Check the section name. Unfortunately necessary. */ + if (strcmp (section_name (ebl, idx), rela ? ".rela.dyn" : ".rel.dyn")) + return false; + + /* When a .rel.dyn section is used a DT_RELCOUNT dynamic section + entry can be present as well. */ + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + GElf_Shdr rcshdr_mem; + const GElf_Shdr *rcshdr = gelf_getshdr (scn, &rcshdr_mem); + assert (rcshdr != NULL); + + if (rcshdr->sh_type == SHT_DYNAMIC) + { + /* Found the dynamic section. Look through it. */ + Elf_Data *d = elf_getdata (scn, NULL); + size_t cnt; + + for (cnt = 1; cnt < rcshdr->sh_size / rcshdr->sh_entsize; ++cnt) + { + GElf_Dyn dyn_mem; + GElf_Dyn *dyn = gelf_getdyn (d, cnt, &dyn_mem); + assert (dyn != NULL); + + if (dyn->d_tag == DT_RELCOUNT) + { + /* Found it. One last check: does the number + specified number of relative relocations exceed + the total number of relocations? */ + if (dyn->d_un.d_val > shdr->sh_size / shdr->sh_entsize) + ERROR (gettext ("\ +section [%2d] '%s': DT_RELCOUNT value %d too high for this section\n"), + idx, section_name (ebl, idx), + (int) dyn->d_un.d_val); + } + } + + break; + } + } + + return true; +} + + +static void +check_rela (Ebl *ebl, GElf_Ehdr *ehdr, int idx) +{ + Elf_Scn *scn; + GElf_Shdr shdr_mem; + GElf_Shdr *shdr; + Elf_Data *data; + GElf_Shdr destshdr_mem; + GElf_Shdr *destshdr = NULL; + size_t cnt; + bool reldyn = false; + bool known_broken = gnuld; + + scn = elf_getscn (ebl->elf, idx); + shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + return; + data = elf_getdata (scn, NULL); + if (data == NULL) + { + ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), + idx, section_name (ebl, idx)); + return; + } + + /* Check whether the link to the section we relocate is reasonable. */ + if (shdr->sh_info >= shnum) + ERROR (gettext ("section [%2d] '%s': invalid destination section index\n"), + idx, section_name (ebl, idx)); + else + { + destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info), + &destshdr_mem); + if (destshdr != NULL) + { + if(destshdr->sh_type != SHT_PROGBITS + && destshdr->sh_type != SHT_NOBITS) + { + reldyn = is_rel_dyn (ebl, ehdr, idx, shdr, true); + if (!reldyn) + ERROR (gettext ("\ +section [%2d] '%s': invalid destination section type\n"), + idx, section_name (ebl, idx)); + else + { + /* There is no standard, but we require that .rela.dyn + sections have a sh_info value of zero. */ + if (shdr->sh_info != 0) + ERROR (gettext ("\ +section [%2d] '%s': sh_info should be zero\n"), + idx, section_name (ebl, idx)); + } + } + + if ((destshdr->sh_flags & (SHF_MERGE | SHF_STRINGS)) != 0) + ERROR (gettext ("\ +section [%2d] '%s': no relocations for merge-able sections possible\n"), + idx, section_name (ebl, idx)); + } + } + + if (shdr->sh_entsize != gelf_fsize (ebl->elf, ELF_T_RELA, 1, EV_CURRENT)) + ERROR (gettext ("\ +section [%2d] '%s': section entry size does not match ElfXX_Rela\n"), + idx, section_name (ebl, idx)); + + Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); + Elf_Data *symdata = elf_getdata (symscn, NULL); + + for (cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) + { + GElf_Rela rela_mem; + GElf_Rela *rela; + + rela = gelf_getrela (data, cnt, &rela_mem); + if (rela == NULL) + { + ERROR (gettext ("\ +section [%2d] '%s': cannot get relocation %zu: %s\n"), + idx, section_name (ebl, idx), cnt, elf_errmsg (-1)); + continue; + } + + if (!ebl_reloc_type_check (ebl, GELF_R_TYPE (rela->r_info))) + ERROR (gettext ("section [%2d] '%s': relocation %zu: invalid type\n"), + idx, section_name (ebl, idx), cnt); + else if (!ebl_reloc_valid_use (ebl, GELF_R_TYPE (rela->r_info))) + ERROR (gettext ("\ +section [%2d] '%s': relocation %zu: relocation type invalid for the file type\n"), + idx, section_name (ebl, idx), cnt); + + if (symshdr != NULL + && ((GELF_R_SYM (rela->r_info) + 1) + * gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT) + > symshdr->sh_size)) + ERROR (gettext ("\ +section [%2d] '%s': relocation %zu: invalid symbol index\n"), + idx, section_name (ebl, idx), cnt); + + if (ebl_gotpc_reloc_check (ebl, GELF_R_TYPE (rela->r_info))) + { + const char *name; + char buf[64]; + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symdata, GELF_R_SYM (rela->r_info), + &sym_mem); + if (sym != NULL + /* Get the name for the symbol. */ + && (name = elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name)) + && strcmp (name, "_GLOBAL_OFFSET_TABLE_") !=0 ) + ERROR (gettext ("\ +section [%2d] '%s': relocation %zu: only symbol '_GLOBAL_OFFSET_TABLE_' can be used with %s\n"), + idx, section_name (ebl, idx), cnt, + ebl_reloc_type_name (ebl, GELF_R_SYM (rela->r_info), + buf, sizeof (buf))); + } + + if (reldyn) + { + // XXX TODO Check .rel.dyn section addresses. + } + else if (!known_broken) + { + if (destshdr != NULL + && (rela->r_offset - destshdr->sh_addr) >= destshdr->sh_size) + ERROR (gettext ("\ +section [%2d] '%s': relocation %zu: offset out of bounds\n"), + idx, section_name (ebl, idx), cnt); + } + + if (symdata != NULL + && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rela->r_info))) + { + /* Make sure the referenced symbol is an object or unspecified. */ + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symdata, GELF_R_SYM (rela->r_info), + &sym_mem); + if (sym != NULL + && GELF_ST_TYPE (sym->st_info) != STT_NOTYPE + && GELF_ST_TYPE (sym->st_info) != STT_OBJECT) + { + char buf[64]; + ERROR (gettext ("section [%2d] '%s': relocation %zu: copy relocation against symbol of type %s\n"), + idx, section_name (ebl, idx), cnt, + ebl_symbol_type_name (ebl, GELF_ST_TYPE (sym->st_info), + buf, sizeof (buf))); + } + } + } +} + + +static void +check_rel (Ebl *ebl, GElf_Ehdr *ehdr, int idx) +{ + Elf_Scn *scn; + GElf_Shdr shdr_mem; + GElf_Shdr *shdr; + Elf_Data *data; + GElf_Shdr destshdr_mem; + GElf_Shdr *destshdr = NULL; + size_t cnt; + bool reldyn = false; + bool known_broken = gnuld; + + scn = elf_getscn (ebl->elf, idx); + shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + return; + data = elf_getdata (scn, NULL); + if (data == NULL) + { + ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), + idx, section_name (ebl, idx)); + return; + } + + /* Check whether the link to the section we relocate is reasonable. */ + if (shdr->sh_info >= shnum) + ERROR (gettext ("section [%2d] '%s': invalid destination section index\n"), + idx, section_name (ebl, idx)); + else + { + destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info), + &destshdr_mem); + if (destshdr != NULL) + { + if (destshdr->sh_type != SHT_PROGBITS + && destshdr->sh_type != SHT_NOBITS) + { + reldyn = is_rel_dyn (ebl, ehdr, idx, shdr, false); + if (!reldyn) + ERROR (gettext ("\ +section [%2d] '%s': invalid destination section type\n"), + idx, section_name (ebl, idx)); + else + { + /* There is no standard, but we require that .rela.dyn + sections have a sh_info value of zero. */ + if (shdr->sh_info != 0) + ERROR (gettext ("\ +section [%2d] '%s': sh_info should be zero\n"), + idx, section_name (ebl, idx)); + } + } + + if ((destshdr->sh_flags & (SHF_MERGE | SHF_STRINGS)) != 0) + ERROR (gettext ("\ +section [%2d] '%s': no relocations for merge-able sections possible\n"), + idx, section_name (ebl, idx)); + } + } + + if (shdr->sh_entsize != gelf_fsize (ebl->elf, ELF_T_REL, 1, EV_CURRENT)) + ERROR (gettext ("\ +section [%2d] '%s': section entry size does not match ElfXX_Rel\n"), + idx, section_name (ebl, idx)); + + Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); + Elf_Data *symdata = elf_getdata (symscn, NULL); + + for (cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) + { + GElf_Rel rel_mem; + GElf_Rel *rel; + + rel = gelf_getrel (data, cnt, &rel_mem); + if (rel == NULL) + { + ERROR (gettext ("\ +section [%2d] '%s': cannot get relocation %zu: %s\n"), + idx, section_name (ebl, idx), cnt, elf_errmsg (-1)); + continue; + } + + if (!ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))) + ERROR (gettext ("section [%2d] '%s': relocation %zu: invalid type\n"), + idx, section_name (ebl, idx), cnt); + else if (!ebl_reloc_valid_use (ebl, GELF_R_TYPE (rel->r_info))) + ERROR (gettext ("\ +section [%2d] '%s': relocation %zu: relocation type invalid for the file type\n"), + idx, section_name (ebl, idx), cnt); + + if (symshdr != NULL + && ((GELF_R_SYM (rel->r_info) + 1) + * gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT) + > symshdr->sh_size)) + ERROR (gettext ("\ +section [%2d] '%s': relocation %zu: invalid symbol index\n"), + idx, section_name (ebl, idx), cnt); + + if (ebl_gotpc_reloc_check (ebl, GELF_R_TYPE (rel->r_info))) + { + const char *name; + char buf[64]; + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symdata, GELF_R_SYM (rel->r_info), + &sym_mem); + if (sym != NULL + /* Get the name for the symbol. */ + && (name = elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name)) + && strcmp (name, "_GLOBAL_OFFSET_TABLE_") !=0 ) + ERROR (gettext ("\ +section [%2d] '%s': relocation %zu: only symbol '_GLOBAL_OFFSET_TABLE_' can be used with %s\n"), + idx, section_name (ebl, idx), cnt, + ebl_reloc_type_name (ebl, GELF_R_SYM (rel->r_info), + buf, sizeof (buf))); + } + + if (reldyn) + { + // XXX TODO Check .rel.dyn section addresses. + } + else if (!known_broken) + { + if (destshdr != NULL + && GELF_R_TYPE (rel->r_info) != 0 + && (rel->r_offset - destshdr->sh_addr) >= destshdr->sh_size) + ERROR (gettext ("\ +section [%2d] '%s': relocation %zu: offset out of bounds\n"), + idx, section_name (ebl, idx), cnt); + } + + if (symdata != NULL + && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rel->r_info))) + { + /* Make sure the referenced symbol is an object or unspecified. */ + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symdata, GELF_R_SYM (rel->r_info), + &sym_mem); + if (sym != NULL + && GELF_ST_TYPE (sym->st_info) != STT_NOTYPE + && GELF_ST_TYPE (sym->st_info) != STT_OBJECT) + { + char buf[64]; + ERROR (gettext ("section [%2d] '%s': relocation %zu: copy relocation against symbol of type %s\n"), + idx, section_name (ebl, idx), cnt, + ebl_symbol_type_name (ebl, GELF_ST_TYPE (sym->st_info), + buf, sizeof (buf))); + } + } + } +} + + +/* Number of dynamic sections. */ +static int ndynamic; + + +static void +check_dynamic (Ebl *ebl, int idx) +{ + Elf_Scn *scn; + GElf_Shdr shdr_mem; + GElf_Shdr *shdr; + Elf_Data *data; + GElf_Shdr strshdr_mem; + GElf_Shdr *strshdr; + size_t cnt; + static const bool dependencies[DT_NUM][DT_NUM] = + { + [DT_NEEDED] = { [DT_STRTAB] = true }, + [DT_PLTRELSZ] = { [DT_JMPREL] = true }, + [DT_HASH] = { [DT_SYMTAB] = true }, + [DT_STRTAB] = { [DT_STRSZ] = true }, + [DT_SYMTAB] = { [DT_STRTAB] = true, [DT_HASH] = true, + [DT_SYMENT] = true }, + [DT_RELA] = { [DT_RELASZ] = true, [DT_RELAENT] = true }, + [DT_RELASZ] = { [DT_RELA] = true }, + [DT_RELAENT] = { [DT_RELA] = true }, + [DT_STRSZ] = { [DT_STRTAB] = true }, + [DT_SYMENT] = { [DT_SYMTAB] = true }, + [DT_SONAME] = { [DT_STRTAB] = true }, + [DT_RPATH] = { [DT_STRTAB] = true }, + [DT_REL] = { [DT_RELSZ] = true, [DT_RELENT] = true }, + [DT_RELSZ] = { [DT_REL] = true }, + [DT_RELENT] = { [DT_REL] = true }, + [DT_JMPREL] = { [DT_PLTRELSZ] = true, [DT_PLTREL] = true }, + [DT_RUNPATH] = { [DT_STRTAB] = true }, + [DT_PLTREL] = { [DT_JMPREL] = true }, + [DT_PLTRELSZ] = { [DT_JMPREL] = true } + }; + bool has_dt[DT_NUM]; + static const bool level2[DT_NUM] = + { + [DT_RPATH] = true, + [DT_SYMBOLIC] = true, + [DT_TEXTREL] = true, + [DT_BIND_NOW] = true + }; + static const bool mandatory[DT_NUM] = + { + [DT_NULL] = true, + [DT_HASH] = true, + [DT_STRTAB] = true, + [DT_SYMTAB] = true, + [DT_STRSZ] = true, + [DT_SYMENT] = true + }; + GElf_Addr reladdr = 0; + GElf_Word relsz = 0; + GElf_Addr pltreladdr = 0; + GElf_Word pltrelsz = 0; + + memset (has_dt, '\0', sizeof (has_dt)); + + if (++ndynamic == 2) + ERROR (gettext ("more than one dynamic section present\n")); + + scn = elf_getscn (ebl->elf, idx); + shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + return; + data = elf_getdata (scn, NULL); + if (data == NULL) + { + ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), + idx, section_name (ebl, idx)); + return; + } + + strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), &strshdr_mem); + if (strshdr != NULL && strshdr->sh_type != SHT_STRTAB) + ERROR (gettext ("\ +section [%2d] '%s': referenced as string table for section [%2d] '%s' but type is not SHT_STRTAB\n"), + shdr->sh_link, section_name (ebl, shdr->sh_link), + idx, section_name (ebl, idx)); + + if (shdr->sh_entsize != gelf_fsize (ebl->elf, ELF_T_DYN, 1, EV_CURRENT)) + ERROR (gettext ("\ +section [%2d] '%s': section entry size does not match ElfXX_Dyn\n"), + idx, section_name (ebl, idx)); + + if (shdr->sh_info != 0) + ERROR (gettext ("section [%2d] '%s': sh_info not zero\n"), + idx, section_name (ebl, idx)); + + bool non_null_warned = false; + for (cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) + { + GElf_Dyn dyn_mem; + GElf_Dyn *dyn; + + dyn = gelf_getdyn (data, cnt, &dyn_mem); + if (dyn == NULL) + { + ERROR (gettext ("\ +section [%2d] '%s': cannot get dynamic section entry %zu: %s\n"), + idx, section_name (ebl, idx), cnt, elf_errmsg (-1)); + continue; + } + + if (has_dt[DT_NULL] && dyn->d_tag != DT_NULL && ! non_null_warned) + { + ERROR (gettext ("\ +section [%2d] '%s': non-DT_NULL entries follow DT_NULL entry\n"), + idx, section_name (ebl, idx)); + non_null_warned = true; + } + + if (!ebl_dynamic_tag_check (ebl, dyn->d_tag)) + ERROR (gettext ("section [%2d] '%s': entry %zu: unknown tag\n"), + idx, section_name (ebl, idx), cnt); + + if (dyn->d_tag >= 0 && dyn->d_tag < DT_NUM) + { + if (has_dt[dyn->d_tag] + && dyn->d_tag != DT_NEEDED + && dyn->d_tag != DT_NULL + && dyn->d_tag != DT_POSFLAG_1) + { + char buf[50]; + ERROR (gettext ("\ +section [%2d] '%s': entry %zu: more than one entry with tag %s\n"), + idx, section_name (ebl, idx), cnt, + ebl_dynamic_tag_name (ebl, dyn->d_tag, + buf, sizeof (buf))); + } + + if (be_strict && level2[dyn->d_tag]) + { + char buf[50]; + ERROR (gettext ("\ +section [%2d] '%s': entry %zu: level 2 tag %s used\n"), + idx, section_name (ebl, idx), cnt, + ebl_dynamic_tag_name (ebl, dyn->d_tag, + buf, sizeof (buf))); + } + + has_dt[dyn->d_tag] = true; + } + + if (dyn->d_tag == DT_PLTREL && dyn->d_un.d_val != DT_REL + && dyn->d_un.d_val != DT_RELA) + ERROR (gettext ("\ +section [%2d] '%s': entry %zu: DT_PLTREL value must be DT_REL or DT_RELA\n"), + idx, section_name (ebl, idx), cnt); + + if (dyn->d_tag == DT_REL) + reladdr = dyn->d_un.d_ptr; + if (dyn->d_tag == DT_RELSZ) + relsz = dyn->d_un.d_val; + if (dyn->d_tag == DT_JMPREL) + pltreladdr = dyn->d_un.d_ptr; + if (dyn->d_tag == DT_PLTRELSZ) + pltrelsz = dyn->d_un.d_val; + } + + for (cnt = 1; cnt < DT_NUM; ++cnt) + if (has_dt[cnt]) + { + int inner; + + for (inner = 0; inner < DT_NUM; ++inner) + if (dependencies[cnt][inner] && ! has_dt[inner]) + { + char buf1[50]; + char buf2[50]; + + ERROR (gettext ("\ +section [%2d] '%s': contains %s entry but not %s\n"), + idx, section_name (ebl, idx), + ebl_dynamic_tag_name (ebl, cnt, buf1, sizeof (buf1)), + ebl_dynamic_tag_name (ebl, inner, buf2, sizeof (buf2))); + } + } + else + { + if (mandatory[cnt]) + { + char buf[50]; + ERROR (gettext ("\ +section [%2d] '%s': mandatory tag %s not present\n"), + idx, section_name (ebl, idx), + ebl_dynamic_tag_name (ebl, cnt, buf, sizeof (buf))); + } + } + + /* Check the rel/rela tags. At least one group must be available. */ + if ((has_dt[DT_RELA] || has_dt[DT_RELASZ] || has_dt[DT_RELAENT]) + && (!has_dt[DT_RELA] || !has_dt[DT_RELASZ] || !has_dt[DT_RELAENT])) + ERROR (gettext ("\ +section [%2d] '%s': not all of %s, %s, and %s are present\n"), + idx, section_name (ebl, idx), + "DT_RELA", "DT_RELASZ", "DT_RELAENT"); + + if ((has_dt[DT_REL] || has_dt[DT_RELSZ] || has_dt[DT_RELENT]) + && (!has_dt[DT_REL] || !has_dt[DT_RELSZ] || !has_dt[DT_RELENT])) + ERROR (gettext ("\ +section [%2d] '%s': not all of %s, %s, and %s are present\n"), + idx, section_name (ebl, idx), + "DT_REL", "DT_RELSZ", "DT_RELENT"); +} + + +static void +check_symtab_shndx (Ebl *ebl, int idx) +{ + Elf_Scn *scn; + GElf_Shdr shdr_mem; + GElf_Shdr *shdr; + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr; + Elf_Scn *symscn; + size_t cnt; + Elf_Data *data; + Elf_Data *symdata; + + scn = elf_getscn (ebl->elf, idx); + shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + return; + + symscn = elf_getscn (ebl->elf, shdr->sh_link); + symshdr = gelf_getshdr (symscn, &symshdr_mem); + if (symshdr != NULL && symshdr->sh_type != SHT_SYMTAB) + ERROR (gettext ("\ +section [%2d] '%s': extended section index section not for symbol table\n"), + idx, section_name (ebl, idx)); + symdata = elf_getdata (symscn, NULL); + if (symdata == NULL) + ERROR (gettext ("cannot get data for symbol section\n")); + + if (shdr->sh_entsize != sizeof (Elf32_Word)) + ERROR (gettext ("\ +section [%2d] '%s': entry size does not match Elf32_Word\n"), + idx, section_name (ebl, idx)); + + if (symshdr != NULL + && (shdr->sh_size / shdr->sh_entsize + < symshdr->sh_size / symshdr->sh_entsize)) + ERROR (gettext ("\ +section [%2d] '%s': extended index table too small for symbol table\n"), + idx, section_name (ebl, idx)); + + if (shdr->sh_info != 0) + ERROR (gettext ("section [%2d] '%s': sh_info not zero\n"), + idx, section_name (ebl, idx)); + + for (cnt = idx + 1; cnt < shnum; ++cnt) + { + GElf_Shdr rshdr_mem; + GElf_Shdr *rshdr; + + rshdr = gelf_getshdr (elf_getscn (ebl->elf, cnt), &rshdr_mem); + if (rshdr != NULL && rshdr->sh_type == SHT_SYMTAB_SHNDX + && rshdr->sh_link == shdr->sh_link) + { + ERROR (gettext ("\ +section [%2d] '%s': extended section index in section [%2zu] '%s' refers to same symbol table\n"), + idx, section_name (ebl, idx), + cnt, section_name (ebl, cnt)); + break; + } + } + + data = elf_getdata (scn, NULL); + + if (*((Elf32_Word *) data->d_buf) != 0) + ERROR (gettext ("symbol 0 should have zero extended section index\n")); + + for (cnt = 1; cnt < data->d_size / sizeof (Elf32_Word); ++cnt) + { + Elf32_Word xndx = ((Elf32_Word *) data->d_buf)[cnt]; + + if (xndx != 0) + { + GElf_Sym sym_data; + GElf_Sym *sym = gelf_getsym (symdata, cnt, &sym_data); + if (sym == NULL) + { + ERROR (gettext ("cannot get data for symbol %zu\n"), cnt); + continue; + } + + if (sym->st_shndx != SHN_XINDEX) + ERROR (gettext ("\ +extended section index is %" PRIu32 " but symbol index is not XINDEX\n"), + (uint32_t) xndx); + } + } +} + + +static void +check_hash (Ebl *ebl, int idx) +{ + Elf_Scn *scn; + GElf_Shdr shdr_mem; + GElf_Shdr *shdr; + Elf_Data *data; + Elf32_Word nbucket; + Elf32_Word nchain; + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr; + + scn = elf_getscn (ebl->elf, idx); + shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + return; + data = elf_getdata (scn, NULL); + if (data == NULL) + { + ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), + idx, section_name (ebl, idx)); + return; + } + + symshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), &symshdr_mem); + if (symshdr != NULL && symshdr->sh_type != SHT_DYNSYM) + ERROR (gettext ("\ +section [%2d] '%s': hash table not for dynamic symbol table\n"), + idx, section_name (ebl, idx)); + + if (shdr->sh_entsize != sizeof (Elf32_Word)) + ERROR (gettext ("\ +section [%2d] '%s': entry size does not match Elf32_Word\n"), + idx, section_name (ebl, idx)); + + if ((shdr->sh_flags & SHF_ALLOC) == 0) + ERROR (gettext ("section [%2d] '%s': not marked to be allocated\n"), + idx, section_name (ebl, idx)); + + if (shdr->sh_size < 2 * shdr->sh_entsize) + { + ERROR (gettext ("\ +section [%2d] '%s': hash table has not even room for nbucket and nchain\n"), + idx, section_name (ebl, idx)); + return; + } + + nbucket = ((Elf32_Word *) data->d_buf)[0]; + nchain = ((Elf32_Word *) data->d_buf)[1]; + + if (shdr->sh_size < (2 + nbucket + nchain) * shdr->sh_entsize) + ERROR (gettext ("\ +section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"), + idx, section_name (ebl, idx), (long int) shdr->sh_size, + (long int) ((2 + nbucket + nchain) * shdr->sh_entsize)); + + if (symshdr != NULL) + { + size_t symsize = symshdr->sh_size / symshdr->sh_entsize; + size_t cnt; + + if (nchain < symshdr->sh_size / symshdr->sh_entsize) + ERROR (gettext ("section [%2d] '%s': chain array not large enough\n"), + idx, section_name (ebl, idx)); + + for (cnt = 2; cnt < 2 + nbucket; ++cnt) + if (((Elf32_Word *) data->d_buf)[cnt] >= symsize) + ERROR (gettext ("\ +section [%2d] '%s': hash bucket reference %zu out of bounds\n"), + idx, section_name (ebl, idx), cnt - 2); + + for (; cnt < 2 + nbucket + nchain; ++cnt) + if (((Elf32_Word *) data->d_buf)[cnt] >= symsize) + ERROR (gettext ("\ +section [%2d] '%s': hash chain reference %zu out of bounds\n"), + idx, section_name (ebl, idx), cnt - 2 - nbucket); + } +} + + +static void +check_null (Ebl *ebl, GElf_Shdr *shdr, int idx) +{ +#define TEST(name, extra) \ + if (extra && shdr->sh_##name != 0) \ + ERROR (gettext ("section [%2d] '%s': nonzero sh_%s for NULL section\n"), \ + idx, section_name (ebl, idx), #name) + + TEST (name, 1); + TEST (flags, 1); + TEST (addr, 1); + TEST (offset, 1); + TEST (size, idx != 0); + TEST (link, idx != 0); + TEST (info, 1); + TEST (addralign, 1); + TEST (entsize, 1); +} + + +static void +check_group (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) +{ + if (ehdr->e_type != ET_REL) + { + ERROR (gettext ("\ +section [%2d] '%s': section groups only allowed in relocatable object files\n"), + idx, section_name (ebl, idx)); + return; + } + + /* Check that sh_link is an index of a symbol table. */ + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &symshdr_mem); + if (symshdr == NULL) + ERROR (gettext ("section [%2d] '%s': cannot get symbol table: %s\n"), + idx, section_name (ebl, idx), elf_errmsg (-1)); + else + { + if (symshdr->sh_type != SHT_SYMTAB) + ERROR (gettext ("\ +section [%2d] '%s': section reference in sh_link is no symbol table\n"), + idx, section_name (ebl, idx)); + + if (shdr->sh_info >= symshdr->sh_size / gelf_fsize (ebl->elf, ELF_T_SYM, + 1, EV_CURRENT)) + ERROR (gettext ("\ +section [%2d] '%s': invalid symbol index in sh_info\n"), + idx, section_name (ebl, idx)); + + if (shdr->sh_flags != 0) + ERROR (gettext ("section [%2d] '%s': sh_flags not zero\n"), + idx, section_name (ebl, idx)); + + if (be_strict + && shdr->sh_entsize != elf32_fsize (ELF_T_WORD, 1, EV_CURRENT)) + ERROR (gettext ("section [%2d] '%s': sh_flags not set correctly\n"), + idx, section_name (ebl, idx)); + } + + Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); + if (data == NULL) + ERROR (gettext ("section [%2d] '%s': cannot get data: %s\n"), + idx, section_name (ebl, idx), elf_errmsg (-1)); + else + { + size_t elsize = elf32_fsize (ELF_T_WORD, 1, EV_CURRENT); + size_t cnt; + Elf32_Word val; + + if (data->d_size % elsize != 0) + ERROR (gettext ("\ +section [%2d] '%s': section size not multiple of sizeof(Elf32_Word)\n"), + idx, section_name (ebl, idx)); + + if (data->d_size < elsize) + ERROR (gettext ("\ +section [%2d] '%s': section group without flags word\n"), + idx, section_name (ebl, idx)); + else if (be_strict) + { + if (data->d_size < 2 * elsize) + ERROR (gettext ("\ +section [%2d] '%s': section group without member\n"), + idx, section_name (ebl, idx)); + else if (data->d_size < 3 * elsize) + ERROR (gettext ("\ +section [%2d] '%s': section group with only one member\n"), + idx, section_name (ebl, idx)); + } + +#if ALLOW_UNALIGNED + val = *((Elf32_Word *) data->d_buf); +#else + memcpy (&val, data->d_buf, elsize); +#endif + if ((val & ~GRP_COMDAT) != 0) + ERROR (gettext ("section [%2d] '%s': unknown section group flags\n"), + idx, section_name (ebl, idx)); + + for (cnt = elsize; cnt < data->d_size; cnt += elsize) + { +#if ALLOW_UNALIGNED + val = *((Elf32_Word *) ((char *) data->d_buf + cnt)); +#else + memcpy (&val, (char *) data->d_buf + cnt, elsize); +#endif + + if (val > shnum) + ERROR (gettext ("\ +section [%2d] '%s': section index %Zu out of range\n"), + idx, section_name (ebl, idx), cnt / elsize); + else + { + GElf_Shdr refshdr_mem; + GElf_Shdr *refshdr; + + refshdr = gelf_getshdr (elf_getscn (ebl->elf, val), + &refshdr_mem); + if (refshdr == NULL) + ERROR (gettext ("\ +section [%2d] '%s': cannot get section header for element %zu: %s\n"), + idx, section_name (ebl, idx), cnt / elsize, + elf_errmsg (-1)); + else + { + if (refshdr->sh_type == SHT_GROUP) + ERROR (gettext ("\ +section [%2d] '%s': section group contains another group [%2d] '%s'\n"), + idx, section_name (ebl, idx), + val, section_name (ebl, val)); + + if ((refshdr->sh_flags & SHF_GROUP) == 0) + ERROR (gettext ("\ +section [%2d] '%s': element %Zu references section [%2d] '%s' without SHF_GROUP flag set\n"), + idx, section_name (ebl, idx), cnt / elsize, + val, section_name (ebl, val)); + } + + if (++scnref[val] == 2) + ERROR (gettext ("\ +section [%2d] '%s' is contained in more than one section group\n"), + val, section_name (ebl, val)); + } + } + } +} + + +static bool has_loadable_segment; +static bool has_interp_segment; + +static const struct +{ + const char *name; + size_t namelen; + GElf_Word type; + enum { unused, exact, atleast } attrflag; + GElf_Word attr; + GElf_Word attr2; +} special_sections[] = + { + /* See figure 4-14 in the gABI. */ + { ".bss", 5, SHT_NOBITS, exact, SHF_ALLOC | SHF_WRITE, 0 }, + { ".comment", 8, SHT_PROGBITS, exact, 0, 0 }, + { ".data", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 }, + { ".data1", 7, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 }, + { ".debug", 7, SHT_PROGBITS, exact, 0, 0 }, + { ".dynamic", 9, SHT_DYNAMIC, atleast, SHF_ALLOC, SHF_WRITE }, + { ".dynstr", 8, SHT_STRTAB, exact, SHF_ALLOC, 0 }, + { ".dynsym", 8, SHT_DYNSYM, exact, SHF_ALLOC, 0 }, + { ".fini", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 }, + { ".fini_array", 12, SHT_FINI_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 }, + { ".got", 5, SHT_PROGBITS, unused, 0, 0 }, // XXX more info? + { ".hash", 6, SHT_HASH, exact, SHF_ALLOC, 0 }, + { ".init", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 }, + { ".init_array", 12, SHT_INIT_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 }, + { ".interp", 8, SHT_PROGBITS, atleast, 0, SHF_ALLOC }, // XXX more tests? + { ".line", 6, SHT_PROGBITS, exact, 0, 0 }, + { ".note", 6, SHT_NOTE, exact, 0, 0 }, + { ".plt", 5, SHT_PROGBITS, unused, 0, 0 }, // XXX more tests + { ".preinit_array", 15, SHT_PREINIT_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 }, + { ".rela", 5, SHT_RELA, atleast, 0, SHF_ALLOC }, // XXX more tests + { ".rel", 4, SHT_REL, atleast, 0, SHF_ALLOC }, // XXX more tests + { ".rodata", 8, SHT_PROGBITS, exact, SHF_ALLOC, 0 }, + { ".rodata1", 9, SHT_PROGBITS, exact, SHF_ALLOC, 0 }, + { ".shstrtab", 10, SHT_STRTAB, exact, 0, 0 }, + { ".strtab", 8, SHT_STRTAB, atleast, 0, SHF_ALLOC }, // XXX more tests + { ".symtab", 8, SHT_SYMTAB, atleast, 0, SHF_ALLOC }, // XXX more tests + { ".symtab_shndx", 14, SHT_SYMTAB_SHNDX, atleast, 0, SHF_ALLOC }, // XXX more tests + { ".tbss", 6, SHT_NOBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 }, + { ".tdata", 7, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 }, + { ".tdata1", 8, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 }, + { ".text", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 } + }; +#define nspecial_sections \ + (sizeof (special_sections) / sizeof (special_sections[0])) + + +static const char * +section_flags_string (GElf_Word flags, char *buf, size_t len) +{ + static const struct + { + GElf_Word flag; + const char *name; + } known_flags[] = + { +#define NEWFLAG(name) { SHF_##name, #name } + NEWFLAG (WRITE), + NEWFLAG (ALLOC), + NEWFLAG (EXECINSTR), + NEWFLAG (MERGE), + NEWFLAG (STRINGS), + NEWFLAG (INFO_LINK), + NEWFLAG (LINK_ORDER), + NEWFLAG (OS_NONCONFORMING), + NEWFLAG (GROUP), + NEWFLAG (TLS) + }; +#undef NEWFLAG + const size_t nknown_flags = sizeof (known_flags) / sizeof (known_flags[0]); + + char *cp = buf; + size_t cnt; + + for (cnt = 0; cnt < nknown_flags; ++cnt) + if (flags & known_flags[cnt].flag) + { + if (cp != buf && len > 1) + { + *cp++ = '|'; + --len; + } + + size_t ncopy = MIN (len - 1, strlen (known_flags[cnt].name)); + cp = mempcpy (cp, known_flags[cnt].name, ncopy); + len -= ncopy; + + flags ^= known_flags[cnt].flag; + } + + if (flags != 0 || cp == buf) + snprintf (cp, len - 1, "%" PRIx64, (uint64_t) flags); + + *cp = '\0'; + + return buf; +} + + +static void +check_versym (Ebl *ebl, GElf_Shdr *shdr, int idx) +{ + /* The number of elements in the version symbol table must be the + same as the number of symbols. */ + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &symshdr_mem); + if (symshdr == NULL) + /* The error has already been reported. */ + return; + + if (symshdr->sh_type != SHT_DYNSYM) + { + ERROR (gettext ("\ +section [%2d] '%s' refers in sh_link to section [%2d] '%s' which is no dynamic symbol table\n"), + idx, section_name (ebl, idx), + shdr->sh_link, section_name (ebl, shdr->sh_link)); + return; + } + + if (shdr->sh_size / shdr->sh_entsize + != symshdr->sh_size / symshdr->sh_entsize) + ERROR (gettext ("\ +section [%2d] '%s' has different number of entries than symbol table [%2d] '%s'\n"), + idx, section_name (ebl, idx), + shdr->sh_link, section_name (ebl, shdr->sh_link)); + + // XXX TODO A lot more tests + // check value of the fields. local symbols must have zero entries. + // nonlocal symbols refer to valid version. Check that version index + // in bound. +} + + +static void +check_sections (Ebl *ebl, GElf_Ehdr *ehdr) +{ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr; + size_t cnt; + bool dot_interp_section = false; + + if (ehdr->e_shoff == 0) + /* No section header. */ + return; + + /* Allocate array to count references in section groups. */ + scnref = (int *) xcalloc (shnum, sizeof (int)); + + /* Check the zeroth section first. It must not have any contents + and the section header must contain nonzero value at most in the + sh_size and sh_link fields. */ + shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); + if (shdr == NULL) + ERROR (gettext ("cannot get section header of zeroth section\n")); + else + { + if (shdr->sh_name != 0) + ERROR (gettext ("zeroth section has nonzero name\n")); + if (shdr->sh_type != 0) + ERROR (gettext ("zeroth section has nonzero type\n")); + if (shdr->sh_flags != 0) + ERROR (gettext ("zeroth section has nonzero flags\n")); + if (shdr->sh_addr != 0) + ERROR (gettext ("zeroth section has nonzero address\n")); + if (shdr->sh_offset != 0) + ERROR (gettext ("zeroth section has nonzero offset\n")); + if (shdr->sh_info != 0) + ERROR (gettext ("zeroth section has nonzero info field\n")); + if (shdr->sh_addralign != 0) + ERROR (gettext ("zeroth section has nonzero align value\n")); + if (shdr->sh_entsize != 0) + ERROR (gettext ("zeroth section has nonzero entry size value\n")); + + if (shdr->sh_size != 0 && ehdr->e_shnum != 0) + ERROR (gettext ("\ +zeroth section has nonzero size value while ELF header has nonzero shnum value\n")); + + if (shdr->sh_link != 0 && ehdr->e_shstrndx != SHN_XINDEX) + ERROR (gettext ("\ +zeroth section has nonzero link value while ELF header does not signal overflow in shstrndx\n")); + } + + for (cnt = 1; cnt < shnum; ++cnt) + { + Elf_Scn *scn; + + scn = elf_getscn (ebl->elf, cnt); + shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + { + ERROR (gettext ("\ +cannot get section header for section [%2zu] '%s': %s\n"), + cnt, section_name (ebl, cnt), elf_errmsg (-1)); + continue; + } + + const char *scnname = elf_strptr (ebl->elf, shstrndx, shdr->sh_name); + + if (scnname == NULL) + ERROR (gettext ("section [%2zu]: invalid name\n"), cnt); + else + { + /* Check whether it is one of the special sections defined in + the gABI. */ + size_t s; + for (s = 0; s < nspecial_sections; ++s) + if (strncmp (scnname, special_sections[s].name, + special_sections[s].namelen) == 0) + { + char stbuf1[100]; + char stbuf2[100]; + char stbuf3[100]; + + if (shdr->sh_type != special_sections[s].type + && !(is_debuginfo && shdr->sh_type == SHT_NOBITS)) + ERROR (gettext ("\ +section [%2d] '%s' has wrong type: expected %s, is %s\n"), + (int) cnt, scnname, + ebl_section_type_name (ebl, special_sections[s].type, + stbuf1, sizeof (stbuf1)), + ebl_section_type_name (ebl, shdr->sh_type, + stbuf2, sizeof (stbuf2))); + + if (special_sections[s].attrflag == exact) + { + /* Except for the link order and group bit all the + other bits should match exactly. */ + if ((shdr->sh_flags & ~(SHF_LINK_ORDER | SHF_GROUP)) + != special_sections[s].attr) + ERROR (gettext ("\ +section [%2zu] '%s' has wrong flags: expected %s, is %s\n"), + cnt, scnname, + section_flags_string (special_sections[s].attr, + stbuf1, sizeof (stbuf1)), + section_flags_string (shdr->sh_flags + & ~SHF_LINK_ORDER, + stbuf2, sizeof (stbuf2))); + } + else if (special_sections[s].attrflag == atleast) + { + if ((shdr->sh_flags & special_sections[s].attr) + != special_sections[s].attr + || ((shdr->sh_flags & ~(SHF_LINK_ORDER | SHF_GROUP + | special_sections[s].attr + | special_sections[s].attr2)) + != 0)) + ERROR (gettext ("\ +section [%2zu] '%s' has wrong flags: expected %s and possibly %s, is %s\n"), + cnt, scnname, + section_flags_string (special_sections[s].attr, + stbuf1, sizeof (stbuf1)), + section_flags_string (special_sections[s].attr2, + stbuf2, sizeof (stbuf2)), + section_flags_string (shdr->sh_flags + & ~(SHF_LINK_ORDER + | SHF_GROUP), + stbuf3, sizeof (stbuf3))); + } + + if (strcmp (scnname, ".interp") == 0) + { + dot_interp_section = true; + + if (ehdr->e_type == ET_REL) + ERROR (gettext ("\ +section [%2zu] '%s' present in object file\n"), + cnt, scnname); + + if ((shdr->sh_flags & SHF_ALLOC) != 0 + && !has_loadable_segment) + ERROR (gettext ("\ +section [%2zu] '%s' has SHF_ALLOC flag set but there is no loadable segment\n"), + cnt, scnname); + else if ((shdr->sh_flags & SHF_ALLOC) == 0 + && has_loadable_segment) + ERROR (gettext ("\ +section [%2zu] '%s' has SHF_ALLOC flag not set but there are loadable segments\n"), + cnt, scnname); + } + else + { + if (strcmp (scnname, ".symtab_shndx") == 0 + && ehdr->e_type != ET_REL) + ERROR (gettext ("\ +section [%2zu] '%s' is extension section index table in non-object file\n"), + cnt, scnname); + + /* These sections must have the SHF_ALLOC flag set iff + a loadable segment is available. + + .relxxx + .strtab + .symtab + .symtab_shndx + + Check that if there is a reference from the + loaded section these sections also have the + ALLOC flag set. */ +#if 0 + // XXX TODO + if ((shdr->sh_flags & SHF_ALLOC) != 0 + && !has_loadable_segment) + ERROR (gettext ("\ +section [%2zu] '%s' has SHF_ALLOC flag set but there is no loadable segment\n"), + cnt, scnname); + else if ((shdr->sh_flags & SHF_ALLOC) == 0 + && has_loadable_segment) + ERROR (gettext ("\ +section [%2zu] '%s' has SHF_ALLOC flag not set but there are loadable segments\n"), + cnt, scnname); +#endif + } + + break; + } + } + + if (shdr->sh_entsize != 0 && shdr->sh_size % shdr->sh_entsize) + ERROR (gettext ("\ +section [%2zu] '%s': size not multiple of entry size\n"), + cnt, section_name (ebl, cnt)); + + if (elf_strptr (ebl->elf, shstrndx, shdr->sh_name) == NULL) + ERROR (gettext ("cannot get section header\n")); + + if (shdr->sh_type >= SHT_NUM + && shdr->sh_type != SHT_GNU_LIBLIST + && shdr->sh_type != SHT_CHECKSUM + && shdr->sh_type != SHT_GNU_verdef + && shdr->sh_type != SHT_GNU_verneed + && shdr->sh_type != SHT_GNU_versym) + ERROR (gettext ("unsupported section type %d\n"), (int) shdr->sh_type); + +#define ALL_SH_FLAGS (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR | SHF_MERGE \ + | SHF_STRINGS | SHF_INFO_LINK | SHF_LINK_ORDER \ + | SHF_OS_NONCONFORMING | SHF_GROUP | SHF_TLS) + if (shdr->sh_flags & ~ALL_SH_FLAGS) + ERROR (gettext ("section [%2zu] '%s' contain unknown flag(s) %d\n"), + cnt, section_name (ebl, cnt), + (int) shdr->sh_flags & ~ALL_SH_FLAGS); + else if (shdr->sh_flags & SHF_TLS) + { + // XXX Correct? + if (shdr->sh_addr != 0 && !gnuld) + ERROR (gettext ("\ +section [%2zu] '%s': thread-local data sections address not zero\n"), + cnt, section_name (ebl, cnt)); + + // XXX TODO more tests!? + } + + if (shdr->sh_link >= shnum) + ERROR (gettext ("\ +section [%2zu] '%s': invalid section reference in link value\n"), + cnt, section_name (ebl, cnt)); + + if (SH_INFO_LINK_P (shdr) && shdr->sh_info >= shnum) + ERROR (gettext ("\ +section [%2zu] '%s': invalid section reference in info value\n"), + cnt, section_name (ebl, cnt)); + + if ((shdr->sh_flags & SHF_MERGE) == 0 + && (shdr->sh_flags & SHF_STRINGS) != 0 + && be_strict) + ERROR (gettext ("\ +section [%2zu] '%s': strings flag set without merge flag\n"), + cnt, section_name (ebl, cnt)); + + if ((shdr->sh_flags & SHF_MERGE) != 0 && shdr->sh_entsize == 0) + ERROR (gettext ("\ +section [%2zu] '%s': merge flag set but entry size is zero\n"), + cnt, section_name (ebl, cnt)); + + if (shdr->sh_flags & SHF_GROUP) + check_scn_group (ebl, cnt); + + if (ehdr->e_type != ET_REL && (shdr->sh_flags & SHF_ALLOC) != 0) + { + /* Make sure the section is contained in a loaded segment + and that the initialization part matches NOBITS sections. */ + int pcnt; + GElf_Phdr phdr_mem; + GElf_Phdr *phdr; + + for (pcnt = 0; pcnt < ehdr->e_phnum; ++pcnt) + if ((phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem)) != NULL + && ((phdr->p_type == PT_LOAD + && (shdr->sh_flags & SHF_TLS) == 0) + || (phdr->p_type == PT_TLS + && (shdr->sh_flags & SHF_TLS) != 0)) + && phdr->p_offset <= shdr->sh_offset + && phdr->p_offset + phdr->p_memsz > shdr->sh_offset) + { + /* Found the segment. */ + if (phdr->p_offset + phdr->p_memsz + < shdr->sh_offset + shdr->sh_size) + ERROR (gettext ("\ +section [%2zu] '%s' not fully contained in segment of program header entry %d\n"), + cnt, section_name (ebl, cnt), pcnt); + + if (shdr->sh_type == SHT_NOBITS) + { + if (shdr->sh_offset < phdr->p_offset + phdr->p_filesz + && !is_debuginfo) + ERROR (gettext ("\ +section [%2zu] '%s' has type NOBITS but is read from the file in segment of program header entry %d\n"), + cnt, section_name (ebl, cnt), pcnt); + } + else + { + if (shdr->sh_offset >= phdr->p_offset + phdr->p_filesz) + ERROR (gettext ("\ +section [%2zu] '%s' has not type NOBITS but is not read from the file in segment of program header entry %d\n"), + cnt, section_name (ebl, cnt), pcnt); + } + + break; + } + + if (pcnt == ehdr->e_phnum) + ERROR (gettext ("\ +section [%2zu] '%s': alloc flag set but section not in any loaded segment\n"), + cnt, section_name (ebl, cnt)); + } + + if (cnt == shstrndx && shdr->sh_type != SHT_STRTAB) + ERROR (gettext ("\ +section [%2zu] '%s': ELF header says this is the section header string table but type is not SHT_TYPE\n"), + cnt, section_name (ebl, cnt)); + + switch (shdr->sh_type) + { + case SHT_SYMTAB: + case SHT_DYNSYM: + check_symtab (ebl, ehdr, cnt); + break; + + case SHT_RELA: + check_rela (ebl, ehdr, cnt); + break; + + case SHT_REL: + check_rel (ebl, ehdr, cnt); + break; + + case SHT_DYNAMIC: + check_dynamic (ebl, cnt); + break; + + case SHT_SYMTAB_SHNDX: + check_symtab_shndx (ebl, cnt); + break; + + case SHT_HASH: + check_hash (ebl, cnt); + break; + + case SHT_NULL: + check_null (ebl, shdr, cnt); + break; + + case SHT_GROUP: + check_group (ebl, ehdr, shdr, cnt); + break; + + case SHT_GNU_versym: + check_versym (ebl, shdr, cnt); + break; + + default: + /* Nothing. */ + break; + } + } + + if (has_interp_segment && !dot_interp_section) + ERROR (gettext ("INTERP program header entry but no .interp section\n")); + + free (scnref); +} + + +static void +check_note (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Phdr *phdr, int cnt) +{ + if (ehdr->e_type != ET_CORE && ehdr->e_type != ET_REL + && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) + ERROR (gettext ("\ +phdr[%d]: no note entries defined for the type of file\n"), + cnt); + + if (is_debuginfo) + /* The p_offset values in a separate debug file are bogus. */ + return; + + char *notemem = gelf_rawchunk (ebl->elf, phdr->p_offset, phdr->p_filesz); + + /* ELF64 files often use note section entries in the 32-bit format. + The p_align field is set to 8 in case the 64-bit format is used. + In case the p_align value is 0 or 4 the 32-bit format is + used. */ + GElf_Xword align = phdr->p_align == 0 || phdr->p_align == 4 ? 4 : 8; +#define ALIGNED_LEN(len) (((len) + align - 1) & ~(align - 1)) + + GElf_Xword idx = 0; + while (idx < phdr->p_filesz) + { + uint64_t namesz; + uint64_t descsz; + uint64_t type; + uint32_t namesz32; + uint32_t descsz32; + + if (align == 4) + { + uint32_t *ptr = (uint32_t *) (notemem + idx); + + if ((__BYTE_ORDER == __LITTLE_ENDIAN + && ehdr->e_ident[EI_DATA] == ELFDATA2MSB) + || (__BYTE_ORDER == __BIG_ENDIAN + && ehdr->e_ident[EI_DATA] == ELFDATA2LSB)) + { + namesz32 = namesz = bswap_32 (*ptr); + ++ptr; + descsz32 = descsz = bswap_32 (*ptr); + ++ptr; + type = bswap_32 (*ptr); + } + else + { + namesz32 = namesz = *ptr++; + descsz32 = descsz = *ptr++; + type = *ptr; + } + } + else + { + uint64_t *ptr = (uint64_t *) (notemem + idx); + uint32_t *ptr32 = (uint32_t *) (notemem + idx); + + if ((__BYTE_ORDER == __LITTLE_ENDIAN + && ehdr->e_ident[EI_DATA] == ELFDATA2MSB) + || (__BYTE_ORDER == __BIG_ENDIAN + && ehdr->e_ident[EI_DATA] == ELFDATA2LSB)) + { + namesz = bswap_64 (*ptr); + ++ptr; + descsz = bswap_64 (*ptr); + ++ptr; + type = bswap_64 (*ptr); + + namesz32 = bswap_32 (*ptr32); + ++ptr32; + descsz32 = bswap_32 (*ptr32); + } + else + { + namesz = *ptr++; + descsz = *ptr++; + type = *ptr; + + namesz32 = *ptr32++; + descsz32 = *ptr32; + } + } + + if (idx + 3 * align > phdr->p_filesz + || (idx + 3 * align + ALIGNED_LEN (namesz) + ALIGNED_LEN (descsz) + > phdr->p_filesz)) + { + if (ehdr->e_ident[EI_CLASS] == ELFCLASS64 + && idx + 3 * 4 <= phdr->p_filesz + && (idx + 3 * 4 + ALIGNED_LEN (namesz32) + ALIGNED_LEN (descsz32) + <= phdr->p_filesz)) + ERROR (gettext ("\ +phdr[%d]: note entries probably in form of a 32-bit ELF file\n"), cnt); + else + ERROR (gettext ("phdr[%d]: extra %zu bytes after last note\n"), + cnt, (size_t) (phdr->p_filesz - idx)); + break; + } + + /* Make sure it is one of the note types we know about. */ + if (ehdr->e_type == ET_CORE) + { + switch (type) + { + case NT_PRSTATUS: + case NT_FPREGSET: + case NT_PRPSINFO: + case NT_TASKSTRUCT: /* NT_PRXREG on Solaris. */ + case NT_PLATFORM: + case NT_AUXV: + case NT_GWINDOWS: + case NT_ASRS: + case NT_PSTATUS: + case NT_PSINFO: + case NT_PRCRED: + case NT_UTSNAME: + case NT_LWPSTATUS: + case NT_LWPSINFO: + case NT_PRFPXREG: + /* Known type. */ + break; + + default: + ERROR (gettext ("\ +phdr[%d]: unknown core file note type %" PRIu64 " at offset %" PRIu64 "\n"), + cnt, type, idx); + } + } + else + { + if (type != NT_VERSION) + ERROR (gettext ("\ +phdr[%d]: unknown object file note type %" PRIu64 " at offset %" PRIu64 "\n"), + cnt, type, idx); + } + + /* Move to the next entry. */ + idx += 3 * align + ALIGNED_LEN (namesz) + ALIGNED_LEN (descsz); + + } + + gelf_freechunk (ebl->elf, notemem); +} + + +static void +check_program_header (Ebl *ebl, GElf_Ehdr *ehdr) +{ + if (ehdr->e_phoff == 0) + return; + + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN + && ehdr->e_type != ET_CORE) + ERROR (gettext ("\ +only executables, shared objects, and core files can have program headers\n")); + + int num_pt_interp = 0; + int num_pt_tls = 0; + int num_pt_relro = 0; + + for (int cnt = 0; cnt < ehdr->e_phnum; ++cnt) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr; + + phdr = gelf_getphdr (ebl->elf, cnt, &phdr_mem); + if (phdr == NULL) + { + ERROR (gettext ("cannot get program header entry %d: %s\n"), + cnt, elf_errmsg (-1)); + continue; + } + + if (phdr->p_type >= PT_NUM && phdr->p_type != PT_GNU_EH_FRAME + && phdr->p_type != PT_GNU_STACK && phdr->p_type != PT_GNU_RELRO) + ERROR (gettext ("\ +program header entry %d: unknown program header entry type\n"), + cnt); + + if (phdr->p_type == PT_LOAD) + has_loadable_segment = true; + else if (phdr->p_type == PT_INTERP) + { + if (++num_pt_interp != 1) + { + if (num_pt_interp == 2) + ERROR (gettext ("\ +more than one INTERP entry in program header\n")); + } + has_interp_segment = true; + } + else if (phdr->p_type == PT_TLS) + { + if (++num_pt_tls == 2) + ERROR (gettext ("more than one TLS entry in program header\n")); + } + else if (phdr->p_type == PT_NOTE) + check_note (ebl, ehdr, phdr, cnt); + else if (phdr->p_type == PT_DYNAMIC + && ehdr->e_type == ET_EXEC && ! has_interp_segment) + ERROR (gettext ("static executable cannot have dynamic sections\n")); + else if (phdr->p_type == PT_GNU_RELRO) + { + if (++num_pt_relro == 2) + ERROR (gettext ("\ +more than one GNU_RELRO entry in program header\n")); + else + { + /* Check that the region is in a writable segment. */ + int inner; + for (inner = 0; inner < ehdr->e_phnum; ++inner) + { + GElf_Phdr phdr2_mem; + GElf_Phdr *phdr2; + + phdr2 = gelf_getphdr (ebl->elf, inner, &phdr2_mem); + if (phdr2 == NULL) + continue; + + if (phdr2->p_type == PT_LOAD + && phdr->p_vaddr >= phdr2->p_vaddr + && (phdr->p_vaddr + phdr->p_memsz + <= phdr2->p_vaddr + phdr2->p_memsz)) + { + if ((phdr2->p_flags & PF_W) == 0) + ERROR (gettext ("\ +loadable segment GNU_RELRO applies to is not writable\n")); + if ((phdr2->p_flags & PF_X) != 0) + ERROR (gettext ("\ +loadable segment GNU_RELRO applies to is executable\n")); + break; + } + } + + if (inner >= ehdr->e_phnum) + ERROR (gettext ("\ +GNU_RELRO segment not contained in a loaded segment\n")); + } + } + + if (phdr->p_filesz > phdr->p_memsz) + ERROR (gettext ("\ +program header entry %d: file size greater than memory size\n"), + cnt); + + if (phdr->p_align > 1) + { + if (!powerof2 (phdr->p_align)) + ERROR (gettext ("\ +program header entry %d: alignment not a power of 2\n"), cnt); + else if ((phdr->p_vaddr - phdr->p_offset) % phdr->p_align != 0) + ERROR (gettext ("\ +program header entry %d: file offset and virtual address not module of alignment\n"), cnt); + } + } +} + + +/* Process one file. */ +static void +process_elf_file (Elf *elf, const char *prefix, const char *suffix, + const char *fname, size_t size, bool only_one) +{ + /* Reset variables. */ + ndynamic = 0; + + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); + Ebl *ebl; + + /* Print the file name. */ + if (!only_one) + { + if (prefix != NULL) + printf ("\n%s(%s)%s:\n", prefix, fname, suffix); + else + printf ("\n%s:\n", fname); + } + + if (ehdr == NULL) + { + ERROR (gettext ("cannot read ELF header: %s\n"), elf_errmsg (-1)); + return; + } + + ebl = ebl_openbackend (elf); + /* If there is no appropriate backend library we cannot test + architecture and OS specific features. Any encountered extension + is an error. */ + + /* Go straight by the gABI, check all the parts in turn. */ + check_elf_header (ebl, ehdr, size); + + /* Check the program header. */ + check_program_header (ebl, ehdr); + + /* Next the section headers. It is OK if there are no section + headers at all. */ + check_sections (ebl, ehdr); + + /* Free the resources. */ + ebl_closebackend (ebl); +} diff --git a/src/findtextrel.c b/src/findtextrel.c new file mode 100644 index 00000000..662fe95c --- /dev/null +++ b/src/findtextrel.c @@ -0,0 +1,596 @@ +/* Locate source files or functions which caused text relocations. + Copyright (C) 2005 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2005. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <argp.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <libdw.h> +#include <libintl.h> +#include <locale.h> +#include <search.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + + +struct segments +{ + GElf_Addr from; + GElf_Addr to; +}; + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + +/* Values for the parameters which have no short form. */ +#define OPT_DEBUGINFO 0x100 + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Input Selection:"), 0 }, + { "root", 'r', "PATH", 0, N_("Prepend PATH to all file names"), 0 }, + { "debuginfo", OPT_DEBUGINFO, "PATH", 0, + N_("Use PATH as root of debuginfo hierarchy"), 0 }, + + { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("\ +Locate source of text relocations in FILEs (a.out by default)."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("[FILE...]"); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, NULL, NULL, NULL +}; + + +/* Print symbols in file named FNAME. */ +static int process_file (const char *fname, bool more_than_one); + +/* Check for text relocations in the given file. The segment + information is known. */ +static void check_rel (size_t nsegments, struct segments segments[nsegments], + GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw, + const char *fname, bool more_than_one, + void **knownsrcs); + + + +/* User-provided root directory. */ +static const char *rootdir = "/"; + +/* Root of debuginfo directory hierarchy. */ +static const char *debuginfo_root; + + +int +main (int argc, char *argv[]) +{ + int remaining; + int result = 0; + + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + (void) bindtextdomain (PACKAGE, LOCALEDIR); + + /* Initialize the message catalog. */ + (void) textdomain (PACKAGE); + + /* Parse and process arguments. */ + (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + /* Tell the library which version we are expecting. */ + elf_version (EV_CURRENT); + + /* If the user has not specified the root directory for the + debuginfo hierarchy, we have to determine it ourselves. */ + if (debuginfo_root == NULL) + { + // XXX The runtime should provide this information. +#if defined __ia64__ || defined __alpha__ + debuginfo_root = "/usr/lib/debug"; +#else + debuginfo_root = (sizeof (long int) == 4 + ? "/usr/lib/debug" : "/usr/lib64/debug"); +#endif + } + + if (remaining == argc) + result = process_file ("a.out", false); + else + { + /* Process all the remaining files. */ + const bool more_than_one = remaining + 1 < argc; + + do + result |= process_file (argv[remaining], more_than_one); + while (++remaining < argc); + } + + return result; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "findtextrel (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2005"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case 'r': + rootdir = arg; + break; + + case OPT_DEBUGINFO: + debuginfo_root = arg; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +static void +noop (void *arg __attribute__ ((unused))) +{ +} + + +static int +process_file (const char *fname, bool more_than_one) +{ + int result = 0; + void *knownsrcs = NULL; + + size_t fname_len = strlen (fname); + size_t rootdir_len = strlen (rootdir); + const char *real_fname = fname; + if (fname[0] == '/' && (rootdir[0] != '/' || rootdir[1] != '\0')) + { + /* Prepend the user-provided root directory. */ + char *new_fname = alloca (rootdir_len + fname_len + 2); + *((char *) mempcpy (stpcpy (mempcpy (new_fname, rootdir, rootdir_len), + "/"), + fname, fname_len)) = '\0'; + real_fname = new_fname; + } + + int fd = open64 (real_fname, O_RDONLY); + if (fd == -1) + { + error (0, errno, gettext ("cannot open '%s'"), fname); + return 1; + } + + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (elf == NULL) + { + error (0, 0, gettext ("cannot create ELF descriptor for '%s': %s"), + fname, elf_errmsg (-1)); + goto err_close; + } + + /* Make sure the file is a DSO. */ + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); + if (ehdr == NULL) + { + error (0, 0, gettext ("cannot get ELF header '%s': %s"), + fname, elf_errmsg (-1)); + err_elf_close: + elf_end (elf); + err_close: + close (fd); + return 1; + } + + if (ehdr->e_type != ET_DYN) + { + error (0, 0, gettext ("'%s' is not a DSO or PIE"), fname); + goto err_elf_close; + } + + /* Determine whether the DSO has text relocations at all and locate + the symbol table. */ + Elf_Scn *symscn = NULL; + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + /* Handle the section if it is a symbol table. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr == NULL) + { + error (0, 0, + gettext ("getting get section header of section %zu: %s"), + elf_ndxscn (scn), elf_errmsg (-1)); + goto err_elf_close; + } + + if (shdr->sh_type == SHT_DYNAMIC) + { + Elf_Data *data = elf_getdata (scn, NULL); + + for (size_t cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; + ++cnt) + { + GElf_Dyn dynmem; + GElf_Dyn *dyn; + + dyn = gelf_getdyn (data, cnt, &dynmem); + if (dyn == NULL) + { + error (0, 0, gettext ("cannot read dynamic section: %s"), + elf_errmsg (-1)); + goto err_elf_close; + } + + if (dyn->d_tag == DT_TEXTREL + || (dyn->d_tag == DT_FLAGS + && (dyn->d_un.d_val & DF_TEXTREL) != 0)) + goto have_textrel; + } + } + else if (shdr->sh_type == SHT_SYMTAB) + symscn = scn; + } + + error (0, 0, gettext ("no text relocations reported in '%s'"), fname); + return 1; + + have_textrel:; + int fd2 = -1; + Elf *elf2 = NULL; + /* Get the address ranges for the loaded segments. */ + size_t nsegments_max = 10; + size_t nsegments = 0; + struct segments *segments + = (struct segments *) malloc (nsegments_max * sizeof (segments[0])); + if (segments == NULL) + error (1, errno, gettext ("while reading ELF file")); + + for (int i = 0; i < ehdr->e_phnum; ++i) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem); + if (phdr == NULL) + { + error (0, 0, + gettext ("cannot get program header index at offset %d: %s"), + i, elf_errmsg (-1)); + result = 1; + goto next; + } + + if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_W) == 0) + { + if (nsegments == nsegments_max) + { + nsegments_max *= 2; + segments + = (struct segments *) realloc (segments, + nsegments_max + * sizeof (segments[0])); + if (segments == NULL) + { + error (0, 0, gettext ("\ +cannot get program header index at offset %d: %s"), + i, elf_errmsg (-1)); + result = 1; + goto next; + } + } + + segments[nsegments].from = phdr->p_vaddr; + segments[nsegments].to = phdr->p_vaddr + phdr->p_memsz; + ++nsegments; + } + } + + if (nsegments > 0) + { + + Dwarf *dw = dwarf_begin_elf (elf, DWARF_C_READ, NULL); + /* Look for debuginfo files if the information is not the in + opened file itself. This makes only sense if the input file + is specified with an absolute path. */ + if (dw == NULL && fname[0] == '/') + { + size_t debuginfo_rootlen = strlen (debuginfo_root); + char *difname = (char *) alloca (rootdir_len + debuginfo_rootlen + + fname_len + 8); + strcpy (mempcpy (stpcpy (mempcpy (mempcpy (difname, rootdir, + rootdir_len), + debuginfo_root, + debuginfo_rootlen), + "/"), + fname, fname_len), + ".debug"); + + fd2 = open64 (difname, O_RDONLY); + if (fd2 != -1 + && (elf2 = elf_begin (fd2, ELF_C_READ_MMAP, NULL)) != NULL) + dw = dwarf_begin_elf (elf2, DWARF_C_READ, NULL); + } + + /* Look at all relocations and determine which modify + write-protected segments. */ + scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + /* Handle the section if it is a symbol table. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr == NULL) + { + error (0, 0, + gettext ("cannot get section header of section %Zu: %s"), + elf_ndxscn (scn), elf_errmsg (-1)); + result = 1; + goto next; + } + + if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) + && symscn == NULL) + { + symscn = elf_getscn (elf, shdr->sh_link); + if (symscn == NULL) + { + error (0, 0, gettext ("\ +cannot get symbol table section %zu in '%s': %s"), + (size_t) shdr->sh_link, fname, elf_errmsg (-1)); + result = 1; + goto next; + } + } + + if (shdr->sh_type == SHT_REL) + { + Elf_Data *data = elf_getdata (scn, NULL); + + for (int cnt = 0; + (size_t) cnt < shdr->sh_size / shdr->sh_entsize; + ++cnt) + { + GElf_Rel rel_mem; + GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem); + if (rel == NULL) + { + error (0, 0, gettext ("\ +cannot get relocation at index %d in section %zu in '%s': %s"), + cnt, elf_ndxscn (scn), fname, elf_errmsg (-1)); + result = 1; + goto next; + } + + check_rel (nsegments, segments, rel->r_offset, elf, + symscn, dw, fname, more_than_one, &knownsrcs); + } + } + else if (shdr->sh_type == SHT_RELA) + { + Elf_Data *data = elf_getdata (scn, NULL); + + for (int cnt = 0; + (size_t) cnt < shdr->sh_size / shdr->sh_entsize; + ++cnt) + { + GElf_Rela rela_mem; + GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem); + if (rela == NULL) + { + error (0, 0, gettext ("\ +cannot get relocation at index %d in section %zu in '%s': %s"), + cnt, elf_ndxscn (scn), fname, elf_errmsg (-1)); + result = 1; + goto next; + } + + check_rel (nsegments, segments, rela->r_offset, elf, + symscn, dw, fname, more_than_one, &knownsrcs); + } + } + } + + dwarf_end (dw); + } + + next: + elf_end (elf); + elf_end (elf2); + close (fd); + if (fd2 != -1) + close (fd2); + + tdestroy (knownsrcs, noop); + + return result; +} + + +static int +ptrcompare (const void *p1, const void *p2) +{ + if ((uintptr_t) p1 < (uintptr_t) p2) + return -1; + if ((uintptr_t) p1 > (uintptr_t) p2) + return 1; + return 0; +} + + +static void +check_rel (size_t nsegments, struct segments segments[nsegments], + GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw, + const char *fname, bool more_than_one, void **knownsrcs) +{ + for (size_t cnt = 0; cnt < nsegments; ++cnt) + if (segments[cnt].from <= addr && segments[cnt].to > addr) + { + Dwarf_Die die_mem; + Dwarf_Die *die; + Dwarf_Line *line; + const char *src; + + if (more_than_one) + printf ("%s: ", fname); + + if ((die = dwarf_addrdie (dw, addr, &die_mem)) != NULL + && (line = dwarf_getsrc_die (die, addr)) != NULL + && (src = dwarf_linesrc (line, NULL, NULL)) != NULL) + { + /* There can be more than one relocation against one file. + Try to avoid multiple messages. And yes, the code uses + pointer comparison. */ + if (tfind (src, knownsrcs, ptrcompare) == NULL) + { + printf (gettext ("%s not compiled with -fpic/-fPIC\n"), src); + tsearch (src, knownsrcs, ptrcompare); + } + return; + } + else + { + /* At least look at the symbol table to see which function + the modified address is in. */ + Elf_Data *symdata = elf_getdata (symscn, NULL); + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem); + if (shdr != NULL) + { + GElf_Addr lowaddr = 0; + int lowidx = -1; + GElf_Addr highaddr = ~0ul; + int highidx = -1; + GElf_Sym sym_mem; + GElf_Sym *sym; + + for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize; + ++i) + { + sym = gelf_getsym (symdata, i, &sym_mem); + if (sym == NULL) + continue; + + if (sym->st_value < addr && sym->st_value > lowaddr) + { + lowaddr = sym->st_value; + lowidx = i; + } + if (sym->st_value > addr && sym->st_value < highaddr) + { + highaddr = sym->st_value; + highidx = i; + } + } + + if (lowidx != -1) + { + sym = gelf_getsym (symdata, lowidx, &sym_mem); + assert (sym != NULL); + + const char *lowstr = elf_strptr (elf, shdr->sh_link, + sym->st_name); + + if (sym->st_value + sym->st_size > addr) + { + /* It is this function. */ + if (tfind (lowstr, knownsrcs, ptrcompare) == NULL) + { + printf (gettext ("\ +the file containing the function '%s' is not compiled with -fpic/-fPIC\n"), + lowstr); + tsearch (lowstr, knownsrcs, ptrcompare); + } + } + else if (highidx == -1) + printf (gettext ("\ +the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"), + lowstr); + else + { + sym = gelf_getsym (symdata, highidx, &sym_mem); + assert (sym != NULL); + + printf (gettext ("\ +either the file containing the function '%s' or the file containing the function '%s' is not compiled with -fpic/-fPIC\n"), + lowstr, elf_strptr (elf, shdr->sh_link, + sym->st_name)); + } + return; + } + else if (highidx != -1) + { + sym = gelf_getsym (symdata, highidx, &sym_mem); + assert (sym != NULL); + + printf (gettext ("\ +the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"), + elf_strptr (elf, shdr->sh_link, sym->st_name)); + return; + } + } + } + + printf (gettext ("\ +a relocation modifies memory at offset %llu in a write-protected segment\n"), + (unsigned long long int) addr); + break; + } +} diff --git a/src/i386_ld.c b/src/i386_ld.c new file mode 100644 index 00000000..ee1cb966 --- /dev/null +++ b/src/i386_ld.c @@ -0,0 +1,894 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2001. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <assert.h> +#include <error.h> +#include <libintl.h> +#include <stdlib.h> +#include <string.h> + +// XXX For debugging +#include <stdio.h> + +#include <system.h> +#include "ld.h" +#include "list.h" +/* x86 is little endian. */ +#define UNALIGNED_ACCESS_CLASS LITTLE_ENDIAN +#include "unaligned.h" +#include "xelf.h" + + +/* The old callbacks. */ +static int (*old_open_outfile) (struct ld_state *, int, int, int); + + +static int +elf_i386_open_outfile (struct ld_state *statep, + int machine __attribute__ ((unused)), + int klass __attribute__ ((unused)), + int data __attribute__ ((unused))) +{ + /* This backend only handles 32-bit object files. */ + /* XXX For now just use the generic backend. */ + return old_open_outfile (statep, EM_386, ELFCLASS32, ELFDATA2LSB); +} + + +/* Process relocations for the output in a relocatable file. This + only means adjusting offset and symbol indices. */ +static void +elf_i386_relocate_section (struct ld_state *statep __attribute__ ((unused)), + Elf_Scn *outscn, struct scninfo *firstp, + const Elf32_Word *dblindirect) +{ + struct scninfo *runp; + Elf_Data *data; + + /* Iterate over all the input sections. Appropriate data buffers in the + output sections were already created. I get them iteratively, too. */ + runp = firstp; + data = NULL; + do + { + Elf_Data *reltgtdata; + Elf_Data *insymdata; + Elf_Data *inxndxdata = NULL; + size_t maxcnt; + size_t cnt; + const Elf32_Word *symindirect; + struct symbol **symref; + struct usedfiles *file = runp->fileinfo; + XElf_Shdr *shdr = &SCNINFO_SHDR (runp->shdr); + + /* Get the output section data buffer for this input section. */ + data = elf_getdata (outscn, data); + assert (data != NULL); + + /* Get the data for section in the input file this relocation + section is relocating. Since these buffers are reused in the + output modifying these buffers has the correct result. */ + reltgtdata = elf_getdata (file->scninfo[shdr->sh_info].scn, NULL); + + /* Get the data for the input section symbol table for this + relocation section. */ + insymdata = elf_getdata (file->scninfo[shdr->sh_link].scn, NULL); + assert (insymdata != NULL); + + /* And the extended section index table. */ + inxndxdata = runp->fileinfo->xndxdata; + + /* Number of relocations. */ + maxcnt = shdr->sh_size / shdr->sh_entsize; + + /* Array directing local symbol table offsets to output symbol + table offsets. */ + symindirect = file->symindirect; + + /* References to the symbol records. */ + symref = file->symref; + + /* Iterate over all the relocations in the section. */ + for (cnt = 0; cnt < maxcnt; ++cnt) + { + XElf_Rel_vardef (rel); + Elf32_Word si; + XElf_Sym_vardef (sym); + Elf32_Word xndx; + + /* Get the relocation data itself. x86 uses Rel + relocations. In case we have to handle Rela as well the + whole loop probably should be duplicated. */ + xelf_getrel (data, cnt, rel); + assert (rel != NULL); + + /* Compute the symbol index in the output file. */ + si = symindirect[XELF_R_SYM (rel->r_info)]; + if (si == 0) + { + /* This happens if the symbol is locally undefined or + superceded by some other definition. */ + assert (symref[XELF_R_SYM (rel->r_info)] != NULL); + si = symref[XELF_R_SYM (rel->r_info)]->outsymidx; + } + /* Take reordering performed to sort the symbol table into + account. */ + si = dblindirect[si]; + + /* Get the symbol table entry. */ + xelf_getsymshndx (insymdata, inxndxdata, XELF_R_SYM (rel->r_info), + sym, xndx); + if (sym->st_shndx != SHN_XINDEX) + xndx = sym->st_shndx; + assert (xndx < SHN_LORESERVE || xndx > SHN_HIRESERVE); + + /* We fortunately don't have to do much. The relocations + mostly get only updates of the offset. Only is a + relocation referred to a section do we have to do + something. In this case the reference to the sections + has no direct equivalent since the part the input section + contributes need not start at the same offset as in the + input file. Therefore we have to adjust the addend which + in the case of Rel relocations is in the target section + itself. */ + if (XELF_ST_TYPE (sym->st_info) == STT_SECTION) + { + Elf32_Word toadd; + + /* We expect here on R_386_32 relocations. */ + assert (XELF_R_TYPE (rel->r_info) == R_386_32); + + /* Avoid writing to the section memory if this is + effectively a no-op since it might save a + copy-on-write operation. */ + toadd = file->scninfo[xndx].offset; + if (toadd != 0) + add_4ubyte_unaligned (reltgtdata->d_buf + rel->r_offset, + toadd); + } + + /* Adjust the offset for the position of the input section + content in the output section. */ + rel->r_offset += file->scninfo[shdr->sh_info].offset; + + /* And finally adjust the index of the symbol in the output + symbol table. */ + rel->r_info = XELF_R_INFO (si, XELF_R_TYPE (rel->r_info)); + + /* Store the result. */ + (void) xelf_update_rel (data, cnt, rel); + } + + runp = runp->next; + } + while (runp != firstp); +} + + +/* Each PLT entry has 16 bytes. We need one entry as overhead for + the code to set up the call into the runtime relocation. */ +#define PLT_ENTRY_SIZE 16 + +static void +elf_i386_initialize_plt (struct ld_state *statep, Elf_Scn *scn) +{ + Elf_Data *data; + XElf_Shdr_vardef (shdr); + + /* Change the entry size in the section header. */ + xelf_getshdr (scn, shdr); + assert (shdr != NULL); + shdr->sh_entsize = PLT_ENTRY_SIZE; + (void) xelf_update_shdr (scn, shdr); + + data = elf_newdata (scn); + if (data == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot allocate PLT section: %s"), + elf_errmsg (-1)); + + /* We need one special PLT entry (performing the jump to the runtime + relocation routines) and one for each function we call in a DSO. */ + data->d_size = (1 + statep->nplt) * PLT_ENTRY_SIZE; + data->d_buf = xcalloc (1, data->d_size); + data->d_align = 8; + data->d_off = 0; + + statep->nplt_used = 1; +} + + +static void +elf_i386_initialize_pltrel (struct ld_state *statep, Elf_Scn *scn) +{ + Elf_Data *data; + + data = elf_newdata (scn); + if (data == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot allocate PLTREL section: %s"), + elf_errmsg (-1)); + + /* One relocation per PLT entry. */ + data->d_size = statep->nplt * sizeof (Elf32_Rel); + data->d_buf = xcalloc (1, data->d_size); + data->d_type = ELF_T_REL; + data->d_align = 4; + data->d_off = 0; +} + + +static void +elf_i386_initialize_got (struct ld_state *statep, Elf_Scn *scn) +{ + Elf_Data *data; + + /* If we have no .plt we don't need the special entries we normally + create for it. The other contents is created later. */ + if (statep->ngot + statep->nplt == 0) + return; + + data = elf_newdata (scn); + if (data == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot allocate GOT section: %s"), + elf_errmsg (-1)); + + /* We construct the .got section in pieces. Here we only add the data + structures which are used by the PLT. This includes three reserved + entries at the beginning (the first will contain a pointer to the + .dynamic section), and one word for each PLT entry. */ + data->d_size = (3 + statep->ngot + statep->nplt) * sizeof (Elf32_Addr); + data->d_buf = xcalloc (1, data->d_size); + data->d_align = sizeof (Elf32_Addr); + data->d_off = 0; +} + + +/* The first entry in an absolute procedure linkage table looks like + this. See the SVR4 ABI i386 supplement to see how this works. */ +static const unsigned char elf_i386_plt0_entry[PLT_ENTRY_SIZE] = +{ + 0xff, 0x35, /* pushl contents of address */ + 0, 0, 0, 0, /* replaced with address of .got + 4. */ + 0xff, 0x25, /* jmp indirect */ + 0, 0, 0, 0, /* replaced with address of .got + 8. */ + 0, 0, 0, 0 /* pad out to 16 bytes. */ +}; + +/* Type describing the first PLT entry in non-PIC. */ +struct plt0_entry +{ + /* First a 'push' of the second GOT entry. */ + unsigned char push_instr[2]; + uint32_t gotp4_addr; + /* Second, a 'jmp indirect' to the third GOT entry. */ + unsigned char jmp_instr[2]; + uint32_t gotp8_addr; + /* Padding. */ + unsigned char padding[4]; +} __attribute__ ((packed)); + +/* The first entry in a PIC procedure linkage table look like this. */ +static const unsigned char elf_i386_pic_plt0_entry[PLT_ENTRY_SIZE] = +{ + 0xff, 0xb3, 4, 0, 0, 0, /* pushl 4(%ebx) */ + 0xff, 0xa3, 8, 0, 0, 0, /* jmp *8(%ebx) */ + 0, 0, 0, 0 /* pad out to 16 bytes. */ +}; + +/* Contents of all but the first PLT entry in executable. */ +static const unsigned char elf_i386_plt_entry[PLT_ENTRY_SIZE] = +{ + 0xff, 0x25, /* jmp indirect */ + 0, 0, 0, 0, /* replaced with address of this symbol in .got. */ + 0x68, /* pushl immediate */ + 0, 0, 0, 0, /* replaced with offset into relocation table. */ + 0xe9, /* jmp relative */ + 0, 0, 0, 0 /* replaced with offset to start of .plt. */ +}; + +/* Contents of all but the first PLT entry in DSOs. */ +static const unsigned char elf_i386_pic_plt_entry[PLT_ENTRY_SIZE] = +{ + 0xff, 0xa3, /* jmp *offset(%ebx) */ + 0, 0, 0, 0, /* replaced with offset of this symbol in .got. */ + 0x68, /* pushl immediate */ + 0, 0, 0, 0, /* replaced with offset into relocation table. */ + 0xe9, /* jmp relative */ + 0, 0, 0, 0 /* replaced with offset to start of .plt. */ +}; + +/* Type describing a PLT entry. */ +struct plt_entry +{ + /* The first instruction is 'jmp indirect' or 'jmp *offset(%ebs)'. */ + unsigned char jmp_instr[2]; + uint32_t offset_got; + /* The second instruction is 'push immediate'. */ + unsigned char push_instr; + uint32_t push_imm; + /* Finally a 'jmp relative'. */ + unsigned char jmp_instr2; + uint32_t plt0_offset; +} __attribute__ ((packed)); + + +static void +elf_i386_finalize_plt (struct ld_state *statep, size_t nsym, + size_t nsym_dyn __attribute__ ((unused))) +{ + Elf_Scn *scn; + XElf_Shdr_vardef (shdr); + Elf_Data *data; + Elf_Data *symdata = NULL; + Elf_Data *dynsymdata; + size_t cnt; + const bool build_dso = statep->file_type == dso_file_type; + + if (unlikely (statep->nplt + statep->ngot == 0)) + /* Nothing to be done. */ + return; + + /* Get the address of the got section. */ + scn = elf_getscn (statep->outelf, statep->gotscnidx); + xelf_getshdr (scn, shdr); + data = elf_getdata (scn, NULL); + assert (shdr != NULL && data != NULL); + Elf32_Addr gotaddr = shdr->sh_addr; + + /* Now create the initial values for the .got section. The first + word contains the address of the .dynamic section. */ + xelf_getshdr (elf_getscn (statep->outelf, statep->dynamicscnidx), shdr); + assert (shdr != NULL); + ((Elf32_Word *) data->d_buf)[0] = shdr->sh_addr; + + /* The second and third entry are left empty for use by the dynamic + linker. The following entries are pointers to the instructions + following the initial jmp instruction in the corresponding PLT + entry. Since the first PLT entry is special the first used one + has the index 1. */ + scn = elf_getscn (statep->outelf, statep->pltscnidx); + xelf_getshdr (scn, shdr); + assert (shdr != NULL); + + dynsymdata = elf_getdata (elf_getscn (statep->outelf, statep->dynsymscnidx), + NULL); + assert (dynsymdata != NULL); + + if (statep->symscnidx != 0) + { + symdata = elf_getdata (elf_getscn (statep->outelf, statep->symscnidx), + NULL); + assert (symdata != NULL); + } + + for (cnt = 0; cnt < statep->nplt; ++cnt) + { + assert ((4 + cnt) * sizeof (Elf32_Word) <= data->d_size); + + /* Address in the PLT. */ + Elf32_Addr pltentryaddr = shdr->sh_addr + (1 + cnt) * PLT_ENTRY_SIZE; + + /* Point the GOT entry at the PLT entry, after the initial jmp. */ + ((Elf32_Word *) data->d_buf)[3 + cnt] = pltentryaddr + 6; + + /* The value of the symbol is the address of the corresponding PLT + entry. Store the address, also for the normal symbol table if + this is necessary. */ + ((Elf32_Sym *) dynsymdata->d_buf)[1 + cnt].st_value = pltentryaddr; + + if (symdata != NULL) + ((Elf32_Sym *) symdata->d_buf)[nsym - statep->nplt + cnt].st_value + = pltentryaddr; + } + + /* Create the .plt section. */ + scn = elf_getscn (statep->outelf, statep->pltscnidx); + data = elf_getdata (scn, NULL); + assert (data != NULL); + + /* Create the first entry. */ + assert (data->d_size >= PLT_ENTRY_SIZE); + if (build_dso) + /* Copy the entry. It's complete, no relocation needed. */ + memcpy (data->d_buf, elf_i386_pic_plt0_entry, PLT_ENTRY_SIZE); + else + { + /* Copy the skeleton. */ + memcpy (data->d_buf, elf_i386_plt0_entry, PLT_ENTRY_SIZE); + + /* And fill in the addresses. */ + struct plt0_entry *addr = (struct plt0_entry *) data->d_buf; + addr->gotp4_addr = target_bswap_32 (gotaddr + 4); + addr->gotp8_addr = target_bswap_32 (gotaddr + 8); + } + + /* For DSOs we need GOT offsets, otherwise the GOT address. */ + Elf32_Addr gotaddr_off = build_dso ? 0 : gotaddr; + + /* Create the remaining entries. */ + const unsigned char *plt_template + = build_dso ? elf_i386_pic_plt_entry : elf_i386_plt_entry; + + for (cnt = 0; cnt < statep->nplt; ++cnt) + { + struct plt_entry *addr; + + /* Copy the template. */ + assert (data->d_size >= (2 + cnt) * PLT_ENTRY_SIZE); + addr = (struct plt_entry *) ((char *) data->d_buf + + (1 + cnt) * PLT_ENTRY_SIZE); + memcpy (addr, plt_template, PLT_ENTRY_SIZE); + + /* And once more, fill in the addresses. First the address of + this symbol in .got. */ + addr->offset_got = target_bswap_32 (gotaddr_off + + (3 + cnt) * sizeof (Elf32_Addr)); + /* Offset into relocation table. */ + addr->push_imm = target_bswap_32 (cnt * sizeof (Elf32_Rel)); + /* Offset to start of .plt. */ + addr->plt0_offset = target_bswap_32 (-(2 + cnt) * PLT_ENTRY_SIZE); + } + + /* Create the .rel.plt section data. It simply means relocations + addressing the corresponding entry in the .got section. The + section name is misleading. */ + scn = elf_getscn (statep->outelf, statep->pltrelscnidx); + xelf_getshdr (scn, shdr); + data = elf_getdata (scn, NULL); + assert (shdr != NULL && data != NULL); + + /* Update the sh_link to point to the section being modified. We + point it here (correctly) to the .got section. Some linkers + (e.g., the GNU binutils linker) point to the .plt section. This + is wrong since the .plt section isn't modified even though the + name .rel.plt suggests that this is correct. */ + shdr->sh_link = statep->dynsymscnidx; + shdr->sh_info = statep->gotscnidx; + (void) xelf_update_shdr (scn, shdr); + + for (cnt = 0; cnt < statep->nplt; ++cnt) + { + XElf_Rel_vardef (rel); + + assert ((1 + cnt) * sizeof (Elf32_Rel) <= data->d_size); + xelf_getrel_ptr (data, cnt, rel); + rel->r_offset = gotaddr + (3 + cnt) * sizeof (Elf32_Addr); + /* The symbol table entries for the functions from DSOs are at + the end of the symbol table. */ + rel->r_info = XELF_R_INFO (1 + cnt, R_386_JMP_SLOT); + (void) xelf_update_rel (data, cnt, rel); + } +} + + +static int +elf_i386_rel_type (struct ld_state *statep __attribute__ ((__unused__))) +{ + /* ELF/i386 uses REL. */ + return DT_REL; +} + + +static void +elf_i386_count_relocations (struct ld_state *statep, struct scninfo *scninfo) +{ + /* We go through the list of input sections and count those relocations + which are not handled by the linker. At the same time we have to + see how many GOT entries we need and how much .bss space is needed + for copy relocations. */ + Elf_Data *data = elf_getdata (scninfo->scn, NULL); + XElf_Shdr *shdr = &SCNINFO_SHDR (scninfo->shdr); + size_t maxcnt = shdr->sh_size / shdr->sh_entsize; + size_t relsize = 0; + size_t cnt; + struct symbol *sym; + + assert (shdr->sh_type == SHT_REL); + + for (cnt = 0; cnt < maxcnt; ++cnt) + { + XElf_Rel_vardef (rel); + + xelf_getrel (data, cnt, rel); + /* XXX Should we complain about failing accesses? */ + if (rel != NULL) + { + Elf32_Word r_sym = XELF_R_SYM (rel->r_info); + + switch (XELF_R_TYPE (rel->r_info)) + { + case R_386_GOT32: + if (! scninfo->fileinfo->symref[r_sym]->defined) + relsize += sizeof (Elf32_Rel); + + /* This relocation is not emitted in the output file but + requires a GOT entry. */ + ++statep->ngot; + ++statep->nrel_got; + + /* FALLTHROUGH */ + + case R_386_GOTOFF: + case R_386_GOTPC: + statep->need_got = true; + break; + + case R_386_32: + case R_386_PC32: + /* These relocations cause text relocations in DSOs. */ + if (linked_from_dso_p (scninfo, r_sym)) + { + if (statep->file_type == dso_file_type) + { + relsize += sizeof (Elf32_Rel); + statep->dt_flags |= DF_TEXTREL; + } + else + { + /* Non-function objects from a DSO need to get a + copy relocation. */ + sym = scninfo->fileinfo->symref[r_sym]; + + /* Only do this if we have not requested a copy + relocation already. */ + if (unlikely (sym->type != STT_FUNC) && ! sym->need_copy) + { + sym->need_copy = 1; + ++statep->ncopy; + relsize += sizeof (Elf32_Rel); + } + } + } + else if (statep->file_type == dso_file_type + && r_sym >= SCNINFO_SHDR (scninfo->fileinfo->scninfo[shdr->sh_link].shdr).sh_info + && scninfo->fileinfo->symref[r_sym]->outdynsymidx != 0 + && XELF_R_TYPE (rel->r_info) == R_386_32) + relsize += sizeof (Elf32_Rel); + break; + + case R_386_PLT32: + /* We might need a PLT entry. But we cannot say for sure + here since one of the symbols might turn up being + defined in the executable (if we create such a thing). + If a DSO is created we still might use a local + definition. + + If the symbol is not defined and we are not creating + a statically linked binary, then we need in any case + a PLT entry. */ + if (! scninfo->fileinfo->symref[r_sym]->defined) + { + assert (!statep->statically); + + sym = scninfo->fileinfo->symref[r_sym]; + sym->type = STT_FUNC; + sym->in_dso = 1; + sym->defined = 1; + + /* Remove from the list of unresolved symbols. */ + --statep->nunresolved; + if (! sym->weak) + --statep->nunresolved_nonweak; + CDBL_LIST_DEL (statep->unresolved, sym); + + /* Add to the list of symbols we expect from a DSO. */ + ++statep->nplt; + ++statep->nfrom_dso; + CDBL_LIST_ADD_REAR (statep->from_dso, sym); + } + break; + + case R_386_TLS_GD: + case R_386_TLS_LDM: + case R_386_TLS_GD_32: + case R_386_TLS_GD_PUSH: + case R_386_TLS_GD_CALL: + case R_386_TLS_GD_POP: + case R_386_TLS_LDM_32: + case R_386_TLS_LDM_PUSH: + case R_386_TLS_LDM_CALL: + case R_386_TLS_LDM_POP: + case R_386_TLS_LDO_32: + case R_386_TLS_IE_32: + case R_386_TLS_LE_32: + /* XXX */ + abort (); + break; + + case R_386_NONE: + /* Nothing to be done. */ + break; + + /* These relocation should never be generated by an + assembler. */ + case R_386_COPY: + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: + case R_386_RELATIVE: + case R_386_TLS_DTPMOD32: + case R_386_TLS_DTPOFF32: + case R_386_TLS_TPOFF32: + /* Unknown relocation. */ + default: + abort (); + } + } + } + + scninfo->relsize = relsize; +} + + +static void +elf_i386_create_relocations (struct ld_state *statep, + const Elf32_Word *dblindirect __attribute__ ((unused))) +{ + /* Get the address of the got section. */ + Elf_Scn *pltscn = elf_getscn (statep->outelf, statep->pltscnidx); + Elf32_Shdr *shdr = elf32_getshdr (pltscn); + assert (shdr != NULL); + Elf32_Addr pltaddr = shdr->sh_addr; + + Elf_Scn *gotscn = elf_getscn (statep->outelf, statep->gotscnidx); + shdr = elf32_getshdr (gotscn); + assert (shdr != NULL); + Elf32_Addr gotaddr = shdr->sh_addr; + + Elf_Scn *reldynscn = elf_getscn (statep->outelf, statep->reldynscnidx); + Elf_Data *reldyndata = elf_getdata (reldynscn, NULL); + + size_t nreldyn = 0; +#define ngot_used (3 + statep->nplt + nreldyn) + + struct scninfo *first = statep->rellist->next; + struct scninfo *runp = first; + do + { + XElf_Shdr *rshdr = &SCNINFO_SHDR (runp->shdr); + Elf_Data *reldata = elf_getdata (runp->scn, NULL); + int nrels = rshdr->sh_size / rshdr->sh_entsize; + + /* We will need the following vlaues a couple of times. Help + the compiler and improve readability. */ + struct symbol **symref = runp->fileinfo->symref; + struct scninfo *scninfo = runp->fileinfo->scninfo; + + /* This is the offset of the input section we are looking at in + the output file. */ + XElf_Addr inscnoffset = scninfo[rshdr->sh_info].offset; + + /* The target section. We use the data from the input file. */ + Elf_Data *data = elf_getdata (scninfo[rshdr->sh_info].scn, NULL); + + /* We cannot handle relocations against merge-able sections. */ + assert ((SCNINFO_SHDR (scninfo[rshdr->sh_link].shdr).sh_flags + & SHF_MERGE) == 0); + + /* Cache the access to the symbol table data. */ + Elf_Data *symdata = elf_getdata (scninfo[rshdr->sh_link].scn, NULL); + + int cnt; + for (cnt = 0; cnt < nrels; ++cnt) + { + XElf_Rel_vardef (rel); + XElf_Rel *rel2; + xelf_getrel (reldata, cnt, rel); + assert (rel != NULL); + XElf_Addr reladdr = inscnoffset + rel->r_offset; + XElf_Addr value; + + size_t idx = XELF_R_SYM (rel->r_info); + if (idx < runp->fileinfo->nlocalsymbols) + { + XElf_Sym_vardef (sym); + xelf_getsym (symdata, idx, sym); + + /* The value just depends on the position of the referenced + section in the output file and the addend. */ + value = scninfo[sym->st_shndx].offset + sym->st_value; + } + else if (symref[idx]->in_dso) + { + /* MERGE.VALUE contains the PLT index. We have to add 1 since + there is this one special PLT entry at the beginning. */ + assert (symref[idx]->merge.value != 0 + || symref[idx]->type != STT_FUNC); + value = pltaddr + symref[idx]->merge.value * PLT_ENTRY_SIZE; + } + else + value = symref[idx]->merge.value; + + /* Address of the relocated memory in the data buffer. */ + void *relloc = (char *) data->d_buf + rel->r_offset; + + switch (XELF_R_TYPE (rel->r_info)) + { + /* These three cases can be handled together since the + symbol associated with the R_386_GOTPC relocation is + _GLOBAL_OFFSET_TABLE_ which has a value corresponding + to the address of the GOT and the address of the PLT + entry required for R_386_PLT32 is computed above. */ + case R_386_PC32: + case R_386_GOTPC: + case R_386_PLT32: + value -= reladdr; + /* FALLTHROUGH */ + + case R_386_32: + if (linked_from_dso_p (scninfo, idx) + && statep->file_type != dso_file_type + && symref[idx]->type != STT_FUNC) + { + value = (ld_state.copy_section->offset + + symref[idx]->merge.value); + + if (unlikely (symref[idx]->need_copy)) + { + /* Add a relocation to initialize the GOT entry. */ + assert (symref[idx]->outdynsymidx != 0); +#if NATIVE_ELF != 0 + xelf_getrel_ptr (reldyndata, nreldyn, rel2); +#else + rel2 = &rel_mem; +#endif + rel2->r_offset = value; + rel2->r_info + = XELF_R_INFO (symref[idx]->outdynsymidx, R_386_COPY); + (void) xelf_update_rel (reldyndata, nreldyn, rel2); + ++nreldyn; + + /* Update the symbol table record for the new + address. */ + Elf32_Word symidx = symref[idx]->outdynsymidx; + Elf_Scn *symscn = elf_getscn (statep->outelf, + statep->dynsymscnidx); + Elf_Data *outsymdata = elf_getdata (symscn, NULL); + assert (outsymdata != NULL); + XElf_Sym_vardef (sym); + xelf_getsym (outsymdata, symidx, sym); + sym->st_value = value; + sym->st_shndx = statep->copy_section->outscnndx; + (void) xelf_update_sym (outsymdata, symidx, sym); + + symidx = symref[idx]->outsymidx; + if (symidx != 0) + { + symidx = statep->dblindirect[symidx]; + symscn = elf_getscn (statep->outelf, + statep->symscnidx); + outsymdata = elf_getdata (symscn, NULL); + assert (outsymdata != NULL); + xelf_getsym (outsymdata, symidx, sym); + sym->st_value = value; + sym->st_shndx = statep->copy_section->outscnndx; + (void) xelf_update_sym (outsymdata, symidx, sym); + } + + /* Remember that we set up the copy relocation. */ + symref[idx]->need_copy = 0; + } + } + else if (statep->file_type == dso_file_type + && idx >= SCNINFO_SHDR (scninfo[rshdr->sh_link].shdr).sh_info + && symref[idx]->outdynsymidx != 0) + { +#if NATIVE_ELF != 0 + xelf_getrel_ptr (reldyndata, nreldyn, rel2); +#else + rel2 = &rel_mem; +#endif + rel2->r_offset = value; + rel2->r_info + = XELF_R_INFO (symref[idx]->outdynsymidx, R_386_32); + (void) xelf_update_rel (reldyndata, nreldyn, rel2); + ++nreldyn; + + value = 0; + } + add_4ubyte_unaligned (relloc, value); + break; + + case R_386_GOT32: + store_4ubyte_unaligned (relloc, ngot_used * sizeof (Elf32_Addr)); + + /* Add a relocation to initialize the GOT entry. */ +#if NATIVE_ELF != 0 + xelf_getrel_ptr (reldyndata, nreldyn, rel2); +#else + rel2 = &rel_mem; +#endif + rel2->r_offset = gotaddr + ngot_used * sizeof (Elf32_Addr); + rel2->r_info + = XELF_R_INFO (symref[idx]->outdynsymidx, R_386_GLOB_DAT); + (void) xelf_update_rel (reldyndata, nreldyn, rel2); + ++nreldyn; + break; + + case R_386_GOTOFF: + add_4ubyte_unaligned (relloc, value - gotaddr); + break; + + case R_386_32PLT: + case R_386_TLS_TPOFF: + case R_386_TLS_IE: + case R_386_TLS_GOTIE: + case R_386_TLS_LE: + case R_386_TLS_GD: + case R_386_TLS_LDM: + case R_386_16: + case R_386_PC16: + case R_386_8: + case R_386_PC8: + case R_386_TLS_GD_32: + case R_386_TLS_GD_PUSH: + case R_386_TLS_GD_CALL: + case R_386_TLS_GD_POP: + case R_386_TLS_LDM_32: + case R_386_TLS_LDM_PUSH: + case R_386_TLS_LDM_CALL: + case R_386_TLS_LDM_POP: + case R_386_TLS_LDO_32: + case R_386_TLS_IE_32: + case R_386_TLS_LE_32: + // XXX For now fall through + printf("ignored relocation %d\n", (int) XELF_R_TYPE (rel->r_info)); + break; + + case R_386_NONE: + /* Nothing to do. */ + break; + + case R_386_COPY: + case R_386_JMP_SLOT: + case R_386_RELATIVE: + case R_386_GLOB_DAT: + case R_386_TLS_DTPMOD32: + case R_386_TLS_DTPOFF32: + case R_386_TLS_TPOFF32: + default: + /* Should not happen. */ + abort (); + } + } + } + while ((runp = runp->next) != first); +} + + +int +elf_i386_ld_init (struct ld_state *statep) +{ + /* We have a few callbacks available. */ + old_open_outfile = statep->callbacks.open_outfile; + statep->callbacks.open_outfile = elf_i386_open_outfile; + + statep->callbacks.relocate_section = elf_i386_relocate_section; + + statep->callbacks.initialize_plt = elf_i386_initialize_plt; + statep->callbacks.initialize_pltrel = elf_i386_initialize_pltrel; + + statep->callbacks.initialize_got = elf_i386_initialize_got; + + statep->callbacks.finalize_plt = elf_i386_finalize_plt; + + statep->callbacks.rel_type = elf_i386_rel_type; + + statep->callbacks.count_relocations = elf_i386_count_relocations; + + statep->callbacks.create_relocations = elf_i386_create_relocations; + + return 0; +} diff --git a/src/ld.c b/src/ld.c new file mode 100644 index 00000000..2aece00e --- /dev/null +++ b/src/ld.c @@ -0,0 +1,1506 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2001. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <argp.h> +#include <assert.h> +#include <error.h> +#include <fcntl.h> +#include <libelf.h> +#include <libintl.h> +#include <locale.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <system.h> +#include "ld.h" +#include "list.h" + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + + +/* Values for the various options. */ +enum + { + ARGP_whole_archive = 300, + ARGP_no_whole_archive, + ARGP_static, + ARGP_dynamic, + ARGP_pagesize, + ARGP_rpath, + ARGP_rpath_link, + ARGP_runpath, + ARGP_runpath_link, + ARGP_version_script, + ARGP_gc_sections, + ARGP_no_gc_sections, + ARGP_no_undefined, + ARGP_conserve, +#if YYDEBUG + ARGP_yydebug, +#endif + }; + + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + /* XXX This list will be reordered and section names will be added. + Just not right now. */ + { "whole-archive", ARGP_whole_archive, NULL, 0, + N_("Include whole archives in the output from now on."), 0 }, + { "no-whole-archive", ARGP_no_whole_archive, NULL, 0, + N_("Stop including the whole arhives in the output."), 0 }, + + { "output", 'o', N_("FILE"), 0, N_("Place output in FILE."), 0 }, + + { NULL, 'O', N_("LEVEL"), OPTION_ARG_OPTIONAL, + N_("Set optimization level to LEVEL."), 0 }, + + { "verbose", 'v', NULL, 0, N_("Verbose messages."), 0 }, + { "trace", 't', NULL, 0, N_("Trace file opens."), 0 }, + { "conserve-memory", ARGP_conserve, NULL, 0, + N_("Trade speed for less memory usage"), 0 }, + + { NULL, 'z', "KEYWORD", OPTION_HIDDEN, NULL, 0 }, + { "-z nodefaultlib", '\0', NULL, OPTION_DOC, + N_("Object is marked to not use default search path at runtime."), 0 }, + { "-z allextract", '\0', NULL, OPTION_DOC, + N_("Same as --whole-archive."), 0 }, + { "-z defaultextract", '\0', NULL, OPTION_DOC, N_("\ +Default rules of extracting from archive; weak references are not enough."), + 0 }, + { "-z weakextract", '\0', NULL, OPTION_DOC, + N_("Weak references cause extraction from archive."), 0 }, + { "-z muldefs", '\0', NULL, OPTION_DOC, + N_("Allow multiple definitions; first is used."), 0 }, + { "-z defs | nodefs", '\0', NULL, OPTION_DOC, + N_("Disallow/allow undefined symbols in DSOs."), 0 }, + { "no-undefined", ARGP_no_undefined, NULL, OPTION_HIDDEN, NULL, 0 }, + { "-z origin", '\0', NULL, OPTION_DOC, + N_("Object requires immediate handling of $ORIGIN."), 0 }, + { "-z now", '\0', NULL, OPTION_DOC, + N_("Relocation will not be processed lazily."), 0 }, + { "-z nodelete", '\0', NULL, OPTION_DOC, + N_("Object cannot be unloaded at runtime."), 0 }, + { "-z initfirst", '\0', NULL, OPTION_DOC, + N_("Mark object to be initialized first."), 0 }, + { "-z lazyload | nolazyload", '\0', NULL, OPTION_DOC, + N_("Enable/disable lazy-loading flag for following dependencies."), 0 }, + { "-z nodlopen", '\0', NULL, OPTION_DOC, + N_("Mark object as not loadable with 'dlopen'."), 0 }, + { "-z ignore | record", '\0', NULL, OPTION_DOC, + N_("Ignore/record dependencies on unused DSOs."), 0 }, + { "-z systemlibrary", '\0', NULL, OPTION_DOC, + N_("Generated DSO will be a system library."), 0 }, + + { NULL, 'l', N_("FILE"), OPTION_HIDDEN, NULL, 0 }, + + { NULL, '(', NULL, 0, N_("Start a group."), 0 }, + { NULL, ')', NULL, 0, N_("End a group."), 0 }, + + { NULL, 'L', N_("PATH"), 0, + N_("Add PATH to list of directories files are searched in."), 0 }, + + { NULL, 'c', N_("FILE"), 0, N_("Use linker script in FILE."), 0 }, + + { "entry", 'e', N_("ADDRESS"), 0, N_("Set entry point address."), 0 }, + + { "static", ARGP_static, NULL, OPTION_HIDDEN, NULL, 0 }, + { "-B static", ARGP_static, NULL, OPTION_DOC, + N_("Do not link against shared libraries."), 0 }, + { "dynamic", ARGP_dynamic, NULL, OPTION_HIDDEN, NULL, 0 }, + { "-B dynamic", ARGP_dynamic, NULL, OPTION_DOC, + N_("Prefer linking against shared libraries."), 0 }, + + { "export-dynamic", 'E', NULL, 0, N_("Export all dynamic symbols."), 0 }, + + { "strip-all", 's', NULL, 0, N_("Strip all symbols."), 0 }, + { "strip-debug", 'S', NULL, 0, N_("Strip debugging symbols."), 0 }, + + { "pagesize", ARGP_pagesize, "SIZE", 0, + N_("Assume pagesize for the target system to be SIZE."), 0 }, + + { "rpath", ARGP_rpath, "PATH", OPTION_HIDDEN, NULL, 0 }, + { "rpath-link", ARGP_rpath_link, "PATH", OPTION_HIDDEN, NULL, 0 }, + + { "runpath", ARGP_runpath, "PATH", 0, N_("Set runtime DSO search path."), + 0 }, + { "runpath-link", ARGP_runpath_link, "PATH", 0, + N_("Set link time DSO search path."), 0 }, + + { NULL, 'i', NULL, 0, N_("Ignore LD_LIBRARY_PATH environment variable."), + 0 }, + + { "version-script", ARGP_version_script, "FILE", 0, + N_("Read version information from FILE."), 0 }, + + { "emulation", 'm', "NAME", 0, N_("Set emulation to NAME."), 0 }, + + { "shared", 'G', NULL, 0, N_("Generate dynamic shared object."), 0 }, + { NULL, 'r', NULL, 0L, N_("Generate relocatable object."), 0 }, + + { NULL, 'B', "KEYWORD", OPTION_HIDDEN, "", 0 }, + { "-B local", 'B', NULL, OPTION_DOC, + N_("Causes symbol not assigned to a version be reduced to local."), 0 }, + + { "gc-sections", ARGP_gc_sections, NULL, 0, N_("Remove unused sections."), + 0 }, + { "no-gc-sections", ARGP_no_gc_sections, NULL, 0, + N_("Don't remove unused sections."), 0 }, + + { "soname", 'h', "NAME", 0, N_("Set soname of shared object."), 0 }, + { "dynamic-linker", 'I', "NAME", 0, N_("Set the dynamic linker name."), 0 }, + + { NULL, 'Q', "YN", OPTION_HIDDEN, NULL, 0 }, + { "-Q y | n", 'Q', NULL, OPTION_DOC, + N_("Add/suppress addition indentifying link-editor to .comment section"), + 0 }, + +#if YYDEBUG + { "yydebug", ARGP_yydebug, NULL, 0, + N_("Select to get parser debug information"), 0 }, +#endif + + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("Combine object and archive files."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("[FILE]..."); + +/* Prototype for option handler. */ +static error_t parse_opt_1st (int key, char *arg, struct argp_state *state); +static error_t parse_opt_2nd (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp_1st = +{ + options, parse_opt_1st, args_doc, doc, NULL, NULL, NULL +}; +static struct argp argp_2nd = +{ + options, parse_opt_2nd, args_doc, doc, NULL, NULL, NULL +}; + + +/* Linker state. This contains all global information. */ +struct ld_state ld_state; + +/* List of the input files. */ +static struct file_list +{ + const char *name; + struct file_list *next; +} *input_file_list; + +/* If nonzero be verbose. */ +int verbose; + +/* If nonzero, trade speed for less memory/address space usage. */ +int conserve_memory; + +/* The emulation name to use. */ +static const char *emulation; + +/* Keep track of the nesting level. Even though we don't handle nested + groups we still keep track to improve the error messages. */ +static int group_level; + +/* The last file we processed. */ +static struct usedfiles *last_file; + +/* The default linker script. */ +/* XXX We'll do this a bit different in the real solution. */ +static const char *linker_script = SRCDIR "/elf32-i386.script"; + +/* Nonzero if an error occurred while loading the input files. */ +static int error_loading; + + +/* Intermediate storage for the LD_LIBRARY_PATH information from the + environment. */ +static char *ld_library_path1; + +/* Flag used to communicate with the scanner. */ +int ld_scan_version_script; + +/* Name of the input file. */ +const char *ldin_fname; + +/* Define by parser if required. */ +extern int lddebug; + + +/* Prototypes for local functions. */ +static void parse_z_option (const char *arg); +static void parse_z_option_2 (const char *arg); +static void parse_B_option (const char *arg); +static void parse_B_option_2 (const char *arg); +static void determine_output_format (void); +static void load_needed (void); +static void collect_sections (void); +static void add_rxxpath (struct pathelement **pathp, const char *str); +static void gen_rxxpath_data (void); +static void read_version_script (const char *fname); +static void create_lscript_symbols (void); +static void create_special_section_symbol (struct symbol **symp, + const char *name); + + +int +main (int argc, char *argv[]) +{ + int remaining; + int err; + +#ifndef NDEBUG + /* Enable memory debugging. */ + mtrace (); +#endif + + /* Sanity check. We always want to use the LFS functionality. */ + if (sizeof (off_t) != sizeof (off64_t)) + abort (); + + /* We use no threads here which can interfere with handling a stream. */ + __fsetlocking (stdin, FSETLOCKING_BYCALLER); + __fsetlocking (stdout, FSETLOCKING_BYCALLER); + __fsetlocking (stderr, FSETLOCKING_BYCALLER); + + /* Set locale. */ + setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + bindtextdomain (PACKAGE, LOCALEDIR); + + /* Initialize the message catalog. */ + textdomain (PACKAGE); + + /* Before we start tell the ELF library which version we are using. */ + elf_version (EV_CURRENT); + + /* The user can use the LD_LIBRARY_PATH environment variable to add + additional lookup directories. */ + ld_library_path1 = getenv ("LD_LIBRARY_PATH"); + + /* Initialize the memory handling. */ +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free + obstack_init (&ld_state.smem); + + /* One quick pass over the parameters which allows us to scan for options + with global effect which influence the rest of the processing. */ + argp_parse (&argp_1st, argc, argv, ARGP_IN_ORDER, &remaining, NULL); + + /* We need at least one input file. */ + if (input_file_list == NULL) + { + error (0, 0, gettext ("At least one input file needed")); + argp_help (&argp_1st, stderr, ARGP_HELP_SEE, "ld"); + exit (EXIT_FAILURE); + } + + /* Determine which ELF backend to use. */ + determine_output_format (); + + /* Prepare state. */ + err = ld_prepare_state (emulation); + if (err != 0) + error (EXIT_FAILURE, 0, gettext ("error while preparing linking")); + + /* XXX Read the linker script now. Since we later will have the linker + script built in we don't go into trouble to make sure we handle GROUP + statements in the script. This simply must not happen. */ + ldin = fopen (linker_script, "r"); + if (ldin == NULL) + error (EXIT_FAILURE, errno, gettext ("cannot open linker script \"%s\""), + linker_script); + /* No need for locking. */ + __fsetlocking (ldin, FSETLOCKING_BYCALLER); + + ld_state.srcfiles = NULL; + ldlineno = 1; + ld_scan_version_script = 0; + ldin_fname = linker_script; + if (ldparse () != 0) + /* Something went wrong during parsing. */ + exit (EXIT_FAILURE); + fclose (ldin); + + /* We now might have a list of directories to look for libraries in + named by the linker script. Put them in a different list so that + they are searched after all paths given by the user on the + command line. */ + ld_state.default_paths = ld_state.paths; + ld_state.paths = ld_state.tailpaths = NULL; + + /* Get runpath/rpath information in usable form. */ + gen_rxxpath_data (); + + /* Parse and process arguments for real. */ + argp_parse (&argp_2nd, argc, argv, ARGP_IN_ORDER, &remaining, NULL); + /* All options should have been processed by the argp parser. */ + assert (remaining == argc); + + /* Process the last file. */ + while (last_file != NULL) + /* Try to open the file. */ + error_loading |= FILE_PROCESS (-1, last_file, &ld_state, &last_file); + + /* Stop if there has been a problem while reading the input files. */ + if (error_loading) + exit (error_loading); + + /* See whether all opened -( were closed. */ + if (group_level > 0) + { + error (0, 0, gettext ("-( without matching -)")); + argp_help (&argp_1st, stderr, ARGP_HELP_SEE, "ld"); + exit (EXIT_FAILURE); + } + + /* When we create a relocatable file we don't have to look for the + DT_NEEDED DSOs and we also don't test for undefined symbols. */ + if (ld_state.file_type != relocatable_file_type) + { + /* At this point we have loaded all the direct dependencies. What + remains to be done is find the indirect dependencies. These are + DSOs which are referenced by the DT_NEEDED entries in the DSOs + which are direct dependencies. We have to transitively find and + load all these dependencies. */ + load_needed (); + + /* At this point all object files and DSOs are read. If there + are still undefined symbols left they might have to be + synthesized from the linker script. */ + create_lscript_symbols (); + + /* Now that we have loaded all the object files we can determine + whether we have any non-weak unresolved references left. If + there are any we stop. If the user used the '-z nodefs' option + and we are creating a DSO don't perform the tests. */ + if (FLAG_UNRESOLVED (&ld_state) != 0) + exit (1); + } + + /* Collect information about the relocations which will be carried + forward into the output. We have to do this here and now since + we need to know which sections have to be created. */ + if (ld_state.file_type != relocatable_file_type) + { + void *p ; + struct scnhead *h; + + p = NULL; + while ((h = ld_section_tab_iterate (&ld_state.section_tab, &p)) != NULL) + if (h->type == SHT_REL || h->type == SHT_RELA) + { + struct scninfo *runp = h->last; + do + { + /* If we are processing the relocations determine how + many will be in the output file. Also determine + how many GOT entries are needed. */ + COUNT_RELOCATIONS (&ld_state, runp); + + ld_state.relsize_total += runp->relsize; + } + while ((runp = runp->next) != h->last); + } + } + + /* Not part of the gABI, but part of every psABI: the symbols for the + GOT section. Add the symbol if necessary. */ + if (ld_state.need_got) + create_special_section_symbol (&ld_state.got_symbol, + "_GLOBAL_OFFSET_TABLE_"); + /* Similarly for the _DYNAMIC symbol which points to the dynamic + section. */ + if (dynamically_linked_p ()) + create_special_section_symbol (&ld_state.dyn_symbol, "_DYNAMIC"); + + /* We are ready to start working on the output file. Not all + information has been gather or created yet. This will be done as + we go. Open the file now. */ + if (OPEN_OUTFILE (&ld_state, EM_NONE, ELFCLASSNONE, ELFDATANONE) != 0) + exit (1); + + /* Create the sections which are generated by the linker and are not + present in the input file. The output file must already have + been opened since we need the ELF descriptor to deduce type + sizes. */ + GENERATE_SECTIONS (&ld_state); + + /* At this point we have read all the files and know all the + sections which have to be linked into the application. We do now + create an array listing all the sections. We will than pass this + array to a system specific function which can reorder it at will. + The functions can also merge sections if this is what is + wanted. */ + collect_sections (); + + /* Create the output sections now. This may requires sorting them + first. */ + CREATE_SECTIONS (&ld_state); + + /* Create the output file data. Appropriate code for the selected + output file type is called. */ + if (CREATE_OUTFILE (&ld_state) != 0) + exit (1); + + /* Finalize the output file, write the data out. */ + err |= FINALIZE (&ld_state); + + /* Return with an non-zero exit status also if any error message has + been printed. */ + return err | (error_message_count != 0); +} + + +/* Quick scan of the parameter list for options with global effect. */ +static error_t +parse_opt_1st (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case 'B': + parse_B_option (arg); + break; + + case 'c': + linker_script = arg; + break; + + case 'E': + ld_state.export_all_dynamic = true; + break; + + case 'G': + if (ld_state.file_type != no_file_type) + error (EXIT_FAILURE, 0, + gettext ("only one option of -G and -r is allowed")); + ld_state.file_type = dso_file_type; + + /* If we generate a DSO we have to export all symbols. */ + ld_state.export_all_dynamic = true; + break; + + case 'h': + ld_state.soname = arg; + break; + + case 'i': + /* Discard the LD_LIBRARY_PATH value we found. */ + ld_library_path1 = NULL; + break; + + case 'I': + ld_state.interp = arg; + break; + + case 'm': + if (emulation != NULL) + error (EXIT_FAILURE, 0, gettext ("more than one '-m' parameter")); + emulation = arg; + break; + + case 'Q': + if (arg[1] == '\0' && (arg[0] == 'y' || arg[0] == 'Y')) + ld_state.add_ld_comment = true; + else if (arg[1] == '\0' && (arg[0] == 'n' || arg[0] == 'N')) + ld_state.add_ld_comment = true; + else + error (EXIT_FAILURE, 0, gettext ("unknown option `-%c %s'"), 'Q', arg); + break; + + case 'r': + if (ld_state.file_type != no_file_type) + error (EXIT_FAILURE, 0, + gettext ("only one option of -G and -r is allowed")); + ld_state.file_type = relocatable_file_type; + break; + + case 'S': + ld_state.strip = strip_debug; + break; + + case 't': + ld_state.trace_files = true; + break; + + case 'v': + verbose = 1; + break; + + case 'z': + /* The SysV linker used 'z' to pass various flags to the linker. + We follow this. See 'parse_z_option' for the options we + recognize. */ + parse_z_option (arg); + break; + + case ARGP_pagesize: + { + char *endp; + ld_state.pagesize = strtoul (arg, &endp, 0); + if (*endp != '\0') + { + if (endp[1] == '\0' && tolower (*endp) == 'k') + ld_state.pagesize *= 1024; + else if (endp[1] == '\0' && tolower (*endp) == 'm') + ld_state.pagesize *= 1024 * 1024; + else + { + error (0, 0, + gettext ("invalid page size value \"%s\": ignored"), + arg); + ld_state.pagesize = 0; + } + } + } + break; + + case ARGP_rpath: + add_rxxpath (&ld_state.rpath, arg); + break; + + case ARGP_rpath_link: + add_rxxpath (&ld_state.rpath_link, arg); + break; + + case ARGP_runpath: + add_rxxpath (&ld_state.runpath, arg); + break; + + case ARGP_runpath_link: + add_rxxpath (&ld_state.runpath_link, arg); + break; + + case ARGP_gc_sections: + case ARGP_no_gc_sections: + ld_state.gc_sections = key == ARGP_gc_sections; + break; + + case 's': + if (arg == NULL) + { + if (ld_state.strip == strip_all) + ld_state.strip = strip_everything; + else + ld_state.strip = strip_all; + break; + } + /* FALLTHROUGH */ + + case 'e': + case 'o': + case 'O': + case ARGP_whole_archive: + case ARGP_no_whole_archive: + case 'L': + case '(': + case ')': + case 'l': + case ARGP_static: + case ARGP_dynamic: + case ARGP_version_script: + /* We'll handle these in the second pass. */ + break; + + case ARGP_KEY_ARG: + { + struct file_list *newp; + + newp = (struct file_list *) xmalloc (sizeof (struct file_list)); + newp->name = arg; +#ifndef NDEBUG + newp->next = NULL; +#endif + CSNGL_LIST_ADD_REAR (input_file_list, newp); + } + break; + +#if YYDEBUG + case ARGP_yydebug: + lddebug = 1; + break; +#endif + + case ARGP_no_undefined: + ld_state.nodefs = false; + break; + + case ARGP_conserve: + conserve_memory = 1; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +/* Handle program arguments for real. */ +static error_t +parse_opt_2nd (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + static bool group_start_requested; + static bool group_end_requested; + + switch (key) + { + case 'B': + parse_B_option_2 (arg); + break; + + case 'e': + ld_state.entry = arg; + break; + + case 'o': + if (ld_state.outfname != NULL) + { + error (0, 0, gettext ("More than one output file name given.")); + see_help: + argp_help (&argp_2nd, stderr, ARGP_HELP_SEE, "ld"); + exit (EXIT_FAILURE); + } + ld_state.outfname = arg; + break; + + case 'O': + if (arg == NULL) + ld_state.optlevel = 1; + else + { + char *endp; + unsigned long int level = strtoul (arg, &endp, 10); + if (*endp != '\0') + { + error (0, 0, gettext ("Invalid optimization level `%s'"), arg); + goto see_help; + } + ld_state.optlevel = level; + } + break; + + case ARGP_whole_archive: + ld_state.extract_rule = allextract; + break; + case ARGP_no_whole_archive: + ld_state.extract_rule = defaultextract; + break; + + case ARGP_static: + case ARGP_dynamic: + /* Enable/disable use for DSOs. */ + ld_state.statically = key == ARGP_static; + break; + + case 'z': + /* The SysV linker used 'z' to pass various flags to the linker. + We follow this. See 'parse_z_option' for the options we + recognize. */ + parse_z_option_2 (arg); + break; + + case ARGP_version_script: + read_version_script (arg); + break; + + case 'L': + /* Add a new search directory. */ + ld_new_searchdir (arg); + break; + + case '(': + /* Start a link group. We have to be able to determine the object + file which is named next. Do this by remembering a pointer to + the pointer which will point to the next object. */ + if (verbose && (group_start_requested || !group_end_requested)) + error (0, 0, gettext ("nested -( -) groups are not allowed")); + + /* Increment the nesting level. */ + ++group_level; + + /* Record group start. */ + group_start_requested = true; + group_end_requested = false; + break; + + case ')': + /* End a link group. If there is no group open this is clearly + a bug. If there is a group open insert a back reference + pointer in the record for the last object of the group. If + there is no new object or just one don't do anything. */ + if (!group_end_requested) + { + if (group_level == 0) + { + error (0, 0, gettext ("-) without matching -(")); + goto see_help; + } + } + else + last_file->group_end = true; + + if (group_level > 0) + --group_level; + break; + + case 'l': + case ARGP_KEY_ARG: + { + while (last_file != NULL) + /* Try to open the file. */ + error_loading |= FILE_PROCESS (-1, last_file, &ld_state, &last_file); + + last_file = ld_new_inputfile (arg, + key == 'l' + ? archive_file_type + : relocatable_file_type); + if (group_start_requested) + { + last_file->group_start = true; + + group_start_requested = false; + group_end_requested = true; + } + } + break; + + default: + /* We can catch all other options here. They either have + already been handled or, if the parameter was not correct, + the error has been reported. */ + break; + } + return 0; +} + + +/* Load all the DSOs named as dependencies in other DSOs we already + loaded. */ +static void +load_needed (void) +{ + struct usedfiles *first; + struct usedfiles *runp; + + /* XXX There is one problem here: do we allow references from + regular object files to be satisfied by these implicit + dependencies? The old linker allows this and several libraries + depend on this. Solaris' linker does not allow this; it provides + the user with a comprehensive error message explaining the + situation. + + XXX IMO the old ld behavior is correct since this is also how the + dynamic linker will work. It will look for unresolved references + in all loaded DSOs. + + XXX Should we add an option to get Solaris compatibility? */ + if (ld_state.needed == NULL) + return; + + runp = first = ld_state.needed->next; + do + { + struct usedfiles *ignore; + struct usedfiles *next = runp->next; + int err; + + err = FILE_PROCESS (-1, runp, &ld_state, &ignore); + if (err != 0) + /* Something went wrong. */ + exit (err); + + runp = next; + } + while (runp != first); +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "ld (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2005"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* There are a lot of -z options, parse them here. Some of them have + to be parsed in the first pass, others must be handled in the + second pass. */ +static void +parse_z_option (const char *arg) +{ + if (strcmp (arg, "nodefaultlib") == 0 + /* This is only meaningful if we create a DSO. */ + && ld_state.file_type == dso_file_type) + ld_state.dt_flags_1 |= DF_1_NODEFLIB; + else if (strcmp (arg, "muldefs") == 0) + ld_state.muldefs = true; + else if (strcmp (arg, "nodefs") == 0) + ld_state.nodefs = true; + else if (strcmp (arg, "defs") == 0) + ld_state.nodefs = false; + else if (strcmp (arg, "now") == 0) + /* We could also set the DF_1_NOW flag in DT_FLAGS_1 but this isn't + necessary. */ + ld_state.dt_flags |= DF_BIND_NOW; + else if (strcmp (arg, "origin") == 0) + /* We could also set the DF_1_ORIGIN flag in DT_FLAGS_1 but this isn't + necessary. */ + ld_state.dt_flags |= DF_ORIGIN; + else if (strcmp (arg, "nodelete") == 0 + /* This is only meaningful if we create a DSO. */ + && ld_state.file_type == dso_file_type) + ld_state.dt_flags_1 |= DF_1_NODELETE; + else if (strcmp (arg, "initfirst") == 0) + ld_state.dt_flags_1 |= DF_1_INITFIRST; + else if (strcmp (arg, "nodlopen") == 0 + /* This is only meaningful if we create a DSO. */ + && ld_state.file_type == dso_file_type) + ld_state.dt_flags_1 |= DF_1_NOOPEN; + else if (strcmp (arg, "ignore") == 0) + ld_state.ignore_unused_dsos = true; + else if (strcmp (arg, "record") == 0) + ld_state.ignore_unused_dsos = false; + else if (strcmp (arg, "systemlibrary") == 0) + ld_state.is_system_library = true; + else if (strcmp (arg, "allextract") != 0 + && strcmp (arg, "defaultextract") != 0 + && strcmp (arg, "weakextract") != 0 + && strcmp (arg, "lazyload") != 0 + && strcmp (arg, "nolazyload") != 0) + error (0, 0, gettext ("unknown option `-%c %s'"), 'z', arg); +} + + +static void +parse_z_option_2 (const char *arg) +{ + if (strcmp (arg, "allextract") == 0) + ld_state.extract_rule = allextract; + else if (strcmp (arg, "defaultextract") == 0) + ld_state.extract_rule = defaultextract; + else if (strcmp (arg, "weakextract") == 0) + ld_state.extract_rule = weakextract; + else if (strcmp (arg, "lazyload") == 0) + ld_state.lazyload = true; + else if (strcmp (arg, "nolazyload") == 0) + ld_state.lazyload = false; +} + + +/* There are a lot of -B options, parse them here. */ +static void +parse_B_option (const char *arg) +{ + if (strcmp (arg, "local") == 0) + ld_state.default_bind_local = true; + else if (strcmp (arg, "symbolic") != 0 + && strcmp (arg, "static") != 0 + && strcmp (arg, "dynamic") != 0) + error (0, 0, gettext ("unknown option '-%c %s'"), 'B', arg); +} + + +/* The same functionality, but called in the second pass over the + parameters. */ +static void +parse_B_option_2 (const char *arg) +{ + if (strcmp (arg, "static") == 0) + ld_state.statically = true; + else if (strcmp (arg, "dynamic") == 0) + ld_state.statically = false; + else if (strcmp (arg, "symbolic") == 0 + /* This is only meaningful if we create a DSO. */ + && ld_state.file_type == dso_file_type) + ld_state.dt_flags |= DF_SYMBOLIC; +} + + +static void +determine_output_format (void) +{ + /* First change the 'input_file_list' variable in a simple + single-linked list. */ + struct file_list *last = input_file_list; + input_file_list = input_file_list->next; + last->next = NULL; + + /* Determine the target configuration which we are supposed to use. + The user can use the '-m' option to select one. If this is + missing we are trying to load one file and determine the + architecture from that. */ + if (emulation != NULL) + { + ld_state.ebl = ebl_openbackend_emulation (emulation); + + assert (ld_state.ebl != NULL); + } + else + { + /* Find an ELF input file and let it determine the ELf backend. */ + struct file_list *runp = input_file_list; + + while (runp != NULL) + { + int fd = open (runp->name, O_RDONLY); + if (fd != -1) + { + int try (Elf *elf) + { + int result = 0; + + if (elf == NULL) + return 0; + + if (elf_kind (elf) == ELF_K_ELF) + { + /* We have an ELF file. We now can find out + what the output format should be. */ + XElf_Ehdr_vardef(ehdr); + + /* Get the ELF header of the object. */ + xelf_getehdr (elf, ehdr); + if (ehdr != NULL) + ld_state.ebl = + ebl_openbackend_machine (ehdr->e_machine); + + result = 1; + } + else if (elf_kind (elf) == ELF_K_AR) + { + /* Try the archive members. This could + potentially lead to wrong results if the + archive contains files for more than one + architecture. But this is the user's + problem. */ + Elf *subelf; + Elf_Cmd cmd = ELF_C_READ_MMAP; + + while ((subelf = elf_begin (fd, cmd, elf)) != NULL) + { + cmd = elf_next (subelf); + + if (try (subelf) != 0) + break; + } + } + + elf_end (elf); + + return result; + } + + if (try (elf_begin (fd, ELF_C_READ_MMAP, NULL)) != 0) + /* Found a file. */ + break; + } + + runp = runp->next; + } + + if (ld_state.ebl == NULL) + { + error (0, 0, gettext ("\ +could not find input file to determine output file format")); + error (EXIT_FAILURE, 0, gettext ("\ +try again with an appropriate '-m' parameter")); + } + } + + /* We don't need the list of input files anymore. The second run over + the parameters will handle them. */ + while (input_file_list != NULL) + { + struct file_list *oldp = input_file_list; + input_file_list = input_file_list->next; + free (oldp); + } + + /* We also know now what kind of file we are supposed to create. If + the user hasn't selected anythign we create and executable. */ + if (ld_state.file_type == no_file_type) + ld_state.file_type = executable_file_type; +} + +/* Add DIR to the list of directories searched for object files and + libraries. */ +void +ld_new_searchdir (const char *dir) +{ + struct pathelement *newpath; + + newpath = (struct pathelement *) + obstack_calloc (&ld_state.smem, sizeof (struct pathelement)); + + newpath->pname = dir; + + /* Enqueue the file. */ + if (ld_state.tailpaths == NULL) + ld_state.paths = ld_state.tailpaths = newpath; + else + { + ld_state.tailpaths->next = newpath; + ld_state.tailpaths = newpath; + } +} + + +struct usedfiles * +ld_new_inputfile (const char *fname, enum file_type type) +{ + struct usedfiles *newfile = (struct usedfiles *) + obstack_calloc (&ld_state.smem, sizeof (struct usedfiles)); + + newfile->soname = newfile->fname = newfile->rfname = fname; + newfile->file_type = type; + newfile->extract_rule = ld_state.extract_rule; + newfile->lazyload = ld_state.lazyload; + newfile->status = not_opened; + + return newfile; +} + + +/* Create an array listing all the sections. We will than pass this + array to a system specific function which can reorder it at will. + The functions can also merge sections if this is what is + wanted. */ +static void +collect_sections (void) +{ + void *p ; + struct scnhead *h; + size_t cnt; + + /* We have that many sections. At least for now. */ + ld_state.nallsections = ld_state.section_tab.filled; + + /* Allocate the array. We allocate one more entry than computed so + far since we might need a new section for the copy relocations. */ + ld_state.allsections = + (struct scnhead **) obstack_alloc (&ld_state.smem, + (ld_state.nallsections + 1) + * sizeof (struct scnhead *)); + + /* Fill the array. We rely here on the hash table iterator to + return the entries in the order they were added. */ + cnt = 0; + p = NULL; + while ((h = ld_section_tab_iterate (&ld_state.section_tab, &p)) != NULL) + { + struct scninfo *runp; + bool used = false; + + if (h->kind == scn_normal) + { + runp = h->last; + do + { + if (h->type == SHT_REL || h->type == SHT_RELA) + { + if (runp->used) + /* This is a relocation section. If the section + it is relocating is used in the result so must + the relocation section. */ + runp->used + = runp->fileinfo->scninfo[SCNINFO_SHDR (runp->shdr).sh_info].used; + } + + /* Accumulate the result. */ + used |= runp->used; + + /* Next input section. */ + runp = runp->next; + } + while (runp != h->last); + + h->used = used; + } + + ld_state.allsections[cnt++] = h; + } + ld_state.nusedsections = cnt; + + assert (cnt == ld_state.nallsections); +} + + +/* Add given path to the end of list. */ +static void +add_rxxpath (struct pathelement **pathp, const char *str) +{ + struct pathelement *newp; + + /* The path elements can in theory be freed after we read all the + files. But the amount of memory we are talking about is small + and the cost of free() calls is not neglectable. */ + newp = (struct pathelement *) obstack_alloc (&ld_state.smem, sizeof (*newp)); + newp->pname = str; + newp->exist = 0; +#ifndef NDEBUG + newp->next = NULL; +#endif + + CSNGL_LIST_ADD_REAR (*pathp, newp); +} + + +/* Convert lists of possibly colon-separated directory lists into lists + where each entry is for a single directory. */ +static void +normalize_dirlist (struct pathelement **pathp) +{ + struct pathelement *firstp = *pathp; + + do + { + const char *pname = (*pathp)->pname; + const char *colonp = strchrnul (pname, ':'); + + if (colonp != NULL) + { + struct pathelement *lastp = *pathp; + struct pathelement *newp; + + while (1) + { + if (colonp == pname) + lastp->pname = "."; + else + lastp->pname = obstack_strndup (&ld_state.smem, pname, + colonp - pname); + + if (*colonp == '\0') + break; + pname = colonp + 1; + + newp = (struct pathelement *) obstack_alloc (&ld_state.smem, + sizeof (*newp)); + newp->next = lastp->next; + newp->exist = 0; + lastp = lastp->next = newp; + + colonp = strchrnul (pname, ':'); + } + + pathp = &lastp->next; + } + else + pathp = &(*pathp)->next; + } + while (*pathp != firstp); +} + + +/* Called after all parameters are parsed to bring the runpath/rpath + information into a usable form. */ +static void +gen_rxxpath_data (void) +{ + char *ld_library_path2; + + /* Convert the information in true single-linked lists for easy use. + At this point we also discard the rpath information if runpath + information is provided. rpath is deprecated and should not be + used (or ever be invented for that matter). */ + if (ld_state.rpath != NULL) + { + struct pathelement *endp = ld_state.rpath; + ld_state.rpath = ld_state.rpath->next; + endp->next = NULL; + } + if (ld_state.rpath_link != NULL) + { + struct pathelement *endp = ld_state.rpath_link; + ld_state.rpath_link = ld_state.rpath_link->next; + endp->next = NULL; + } + + if (ld_state.runpath != NULL) + { + struct pathelement *endp = ld_state.runpath; + ld_state.runpath = ld_state.runpath->next; + endp->next = NULL; + + /* If rpath information is also available discard it. + XXX Should there be a possibility to avoid this? */ + while (ld_state.rpath != NULL) + { + struct pathelement *old = ld_state.rpath; + ld_state.rpath = ld_state.rpath->next; + free (old); + } + } + if (ld_state.runpath_link != NULL) + { + struct pathelement *endp = ld_state.runpath_link; + ld_state.runpath_link = ld_state.runpath_link->next; + endp->next = NULL; + + /* If rpath information is also available discard it. + XXX Should there be a possibility to avoid this? */ + while (ld_state.rpath_link != NULL) + { + struct pathelement *old = ld_state.rpath_link; + ld_state.rpath_link = ld_state.rpath_link->next; + free (old); + } + + /* The information in the strings in the list can actually be + directory lists themselves, with entries separated by colons. + Convert the list now to a list with one list entry for each + directory. */ + normalize_dirlist (&ld_state.runpath_link); + } + else if (ld_state.rpath_link != NULL) + /* Same as for the runpath_link above. */ + normalize_dirlist (&ld_state.rpath_link); + + + /* As a related task, handle the LD_LIBRARY_PATH value here. First + we have to possibly split the value found (if it contains a + semicolon). Then we have to split the value in list of + directories, i.e., split at the colons. */ + if (ld_library_path1 != NULL) + { + ld_library_path2 = strchr (ld_library_path1, ';'); + if (ld_library_path2 == NULL) + { + /* If no semicolon is present the directories are looked at + after the -L parameters (-> ld_library_path2). */ + ld_library_path2 = ld_library_path1; + ld_library_path1 = NULL; + } + else + { + /* NUL terminate the first part. */ + *ld_library_path2++ = '\0'; + + /* Convert the string value in a list. */ + add_rxxpath (&ld_state.ld_library_path1, ld_library_path1); + normalize_dirlist (&ld_state.ld_library_path1); + } + + add_rxxpath (&ld_state.ld_library_path2, ld_library_path2); + normalize_dirlist (&ld_state.ld_library_path2); + } +} + + +static void +read_version_script (const char *fname) +{ + /* Open the file. The name is supposed to be the complete (relative + or absolute) path. No search along a path will be performed. */ + ldin = fopen (fname, "r"); + if (ldin == NULL) + error (EXIT_FAILURE, errno, gettext ("cannot read version script \"%s\""), + fname); + /* No need for locking. */ + __fsetlocking (ldin, FSETLOCKING_BYCALLER); + + /* Tell the parser that this is a version script. */ + ld_scan_version_script = 1; + + ldlineno = 1; + ldin_fname = fname; + if (ldparse () != 0) + /* Something went wrong during parsing. */ + exit (EXIT_FAILURE); + + fclose (ldin); +} + + +static void +create_lscript_symbols (void) +{ + /* Walk through the data from the linker script and generate all the + symbols which are required to be present and and those marked + with PROVIDE if there is a undefined reference. */ + if (ld_state.output_segments == NULL) + return; + + struct output_segment *segment = ld_state.output_segments->next; + do + { + struct output_rule *orule; + + for (orule = segment->output_rules; orule != NULL; orule = orule->next) + if (orule->tag == output_assignment + /* The assignments to "." (i.e., the PC) have to be + ignored here. */ + && strcmp (orule->val.assignment->variable, ".") != 0) + { + struct symbol *s = ld_state.unresolved; + + /* Check whether the symbol is needed. */ + if (likely (s != NULL)) + { + struct symbol *first = s; + const char *providename = orule->val.assignment->variable; + + /* Determine whether the provided symbol is still + undefined. */ + // XXX TODO Loop inside a loop. Gag! Must rewrite. */ + do + if (strcmp (s->name, providename) == 0) + { + /* Not defined but referenced. */ + if (unlikely (!s->defined)) + { + /* Put on the list of symbols. First remove it from + whatever list it currently is on. */ + CDBL_LIST_DEL (ld_state.unresolved, s); + --ld_state.nunresolved; + goto use_it; + } + + if (unlikely (!orule->val.assignment->provide_flag)) + { + /* The symbol is already defined and now again + in the linker script. This is an error. */ + error (0, 0, gettext ("\ +duplicate definition of '%s' in linker script"), + providename); + goto next_rule; + } + } + while ((s = s->next) != first); + } + + /* If the symbol only has to be provided if it is needed, + ignore it here since it is not undefined. */ + if (orule->val.assignment->provide_flag) + continue; + + /* Allocate memory for this new symbol. */ + s = (struct symbol *) + obstack_calloc (&ld_state.smem, sizeof (struct symbol)); + + /* Initialize it. */ + s->name = orule->val.assignment->variable; + + /* Insert it into the symbol hash table. */ + unsigned long int hval = elf_hash (s->name); + if (unlikely (ld_symbol_tab_insert (&ld_state.symbol_tab, + hval, s) != 0)) + { + /* This means the symbol is defined somewhere else. + Maybe it comes from a DSO or so. Get the + definition. */ + free (s); + struct symbol *old = ld_symbol_tab_find (&ld_state.symbol_tab, + hval, s); + assert (old != NULL); + free (s); + + /* If this is a definition from the application itself + this means a duplicate definition. */ + if (! old->in_dso) + { + error (0, 0, gettext ("\ +duplicate definition of '%s' in linker script"), + s->name); + goto next_rule; + } + + /* We use the definition from the linker script. */ + s = old; + } + + use_it: + /* The symbol is (now) defined. */ + s->defined = 1; + s->type = STT_NOTYPE; + + /* Add a reference to the symbol record. We will come + across it when creating the output file. */ + orule->val.assignment->sym = s; + + SNGL_LIST_PUSH (ld_state.lscript_syms, s); + ++ld_state.nlscript_syms; + + next_rule: + ; + } + + segment = segment->next; + } + while (segment != ld_state.output_segments->next); +} + + +/* Create creation of spection section symbols representing sections in the + output file. This is done for symbols like _GLOBAL_OFFSET_TABLE_ and + _DYNAMIC. */ +static void +create_special_section_symbol (struct symbol **symp, const char *name) +{ + if (*symp == NULL) + { + /* No symbol defined found yet. Create one. */ + struct symbol *newsym = (struct symbol *) + obstack_calloc (&ld_state.smem, sizeof (*newsym)); + + newsym->name = name; + // XXX Should we mark the symbol hidden? They are hardly useful + // used outside the current object. + + /* Add to the symbol table. */ + if (unlikely (ld_symbol_tab_insert (&ld_state.symbol_tab, + elf_hash (name), newsym) != 0)) + abort (); + + *symp = newsym; + } + else if ((*symp)->defined) + /* Cannot happen. We do use this symbol from any input file. */ + abort (); + + (*symp)->defined = 1; + (*symp)->type = STT_OBJECT; + + ++ld_state.nsymtab; +} diff --git a/src/ld.h b/src/ld.h new file mode 100644 index 00000000..a463a188 --- /dev/null +++ b/src/ld.h @@ -0,0 +1,1074 @@ +/* Copyright (C) 2001, 2002, 2003, 2005 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2001. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifndef LD_H +#define LD_H 1 + +#include <dlfcn.h> +#include <obstack.h> +#include <stdbool.h> +#include <stdio.h> +#include "xelf.h" + + +/* Recommended size of the buffer passed to ld_strerror. */ +#define ERRBUFSIZE (512) + +/* Character used to introduce version name after symbol. */ +#define VER_CHR '@' + + +/* Methods for handling archives. */ +enum extract_rule + { + defaultextract, /* Weak references don't cause archive member to + be used. */ + weakextract, /* Weak references cause archive member to be + extracted. */ + allextract /* Extract all archive members regardless of + references (aka whole-archive). */ + }; + + +/* Type of output file. */ +enum file_type + { + no_file_type = 0, /* None selected so far. */ + executable_file_type, /* Executable. */ + dso_file_type, /* DSO. */ + dso_needed_file_type, /* DSO introduced by DT_NEEDED. */ + relocatable_file_type, /* Relocatable object file. */ + archive_file_type /* Archive (input only). */ + }; + + +struct usedfiles +{ + /* The next file given at the command line. */ + struct usedfiles *next; + /* Nonzero if this file is the beginning of a group. */ + bool group_start; + /* Nonzero if this file is the end of a group. */ + bool group_end; + /* Pointer to the beginning of the group. It is necessary to + explain why we cannot simply use the 'next' pointer and have a + circular single-linked list like in many cases. The problem is + that the last archive of the group, if it is the last file of the + group, contains the only existing pointer to the next file we + have to look at. All files are initially connected via the + 'next' pointer in a single-linked list. Therefore we cannot + overwrite this value. It instead will be used once the group is + handled and we go on processing the rest of the files. */ + struct usedfiles *group_backref; + + /* Name/path of the file. */ + const char *fname; + /* Resolved file name. */ + const char *rfname; + /* Name used as reference in DT_NEEDED entries. This is normally + the SONAME. If it is missing it's normally the fname above. */ + const char *soname; + /* Handle for the SONAME in the string table. */ + struct Ebl_Strent *sonameent; + + /* Help to identify duplicates. */ + dev_t dev; + ino_t ino; + + enum + { + not_opened, + opened, + in_archive, + closed + } status; + + /* How to extract elements from archives. */ + enum extract_rule extract_rule; + + /* Lazy-loading rule. */ + bool lazyload; + + /* If this is a DSO the flag indicates whether the file is directly + used in a reference. */ + bool used; + + /* If nonzero this is the archive sequence number which can be used to + determine whether back refernces from -( -) or GROUP statements + have to be followed. */ + int archive_seq; + + /* Pointer to the record for the archive containing this file. */ + struct usedfiles *archive_file; + + /* Type of file. We have to distinguish these types since they + are searched for differently. */ + enum file_type file_type; + /* This is the ELF library handle for this file. */ + Elf *elf; + + /* The ELF header. */ +#if NATIVE_ELF != 0 + XElf_Ehdr *ehdr; +# define FILEINFO_EHDR(fi) (*(fi)) +#else + XElf_Ehdr ehdr; +# define FILEINFO_EHDR(fi) (fi) +#endif + + /* Index of the section header string table section. We use a + separate field and not the e_shstrndx field in the ELF header + since in case of a file with more than 64000 sections the index + might be stored in the section header of section zero. The + elf_getshstrndx() function can find the value but it is too + costly to repeat this call over and over. */ + size_t shstrndx; + + /* Info about the sections of the file. */ + struct scninfo + { + /* Handle for the section. Note that we can store a section + handle here because the file is not changing. This together + with the knowledge about the libelf library is enough for us to + assume the section reference remains valid at all times. */ + Elf_Scn *scn; + /* Section header. */ +#if NATIVE_ELF != 0 + XElf_Shdr *shdr; +# define SCNINFO_SHDR(si) (*(si)) +#else + XElf_Shdr shdr; +# define SCNINFO_SHDR(si) (si) +#endif + /* Offset of this files section in the combined section. */ + XElf_Off offset; + /* Index of the section in the output file. */ + Elf32_Word outscnndx; + /* Index of the output section in the 'allsection' array. */ + Elf32_Word allsectionsidx; + /* True if the section is used. */ + bool used; + /* Section group number. This is the index of the SHT_GROUP section. */ + Elf32_Word grpid; + /* Pointer back to the containing file information structure. */ + struct usedfiles *fileinfo; + /* List of symbols in this section (set only for merge-able sections). */ + struct symbol *symbols; + /* Size of relocations in this section. Only used for relocation + sections. */ + size_t relsize; + /* Pointer to next section which is put in the given output + section. */ + struct scninfo *next; + } *scninfo; + + /* List of section group sections. */ + struct scninfo *groups; + + /* The symbol table section. + + XXX Maybe support for more than one symbol table is needed. */ + Elf_Data *symtabdata; + /* Extra section index table section. */ + Elf_Data *xndxdata; + /* Dynamic symbol table section. */ + Elf_Data *dynsymtabdata; + /* The version number section. */ + Elf_Data *versymdata; + /* The defined versions. */ + Elf_Data *verdefdata; + /* Number of versions defined. */ + size_t nverdef; + /* True if the version with the given index number is used in the + output. */ + XElf_Versym *verdefused; + /* How many versions are used. */ + size_t nverdefused; + /* Handle for name of the version. */ + struct Ebl_Strent **verdefent; + /* The needed versions. */ + Elf_Data *verneeddata; + /* String table section associated with the symbol table. */ + Elf32_Word symstridx; + /* String table section associated with the dynamic symbol table. */ + Elf32_Word dynsymstridx; + /* Number of entries in the symbol table. */ + size_t nsymtab; + size_t nlocalsymbols; + size_t ndynsymtab; + /* Dynamic section. */ + Elf_Scn *dynscn; + + /* Indirection table for the symbols defined here. */ + Elf32_Word *symindirect; + Elf32_Word *dynsymindirect; + /* For undefined or common symbols we need a reference to the symbol + record. */ + struct symbol **symref; + struct symbol **dynsymref; + + /* This is the file descriptor. The value is -1 if the descriptor + was already closed. This can happen if we needed file descriptors + to open new files. */ + int fd; + /* This flag is true if the descriptor was passed to the generic + functions from somewhere else. This is an implementation detail; + no machine-specific code must use this flag. */ + bool fd_passed; + + /* True if any of the sections is merge-able. */ + bool has_merge_sections; +}; + + +/* Functions to test for the various types of files we handle. */ +static inline int +ld_file_rel_p (struct usedfiles *file) +{ + return (elf_kind (file->elf) == ELF_K_ELF + && FILEINFO_EHDR (file->ehdr).e_type == ET_REL); +} + +static inline int +ld_file_dso_p (struct usedfiles *file) +{ + return (elf_kind (file->elf) == ELF_K_ELF + && FILEINFO_EHDR (file->ehdr).e_type == ET_DYN); +} + +static inline int +ld_file_ar_p (struct usedfiles *file) +{ + return elf_kind (file->elf) == ELF_K_AR; +} + + +struct pathelement +{ + /* The next path to search. */ + struct pathelement *next; + /* The path name. */ + const char *pname; + /* Larger than zero if the directory exists, smaller than zero if not, + zero if it is not yet known. */ + int exist; +}; + + +/* Forward declaration. */ +struct ld_state; + + +/* Callback functions. */ +struct callbacks +{ + /* Library names passed to the linker as -lXXX represent files named + libXXX.YY. The YY part can have different forms, depending on the + architecture. The generic set is .so and .a (in this order). */ + const char **(*lib_extensions) (struct ld_state *) + __attribute__ ((__const__)); +#define LIB_EXTENSION(state) \ + DL_CALL_FCT ((state)->callbacks.lib_extensions, (state)) + + /* Process the given file. If the file is not yet open, open it. + The first parameter is a file descriptor for the file which can + be -1 to indicate the file has not yet been found. The second + parameter describes the file to be opened, the last one is the + state of the linker which among other information contain the + paths we look at.*/ + int (*file_process) (int fd, struct usedfiles *, struct ld_state *, + struct usedfiles **); +#define FILE_PROCESS(fd, file, state, nextp) \ + DL_CALL_FCT ((state)->callbacks.file_process, (fd, file, state, nextp)) + + /* Close the given file. */ + int (*file_close) (struct usedfiles *, struct ld_state *); +#define FILE_CLOSE(file, state) \ + DL_CALL_FCT ((state)->callbacks.file_close, (file, state)) + + /* Create the output sections now. This requires knowledge about + all the sections we will need. It may be necessary to sort the + sections in the order they are supposed to appear in the + executable. The sorting use many different kinds of information + to optimize the resulting binary. Important is to respect + segment boundaries and the needed alignment. The mode of the + segments will be determined afterwards automatically by the + output routines. */ + void (*create_sections) (struct ld_state *); +#define CREATE_SECTIONS(state) \ + DL_CALL_FCT ((state)->callbacks.create_sections, (state)) + + /* Determine whether we have any non-weak unresolved references left. */ + int (*flag_unresolved) (struct ld_state *); +#define FLAG_UNRESOLVED(state) \ + DL_CALL_FCT ((state)->callbacks.flag_unresolved, (state)) + + /* Create the sections which are generated by the linker and are not + present in the input file. */ + void (*generate_sections) (struct ld_state *); +#define GENERATE_SECTIONS(state) \ + DL_CALL_FCT ((state)->callbacks.generate_sections, (state)) + + /* Open the output file. The file name is given or "a.out". We + create as much of the ELF structure as possible. */ + int (*open_outfile) (struct ld_state *, int, int, int); +#define OPEN_OUTFILE(state, machine, class, data) \ + DL_CALL_FCT ((state)->callbacks.open_outfile, (state, machine, class, data)) + + /* Create the data for the output file. */ + int (*create_outfile) (struct ld_state *); +#define CREATE_OUTFILE(state) \ + DL_CALL_FCT ((state)->callbacks.create_outfile, (state)) + + /* Process a relocation section. */ + void (*relocate_section) (struct ld_state *, Elf_Scn *, struct scninfo *, + const Elf32_Word *); +#define RELOCATE_SECTION(state, outscn, first, dblindirect) \ + DL_CALL_FCT ((state)->callbacks.relocate_section, (state, outscn, first, \ + dblindirect)) + + /* Allocate a data buffer for the relocations of the given output + section. */ + void (*count_relocations) (struct ld_state *, struct scninfo *); +#define COUNT_RELOCATIONS(state, scninfo) \ + DL_CALL_FCT ((state)->callbacks.count_relocations, (state, scninfo)) + + /* Create relocations for executable or DSO. */ + void (*create_relocations) (struct ld_state *, const Elf32_Word *); +#define CREATE_RELOCATIONS(state, dlbindirect) \ + DL_CALL_FCT ((state)->callbacks.create_relocations, (state, dblindirect)) + + /* Finalize the output file. */ + int (*finalize) (struct ld_state *); +#define FINALIZE(state) \ + DL_CALL_FCT ((state)->callbacks.finalize, (state)) + + /* Check whether special section number is known. */ + bool (*special_section_number_p) (struct ld_state *, size_t); +#define SPECIAL_SECTION_NUMBER_P(state, number) \ + DL_CALL_FCT ((state)->callbacks.special_section_number_p, (state, number)) + + /* Check whether section type is known. */ + bool (*section_type_p) (struct ld_state *, XElf_Word); +#define SECTION_TYPE_P(state, type) \ + DL_CALL_FCT ((state)->callbacks.section_type_p, (state, type)) + + /* Return section flags for .dynamic section. */ + XElf_Xword (*dynamic_section_flags) (struct ld_state *); +#define DYNAMIC_SECTION_FLAGS(state) \ + DL_CALL_FCT ((state)->callbacks.dynamic_section_flags, (state)) + + /* Create the data structures for the .plt section and initialize it. */ + void (*initialize_plt) (struct ld_state *, Elf_Scn *scn); +#define INITIALIZE_PLT(state, scn) \ + DL_CALL_FCT ((state)->callbacks.initialize_plt, (state, scn)) + + /* Create the data structures for the .rel.plt section and initialize it. */ + void (*initialize_pltrel) (struct ld_state *, Elf_Scn *scn); +#define INITIALIZE_PLTREL(state, scn) \ + DL_CALL_FCT ((state)->callbacks.initialize_pltrel, (state, scn)) + + /* Finalize the .plt section the what belongs to them. */ + void (*finalize_plt) (struct ld_state *, size_t, size_t); +#define FINALIZE_PLT(state, nsym, nsym_dyn) \ + DL_CALL_FCT ((state)->callbacks.finalize_plt, (state, nsym, nsym_dyn)) + + /* Create the data structures for the .got section and initialize it. */ + void (*initialize_got) (struct ld_state *, Elf_Scn *scn); +#define INITIALIZE_GOT(state, scn) \ + DL_CALL_FCT ((state)->callbacks.initialize_got, (state, scn)) + + /* Return the tag corresponding to the native relocation type for + the platform. */ + int (*rel_type) (struct ld_state *); +#define REL_TYPE(state) \ + DL_CALL_FCT ((state)->callbacks.rel_type, (state)) +}; + + +/* Structure for symbol representation. This data structure is used a + lot, so size is important. */ +struct symbol +{ + /* Symbol name. */ + const char *name; + /* Size of the object. */ + XElf_Xword size; + /* Index of the symbol in the symbol table of the object. */ + size_t symidx; + /* Index of the symbol in the symbol table of the output file. */ + size_t outsymidx; + + /* Description where the symbol is found/needed. */ + size_t scndx; + struct usedfiles *file; + /* Index of the symbol table. */ + Elf32_Word symscndx; + + /* Index of the symbol in the dynamic symbol table of the output + file. Note that the value only needs to be 16 bit wide since + there cannot be more sections in an executable or DSO. */ + unsigned int outdynsymidx:16; + + /* Type of the symbol. */ + unsigned int type:4; + /* Various flags. */ + unsigned int defined:1; + unsigned int common:1; + unsigned int weak:1; + unsigned int added:1; + unsigned int merged:1; + /* Nonzero if the symbol is on the from_dso list. */ + unsigned int on_dsolist:1; + /* Nonzero if symbol needs copy relocation, reset when the + relocation has been created. */ + unsigned int need_copy:1; + unsigned int in_dso:1; + + union + { + /* Pointer to the handle created by the functions which create + merged section contents. We use 'void *' because there are + different implementations used. */ + void *handle; + XElf_Addr value; + } merge; + + /* Pointer to next/previous symbol on whatever list the symbol is. */ + struct symbol *next; + struct symbol *previous; + /* Pointer to next symbol of the same section (only set for merge-able + sections). */ + struct symbol *next_in_scn; +}; + + +/* Get the definition for the symbol table. */ +#include <symbolhash.h> + +/* Simple single linked list of file names. */ +struct filename_list +{ + const char *name; + struct usedfiles *real; + struct filename_list *next; + bool group_start; + bool group_end; + bool as_needed; +}; + + +/* Data structure to describe expression in linker script. */ +struct expression +{ + enum expression_tag + { + exp_num, + exp_sizeof_headers, + exp_pagesize, + exp_id, + exp_mult, + exp_div, + exp_mod, + exp_plus, + exp_minus, + exp_and, + exp_or, + exp_align + } tag; + + union + { + uintmax_t num; + struct expression *child; + struct + { + struct expression *left; + struct expression *right; + } binary; + const char *str; + } val; +}; + + +/* Data structure for section name with flags. */ +struct input_section_name +{ + const char *name; + bool sort_flag; +}; + +/* File name mask with section name. */ +struct filemask_section_name +{ + const char *filemask; + const char *excludemask; + struct input_section_name *section_name; + bool keep_flag; +}; + +/* Data structure for assignments. */ +struct assignment +{ + const char *variable; + struct expression *expression; + struct symbol *sym; + bool provide_flag; +}; + + +/* Data structure describing input for an output section. */ +struct input_rule +{ + enum + { + input_section, + input_assignment + } tag; + + union + { + struct assignment *assignment; + struct filemask_section_name *section; + } val; + + struct input_rule *next; +}; + + +/* Data structure to describe output section. */ +struct output_section +{ + const char *name; + struct input_rule *input; + XElf_Addr max_alignment; + bool ignored; +}; + + +/* Data structure to describe output file format. */ +struct output_rule +{ + enum + { + output_section, + output_assignment + } tag; + + union + { + struct assignment *assignment; + struct output_section section; + } val; + + struct output_rule *next; +}; + + +/* List of all the segments the linker script describes. */ +struct output_segment +{ + int mode; + struct output_rule *output_rules; + struct output_segment *next; + + XElf_Off offset; + XElf_Addr addr; + XElf_Xword align; +}; + + +/* List of identifiers. */ +struct id_list +{ + union + { + enum id_type + { + id_str, /* Normal string. */ + id_all, /* "*", matches all. */ + id_wild /* Globbing wildcard string. */ + } id_type; + struct + { + bool local; + const char *versionname; + } s; + } u; + const char *id; + struct id_list *next; +}; + + +/* Version information. */ +struct version +{ + struct version *next; + struct id_list *local_names; + struct id_list *global_names; + const char *versionname; + const char *parentname; +}; + + +/* Head for list of sections. */ +struct scnhead +{ + /* Name of the sections. */ + const char *name; + + /* Accumulated flags for the sections. */ + XElf_Xword flags; + + /* Type of the sections. */ + XElf_Word type; + + /* Entry size. If there are differencs between the sections with + the same name this field contains 1. */ + XElf_Word entsize; + + /* If non-NULL pointer to group signature. */ + const char *grp_signature; + + /* Maximum alignment for all sections. */ + XElf_Word align; + + /* Distinguish between normal sections coming from the input file + and sections generated by the linker. */ + enum scn_kind + { + scn_normal, /* Section from the input file(s). */ + scn_dot_interp, /* Generated .interp section. */ + scn_dot_got, /* Generated .got section. */ + scn_dot_dynrel, /* Generated .rel.dyn section. */ + scn_dot_dynamic, /* Generated .dynamic section. */ + scn_dot_dynsym, /* Generated .dynsym section. */ + scn_dot_dynstr, /* Generated .dynstr section. */ + scn_dot_hash, /* Generated .hash section. */ + scn_dot_plt, /* Generated .plt section. */ + scn_dot_pltrel, /* Generated .rel.plt section. */ + scn_dot_version, /* Generated .gnu.version section. */ + scn_dot_version_r /* Generated .gnu.version_r section. */ + } kind; + + /* True is the section is used in the output. */ + bool used; + + /* Total size (only determined this way for relocation sections). */ + size_t relsize; + + /* Filled in by the section sorting to indicate which segment the + section goes in. */ + int segment_nr; + + /* Index of the output section. We cannot store the section handle + directly here since the handle is a pointer in a dynamically + allocated table which might move if it becomes too small for all + the sections. Using the index the correct value can be found at + all times. */ + XElf_Word scnidx; + + /* Index of the STT_SECTION entry for this section in the symbol + table. */ + XElf_Word scnsymidx; + + /* Address of the section in the output file. */ + XElf_Addr addr; + + /* Handle for the section name in the output file's section header + string table. */ + struct Ebl_Strent *nameent; + + /* Tail of list of symbols for this section. Only set if the + section is merge-able. */ + struct symbol *symbols; + + /* Pointer to last section. */ + struct scninfo *last; +}; + + +/* Define hash table for sections. */ +#include <sectionhash.h> + +/* Define hash table for version symbols. */ +#include <versionhash.h> + + +/* State of the linker. */ +struct ld_state +{ + /* ELF backend library handle. */ + Ebl *ebl; + + /* List of all archives participating, in this order. */ + struct usedfiles *archives; + /* End of the list. */ + struct usedfiles *tailarchives; + /* If nonzero we are looking for the beginning of a group. */ + bool group_start_requested; + /* Pointer to the archive starting the group. */ + struct usedfiles *group_start_archive; + + /* List of the DSOs we found. */ + struct usedfiles *dsofiles; + /* Number of DSO files. */ + size_t ndsofiles; + /* Ultimate list of object files which are linked in. */ + struct usedfiles *relfiles; + + /* List the DT_NEEDED DSOs. */ + struct usedfiles *needed; + + /* Temporary storage for the parser. */ + struct filename_list *srcfiles; + + /* List of all the paths to look at. */ + struct pathelement *paths; + /* Tail of the list. */ + struct pathelement *tailpaths; + + /* User provided paths for lookup of DSOs. */ + struct pathelement *rpath; + struct pathelement *rpath_link; + struct pathelement *runpath; + struct pathelement *runpath_link; + struct Ebl_Strent *rxxpath_strent; + int rxxpath_tag; + + /* From the environment variable LD_LIBRARY_PATH. */ + struct pathelement *ld_library_path1; + struct pathelement *ld_library_path2; + + /* Name of the output file. */ + const char *outfname; + /* Name of the temporary file we initially create. */ + const char *tempfname; + /* File descriptor opened for the output file. */ + int outfd; + /* The ELF descriptor for the output file. */ + Elf *outelf; + + /* Type of output file. */ + enum file_type file_type; + + /* Is this a system library or not. */ + bool is_system_library; + + /* Page size to be assumed for the binary. */ + size_t pagesize; + + /* Name of the interpreter for dynamically linked objects. */ + const char *interp; + /* Index of the .interp section. */ + Elf32_Word interpscnidx; + + /* Optimization level. */ + unsigned long int optlevel; + + /* If true static linking is requested. */ + bool statically; + + /* How to extract elements from archives. */ + enum extract_rule extract_rule; + + /* Sequence number of the last archive we used. */ + int last_archive_used; + + /* If true print to stdout information about the files we are + trying to open. */ + bool trace_files; + + /* If true multiple definitions are not considered an error; the + first is used. */ + bool muldefs; + + /* If true undefined symbols when building DSOs are not fatal. */ + bool nodefs; + + /* If true add line indentifying link-editor to .comment section. */ + bool add_ld_comment; + + /* Stripping while linking. */ + enum + { + strip_none, + strip_debug, + strip_all, + strip_everything + } strip; + + /* The callback function vector. */ + struct callbacks callbacks; + + /* Name of the entry symbol. Can also be a numeric value. */ + const char *entry; + + /* The description of the segments in the output file. */ + struct output_segment *output_segments; + + /* List of the symbols we created from linker script definitions. */ + struct symbol *lscript_syms; + size_t nlscript_syms; + + /* Table with known symbols. */ + ld_symbol_tab symbol_tab; + + /* Table with used sections. */ + ld_section_tab section_tab; + + /* The list of sections once we collected them. */ + struct scnhead **allsections; + size_t nallsections; + size_t nusedsections; + size_t nnotesections; + + /* Beginning of the list of symbols which are still unresolved. */ + struct symbol *unresolved; + /* Number of truely unresolved entries in the list. */ + size_t nunresolved; + /* Number of truely unresolved, non-weak entries in the list. */ + size_t nunresolved_nonweak; + + /* List of common symbols. */ + struct symbol *common_syms; + /* Section for the common symbols. */ + struct scninfo *common_section; + + /* List of symbols defined in DSOs and used in a relocatable file. + DSO symbols not referenced in the relocatable files are not on + the list. If a symbol is on the list the on_dsolist field in the + 'struct symbol' is nonzero. */ + struct symbol *from_dso; + /* Number of entries in from_dso. */ + size_t nfrom_dso; + /* Number of entries in the dynamic symbol table. */ + size_t ndynsym; + /* Number of PLT entries from DSO references. */ + size_t nplt; + /* Number of PLT entries from DSO references. */ + size_t ngot; + /* Number of copy relocations. */ + size_t ncopy; + /* Section for copy relocations. */ + struct scninfo *copy_section; + + /* Keeping track of the number of symbols in the output file. */ + size_t nsymtab; + size_t nlocalsymbols; + + /* Special symbols. */ + struct symbol *init_symbol; + struct symbol *fini_symbol; + + /* The description of the segments in the output file as described + in the default linker script. This information will be used in + addition to the user-provided information. */ + struct output_segment *default_output_segments; + /* Search paths added by the default linker script. */ + struct pathelement *default_paths; + +#ifndef BASE_ELF_NAME + /* The handle of the ld backend library. */ + void *ldlib; +#endif + + /* String table for the section headers. */ + struct Ebl_Strtab *shstrtab; + + /* True if output file should contain symbol table. */ + bool need_symtab; + /* Symbol table section. */ + Elf32_Word symscnidx; + /* Extended section table section. */ + Elf32_Word xndxscnidx; + /* Symbol string table section. */ + Elf32_Word strscnidx; + + /* True if output file should contain dynamic symbol table. */ + bool need_dynsym; + /* Dynamic symbol table section. */ + Elf32_Word dynsymscnidx; + /* Dynamic symbol string table section. */ + Elf32_Word dynstrscnidx; + /* Dynamic symbol hash table. */ + size_t hashscnidx; + + /* Procedure linkage table section. */ + Elf32_Word pltscnidx; + /* Number of entries already in the PLT section. */ + size_t nplt_used; + /* Relocation for procedure linkage table section. */ + Elf32_Word pltrelscnidx; + + /* Global offset table section. */ + Elf32_Word gotscnidx; + + /* This section will hole all non-PLT relocations. */ + Elf32_Word reldynscnidx; + + /* Index of the sections to handle versioning. */ + Elf32_Word versymscnidx; + Elf32_Word verneedscnidx; + /* XXX Should the following names be verneed...? */ + /* Number of version definitions in input DSOs used. */ + int nverdefused; + /* Number of input DSOs using versioning. */ + int nverdeffile; + /* Index of next version. */ + int nextveridx; + + /* Hash table for version symbol strings. Only strings without + special characters are hashed here. */ + ld_version_str_tab version_str_tab; + /* At most one of the following two variables is set to true if either + global or local symbol binding is selected as the default. */ + bool default_bind_local; + bool default_bind_global; + + /* True if only used sections are used. */ + bool gc_sections; + + /* Array to determine final index of symbol. */ + Elf32_Word *dblindirect; + + /* Section group handling. */ + struct scngroup + { + Elf32_Word outscnidx; + int nscns; + struct member + { + struct scnhead *scn; + struct member *next; + } *member; + struct Ebl_Strent *nameent; + struct symbol *symbol; + struct scngroup *next; + } *groups; + + /* True if the output file needs a .got section. */ + bool need_got; + /* Number of relocations for GOT section caused. */ + size_t nrel_got; + + /* Number of entries needed in the .dynamic section. */ + int ndynamic; + /* To keep track of added entries. */ + int ndynamic_filled; + /* Index for the dynamic section. */ + Elf32_Word dynamicscnidx; + + /* Flags set in the DT_FLAGS word. */ + Elf32_Word dt_flags; + /* Flags set in the DT_FLAGS_1 word. */ + Elf32_Word dt_flags_1; + /* Flags set in the DT_FEATURE_1 word. */ + Elf32_Word dt_feature_1; + + /* Lazy-loading state for dependencies. */ + bool lazyload; + + /* True is DSOs which are not used in the linking process are not + recorded. */ + bool ignore_unused_dsos; + + + /* True if in executables all global symbols should be exported in + the dynamic symbol table. */ + bool export_all_dynamic; + + /* If DSO is generated, this is the SONAME. */ + const char *soname; + + /* List of all relocation sections. */ + struct scninfo *rellist; + /* Total size of non-PLT relocations. */ + size_t relsize_total; + + /* Record for the GOT symbol, if known. */ + struct symbol *got_symbol; + /* Record for the dynamic section symbol, if known. */ + struct symbol *dyn_symbol; + + /* Obstack used for small objects which will not be deleted. */ + struct obstack smem; +}; + + +/* The interface to the scanner. */ + +/* Parser entry point. */ +extern int ldparse (void); + +/* The input file. */ +extern FILE *ldin; + +/* Name of the input file. */ +extern const char *ldin_fname; + +/* Current line number. Must be reset for a new file. */ +extern int ldlineno; + +/* If nonzero we are currently parsing a version script. */ +extern int ld_scan_version_script; + +/* Flags defined in ld.c. */ +extern int verbose; +extern int conserve_memory; + + +/* Linker state. This contains all global information. */ +extern struct ld_state ld_state; + + +/* Generic ld helper functions. */ + +/* Append a new directory to search libraries in. */ +extern void ld_new_searchdir (const char *dir); + +/* Append a new file to the list of input files. */ +extern struct usedfiles *ld_new_inputfile (const char *fname, + enum file_type type); + + +/* These are the generic implementations for the callbacks used by ld. */ + +/* Initialize state object. This callback function is called after the + parameters are parsed but before any file is searched for. */ +extern int ld_prepare_state (const char *emulation); + + +/* Function to determine whether an object will be dynamically linked. */ +extern bool dynamically_linked_p (void); + +/* Helper functions for the architecture specific code. */ + +/* Checked whether the symbol is undefined and referenced from a DSO. */ +extern bool linked_from_dso_p (struct scninfo *scninfo, size_t symidx); +extern inline bool +linked_from_dso_p (struct scninfo *scninfo, size_t symidx) +{ + struct usedfiles *file = scninfo->fileinfo; + + /* If this symbol is not undefined in this file it cannot come from + a DSO. */ + if (symidx < file->nlocalsymbols) + return false; + + struct symbol *sym = file->symref[symidx]; + + return sym->defined && sym->in_dso; +} + +#endif /* ld.h */ diff --git a/src/ldgeneric.c b/src/ldgeneric.c new file mode 100644 index 00000000..1b6c7207 --- /dev/null +++ b/src/ldgeneric.c @@ -0,0 +1,6376 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2001. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <assert.h> +#include <dlfcn.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <fnmatch.h> +#include <gelf.h> +#include <inttypes.h> +#include <libintl.h> +#include <stdbool.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include <system.h> +#include "ld.h" +#include "list.h" + + +/* Prototypes for local functions. */ +static const char **ld_generic_lib_extensions (struct ld_state *) + __attribute__ ((__const__)); +static int ld_generic_file_close (struct usedfiles *fileinfo, + struct ld_state *statep); +static int ld_generic_file_process (int fd, struct usedfiles *fileinfo, + struct ld_state *statep, + struct usedfiles **nextp); +static void ld_generic_generate_sections (struct ld_state *statep); +static void ld_generic_create_sections (struct ld_state *statep); +static int ld_generic_flag_unresolved (struct ld_state *statep); +static int ld_generic_open_outfile (struct ld_state *statep, int machine, + int class, int data); +static int ld_generic_create_outfile (struct ld_state *statep); +static void ld_generic_relocate_section (struct ld_state *statep, + Elf_Scn *outscn, + struct scninfo *firstp, + const Elf32_Word *dblindirect); +static int ld_generic_finalize (struct ld_state *statep); +static bool ld_generic_special_section_number_p (struct ld_state *statep, + size_t number); +static bool ld_generic_section_type_p (struct ld_state *statep, + XElf_Word type); +static XElf_Xword ld_generic_dynamic_section_flags (struct ld_state *statep); +static void ld_generic_initialize_plt (struct ld_state *statep, Elf_Scn *scn); +static void ld_generic_initialize_pltrel (struct ld_state *statep, + Elf_Scn *scn); +static void ld_generic_initialize_got (struct ld_state *statep, Elf_Scn *scn); +static void ld_generic_finalize_plt (struct ld_state *statep, size_t nsym, + size_t nsym_dyn); +static int ld_generic_rel_type (struct ld_state *statep); +static void ld_generic_count_relocations (struct ld_state *statep, + struct scninfo *scninfo); +static void ld_generic_create_relocations (struct ld_state *statep, + const Elf32_Word *dblindirect); + +static int file_process2 (struct usedfiles *fileinfo); +static void mark_section_used (struct scninfo *scninfo, Elf32_Word shndx, + struct scninfo **grpscnp); + + +/* Map symbol index to struct symbol record. */ +static struct symbol **ndxtosym; + +/* String table reference to all symbols in the symbol table. */ +static struct Ebl_Strent **symstrent; + + +/* Check whether file associated with FD is a DSO. */ +static bool +is_dso_p (int fd) +{ + /* We have to read the 'e_type' field. It has the same size (16 + bits) in 32- and 64-bit ELF. */ + XElf_Half e_type; + + return (pread (fd, &e_type, sizeof (e_type), offsetof (XElf_Ehdr, e_type)) + == sizeof (e_type) + && e_type == ET_DYN); +} + + +/* Print the complete name of a file, including the archive it is + contained in. */ +static int +print_file_name (FILE *s, struct usedfiles *fileinfo, int first_level, + int newline) +{ + int npar = 0; + + if (fileinfo->archive_file != NULL) + { + npar = print_file_name (s, fileinfo->archive_file, 0, 0) + 1; + fputc_unlocked ('(', s); + fputs_unlocked (fileinfo->rfname, s); + + if (first_level) + while (npar-- > 0) + fputc_unlocked (')', s); + } + else + fputs_unlocked (fileinfo->rfname, s); + + if (first_level && newline) + fputc_unlocked ('\n', s); + + return npar; +} + + +/* Function to determine whether an object will be dynamically linked. */ +bool +dynamically_linked_p (void) +{ + return (ld_state.file_type == dso_file_type || ld_state.nplt > 0 + || ld_state.ngot > 0); +} + + +bool +linked_from_dso_p (struct scninfo *scninfo, size_t symidx) +{ + struct usedfiles *file = scninfo->fileinfo; + + /* If this symbol is not undefined in this file it cannot come from + a DSO. */ + if (symidx < file->nlocalsymbols) + return false; + + struct symbol *sym = file->symref[symidx]; + + return sym->defined && sym->in_dso; +} + + +/* Initialize state object. This callback function is called after the + parameters are parsed but before any file is searched for. */ +int +ld_prepare_state (const char *emulation) +{ + /* When generating DSO we normally allow undefined symbols. */ + ld_state.nodefs = true; + + /* To be able to detect problems we add a .comment section entry by + default. */ + ld_state.add_ld_comment = true; + + /* XXX We probably should find a better place for this. The index + of the first user-defined version is 2. */ + ld_state.nextveridx = 2; + + /* Pick an not too small number for the initial size of the tables. */ + ld_symbol_tab_init (&ld_state.symbol_tab, 1027); + ld_section_tab_init (&ld_state.section_tab, 67); + ld_version_str_tab_init (&ld_state.version_str_tab, 67); + + /* Initialize the section header string table. */ + ld_state.shstrtab = ebl_strtabinit (true); + if (ld_state.shstrtab == NULL) + error (EXIT_FAILURE, errno, gettext ("cannot create string table")); + + /* Initialize the callbacks. These are the defaults, the appropriate + backend can later install its own callbacks. */ + ld_state.callbacks.lib_extensions = ld_generic_lib_extensions; + ld_state.callbacks.file_process = ld_generic_file_process; + ld_state.callbacks.file_close = ld_generic_file_close; + ld_state.callbacks.generate_sections = ld_generic_generate_sections; + ld_state.callbacks.create_sections = ld_generic_create_sections; + ld_state.callbacks.flag_unresolved = ld_generic_flag_unresolved; + ld_state.callbacks.open_outfile = ld_generic_open_outfile; + ld_state.callbacks.create_outfile = ld_generic_create_outfile; + ld_state.callbacks.relocate_section = ld_generic_relocate_section; + ld_state.callbacks.finalize = ld_generic_finalize; + ld_state.callbacks.special_section_number_p = + ld_generic_special_section_number_p; + ld_state.callbacks.section_type_p = ld_generic_section_type_p; + ld_state.callbacks.dynamic_section_flags = ld_generic_dynamic_section_flags; + ld_state.callbacks.initialize_plt = ld_generic_initialize_plt; + ld_state.callbacks.initialize_pltrel = ld_generic_initialize_pltrel; + ld_state.callbacks.initialize_got = ld_generic_initialize_got; + ld_state.callbacks.finalize_plt = ld_generic_finalize_plt; + ld_state.callbacks.rel_type = ld_generic_rel_type; + ld_state.callbacks.count_relocations = ld_generic_count_relocations; + ld_state.callbacks.create_relocations = ld_generic_create_relocations; + +#ifndef BASE_ELF_NAME + /* Find the ld backend library. Use EBL to determine the name if + the user hasn't provided one on the command line. */ + if (emulation == NULL) + { + emulation = ebl_backend_name (ld_state.ebl); + assert (emulation != NULL); + } + size_t emulation_len = strlen (emulation); + + /* Construct the file name. */ + char *fname = (char *) alloca (sizeof "libld_" - 1 + emulation_len + + sizeof ".so"); + strcpy (mempcpy (stpcpy (fname, "libld_"), emulation, emulation_len), ".so"); + + /* Try loading. */ + void *h = dlopen (fname, RTLD_LAZY); + if (h == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot load ld backend library '%s': %s"), + fname, dlerror ()); + + /* Find the initializer. It must be present. */ + char *initname = (char *) alloca (emulation_len + sizeof "_ld_init"); + strcpy (mempcpy (initname, emulation, emulation_len), "_ld_init"); + int (*initfct) (struct ld_state *) + = (int (*) (struct ld_state *)) dlsym (h, initname); + + if (initfct == NULL) + error (EXIT_FAILURE, 0, gettext ("\ +cannot find init function in ld backend library '%s': %s"), + fname, dlerror ()); + + /* Store the handle. */ + ld_state.ldlib = h; + + /* Call the init function. */ + return initfct (&ld_state); +#else +# define INIT_FCT_NAME(base) _INIT_FCT_NAME(base) +# define _INIT_FCT_NAME(base) base##_ld_init + /* Declare and call the initialization function. */ + extern int INIT_FCT_NAME(BASE_ELF_NAME) (struct ld_state *); + return INIT_FCT_NAME(BASE_ELF_NAME) (&ld_state); +#endif +} + + +static int +check_for_duplicate2 (struct usedfiles *newp, struct usedfiles *list) +{ + struct usedfiles *first; + struct usedfiles *prevp; + + if (list == NULL) + return 0; + + prevp = list; + list = first = list->next; + do + { + /* When searching the needed list we might come across entries + for files which are not yet opened. Stop then, there is + nothing more to test. */ + if (likely (list->status == not_opened)) + break; + + if (unlikely (list->ino == newp->ino) + && unlikely (list->dev == newp->dev)) + { + close (newp->fd); + newp->fd = -1; + newp->status = closed; + if (newp->file_type == relocatable_file_type) + error (0, 0, gettext ("%s listed more than once as input"), + newp->rfname); + + return 1; + } + list = list->next; + } + while (likely (list != first)); + + return 0; +} + + +static int +check_for_duplicate (struct usedfiles *newp) +{ + struct stat st; + + if (unlikely (fstat (newp->fd, &st) < 0)) + { + close (newp->fd); + return errno; + } + + newp->dev = st.st_dev; + newp->ino = st.st_ino; + + return (check_for_duplicate2 (newp, ld_state.relfiles) + || check_for_duplicate2 (newp, ld_state.dsofiles) + || check_for_duplicate2 (newp, ld_state.needed)); +} + + +/* Find a file along the path described in the state. */ +static int +open_along_path2 (struct usedfiles *fileinfo, struct pathelement *path) +{ + const char *fname = fileinfo->fname; + size_t fnamelen = strlen (fname); + int err = ENOENT; + struct pathelement *firstp = path; + + if (path == NULL) + /* Cannot find anything since we have no path. */ + return ENOENT; + + do + { + if (likely (path->exist >= 0)) + { + /* Create the file name. */ + char *rfname = NULL; + size_t dirlen = strlen (path->pname); + int fd = -1; + + if (fileinfo->file_type == archive_file_type) + { + const char **exts = (ld_state.statically + ? (const char *[2]) { ".a", NULL } + : LIB_EXTENSION (&ld_state)); + + /* We have to create the actual file name. We prepend "lib" + and add one of the extensions the platform has. */ + while (*exts != NULL) + { + size_t extlen = strlen (*exts); + rfname = (char *) alloca (dirlen + 5 + fnamelen + extlen); + memcpy (mempcpy (stpcpy (mempcpy (rfname, path->pname, + dirlen), + "/lib"), + fname, fnamelen), + *exts, extlen + 1); + + fd = open (rfname, O_RDONLY); + if (likely (fd != -1) || errno != ENOENT) + { + err = fd == -1 ? errno : 0; + break; + } + + /* Next extension. */ + ++exts; + } + } + else + { + assert (fileinfo->file_type == dso_file_type + || fileinfo->file_type == dso_needed_file_type); + + rfname = (char *) alloca (dirlen + 1 + fnamelen + 1); + memcpy (stpcpy (mempcpy (rfname, path->pname, dirlen), "/"), + fname, fnamelen + 1); + + fd = open (rfname, O_RDONLY); + if (unlikely (fd == -1)) + err = errno; + } + + if (likely (fd != -1)) + { + /* We found the file. This also means the directory + exists. */ + fileinfo->fd = fd; + path->exist = 1; + + /* Check whether we have this file already loaded. */ + if (unlikely (check_for_duplicate (fileinfo) != 0)) + return EAGAIN; + + /* Make a copy of the name. */ + fileinfo->rfname = obstack_strdup (&ld_state.smem, rfname); + + if (unlikely (ld_state.trace_files)) + printf (fileinfo->file_type == archive_file_type + ? gettext ("%s (for -l%s)\n") + : gettext ("%s (for DT_NEEDED %s)\n"), + rfname, fname); + + return 0; + } + + /* The file does not exist. Maybe the whole directory doesn't. + Check it unless we know it exists. */ + if (unlikely (path->exist == 0)) + { + struct stat st; + + /* Keep only the directory name. Note that the path + might be relative. This doesn't matter here. We do + the test in any case even if there is the chance that + somebody wants to change the programs working + directory at some point which would make the result + of this test void. Since changing the working + directory is completely wrong we are not taking this + case into account. */ + rfname[dirlen] = '\0'; + if (unlikely (stat (rfname, &st) < 0) || ! S_ISDIR (st.st_mode)) + /* The directory does not exist or the named file is no + directory. */ + path->exist = -1; + else + path->exist = 1; + } + } + + /* Next path element. */ + path = path->next; + } + while (likely (err == ENOENT && path != firstp)); + + return err; +} + + +static int +open_along_path (struct usedfiles *fileinfo) +{ + const char *fname = fileinfo->fname; + int err = ENOENT; + + if (fileinfo->file_type == relocatable_file_type) + { + /* Only libraries are searched along the path. */ + fileinfo->fd = open (fname, O_RDONLY); + + if (likely (fileinfo->fd != -1)) + { + /* We found the file. */ + if (unlikely (ld_state.trace_files)) + print_file_name (stdout, fileinfo, 1, 1); + + return check_for_duplicate (fileinfo); + } + + /* If the name is an absolute path we are done. */ + err = errno; + } + else + { + /* If the user specified two parts to the LD_LIBRARY_PATH variable + try the first part now. */ + err = open_along_path2 (fileinfo, ld_state.ld_library_path1); + + /* Try the user-specified path next. */ + if (err == ENOENT) + err = open_along_path2 (fileinfo, + fileinfo->file_type == archive_file_type + ? ld_state.paths : ld_state.rpath_link); + + /* Then the second part of the LD_LIBRARY_PATH value. */ + if (unlikely (err == ENOENT)) + { + err = open_along_path2 (fileinfo, ld_state.ld_library_path2); + + /* In case we look for a DSO handle now the RUNPATH. */ + if (err == ENOENT) + { + if (fileinfo->file_type == dso_file_type) + err = open_along_path2 (fileinfo, ld_state.runpath_link); + + /* Finally the path from the default linker script. */ + if (err == ENOENT) + err = open_along_path2 (fileinfo, ld_state.default_paths); + } + } + } + + if (unlikely (err != 0) + && (err != EAGAIN || fileinfo->file_type == relocatable_file_type)) + error (0, err, gettext ("cannot open %s"), fileinfo->fname); + + return err; +} + + +static void +check_type_and_size (const XElf_Sym *sym, struct usedfiles *fileinfo, + struct symbol *oldp) +{ + /* We check the type and size of the symbols. In both cases the + information can be missing (size is zero, type is STT_NOTYPE) in + which case we issue no warnings. Otherwise everything must + match. If the type does not match there is no point in checking + the size. */ + + if (XELF_ST_TYPE (sym->st_info) != STT_NOTYPE && oldp->type != STT_NOTYPE + && unlikely (oldp->type != XELF_ST_TYPE (sym->st_info))) + { + char buf1[64]; + char buf2[64]; + + error (0, 0, gettext ("\ +Warning: type of `%s' changed from %s in %s to %s in %s"), + oldp->name, + ebl_symbol_type_name (ld_state.ebl, oldp->type, + buf1, sizeof (buf1)), + oldp->file->rfname, + ebl_symbol_type_name (ld_state.ebl, XELF_ST_TYPE (sym->st_info), + buf2, sizeof (buf2)), + fileinfo->rfname); + } + else if (XELF_ST_TYPE (sym->st_info) == STT_OBJECT + && oldp->size != 0 + && unlikely (oldp->size != sym->st_size)) + error (0, 0, gettext ("\ +Warning: size of `%s' changed from %" PRIu64 " in %s to %" PRIu64 " in %s"), + oldp->name, (uint64_t) oldp->size, oldp->file->rfname, + (uint64_t) sym->st_size, fileinfo->rfname); +} + + +static int +check_definition (const XElf_Sym *sym, size_t symidx, + struct usedfiles *fileinfo, struct symbol *oldp) +{ + int result = 0; + bool old_in_dso = FILEINFO_EHDR (oldp->file->ehdr).e_type == ET_DYN; + bool new_in_dso = FILEINFO_EHDR (fileinfo->ehdr).e_type == ET_DYN; + bool use_new_def = false; + + if (sym->st_shndx != SHN_UNDEF + && (! oldp->defined + || (sym->st_shndx != SHN_COMMON && oldp->common && ! new_in_dso) + || (old_in_dso && ! new_in_dso))) + { + /* We found a definition for a previously undefined symbol or a + real definition for a previous common-only definition or a + redefinition of a symbol definition in an object file + previously defined in a DSO. First perform some tests which + will show whether the common is really matching the + definition. */ + check_type_and_size (sym, fileinfo, oldp); + + /* We leave the next element intact to not interrupt the list + with the unresolved symbols. Whoever walks the list will + have to check the `defined' flag. But we remember that this + list element is not unresolved anymore. */ + if (! oldp->defined) + { + /* Remove from the list. */ + --ld_state.nunresolved; + if (! oldp->weak) + --ld_state.nunresolved_nonweak; + CDBL_LIST_DEL (ld_state.unresolved, oldp); + } + else if (oldp->common) + /* Remove from the list. */ + CDBL_LIST_DEL (ld_state.common_syms, oldp); + + /* Use the values of the definition from now on. */ + use_new_def = true; + } + else if (sym->st_shndx != SHN_UNDEF + && unlikely (! oldp->common) + && oldp->defined + && sym->st_shndx != SHN_COMMON + /* Multiple definitions are no fatal errors if the -z muldefs flag + is used. We don't warn about the multiple definition unless we + are told to be verbose. */ + && (!ld_state.muldefs || verbose) + && ! old_in_dso && fileinfo->file_type == relocatable_file_type) + { + /* We have a double definition. This is a problem. */ + char buf[64]; + XElf_Sym_vardef (oldsym); + struct usedfiles *oldfile; + const char *scnname; + Elf32_Word xndx; + size_t shndx; + size_t shnum; + + if (elf_getshnum (fileinfo->elf, &shnum) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot determine number of sections: %s"), + elf_errmsg (-1)); + + /* XXX Use only ebl_section_name. */ + if (sym->st_shndx < SHN_LORESERVE // || sym->st_shndx > SHN_HIRESERVE + && sym->st_shndx < shnum) + scnname = elf_strptr (fileinfo->elf, + fileinfo->shstrndx, + SCNINFO_SHDR (fileinfo->scninfo[sym->st_shndx].shdr).sh_name); + else + // XXX extended section + scnname = ebl_section_name (ld_state.ebl, sym->st_shndx, 0, + buf, sizeof (buf), NULL, shnum); + + /* XXX Print source file and line number. */ + print_file_name (stderr, fileinfo, 1, 0); + fprintf (stderr, + gettext ("(%s+%#" PRIx64 "): multiple definition of %s `%s'\n"), + scnname, + (uint64_t) sym->st_value, + ebl_symbol_type_name (ld_state.ebl, XELF_ST_TYPE (sym->st_info), + buf, sizeof (buf)), + oldp->name); + + oldfile = oldp->file; + xelf_getsymshndx (oldfile->symtabdata, oldfile->xndxdata, oldp->symidx, + oldsym, xndx); + if (oldsym == NULL) + /* This should never happen since the same call + succeeded before. */ + abort (); + + shndx = oldsym->st_shndx; + if (unlikely (oldsym->st_shndx == SHN_XINDEX)) + shndx = xndx; + + /* XXX Use only ebl_section_name. */ + if (shndx < SHN_LORESERVE || shndx > SHN_HIRESERVE) + scnname = elf_strptr (oldfile->elf, + oldfile->shstrndx, + SCNINFO_SHDR (oldfile->scninfo[shndx].shdr).sh_name); + else + scnname = ebl_section_name (ld_state.ebl, oldsym->st_shndx, shndx, buf, + sizeof (buf), NULL, shnum); + + /* XXX Print source file and line number. */ + print_file_name (stderr, oldfile, 1, 0); + fprintf (stderr, gettext ("(%s+%#" PRIx64 "): first defined here\n"), + scnname, (uint64_t) oldsym->st_value); + + if (likely (!ld_state.muldefs)) + result = 1; + } + else if (old_in_dso && fileinfo->file_type == relocatable_file_type + && sym->st_shndx != SHN_UNDEF) + /* We use the definition from a normal relocatable file over the + definition in a DSO. This is what the dynamic linker would + do, too. */ + use_new_def = true; + else if (old_in_dso && !new_in_dso && oldp->defined && !oldp->on_dsolist) + { + CDBL_LIST_ADD_REAR (ld_state.from_dso, oldp); + ++ld_state.nfrom_dso; + + /* If the object is a function we allocate a PLT entry, + otherwise only a GOT entry. */ + if (oldp->type == STT_FUNC) + ++ld_state.nplt; + else + ++ld_state.ngot; + + oldp->on_dsolist = 1; + } + else if (oldp->common && sym->st_shndx == SHN_COMMON) + { + /* The symbol size is the largest of all common definitions. */ + oldp->size = MAX (oldp->size, sym->st_size); + /* Similarly for the alignment. */ + oldp->merge.value = MAX (oldp->merge.value, sym->st_value); + } + + if (unlikely (use_new_def)) + { + /* Adjust the symbol record appropriately and remove + the symbol from the list of symbols which are taken from DSOs. */ + if (old_in_dso && fileinfo->file_type == relocatable_file_type) + { + CDBL_LIST_DEL (ld_state.from_dso, oldp); + --ld_state.nfrom_dso; + + if (likely (oldp->type == STT_FUNC)) + --ld_state.nplt; + else + --ld_state.ngot; + + oldp->on_dsolist = 0; + } + + /* Use the values of the definition from now on. */ + oldp->size = sym->st_size; + oldp->type = XELF_ST_TYPE (sym->st_info); + oldp->symidx = symidx; + oldp->scndx = sym->st_shndx; + //oldp->symscndx = THESYMSCNDX must be passed; + oldp->file = fileinfo; + oldp->defined = 1; + oldp->in_dso = new_in_dso; + oldp->common = sym->st_shndx == SHN_COMMON; + if (likely (fileinfo->file_type == relocatable_file_type)) + { + /* If the definition comes from a DSO we pertain the weak flag + and it's indicating whether the reference is weak or not. */ + oldp->weak = XELF_ST_BIND (sym->st_info) == STB_WEAK; + + if (sym->st_shndx != SHN_COMMON) + { + struct scninfo *ignore; + mark_section_used (&fileinfo->scninfo[sym->st_shndx], + sym->st_shndx, &ignore); + } + } + + /* Add to the list of symbols used from DSOs if necessary. */ + if (new_in_dso && !old_in_dso) + { + CDBL_LIST_ADD_REAR (ld_state.from_dso, oldp); + ++ld_state.nfrom_dso; + + /* If the object is a function we allocate a PLT entry, + otherwise only a GOT entry. */ + if (oldp->type == STT_FUNC) + ++ld_state.nplt; + else + ++ld_state.ngot; + + oldp->on_dsolist = 1; + } + else if (sym->st_shndx == SHN_COMMON) + { + /* Store the alignment. */ + oldp->merge.value = sym->st_value; + + CDBL_LIST_ADD_REAR (ld_state.common_syms, oldp); + } + } + + return result; +} + + +static struct scninfo * +find_section_group (struct usedfiles *fileinfo, Elf32_Word shndx, + Elf_Data **datap) +{ + struct scninfo *runp; + + for (runp = fileinfo->groups; runp != NULL; runp = runp->next) + if (!runp->used) + { + Elf32_Word *grpref; + size_t cnt; + Elf_Data *data; + + data = elf_getdata (runp->scn, NULL); + if (data == NULL) + error (EXIT_FAILURE, 0, + gettext ("%s: cannot get section group data: %s"), + fileinfo->fname, elf_errmsg (-1)); + + /* There cannot be another data block. */ + assert (elf_getdata (runp->scn, data) == NULL); + + grpref = (Elf32_Word *) data->d_buf; + cnt = data->d_size / sizeof (Elf32_Word); + /* Note that we stop after looking at index 1 since index 0 + contains the flags for the section group. */ + while (cnt > 1) + if (grpref[--cnt] == shndx) + { + *datap = data; + return runp; + } + } + + /* If we come here no section group contained the given section + despite the SHF_GROUP flag. This is an error in the input + file. */ + error (EXIT_FAILURE, 0, gettext ("\ +%s: section '%s' with group flag set does not belong to any group"), + fileinfo->fname, + elf_strptr (fileinfo->elf, fileinfo->shstrndx, + SCNINFO_SHDR (fileinfo->scninfo[shndx].shdr).sh_name)); + return NULL; +} + + +/* Mark all sections which belong to the same group as section SHNDX + as used. */ +static void +mark_section_group (struct usedfiles *fileinfo, Elf32_Word shndx, + struct scninfo **grpscnp) +{ + /* First locate the section group. There can be several (many) of + them. */ + size_t cnt; + Elf32_Word *grpref; + Elf_Data *data; + struct scninfo *grpscn = find_section_group (fileinfo, shndx, &data); + *grpscnp = grpscn; + + /* Mark all the sections as used. + + XXX Two possible problems here: + + - the gABI says "The section must be referenced by a section of type + SHT_GROUP". I hope everybody reads this as "exactly one section". + + - section groups are also useful to mark the debugging section which + belongs to a text section. Unconditionally adding debugging sections + is therefore probably not what is wanted if stripping is required. */ + + /* Mark the section group as handled. */ + grpscn->used = true; + + grpref = (Elf32_Word *) data->d_buf; + cnt = data->d_size / sizeof (Elf32_Word); + while (cnt > 1) + { + Elf32_Word idx = grpref[--cnt]; + XElf_Shdr *shdr = &SCNINFO_SHDR (fileinfo->scninfo[idx].shdr); + + if (fileinfo->scninfo[idx].grpid != 0) + error (EXIT_FAILURE, 0, gettext ("\ +%s: section [%2d] '%s' is in more than one section group"), + fileinfo->fname, (int) idx, + elf_strptr (fileinfo->elf, fileinfo->shstrndx, shdr->sh_name)); + + fileinfo->scninfo[idx].grpid = grpscn->grpid; + + if (ld_state.strip == strip_none + /* If we are stripping, remove debug sections. */ + || (!ebl_debugscn_p (ld_state.ebl, + elf_strptr (fileinfo->elf, fileinfo->shstrndx, + shdr->sh_name)) + /* And the relocation sections for the debug sections. */ + && ((shdr->sh_type != SHT_RELA && shdr->sh_type != SHT_REL) + || !ebl_debugscn_p (ld_state.ebl, + elf_strptr (fileinfo->elf, + fileinfo->shstrndx, + SCNINFO_SHDR (fileinfo->scninfo[shdr->sh_info].shdr).sh_name))))) + { + struct scninfo *ignore; + + mark_section_used (&fileinfo->scninfo[idx], idx, &ignore); + } + } +} + + +static void +mark_section_used (struct scninfo *scninfo, Elf32_Word shndx, + struct scninfo **grpscnp) +{ + if (likely (scninfo->used)) + /* Nothing to be done. */ + return; + + /* We need this section. */ + scninfo->used = true; + + /* Make sure the section header has been read from the file. */ + XElf_Shdr *shdr = &SCNINFO_SHDR (scninfo->shdr); +#if NATIVE_ELF + if (unlikely (scninfo->shdr == NULL)) +#else + if (unlikely (scninfo->shdr.sh_type == SHT_NULL)) +#endif + { +#if NATIVE_ELF != 0 + shdr = xelf_getshdr (scninfo->scn, scninfo->shdr); +#else + xelf_getshdr_copy (scninfo->scn, shdr, scninfo->shdr); +#endif + if (unlikely (shdr == NULL)) + /* Something is very wrong. The calling code will notice it + soon and print a message. */ + return; + } + + /* Handle section linked by 'sh_link'. */ + if (unlikely (shdr->sh_link != 0)) + { + struct scninfo *ignore; + mark_section_used (&scninfo->fileinfo->scninfo[shdr->sh_link], + shdr->sh_link, &ignore); + } + + /* Handle section linked by 'sh_info'. */ + if (unlikely (shdr->sh_info != 0) && (shdr->sh_flags & SHF_INFO_LINK)) + { + struct scninfo *ignore; + mark_section_used (&scninfo->fileinfo->scninfo[shdr->sh_info], + shdr->sh_info, &ignore); + } + + if (unlikely (shdr->sh_flags & SHF_GROUP) && ld_state.gc_sections) + /* Find the section group which contains this section. */ + mark_section_group (scninfo->fileinfo, shndx, grpscnp); +} + + +/* We collect all sections in a hashing table. All sections with the + same name are collected in a list. Note that we do not determine + which sections are finally collected in the same output section + here. This would be terribly inefficient. It will be done later. */ +static void +add_section (struct usedfiles *fileinfo, struct scninfo *scninfo) +{ + struct scnhead *queued; + struct scnhead search; + unsigned long int hval; + XElf_Shdr *shdr = &SCNINFO_SHDR (scninfo->shdr); + struct scninfo *grpscn = NULL; + Elf_Data *grpscndata = NULL; + + /* See whether we can determine right away whether we need this + section in the output. + + XXX I assume here that --gc-sections only affects extraction + from an archive. If it also affects objects files given on + the command line then somebody must explain to me how the + dependency analysis should work. Should the entry point be + the root? What if it is a numeric value? */ + if (!scninfo->used + && (ld_state.strip == strip_none + || (shdr->sh_flags & SHF_ALLOC) != 0 + || shdr->sh_type == SHT_NOTE + || (shdr->sh_type == SHT_PROGBITS + && strcmp (elf_strptr (fileinfo->elf, + fileinfo->shstrndx, + shdr->sh_name), ".comment") == 0)) + && (fileinfo->status != in_archive || !ld_state.gc_sections)) + /* Mark as used and handle reference recursively if necessary. */ + mark_section_used (scninfo, elf_ndxscn (scninfo->scn), &grpscn); + + if ((shdr->sh_flags & SHF_GROUP) && grpscn == NULL) + /* Determine the symbol which name constitutes the signature + for the section group. */ + grpscn = find_section_group (fileinfo, elf_ndxscn (scninfo->scn), + &grpscndata); + assert (grpscn == NULL || grpscn->symbols->name != NULL); + + /* Determine the section name. */ + search.name = elf_strptr (fileinfo->elf, fileinfo->shstrndx, shdr->sh_name); + search.type = shdr->sh_type; + search.flags = shdr->sh_flags; + search.entsize = shdr->sh_entsize; + search.grp_signature = grpscn != NULL ? grpscn->symbols->name : NULL; + search.kind = scn_normal; + hval = elf_hash (search.name); + + /* Find already queued sections. */ + queued = ld_section_tab_find (&ld_state.section_tab, hval, &search); + if (queued != NULL) + { + bool is_comdat = false; + + /* If this section is part of a COMDAT section group we simply + ignore it since we already have a copy. */ + if (unlikely (shdr->sh_flags & SHF_GROUP)) + { + /* Get the data of the section group section. */ + if (grpscndata == NULL) + { + grpscndata = elf_getdata (grpscn->scn, NULL); + assert (grpscndata != NULL); + } + + /* XXX Possibly unaligned memory access. */ + is_comdat = ((Elf32_Word *) grpscndata->d_buf)[0] & GRP_COMDAT; + } + + if (!is_comdat) + { + /* No COMDAT section, we use the data. */ + scninfo->next = queued->last->next; + queued->last = queued->last->next = scninfo; + + queued->flags = SH_FLAGS_COMBINE (queued->flags, shdr->sh_flags); + queued->align = MAX (queued->align, shdr->sh_addralign); + } + } + else + { + /* We do not use obstacks here since the memory might be + deallocated. */ + queued = (struct scnhead *) xcalloc (sizeof (struct scnhead), 1); + queued->kind = scn_normal; + queued->name = search.name; + queued->type = shdr->sh_type; + queued->flags = shdr->sh_flags; + queued->align = shdr->sh_addralign; + queued->entsize = shdr->sh_entsize; + queued->grp_signature = grpscn != NULL ? grpscn->symbols->name : NULL; + queued->segment_nr = ~0; + queued->last = scninfo->next = scninfo; + + /* Add to the hash table and possibly overwrite existing value. */ + ld_section_tab_insert (&ld_state.section_tab, hval, queued); + } +} + + +static int +add_relocatable_file (struct usedfiles *fileinfo, GElf_Word secttype) +{ + size_t scncnt; + size_t cnt; + Elf_Data *symtabdata = NULL; + Elf_Data *xndxdata = NULL; + Elf_Data *versymdata = NULL; + Elf_Data *verdefdata = NULL; + Elf_Data *verneeddata = NULL; + size_t symstridx = 0; + size_t nsymbols = 0; + size_t nlocalsymbols = 0; + bool has_merge_sections = false; + + /* Prerequisites. */ + assert (fileinfo->elf != NULL); + + /* Allocate memory for the sections. */ + if (unlikely (elf_getshnum (fileinfo->elf, &scncnt) < 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot determine number of sections: %s"), + elf_errmsg (-1)); + + fileinfo->scninfo = (struct scninfo *) + obstack_calloc (&ld_state.smem, scncnt * sizeof (struct scninfo)); + + /* Read all the section headers and find the symbol table. Note + that we don't skip the section with index zero. Even though the + section itself is always empty the section header contains + informaton for the case when the section index for the section + header string table is too large to fit in the ELF header. */ + for (cnt = 0; cnt < scncnt; ++cnt) + { + /* Store the handle for the section. */ + fileinfo->scninfo[cnt].scn = elf_getscn (fileinfo->elf, cnt); + + /* Get the ELF section header and data. */ + XElf_Shdr *shdr; +#if NATIVE_ELF != 0 + if (fileinfo->scninfo[cnt].shdr == NULL) +#else + if (fileinfo->scninfo[cnt].shdr.sh_type == SHT_NULL) +#endif + { +#if NATIVE_ELF != 0 + shdr = xelf_getshdr (fileinfo->scninfo[cnt].scn, + fileinfo->scninfo[cnt].shdr); +#else + xelf_getshdr_copy (fileinfo->scninfo[cnt].scn, shdr, + fileinfo->scninfo[cnt].shdr); +#endif + if (shdr == NULL) + { + /* This should never happen. */ + fprintf (stderr, gettext ("%s: invalid ELF file (%s:%d)\n"), + fileinfo->rfname, __FILE__, __LINE__); + return 1; + } + } + else + shdr = &SCNINFO_SHDR (fileinfo->scninfo[cnt].shdr); + + Elf_Data *data = elf_getdata (fileinfo->scninfo[cnt].scn, NULL); + + /* Check whether this section is marked as merge-able. */ + has_merge_sections |= (shdr->sh_flags & SHF_MERGE) != 0; + + /* Get the ELF section header and data. */ + /* Make the file structure available. */ + fileinfo->scninfo[cnt].fileinfo = fileinfo; + + if (unlikely (shdr->sh_type == SHT_SYMTAB) + || unlikely (shdr->sh_type == SHT_DYNSYM)) + { + if (shdr->sh_type == SHT_SYMTAB) + { + assert (fileinfo->symtabdata == NULL); + fileinfo->symtabdata = data; + fileinfo->nsymtab = shdr->sh_size / shdr->sh_entsize; + fileinfo->nlocalsymbols = shdr->sh_info; + fileinfo->symstridx = shdr->sh_link; + } + else + { + assert (fileinfo->dynsymtabdata == NULL); + fileinfo->dynsymtabdata = data; + fileinfo->ndynsymtab = shdr->sh_size / shdr->sh_entsize; + fileinfo->dynsymstridx = shdr->sh_link; + } + + /* If we are looking for the normal symbol table we just + found it. */ + if (secttype == shdr->sh_type) + { + assert (symtabdata == NULL); + symtabdata = data; + symstridx = shdr->sh_link; + nsymbols = shdr->sh_size / shdr->sh_entsize; + nlocalsymbols = shdr->sh_info; + } + } + else if (unlikely (shdr->sh_type == SHT_SYMTAB_SHNDX)) + { + assert (xndxdata == NULL); + fileinfo->xndxdata = xndxdata = data; + } + else if (unlikely (shdr->sh_type == SHT_GNU_versym)) + { + assert (versymdata == 0); + fileinfo->versymdata = versymdata = data; + } + else if (unlikely (shdr->sh_type == SHT_GNU_verdef)) + { + size_t nversions; + + assert (verdefdata == 0); + fileinfo->verdefdata = verdefdata = data; + + /* Allocate the arrays flagging the use of the version and + to track of allocated names. */ + fileinfo->nverdef = nversions = shdr->sh_info; + /* We have NVERSIONS + 1 because the indeces used to access the + sectino start with one; zero represents local binding. */ + fileinfo->verdefused = (XElf_Versym *) + obstack_calloc (&ld_state.smem, + sizeof (XElf_Versym) * (nversions + 1)); + fileinfo->verdefent = (struct Ebl_Strent **) + obstack_alloc (&ld_state.smem, + sizeof (struct Ebl_Strent *) * (nversions + 1)); + } + else if (unlikely (shdr->sh_type == SHT_GNU_verneed)) + { + assert (verneeddata == 0); + fileinfo->verneeddata = verneeddata = data; + } + else if (unlikely (shdr->sh_type == SHT_DYNAMIC)) + { + assert (fileinfo->dynscn == NULL); + fileinfo->dynscn = fileinfo->scninfo[cnt].scn; + } + else if (unlikely (shdr->sh_type == SHT_GROUP)) + { + Elf_Scn *symscn; + XElf_Shdr_vardef (symshdr); + Elf_Data *symdata; + + if (FILEINFO_EHDR (fileinfo->ehdr).e_type != ET_REL) + error (EXIT_FAILURE, 0, gettext ("\ +%s: only files of type ET_REL might contain section groups"), + fileinfo->fname); + + fileinfo->scninfo[cnt].next = fileinfo->groups; + fileinfo->scninfo[cnt].grpid = cnt; + fileinfo->groups = &fileinfo->scninfo[cnt]; + + /* Determine the signature. We create a symbol record for + it. Only the name element is important. */ + fileinfo->scninfo[cnt].symbols = (struct symbol *) + obstack_calloc (&ld_state.smem, sizeof (struct symbol)); + + symscn = elf_getscn (fileinfo->elf, shdr->sh_link); + xelf_getshdr (symscn, symshdr); + symdata = elf_getdata (symscn, NULL); + if (symshdr != NULL) + { + XElf_Sym_vardef (sym); + + /* We don't need the section index and therefore we don't + have to use 'xelf_getsymshndx'. */ + xelf_getsym (symdata, shdr->sh_info, sym); + if (sym != NULL) + { + struct symbol *symbol = fileinfo->scninfo[cnt].symbols; + + symbol->name = elf_strptr (fileinfo->elf, symshdr->sh_link, + sym->st_name); + symbol->symidx = shdr->sh_info; + symbol->file = fileinfo; + } + } + if (fileinfo->scninfo[cnt].symbols->name == NULL) + error (EXIT_FAILURE, 0, gettext ("\ +%s: cannot determine signature of section group [%2zd] '%s': %s"), + fileinfo->fname, + elf_ndxscn (fileinfo->scninfo[cnt].scn), + elf_strptr (fileinfo->elf, fileinfo->shstrndx, + shdr->sh_name), + elf_errmsg (-1)); + + /* The 'used' flag is used to indicate when the information + in the section group is used to mark all other sections + as used. So it must not be true yet. */ + assert (fileinfo->scninfo[cnt].used == false); + } + else if (! SECTION_TYPE_P (&ld_state, shdr->sh_type) + && unlikely ((shdr->sh_flags & SHF_OS_NONCONFORMING) != 0)) + /* According to the gABI it is a fatal error if the file contains + a section with unknown type and the SHF_OS_NONCONFORMING flag + set. */ + error (EXIT_FAILURE, 0, + gettext ("%s: section '%s' has unknown type: %d"), + fileinfo->fname, + elf_strptr (fileinfo->elf, fileinfo->shstrndx, + shdr->sh_name), + (int) shdr->sh_type); + /* We don't have to add a few section types here. These will be + generated from scratch for the new output file. We also + don't add the sections of DSOs here since these sections are + not used in the resulting object file. */ + else if (likely (fileinfo->file_type == relocatable_file_type) + && likely (cnt > 0) + && likely (shdr->sh_type == SHT_PROGBITS + || shdr->sh_type == SHT_RELA + || shdr->sh_type == SHT_REL + || shdr->sh_type == SHT_NOTE + || shdr->sh_type == SHT_NOBITS + || shdr->sh_type == SHT_INIT_ARRAY + || shdr->sh_type == SHT_FINI_ARRAY + || shdr->sh_type == SHT_PREINIT_ARRAY)) + add_section (fileinfo, &fileinfo->scninfo[cnt]); + } + + /* Handle the symbols. Record defined and undefined symbols in the + hash table. In theory there can be a file without any symbol + table. */ + if (likely (symtabdata != NULL)) + { + /* In case this file contains merge-able sections we have to + locate the symbols which are in these sections. */ + fileinfo->has_merge_sections = has_merge_sections; + if (likely (has_merge_sections)) + { + fileinfo->symref = (struct symbol **) + obstack_calloc (&ld_state.smem, + nsymbols * sizeof (struct symbol *)); + + /* Only handle the local symbols here. */ + for (cnt = 0; cnt < nlocalsymbols; ++cnt) + { + Elf32_Word shndx; + XElf_Sym_vardef (sym); + + xelf_getsymshndx (symtabdata, xndxdata, cnt, sym, shndx); + if (sym == NULL) + { + /* This should never happen. */ + fprintf (stderr, gettext ("%s: invalid ELF file (%s:%d)\n"), + fileinfo->rfname, __FILE__, __LINE__); + return 1; + } + + if (likely (shndx != SHN_XINDEX)) + shndx = sym->st_shndx; + else if (unlikely (shndx == 0)) + { + fprintf (stderr, gettext ("%s: invalid ELF file (%s:%d)\n"), + fileinfo->rfname, __FILE__, __LINE__); + return 1; + } + + if (XELF_ST_TYPE (sym->st_info) != STT_SECTION + && (shndx < SHN_LORESERVE || shndx > SHN_HIRESERVE) + && (SCNINFO_SHDR (fileinfo->scninfo[shndx].shdr).sh_flags + & SHF_MERGE)) + { + /* Create a symbol record for this symbol and add it + to the list for this section. */ + struct symbol *newp; + + newp = (struct symbol *) + obstack_calloc (&ld_state.smem, sizeof (struct symbol)); + + newp->symidx = cnt; + newp->scndx = shndx; + newp->file = fileinfo; + fileinfo->symref[cnt] = newp; + + if (fileinfo->scninfo[shndx].symbols == NULL) + fileinfo->scninfo[shndx].symbols = newp->next_in_scn + = newp; + else + { + newp->next_in_scn + = fileinfo->scninfo[shndx].symbols->next_in_scn; + fileinfo->scninfo[shndx].symbols + = fileinfo->scninfo[shndx].symbols->next_in_scn = newp; + } + } + } + } + else + /* Create array with pointers to the symbol definitions. Note + that we only allocate memory for the non-local symbols + since we have no merge-able sections. But we store the + pointer as if it was for the whole symbol table. This + saves some memory. */ + fileinfo->symref = (struct symbol **) + obstack_calloc (&ld_state.smem, ((nsymbols - nlocalsymbols) + * sizeof (struct symbol *))) + - nlocalsymbols; + + /* Don't handle local symbols here. It's either not necessary + at all or has already happened. */ + for (cnt = nlocalsymbols; cnt < nsymbols; ++cnt) + { + XElf_Sym_vardef (sym); + Elf32_Word shndx; + xelf_getsymshndx (symtabdata, xndxdata, cnt, sym, shndx); + + if (sym == NULL) + { + /* This should never happen. */ + fprintf (stderr, gettext ("%s: invalid ELF file (%s:%d)\n"), + fileinfo->rfname, __FILE__, __LINE__); + return 1; + } + + if (likely (shndx != SHN_XINDEX)) + shndx = sym->st_shndx; + else if (unlikely (shndx == 0)) + { + fprintf (stderr, gettext ("%s: invalid ELF file (%s:%d)\n"), + fileinfo->rfname, __FILE__, __LINE__); + return 1; + } + + /* We ignore ABS symbols from DSOs. */ + // XXX Is this correct? + if (unlikely (shndx == SHN_ABS) && secttype == SHT_DYNSYM) + continue; + + /* If the DSO uses symbols determine whether this is the default + version. Otherwise we'll ignore the symbol. */ + if (versymdata != NULL) + { + XElf_Versym versym; + + if (xelf_getversym_copy (versymdata, cnt, versym) == NULL) + /* XXX Should we handle faulty input files more graceful? */ + assert (! "xelf_getversym failed"); + + if ((versym & 0x8000) != 0) + /* Ignore the symbol, it's not the default version. */ + continue; + } + + /* See whether we know anything about this symbol. */ + struct symbol search; + search.name = elf_strptr (fileinfo->elf, symstridx, sym->st_name); + unsigned long int hval = elf_hash (search.name); + + /* We ignore the symbols the linker generates. This are + _GLOBAL_OFFSET_TABLE_, _DYNAMIC. */ + // XXX This loop is hot and the following tests hardly ever match. + // XXX Maybe move the tests somewhere they are executed less often. + if (((unlikely (hval == 165832675) + && strcmp (search.name, "_DYNAMIC") == 0) + || (unlikely (hval == 102264335) + && strcmp (search.name, "_GLOBAL_OFFSET_TABLE_") == 0)) + && sym->st_shndx != SHN_UNDEF + /* If somebody defines such a variable in a relocatable we + don't ignore it. Let the user get what s/he deserves. */ + && fileinfo->file_type != relocatable_file_type) + continue; + + struct symbol *oldp = ld_symbol_tab_find (&ld_state.symbol_tab, + hval, &search); + struct symbol *newp; + if (likely (oldp == NULL)) + { + /* No symbol of this name know. Add it. */ + newp = (struct symbol *) obstack_alloc (&ld_state.smem, + sizeof (*newp)); + newp->name = search.name; + newp->size = sym->st_size; + newp->type = XELF_ST_TYPE (sym->st_info); + newp->symidx = cnt; + newp->outsymidx = 0; + newp->outdynsymidx = 0; + newp->scndx = shndx; + newp->file = fileinfo; + newp->defined = newp->scndx != SHN_UNDEF; + newp->common = newp->scndx == SHN_COMMON; + newp->weak = XELF_ST_BIND (sym->st_info) == STB_WEAK; + newp->added = 0; + newp->merged = 0; + newp->need_copy = 0; + newp->on_dsolist = 0; + newp->in_dso = secttype == SHT_DYNSYM; + newp->next_in_scn = NULL; +#ifndef NDEBUG + newp->next = NULL; + newp->previous = NULL; +#endif + + if (newp->scndx == SHN_UNDEF) + { + CDBL_LIST_ADD_REAR (ld_state.unresolved, newp); + ++ld_state.nunresolved; + if (! newp->weak) + ++ld_state.nunresolved_nonweak; + } + else if (newp->scndx == SHN_COMMON) + { + /* Store the alignment requirement. */ + newp->merge.value = sym->st_value; + + CDBL_LIST_ADD_REAR (ld_state.common_syms, newp); + } + + /* Insert the new symbol. */ + if (unlikely (ld_symbol_tab_insert (&ld_state.symbol_tab, + hval, newp) != 0)) + /* This cannot happen. */ + abort (); + + fileinfo->symref[cnt] = newp; + + /* We have a few special symbols to recognize. The symbols + _init and _fini are the initialization and finalization + functions respectively. They have to be made known in + the dynamic section and therefore we have to find out + now whether these functions exist or not. */ + if (hval == 6685956 && strcmp (newp->name, "_init") == 0) + ld_state.init_symbol = newp; + else if (hval == 6672457 && strcmp (newp->name, "_fini") == 0) + ld_state.fini_symbol = newp; + } + else if (unlikely (check_definition (sym, cnt, fileinfo, oldp) != 0)) + /* A fatal error (multiple definition of a symbol) + occurred, no need to continue. */ + return 1; + else + /* Use the previously allocated symbol record. It has + been updated in check_definition(), if necessary. */ + newp = fileinfo->symref[cnt] = oldp; + + /* Mark the section the symbol we need comes from as used. */ + if (shndx != SHN_UNDEF + && (shndx < SHN_LORESERVE || shndx > SHN_HIRESERVE)) + { + struct scninfo *ignore; + +#ifndef NDEBUG + size_t shnum; + assert (elf_getshnum (fileinfo->elf, &shnum) == 0); + assert (shndx < shnum); +#endif + + /* Mark section (and all dependencies) as used. */ + mark_section_used (&fileinfo->scninfo[shndx], shndx, &ignore); + + /* Check whether the section is merge-able. In this case we + have to record the symbol. */ + if (SCNINFO_SHDR (fileinfo->scninfo[shndx].shdr).sh_flags + & SHF_MERGE) + { + if (fileinfo->scninfo[shndx].symbols == NULL) + fileinfo->scninfo[shndx].symbols = newp->next_in_scn + = newp; + else + { + newp->next_in_scn + = fileinfo->scninfo[shndx].symbols->next_in_scn; + fileinfo->scninfo[shndx].symbols + = fileinfo->scninfo[shndx].symbols->next_in_scn = newp; + } + } + } + } + + /* This file is used. */ + if (likely (fileinfo->file_type == relocatable_file_type)) + { + if (unlikely (ld_state.relfiles == NULL)) + ld_state.relfiles = fileinfo->next = fileinfo; + else + { + fileinfo->next = ld_state.relfiles->next; + ld_state.relfiles = ld_state.relfiles->next = fileinfo; + } + + /* Update some summary information in the state structure. */ + ld_state.nsymtab += fileinfo->nsymtab; + ld_state.nlocalsymbols += fileinfo->nlocalsymbols; + } + else if (likely (fileinfo->file_type == dso_file_type)) + { + CSNGL_LIST_ADD_REAR (ld_state.dsofiles, fileinfo); + ++ld_state.ndsofiles; + + if (fileinfo->lazyload) + /* We have to create another dynamic section entry for the + DT_POSFLAG_1 entry. + + XXX Once more functionality than the lazyloading flag + are suppported the test must be extended. */ + ++ld_state.ndsofiles; + } + } + + return 0; +} + + +int +ld_handle_filename_list (struct filename_list *fnames) +{ + struct filename_list *runp; + int res = 0; + + for (runp = fnames; runp != NULL; runp = runp->next) + { + struct usedfiles *curp; + + /* Create a record for the new file. */ + curp = runp->real = ld_new_inputfile (runp->name, relocatable_file_type); + + /* Set flags for group handling. */ + runp->real->group_start = runp->group_start; + runp->real->group_end = runp->group_end; + + /* Read the file and everything else which comes up, including + handling groups. */ + do + res |= FILE_PROCESS (-1, curp, &ld_state, &curp); + while (curp != NULL); + } + + /* Free the list. */ + while (fnames != NULL) + { + runp = fnames; + fnames = fnames->next; + free (runp); + } + + return res; +} + + +/* Handle opening of the given file with ELF descriptor. */ +static int +open_elf (struct usedfiles *fileinfo, Elf *elf) +{ + int res = 0; + + if (elf == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get descriptor for ELF file (%s:%d): %s\n"), + __FILE__, __LINE__, elf_errmsg (-1)); + + if (unlikely (elf_kind (elf) == ELF_K_NONE)) + { + struct filename_list *fnames; + + /* We don't have to look at this file again. */ + fileinfo->status = closed; + + /* Let's see whether this is a linker script. */ + if (fileinfo->fd != -1) + /* Create a stream from the file handle we know. */ + ldin = fdopen (fileinfo->fd, "r"); + else + { + /* Get the memory for the archive member. */ + char *content; + size_t contentsize; + + /* Get the content of the file. */ + content = elf_rawfile (elf, &contentsize); + if (content == NULL) + { + fprintf (stderr, gettext ("%s: invalid ELF file (%s:%d)\n"), + fileinfo->rfname, __FILE__, __LINE__); + return 1; + } + + /* The content of the file is available in memory. Read the + memory region as a stream. */ + ldin = fmemopen (content, contentsize, "r"); + } + + /* No need for locking. */ + __fsetlocking (ldin, FSETLOCKING_BYCALLER); + + if (ldin == NULL) + error (EXIT_FAILURE, errno, gettext ("cannot open \"%s\""), + fileinfo->rfname); + + /* Parse the file. If it is a linker script no problems will be + reported. */ + ld_state.srcfiles = NULL; + ldlineno = 1; + ld_scan_version_script = 0; + ldin_fname = fileinfo->rfname; + res = ldparse (); + + fclose (ldin); + if (fileinfo->fd != -1 && !fileinfo->fd_passed) + { + /* We won't need the file descriptor again. */ + close (fileinfo->fd); + fileinfo->fd = -1; + } + + elf_end (elf); + + if (unlikely (res != 0)) + /* Something went wrong during parsing. */ + return 1; + + /* This is no ELF file. */ + fileinfo->elf = NULL; + + /* Now we have to handle eventual INPUT and GROUP statements in + the script. Read the files mentioned. */ + fnames = ld_state.srcfiles; + if (fnames != NULL) + { + struct filename_list *oldp; + + /* Convert the list into a normal single-linked list. */ + oldp = fnames; + fnames = fnames->next; + oldp->next = NULL; + + /* Remove the list from the state structure. */ + ld_state.srcfiles = NULL; + + if (unlikely (ld_handle_filename_list (fnames) != 0)) + return 1; + } + + return 0; + } + + /* Store the file info. */ + fileinfo->elf = elf; + + /* The file is ready for action. */ + fileinfo->status = opened; + + return 0; +} + + +static int +add_whole_archive (struct usedfiles *fileinfo) +{ + Elf *arelf; + Elf_Cmd cmd = ELF_C_READ_MMAP_PRIVATE; + int res = 0; + + while ((arelf = elf_begin (fileinfo->fd, cmd, fileinfo->elf)) != NULL) + { + Elf_Arhdr *arhdr = elf_getarhdr (arelf); + struct usedfiles *newp; + + if (arhdr == NULL) + abort (); + + /* Just to be sure; since these are no files in the archive + these names should never be returned. */ + assert (strcmp (arhdr->ar_name, "/") != 0); + assert (strcmp (arhdr->ar_name, "//") != 0); + + newp = ld_new_inputfile (arhdr->ar_name, relocatable_file_type); + newp->archive_file = fileinfo; + + if (unlikely (ld_state.trace_files)) + print_file_name (stdout, newp, 1, 1); + + /* This shows that this file is contained in an archive. */ + newp->fd = -1; + /* Store the ELF descriptor. */ + newp->elf = arelf; + /* Show that we are open for business. */ + newp->status = opened; + + /* Proces the file, add all the symbols etc. */ + res = file_process2 (newp); + if (unlikely (res != 0)) + break; + + /* Advance to the next archive element. */ + cmd = elf_next (arelf); + } + + return res; +} + + +static int +extract_from_archive (struct usedfiles *fileinfo) +{ + static int archive_seq; + int res = 0; + + /* This is an archive we are not using completely. Give it a + unique number. */ + fileinfo->archive_seq = ++archive_seq; + + /* If there are no unresolved symbols don't do anything. */ + if ((likely (ld_state.extract_rule == defaultextract) + && ld_state.nunresolved_nonweak == 0) + || (unlikely (ld_state.extract_rule == weakextract) + && ld_state.nunresolved == 0)) + return 0; + + Elf_Arsym *syms; + size_t nsyms; + + /* Get all the symbols. */ + syms = elf_getarsym (fileinfo->elf, &nsyms); + if (syms == NULL) + { + cannot_read_archive: + error (0, 0, gettext ("cannot read archive `%s': %s"), + fileinfo->rfname, elf_errmsg (-1)); + + /* We cannot use this archive anymore. */ + fileinfo->status = closed; + + return 1; + } + + /* Now add all the symbols to the hash table. Note that there + can potentially be duplicate definitions. We'll always use + the first definition. */ + // XXX Is this a compatible behavior? + bool any_used; + int nround = 0; + do + { + any_used = false; + + size_t cnt; + for (cnt = 0; cnt < nsyms; ++cnt) + { + struct symbol search = { .name = syms[cnt].as_name }; + struct symbol *sym = ld_symbol_tab_find (&ld_state.symbol_tab, + syms[cnt].as_hash, &search); + if (sym != NULL && ! sym->defined) + { + /* The symbol is referenced and not defined. */ + Elf *arelf; + Elf_Arhdr *arhdr; + struct usedfiles *newp; + + /* Find the archive member for this symbol. */ + if (unlikely (elf_rand (fileinfo->elf, syms[cnt].as_off) + != syms[cnt].as_off)) + goto cannot_read_archive; + + /* Note: no test of a failing 'elf_begin' call. That's fine + since 'elf'getarhdr' will report the problem. */ + arelf = elf_begin (fileinfo->fd, ELF_C_READ_MMAP_PRIVATE, + fileinfo->elf); + arhdr = elf_getarhdr (arelf); + if (arhdr == NULL) + goto cannot_read_archive; + + /* We have all the information and an ELF handle for the + archive member. Create the normal data structure for + a file now. */ + newp = ld_new_inputfile (obstack_strdup (&ld_state.smem, + arhdr->ar_name), + relocatable_file_type); + newp->archive_file = fileinfo; + + if (unlikely (ld_state.trace_files)) + print_file_name (stdout, newp, 1, 1); + + /* This shows that this file is contained in an archive. */ + newp->fd = -1; + /* Store the ELF descriptor. */ + newp->elf = arelf; + /* Show that we are open for business. */ + newp->status = in_archive; + + /* Now read the file and add all the symbols. */ + res = file_process2 (newp); + if (unlikely (res != 0)) + return res; + + any_used = true; + } + } + + if (++nround == 1) + { + /* This is an archive therefore it must have a number. */ + assert (fileinfo->archive_seq != 0); + ld_state.last_archive_used = fileinfo->archive_seq; + } + } + while (any_used); + + return res; +} + + +static int +file_process2 (struct usedfiles *fileinfo) +{ + int res; + + if (likely (elf_kind (fileinfo->elf) == ELF_K_ELF)) + { + /* The first time we get here we read the ELF header. */ +#if NATIVE_ELF != 0 + if (likely (fileinfo->ehdr == NULL)) +#else + if (likely (FILEINFO_EHDR (fileinfo->ehdr).e_type == ET_NONE)) +#endif + { + XElf_Ehdr *ehdr; +#if NATIVE_ELF != 0 + ehdr = xelf_getehdr (fileinfo->elf, fileinfo->ehdr); +#else + xelf_getehdr_copy (fileinfo->elf, ehdr, fileinfo->ehdr); +#endif + if (ehdr == NULL) + { + fprintf (stderr, gettext ("%s: invalid ELF file (%s:%d)\n"), + fileinfo->rfname, __FILE__, __LINE__); + fileinfo->status = closed; + return 1; + } + + if (FILEINFO_EHDR (fileinfo->ehdr).e_type != ET_REL + && unlikely (FILEINFO_EHDR (fileinfo->ehdr).e_type != ET_DYN)) + /* XXX Add ebl* function to query types which are allowed + to link in. */ + { + char buf[64]; + + print_file_name (stderr, fileinfo, 1, 0); + fprintf (stderr, + gettext ("file of type %s cannot be linked in\n"), + ebl_object_type_name (ld_state.ebl, + FILEINFO_EHDR (fileinfo->ehdr).e_type, + buf, sizeof (buf))); + fileinfo->status = closed; + return 1; + } + + /* Determine the section header string table section index. */ + if (unlikely (elf_getshstrndx (fileinfo->elf, &fileinfo->shstrndx) + < 0)) + { + fprintf (stderr, gettext ("\ +%s: cannot get section header string table index: %s\n"), + fileinfo->rfname, elf_errmsg (-1)); + fileinfo->status = closed; + return 1; + } + } + + /* Now handle the different types of files. */ + if (FILEINFO_EHDR (fileinfo->ehdr).e_type == ET_REL) + { + /* Add all the symbol. Relocatable files have symbol + tables. */ + res = add_relocatable_file (fileinfo, SHT_SYMTAB); + } + else + { + bool has_l_name = fileinfo->file_type == archive_file_type; + + assert (FILEINFO_EHDR (fileinfo->ehdr).e_type == ET_DYN); + + /* If the file is a DT_NEEDED dependency then the type is + already correctly specified. */ + if (fileinfo->file_type != dso_needed_file_type) + fileinfo->file_type = dso_file_type; + + /* We cannot use DSOs when generating relocatable objects. */ + if (ld_state.file_type == relocatable_file_type) + { + error (0, 0, gettext ("\ +cannot use DSO '%s' when generating relocatable object file"), + fileinfo->fname); + return 1; + } + + /* Add all the symbols. For DSOs we are looking at the + dynamic symbol table. */ + res = add_relocatable_file (fileinfo, SHT_DYNSYM); + + /* We always have to have a dynamic section. */ + assert (fileinfo->dynscn != NULL); + + /* We have to remember the dependencies for this object. It + is necessary to look them up. */ + XElf_Shdr_vardef (dynshdr); + xelf_getshdr (fileinfo->dynscn, dynshdr); + + Elf_Data *dyndata = elf_getdata (fileinfo->dynscn, NULL); + /* XXX Should we flag the failure to get the dynamic section? */ + if (dynshdr != NULL) + { + int cnt = dynshdr->sh_size / dynshdr->sh_entsize; + XElf_Dyn_vardef (dyn); + + while (--cnt >= 0) + { + xelf_getdyn (dyndata, cnt, dyn); + if (dyn != NULL) + { + if(dyn->d_tag == DT_NEEDED) + { + struct usedfiles *newp; + + newp = ld_new_inputfile (elf_strptr (fileinfo->elf, + dynshdr->sh_link, + dyn->d_un.d_val), + dso_needed_file_type); + + /* Enqueue the newly found dependencies. */ + // XXX Check that there not already a file with the + // same name. + CSNGL_LIST_ADD_REAR (ld_state.needed, newp); + } + else if (dyn->d_tag == DT_SONAME) + { + /* We use the DT_SONAME (this is what's there + for). */ + fileinfo->soname = elf_strptr (fileinfo->elf, + dynshdr->sh_link, + dyn->d_un.d_val); + has_l_name = false; + } + } + } + } + + /* Construct the file name if the DSO has no SONAME and the + file name comes from a -lXX parameter on the comment + line. */ + if (unlikely (has_l_name)) + { + /* The FNAME is the parameter the user specified on the + command line. We prepend "lib" and append ".so". */ + size_t len = strlen (fileinfo->fname) + 7; + char *newp; + + newp = (char *) obstack_alloc (&ld_state.smem, len); + strcpy (stpcpy (stpcpy (newp, "lib"), fileinfo->fname), ".so"); + + fileinfo->soname = newp; + } + } + } + else if (likely (elf_kind (fileinfo->elf) == ELF_K_AR)) + { + if (unlikely (ld_state.extract_rule == allextract)) + /* Which this option enabled we have to add all the object + files in the archive. */ + res = add_whole_archive (fileinfo); + else if (ld_state.file_type == relocatable_file_type) + { + /* When generating a relocatable object we don't find files + in archives. */ + if (verbose) + error (0, 0, gettext ("input file '%s' ignored"), fileinfo->fname); + + res = 0; + } + else + /* Extract only the members from the archive which are + currently referenced by unresolved symbols. */ + res = extract_from_archive (fileinfo); + } + else + /* This should never happen, we know about no other types. */ + abort (); + + return res; +} + + +/* Process a given file. The first parameter is a file descriptor for + the file which can be -1 to indicate the file has not yet been + found. The second parameter describes the file to be opened, the + last one is the state of the linker which among other information + contain the paths we look at. */ +static int +ld_generic_file_process (int fd, struct usedfiles *fileinfo, + struct ld_state *statep, struct usedfiles **nextp) +{ + int res = 0; + + /* By default we go to the next file in the list. */ + *nextp = fileinfo->next; + + /* Set the flag to signal we are looking for a group start. */ + if (unlikely (fileinfo->group_start)) + { + ld_state.group_start_requested = true; + fileinfo->group_start = false; + } + + /* If the file isn't open yet, open it now. */ + if (likely (fileinfo->status == not_opened)) + { + bool fd_passed = true; + + if (likely (fd == -1)) + { + /* Find the file ourselves. */ + int err = open_along_path (fileinfo); + if (unlikely (err != 0)) + /* We allow libraries and DSOs to be named more than once. + Don't report an error to the caller. */ + return err == EAGAIN ? 0 : err; + + fd_passed = false; + } + else + fileinfo->fd = fd; + + /* Remember where we got the descriptor from. */ + fileinfo->fd_passed = fd_passed; + + /* We found the file. Now test whether it is a file type we can + handle. + + XXX Do we have to have the ability to start from a given + position in the search path again to look for another file if + the one found has not the right type? */ + res = open_elf (fileinfo, elf_begin (fileinfo->fd, + is_dso_p (fileinfo->fd) + ? ELF_C_READ_MMAP + : ELF_C_READ_MMAP_PRIVATE, NULL)); + if (unlikely (res != 0)) + return res; + } + + /* Now that we have opened the file start processing it. */ + if (likely (fileinfo->status != closed)) + res = file_process2 (fileinfo); + + /* Determine which file to look at next. */ + if (unlikely (fileinfo->group_backref != NULL)) + { + /* We only go back if an archive other than the one we would go + back to has been used in the last round. */ + if (ld_state.last_archive_used > fileinfo->group_backref->archive_seq) + { + *nextp = fileinfo->group_backref; + ld_state.last_archive_used = 0; + } + else + { + /* If we come here this means that the archives we read so + far are not needed anymore. We can free some of the data + now. */ + struct usedfiles *runp = ld_state.archives; + + do + { + /* We don't need the ELF descriptor anymore. Unless there + are no files from the archive used this will not free + the whole file but only some data structures. */ + elf_end (runp->elf); + runp->elf = NULL; + + runp = runp->next; + } + while (runp != fileinfo->next); + } + } + else if (unlikely (fileinfo->group_end)) + { + /* This is the end of a group. We possibly of to go back. + Determine which file we would go back to and see whether it + makes sense. If there has not been an archive we don't have + to do anything. */ + if (!ld_state.group_start_requested) + { + if (ld_state.group_start_archive != ld_state.tailarchives) + /* The loop would include more than one archive, add the + pointer. */ + { + *nextp = ld_state.tailarchives->group_backref = + ld_state.group_start_archive; + ld_state.last_archive_used = 0; + } + else + /* We might still have to go back to the beginning of the + group if since the last archive other files have been + added. But we go back exactly once. */ + if (ld_state.tailarchives != fileinfo) + { + *nextp = ld_state.group_start_archive; + ld_state.last_archive_used = 0; + } + } + + /* Clear the flags. */ + ld_state.group_start_requested = false; + fileinfo->group_end = false; + } + + return res; +} + + +/* Library names passed to the linker as -lXX represent files named + libXX.YY. The YY part can have different forms, depending on the + platform. The generic set is .so and .a (in this order). */ +static const char ** +ld_generic_lib_extensions (struct ld_state *statep __attribute__ ((__unused__))) +{ + static const char *exts[] = + { + ".so", ".a", NULL + }; + + return exts; +} + + +/* Flag unresolved symbols. */ +static int +ld_generic_flag_unresolved (struct ld_state *statep) +{ + int retval = 0; + + if (ld_state.nunresolved_nonweak > 0) + { + /* Go through the list and determine the unresolved symbols. */ + struct symbol *first; + struct symbol *s; + + s = first = ld_state.unresolved->next; + do + { + if (! s->defined && ! s->weak) + { + /* Two special symbol we recognize: the symbol for the + GOT and the dynamic section. */ + if (strcmp (s->name, "_GLOBAL_OFFSET_TABLE_") == 0 + || strcmp (s->name, "_DYNAMIC") == 0) + { + /* We will have to fill in more information later. */ + ld_state.need_got = true; + + /* Remember that we found it. */ + if (s->name[1] == 'G') + ld_state.got_symbol = s; + else + ld_state.dyn_symbol = s; + } + else if (ld_state.file_type != dso_file_type || !ld_state.nodefs) + { + /* XXX The error message should get better. It should use + the debugging information if present to tell where in the + sources the undefined reference is. */ + error (0, 0, gettext ("undefined symbol `%s' in %s"), + s->name, s->file->fname); + + retval = 1; + } + } + + /* We cannot decide here what to do with undefined + references which will come from DSO since we do not know + what kind of symbol we expect. Only when looking at the + relocations we can see whether we need a PLT entry or + only a GOT entry. */ + + s = s->next; + } + while (s != first); + } + + return retval; +} + + +/* Close the given file. */ +static int +ld_generic_file_close (struct usedfiles *fileinfo, struct ld_state *statep) +{ + /* Close the ELF descriptor. */ + elf_end (fileinfo->elf); + + /* If we have opened the file descriptor close it. But we might + have done this already in which case FD is -1. */ + if (!fileinfo->fd_passed && fileinfo->fd != -1) + close (fileinfo->fd); + + /* We allocated the resolved file name. */ + if (fileinfo->fname != fileinfo->rfname) + free ((char *) fileinfo->rfname); + + return 0; +} + + +static void +new_generated_scn (enum scn_kind kind, const char *name, int type, int flags, + int entsize, int align) +{ + struct scnhead *newp; + + newp = (struct scnhead *) obstack_calloc (&ld_state.smem, + sizeof (struct scnhead)); + newp->kind = kind; + newp->name = name; + newp->nameent = ebl_strtabadd (ld_state.shstrtab, name, 0); + newp->type = type; + newp->flags = flags; + newp->entsize = entsize; + newp->align = align; + newp->grp_signature = NULL; + newp->used = true; + + /* All is well. Create now the data for the section and insert it + into the section table. */ + ld_section_tab_insert (&ld_state.section_tab, elf_hash (name), newp); +} + + +/* Create the sections which are generated by the linker and are not + present in the input file. */ +static void +ld_generic_generate_sections (struct ld_state *statep) +{ + /* The relocation section type. */ + int rel_type = REL_TYPE (&ld_state) == DT_REL ? SHT_REL : SHT_RELA; + + /* When building dynamically linked object we have to include a + section containing a string describing the interpreter. This + should be at the very beginning of the file together with the + other information the ELF loader (kernel or wherever) has to look + at. We put it as the first section in the file. + + We also have to create the dynamic segment which is a special + section the dynamic linker locates through an entry in the + program header. */ + if (dynamically_linked_p ()) + { + int ndt_needed; + /* Use any versioning (defined or required)? */ + bool use_versioning = false; + /* Use version requirements? */ + bool need_version = false; + + /* First the .interp section. */ + new_generated_scn (scn_dot_interp, ".interp", SHT_PROGBITS, SHF_ALLOC, + 0, 1); + + /* Now the .dynamic section. */ + new_generated_scn (scn_dot_dynamic, ".dynamic", SHT_DYNAMIC, + DYNAMIC_SECTION_FLAGS (&ld_state), + xelf_fsize (ld_state.outelf, ELF_T_DYN, 1), + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1)); + + /* We will need in any case the dynamic symbol table (even in + the unlikely case that no symbol is exported or referenced + from a DSO). */ + ld_state.need_dynsym = true; + new_generated_scn (scn_dot_dynsym, ".dynsym", SHT_DYNSYM, SHF_ALLOC, + xelf_fsize (ld_state.outelf, ELF_T_SYM, 1), + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1)); + /* It comes with a string table. */ + new_generated_scn (scn_dot_dynstr, ".dynstr", SHT_STRTAB, SHF_ALLOC, + 0, 1); + /* And a hashing table. */ + // XXX For Linux/Alpha we need other sizes unless they change... + new_generated_scn (scn_dot_hash, ".hash", SHT_HASH, SHF_ALLOC, + sizeof (Elf32_Word), sizeof (Elf32_Word)); + + /* By default we add all DSOs provided on the command line. If + the user added '-z ignore' to the command line we only add + those which are actually used. */ + ndt_needed = ld_state.ignore_unused_dsos ? 0 : ld_state.ndsofiles; + + /* Create the section associated with the PLT if necessary. */ + if (ld_state.nplt > 0) + { + /* Create the .plt section. */ + /* XXX We might need a function which returns the section flags. */ + new_generated_scn (scn_dot_plt, ".plt", SHT_PROGBITS, + SHF_ALLOC | SHF_EXECINSTR, + /* XXX Is the size correct? */ + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1), + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1)); + + /* Create the relocation section for the .plt. This is always + separate even if the other relocation sections are combined. */ + new_generated_scn (scn_dot_pltrel, ".rel.plt", rel_type, SHF_ALLOC, + rel_type == SHT_REL + ? xelf_fsize (ld_state.outelf, ELF_T_REL, 1) + : xelf_fsize (ld_state.outelf, ELF_T_RELA, 1), + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1)); + + /* This means we will also need the .got section. */ + ld_state.need_got = true; + + /* Mark all used DSOs as used. Determine whether any referenced + object uses symbol versioning. */ + if (ld_state.from_dso != NULL) + { + struct symbol *srunp = ld_state.from_dso; + + do + { + srunp->file->used = true; + + if (srunp->file->verdefdata != NULL) + { + XElf_Versym versym; + + /* The input DSO uses versioning. */ + use_versioning = true; + /* We reference versions. */ + need_version = true; + + if (xelf_getversym_copy (srunp->file->versymdata, + srunp->symidx, versym) == NULL) + assert (! "xelf_getversym failed"); + + /* We cannot link explicitly with an older + version of a symbol. */ + assert ((versym & 0x8000) == 0); + /* We cannot reference local (index 0) or plain + global (index 1) versions. */ + assert (versym > 1); + + /* Check whether we have already seen the + version and if not add it to the referenced + versions in the output file. */ + if (! srunp->file->verdefused[versym]) + { + srunp->file->verdefused[versym] = 1; + + if (++srunp->file->nverdefused == 1) + /* Count the file if it is using versioning. */ + ++ld_state.nverdeffile; + ++ld_state.nverdefused; + } + } + } + while ((srunp = srunp->next) != ld_state.from_dso); + } + + /* Create the sections used to record version dependencies. */ + if (need_version) + new_generated_scn (scn_dot_version_r, ".gnu.version_r", + SHT_GNU_verneed, SHF_ALLOC, 0, + xelf_fsize (ld_state.outelf, ELF_T_WORD, 1)); + + /* Now count the used DSOs since this is what the user + wants. */ + ndt_needed = 0; + if (ld_state.ndsofiles > 0) + { + struct usedfiles *frunp = ld_state.dsofiles; + + do + if (! ld_state.ignore_unused_dsos || frunp->used) + { + ++ndt_needed; + if (frunp->lazyload) + /* We have to create another dynamic section + entry for the DT_POSFLAG_1 entry. + + XXX Once more functionality than the + lazyloading flag are suppported the test + must be extended. */ + ++ndt_needed; + } + while ((frunp = frunp->next) != ld_state.dsofiles); + } + } + + if (use_versioning) + new_generated_scn (scn_dot_version, ".gnu.version", SHT_GNU_versym, + SHF_ALLOC, + xelf_fsize (ld_state.outelf, ELF_T_HALF, 1), + xelf_fsize (ld_state.outelf, ELF_T_HALF, 1)); + + /* We need some entries all the time. */ + ld_state.ndynamic = (7 + (ld_state.runpath != NULL + || ld_state.rpath != NULL) + + ndt_needed + + (ld_state.init_symbol != NULL ? 1 : 0) + + (ld_state.fini_symbol != NULL ? 1 : 0) + + (use_versioning ? 1 : 0) + + (need_version ? 2 : 0) + + (ld_state.nplt > 0 ? 4 : 0) + + (ld_state.relsize_total > 0 ? 3 : 0)); + } + + /* When creating a relocatable file or when we are not stripping the + output file we create a symbol table. */ + ld_state.need_symtab = (ld_state.file_type == relocatable_file_type + || ld_state.strip == strip_none); + + /* Add the .got section if needed. */ + if (ld_state.need_got) + /* XXX We might need a function which returns the section flags. */ + new_generated_scn (scn_dot_got, ".got", SHT_PROGBITS, + SHF_ALLOC | SHF_WRITE, + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1), + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1)); + + /* Add the .rel.dyn section. */ + if (ld_state.relsize_total > 0) + new_generated_scn (scn_dot_dynrel, ".rel.dyn", rel_type, SHF_ALLOC, + rel_type == SHT_REL + ? xelf_fsize (ld_state.outelf, ELF_T_REL, 1) + : xelf_fsize (ld_state.outelf, ELF_T_RELA, 1), + xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1)); +} + + +/* Callback function registered with on_exit to make sure the temporary + files gets removed if something goes wrong. */ +static void +remove_tempfile (int status, void *arg) +{ + if (status != 0 && ld_state.tempfname != NULL) + unlink (ld_state.tempfname); +} + + +/* Create the output file. The file name is given or "a.out". We + create as much of the ELF structure as possible. */ +static int +ld_generic_open_outfile (struct ld_state *statep, int machine, int klass, + int data) +{ + /* We do not create the new file right away with the final name. + This would destroy an existing file with this name before a + replacement is finalized. We create instead a temporary file in + the same directory. */ + if (ld_state.outfname == NULL) + ld_state.outfname = "a.out"; + + size_t outfname_len = strlen (ld_state.outfname); + char *tempfname = (char *) obstack_alloc (&ld_state.smem, + outfname_len + sizeof (".XXXXXX")); + ld_state.tempfname = tempfname; + + int fd; + int try = 0; + while (1) + { + strcpy (mempcpy (tempfname, ld_state.outfname, outfname_len), ".XXXXXX"); + + /* The useof mktemp() here is fine. We do not want to use + mkstemp() since then the umask isn't used. And the output + file will have these permissions anyhow. Any intruder could + change the file later if it would be possible now. */ + if (mktemp (tempfname) != NULL + && (fd = open (tempfname, O_RDWR | O_EXCL | O_CREAT | O_NOFOLLOW, + ld_state.file_type == relocatable_file_type + ? DEFFILEMODE : ACCESSPERMS)) != -1) + break; + + /* Failed this round. We keep trying a number of times. */ + if (++try >= 10) + error (EXIT_FAILURE, errno, gettext ("cannot create output file")); + } + ld_state.outfd = fd; + + /* Make sure we remove the temporary file in case something goes + wrong. */ + on_exit (remove_tempfile, NULL); + + /* Create the ELF file data for the output file. */ + Elf *elf = ld_state.outelf = elf_begin (fd, + conserve_memory + ? ELF_C_WRITE : ELF_C_WRITE_MMAP, + NULL); + if (elf == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create ELF descriptor for output file: %s"), + elf_errmsg (-1)); + + /* Create the basic data structures. */ + if (! xelf_newehdr (elf, klass)) + /* Couldn't create the ELF header. Very bad. */ + error (EXIT_FAILURE, 0, + gettext ("could not create ELF header for output file: %s"), + elf_errmsg (-1)); + + /* And get the current header so that we can modify it. */ + XElf_Ehdr_vardef (ehdr); + xelf_getehdr (elf, ehdr); + assert (ehdr != NULL); + + /* Set the machine type. */ + ehdr->e_machine = machine; + + /* Modify it according to the info we have here and now. */ + if (ld_state.file_type == executable_file_type) + ehdr->e_type = ET_EXEC; + else if (ld_state.file_type == dso_file_type) + ehdr->e_type = ET_DYN; + else + { + assert (ld_state.file_type == relocatable_file_type); + ehdr->e_type = ET_REL; + } + + /* Set the ELF version. */ + ehdr->e_version = EV_CURRENT; + + /* Set the endianness. */ + ehdr->e_ident[EI_DATA] = data; + + /* Write the ELF header information back. */ + (void) xelf_update_ehdr (elf, ehdr); + + return 0; +} + + +/* We compute the offsets of the various copied objects and the total + size of the memory needed. */ +// XXX The method used here is simple: go from front to back and pack +// the objects in this order. A more space efficient way would +// actually trying to pack the objects as dense as possible. But this +// is more expensive. +static void +compute_copy_reloc_offset (XElf_Shdr *shdr) +{ + struct symbol *runp = ld_state.from_dso; + assert (runp != NULL); + + XElf_Off maxalign = 1; + XElf_Off offset = 0; + + do + if (runp->need_copy) + { + /* Determine alignment for the symbol. */ + // XXX The question is how? The symbol record itself does not + // have the information. So we have to be conservative and + // assume the alignment of the section the symbol is in. + + // XXX We can be more precise. Use the offset from the beginning + // of the section and determine the largest power of two with + // module zero. + XElf_Off symalign = MAX (SCNINFO_SHDR (runp->file->scninfo[runp->scndx].shdr).sh_addralign, 1); + /* Keep track of the maximum alignment requirement. */ + maxalign = MAX (maxalign, symalign); + + /* Align current position. */ + offset = (offset + symalign - 1) & ~(symalign - 1); + + runp->merge.value = offset; + + offset += runp->size; + } + while ((runp = runp->next) != ld_state.from_dso); + + shdr->sh_type = SHT_NOBITS; + shdr->sh_size = offset; + shdr->sh_addralign = maxalign; +} + + +static void +compute_common_symbol_offset (XElf_Shdr *shdr) +{ + struct symbol *runp = ld_state.common_syms; + assert (runp != NULL); + + XElf_Off maxalign = 1; + XElf_Off offset = 0; + + do + { + /* Determine alignment for the symbol. */ + XElf_Off symalign = runp->merge.value; + + /* Keep track of the maximum alignment requirement. */ + maxalign = MAX (maxalign, symalign); + + /* Align current position. */ + offset = (offset + symalign - 1) & ~(symalign - 1); + + runp->merge.value = offset; + + offset += runp->size; + } + while ((runp = runp->next) != ld_state.common_syms); + + shdr->sh_type = SHT_NOBITS; + shdr->sh_size = offset; + shdr->sh_addralign = maxalign; +} + + +static void +sort_sections_generic (void) +{ + /* XXX TBI */ + abort (); +} + + +static int +match_section (const char *osectname, struct filemask_section_name *sectmask, + struct scnhead **scnhead, bool new_section, size_t segment_nr) +{ + struct scninfo *prevp; + struct scninfo *runp; + struct scninfo *notused; + + if (fnmatch (sectmask->section_name->name, (*scnhead)->name, 0) != 0) + /* The section name does not match. */ + return new_section; + + /* If this is a section generated by the linker it doesn't contain + the regular information (i.e., input section data etc) and must + be handle special. */ + if ((*scnhead)->kind != scn_normal) + { + (*scnhead)->name = osectname; + (*scnhead)->segment_nr = segment_nr; + + /* We have to count note section since they get their own + program header entry. */ + if ((*scnhead)->type == SHT_NOTE) + ++ld_state.nnotesections; + + ld_state.allsections[ld_state.nallsections++] = (*scnhead); + return true; + } + + /* Now we have to match the file names of the input files. Some of + the sections here might not match. */ + runp = (*scnhead)->last->next; + prevp = (*scnhead)->last; + notused = NULL; + + do + { + /* Base of the file name the section comes from. */ + const char *brfname = basename (runp->fileinfo->rfname); + + /* If the section isn't used, the name doesn't match the positive + inclusion list or the name does match the negative inclusion + list, ignore the section. */ + if (!runp->used + || (sectmask->filemask != NULL + && fnmatch (sectmask->filemask, brfname, 0) != 0) + || (sectmask->excludemask != NULL + && fnmatch (sectmask->excludemask, brfname, 0) == 0)) + { + /* This file does not match the file name masks. */ + if (notused == NULL) + notused = runp; + + prevp = runp; + runp = runp->next; + if (runp == notused) + runp = NULL; + } + /* The section fulfills all requirements, add it to the output + file with the correct section name etc. */ + else + { + struct scninfo *found = runp; + + /* Remove this input section data buffer from the list. */ + if (prevp != runp) + runp = prevp->next = runp->next; + else + { + free (*scnhead); + *scnhead = NULL; + runp = NULL; + } + + /* Create a new section for the output file if the 'new_section' + flag says so. Otherwise append the buffer to the last + section which we created in one of the last calls. */ + if (new_section) + { + struct scnhead *newp; + + newp = (struct scnhead *) obstack_calloc (&ld_state.smem, + sizeof (*newp)); + newp->kind = scn_normal; + newp->name = osectname; + newp->type = SCNINFO_SHDR (found->shdr).sh_type; + newp->flags = SCNINFO_SHDR (found->shdr).sh_flags; + newp->segment_nr = segment_nr; + newp->last = found->next = found; + newp->used = true; + newp->relsize = found->relsize; + newp->entsize = SCNINFO_SHDR (found->shdr).sh_entsize; + + /* We have to count note section since they get their own + program header entry. */ + if (newp->type == SHT_NOTE) + ++ld_state.nnotesections; + + ld_state.allsections[ld_state.nallsections++] = newp; + new_section = false; + } + else + { + struct scnhead *queued; + + queued = ld_state.allsections[ld_state.nallsections - 1]; + + found->next = queued->last->next; + queued->last = queued->last->next = found; + + /* If the linker script forces us to add incompatible + sections together do so. But reflect this in the + type and flags of the resulting file. */ + if (queued->type != SCNINFO_SHDR (found->shdr).sh_type) + /* XXX Any better choice? */ + queued->type = SHT_PROGBITS; + if (queued->flags != SCNINFO_SHDR (found->shdr).sh_flags) + queued->flags = ebl_sh_flags_combine (ld_state.ebl, + queued->flags, + SCNINFO_SHDR (found->shdr).sh_flags); + + /* Accumulate the relocation section size. */ + queued->relsize += found->relsize; + } + } + } + while (runp != NULL); + + return new_section; +} + + +static void +sort_sections_lscript (void) +{ + struct scnhead *temp[ld_state.nallsections]; + + /* Make a copy of the section head pointer array. */ + memcpy (temp, ld_state.allsections, + ld_state.nallsections * sizeof (temp[0])); + size_t nallsections = ld_state.nallsections; + + /* Convert the output segment list in a single-linked list. */ + struct output_segment *segment = ld_state.output_segments->next; + ld_state.output_segments->next = NULL; + ld_state.output_segments = segment; + + /* Put the sections in the correct order in the array in the state + structure. This might involve merging of sections and also + renaming the containing section in the output file. */ + ld_state.nallsections = 0; + size_t segment_nr; + size_t last_writable = ~0ul; + for (segment_nr = 0; segment != NULL; segment = segment->next, ++segment_nr) + { + struct output_rule *orule; + + for (orule = segment->output_rules; orule != NULL; orule = orule->next) + if (orule->tag == output_section) + { + struct input_rule *irule; + bool new_section = true; + + for (irule = orule->val.section.input; irule != NULL; + irule = irule->next) + if (irule->tag == input_section) + { + size_t cnt; + + for (cnt = 0; cnt < nallsections; ++cnt) + if (temp[cnt] != NULL) + new_section = + match_section (orule->val.section.name, + irule->val.section, &temp[cnt], + new_section, segment_nr); + } + } + + if ((segment->mode & PF_W) != 0) + last_writable = ld_state.nallsections - 1; + } + + /* In case we have to create copy relocations or we have common + symbols, find the last writable segment and add one more data + block. It will be a NOBITS block and take up no disk space. + This is why it is important to get the last block. */ + if (ld_state.ncopy > 0 || ld_state.common_syms != NULL) + { + if (last_writable == ~0ul) + error (EXIT_FAILURE, 0, "no writable segment"); + + if (ld_state.allsections[last_writable]->type != SHT_NOBITS) + { + /* Make room in the ALLSECTIONS array for a new section. + There is guaranteed room in the array. We add the new + entry after the last writable section. */ + ++last_writable; + memmove (&ld_state.allsections[last_writable + 1], + &ld_state.allsections[last_writable], + (ld_state.nallsections - last_writable) + * sizeof (ld_state.allsections[0])); + + ld_state.allsections[last_writable] = (struct scnhead *) + obstack_calloc (&ld_state.smem, sizeof (struct scnhead)); + + /* Name for the new section. */ + ld_state.allsections[last_writable]->name = ".bss"; + /* Type: NOBITS. */ + ld_state.allsections[last_writable]->type = SHT_NOBITS; + /* Same segment as the last writable section. */ + ld_state.allsections[last_writable]->segment_nr + = ld_state.allsections[last_writable - 1]->segment_nr; + } + } + + /* Create common symbol data block. */ + if (ld_state.ncopy > 0) + { +#if NATIVE_ELF + struct scninfo *si = (struct scninfo *) + obstack_calloc (&ld_state.smem, sizeof (*si) + sizeof (XElf_Shdr)); + si->shdr = (XElf_Shdr *) (si + 1); +#else + struct scninfo *si = (struct scninfo *) obstack_calloc (&ld_state.smem, + sizeof (*si)); +#endif + + /* Get the information regarding the symbols with copy relocations. */ + compute_copy_reloc_offset (&SCNINFO_SHDR (si->shdr)); + + /* This section is needed. */ + si->used = true; + /* Remember for later the section data structure. */ + ld_state.copy_section = si; + + if (likely (ld_state.allsections[last_writable]->last != NULL)) + { + si->next = ld_state.allsections[last_writable]->last->next; + ld_state.allsections[last_writable]->last->next = si; + ld_state.allsections[last_writable]->last = si; + } + else + ld_state.allsections[last_writable]->last = si->next = si; + } + + /* Create common symbol data block. */ + if (ld_state.common_syms != NULL) + { +#if NATIVE_ELF + struct scninfo *si = (struct scninfo *) + obstack_calloc (&ld_state.smem, sizeof (*si) + sizeof (XElf_Shdr)); + si->shdr = (XElf_Shdr *) (si + 1); +#else + struct scninfo *si = (struct scninfo *) obstack_calloc (&ld_state.smem, + sizeof (*si)); +#endif + + /* Get the information regarding the symbols with copy relocations. */ + compute_common_symbol_offset (&SCNINFO_SHDR (si->shdr)); + + /* This section is needed. */ + si->used = true; + /* Remember for later the section data structure. */ + ld_state.common_section = si; + + if (likely (ld_state.allsections[last_writable]->last != NULL)) + { + si->next = ld_state.allsections[last_writable]->last->next; + ld_state.allsections[last_writable]->last->next = si; + ld_state.allsections[last_writable]->last = si; + } + else + ld_state.allsections[last_writable]->last = si->next = si; + } +} + + +/* Create the output sections now. This requires knowledge about all + the sections we will need. It may be necessary to sort sections in + the order they are supposed to appear in the executable. The + sorting use many different kinds of information to optimize the + resulting binary. Important is to respect segment boundaries and + the needed alignment. The mode of the segments will be determined + afterwards automatically by the output routines. + + The generic sorting routines work in one of two possible ways: + + - if a linker script specifies the sections to be used in the + output and assigns them to a segment this information is used; + + - otherwise the linker will order the sections based on permissions + and some special knowledge about section names.*/ +static void +ld_generic_create_sections (struct ld_state *statep) +{ + struct scngroup *groups; + size_t cnt; + + /* For relocatable object we don't have to bother sorting the + sections and we do want to preserve the relocation sections as + they appear in the input files. */ + if (ld_state.file_type != relocatable_file_type) + { + /* Collect all the relocation sections. They are handled + separately. */ + struct scninfo *list = NULL; + for (cnt = 0; cnt < ld_state.nallsections; ++cnt) + if ((ld_state.allsections[cnt]->type == SHT_REL + || ld_state.allsections[cnt]->type == SHT_RELA) + /* The generated relocation sections are not of any + interest here. */ + && ld_state.allsections[cnt]->last != NULL) + { + if (list == NULL) + list = ld_state.allsections[cnt]->last; + else + { + /* Merge the sections list. */ + struct scninfo *first = list->next; + list->next = ld_state.allsections[cnt]->last->next; + ld_state.allsections[cnt]->last->next = first; + list = ld_state.allsections[cnt]->last; + } + + /* Remove the entry from the section list. */ + ld_state.allsections[cnt] = NULL; + } + ld_state.rellist = list; + + if (ld_state.output_segments == NULL) + /* Sort using builtin rules. */ + sort_sections_generic (); + else + sort_sections_lscript (); + } + + /* Now iterate over the input sections and create the sections in the + order they are required in the output file. */ + for (cnt = 0; cnt < ld_state.nallsections; ++cnt) + { + struct scnhead *head = ld_state.allsections[cnt]; + Elf_Scn *scn; + XElf_Shdr_vardef (shdr); + + /* Don't handle unused sections. */ + if (!head->used) + continue; + + /* We first have to create the section group if necessary. + Section group sections must come (in section index order) + before any of the section contained. This all is necessary + only for relocatable object as other object types are not + allowed to contain section groups. */ + if (ld_state.file_type == relocatable_file_type + && unlikely (head->flags & SHF_GROUP)) + { + /* There is at least one section which is contained in a + section group in the input file. This means we must + create a section group here as well. The only problem is + that not all input files have to have to same kind of + partitioning of the sections. I.e., sections A and B in + one input file and sections B and C in another input file + can be in one group. That will result in a group + containing the sections A, B, and C in the output + file. */ + struct scninfo *runp; + Elf32_Word here_groupidx = 0; + struct scngroup *here_group; + struct member *newp; + + /* First check whether any section is already in a group. + In this case we have to add this output section, too. */ + runp = head->last; + do + { + assert (runp->grpid != 0); + + here_groupidx = runp->fileinfo->scninfo[runp->grpid].outscnndx; + if (here_groupidx != 0) + break; + } + while ((runp = runp->next) != head->last); + + if (here_groupidx == 0) + { + /* We need a new section group section. */ + scn = elf_newscn (ld_state.outelf); + xelf_getshdr (scn, shdr); + if (shdr == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + here_group = (struct scngroup *) xmalloc (sizeof (*here_group)); + here_group->outscnidx = here_groupidx = elf_ndxscn (scn); + here_group->nscns = 0; + here_group->member = NULL; + here_group->next = ld_state.groups; + /* Pick a name for the section. To keep it meaningful + we use a name used in the input files. If the + section group in the output file should contain + section which were in section groups of different + names in the input files this is the users + problem. */ + here_group->nameent + = ebl_strtabadd (ld_state.shstrtab, + elf_strptr (runp->fileinfo->elf, + runp->fileinfo->shstrndx, + SCNINFO_SHDR (runp->shdr).sh_name), + 0); + /* Signature symbol. */ + here_group->symbol + = runp->fileinfo->scninfo[runp->grpid].symbols; + + ld_state.groups = here_group; + } + else + { + /* Search for the group with this index. */ + here_group = ld_state.groups; + while (here_group->outscnidx != here_groupidx) + here_group = here_group->next; + } + + /* Add the new output section. */ + newp = (struct member *) alloca (sizeof (*newp)); + newp->scn = head; +#ifndef NDT_NEEDED + newp->next = NULL; +#endif + CSNGL_LIST_ADD_REAR (here_group->member, newp); + ++here_group->nscns; + + /* Store the section group index in all input files. */ + runp = head->last; + do + { + assert (runp->grpid != 0); + + if (runp->fileinfo->scninfo[runp->grpid].outscnndx == 0) + runp->fileinfo->scninfo[runp->grpid].outscnndx = here_groupidx; + else + assert (runp->fileinfo->scninfo[runp->grpid].outscnndx + == here_groupidx); + } + while ((runp = runp->next) != head->last); + } + + /* We'll use this section so get it's name in the section header + string table. */ + if (head->kind == scn_normal) + head->nameent = ebl_strtabadd (ld_state.shstrtab, head->name, 0); + + /* Create a new section in the output file and add all data + from all the sections we read. */ + scn = elf_newscn (ld_state.outelf); + head->scnidx = elf_ndxscn (scn); + xelf_getshdr (scn, shdr); + if (shdr == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + assert (head->type != SHT_NULL); + assert (head->type != SHT_SYMTAB); + assert (head->type != SHT_DYNSYM || head->kind != scn_normal); + assert (head->type != SHT_STRTAB || head->kind != scn_normal); + assert (head->type != SHT_GROUP); + shdr->sh_type = head->type; + shdr->sh_flags = head->flags; + shdr->sh_addralign = head->align; + shdr->sh_entsize = head->entsize; + assert (shdr->sh_entsize != 0 || (shdr->sh_flags & SHF_MERGE) == 0); + (void) xelf_update_shdr (scn, shdr); + + /* We have to know the section index of the dynamic symbol table + right away. */ + if (head->kind == scn_dot_dynsym) + ld_state.dynsymscnidx = elf_ndxscn (scn); + } + + /* Actually create the section group sections. */ + groups = ld_state.groups; + while (groups != NULL) + { + Elf_Scn *scn; + Elf_Data *data; + Elf32_Word *grpdata; + struct member *runp; + + scn = elf_getscn (ld_state.outelf, groups->outscnidx); + assert (scn != NULL); + + data = elf_newdata (scn); + if (data == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + data->d_size = (groups->nscns + 1) * sizeof (Elf32_Word); + data->d_buf = grpdata = (Elf32_Word *) xmalloc (data->d_size); + data->d_type = ELF_T_WORD; + data->d_version = EV_CURRENT; + data->d_off = 0; + /* XXX What better to use? */ + data->d_align = sizeof (Elf32_Word); + + /* The first word in the section is the flag word. */ + /* XXX Set COMDATA flag is necessary. */ + grpdata[0] = 0; + + runp = groups->member->next; + cnt = 1; + do + /* Fill in the index of the section. */ + grpdata[cnt++] = runp->scn->scnidx; + while ((runp = runp->next) != groups->member->next); + + groups = groups->next; + } +} + + +static bool +reduce_symbol_p (XElf_Sym *sym, struct Ebl_Strent *strent) +{ + const char *str; + const char *version; + struct id_list search; + struct id_list *verp; + bool result = ld_state.default_bind_local; + + if (XELF_ST_BIND (sym->st_info) == STB_LOCAL || sym->st_shndx == SHN_UNDEF) + /* We don't have to do anything to local symbols here. */ + /* XXX Any section value in [SHN_LORESERVER,SHN_XINDEX) need + special treatment? */ + return false; + + /* XXX Handle other symbol bindings. */ + assert (XELF_ST_BIND (sym->st_info) == STB_GLOBAL + || XELF_ST_BIND (sym->st_info) == STB_WEAK); + + str = ebl_string (strent); + version = strchr (str, VER_CHR); + if (version != NULL) + { + search.id = strndupa (str, version - str); + if (*++version == VER_CHR) + /* Skip the second '@' signalling a default definition. */ + ++version; + } + else + { + search.id = str; + version = ""; + } + + verp = ld_version_str_tab_find (&ld_state.version_str_tab, + elf_hash (search.id), &search); + while (verp != NULL) + { + /* We have this symbol in the version hash table. Now match the + version name. */ + if (strcmp (verp->u.s.versionname, version) == 0) + /* Match! */ + return verp->u.s.local; + + verp = verp->next; + } + + /* XXX Add test for wildcard version symbols. */ + + return result; +} + + +static XElf_Addr +eval_expression (struct expression *expr, XElf_Addr addr) +{ + XElf_Addr val = ~((XElf_Addr) 0); + + switch (expr->tag) + { + case exp_num: + val = expr->val.num; + break; + + case exp_sizeof_headers: + { + /* The 'elf_update' call determine the offset of the first + section. The the size of the header. */ + XElf_Shdr_vardef (shdr); + + xelf_getshdr (elf_getscn (ld_state.outelf, 1), shdr); + assert (shdr != NULL); + + val = shdr->sh_offset; + } + break; + + case exp_pagesize: + val = ld_state.pagesize; + break; + + case exp_id: + /* We are here computing only address expressions. It seems not + to be necessary to handle any variable but ".". Let's avoid + the complication. If it turns up to be needed we can add + it. */ + if (strcmp (expr->val.str, ".") != 0) + error (EXIT_FAILURE, 0, gettext ("\ +address computation expression contains variable '%s'"), + expr->val.str); + + val = addr; + break; + + case exp_mult: + val = (eval_expression (expr->val.binary.left, addr) + * eval_expression (expr->val.binary.right, addr)); + break; + + case exp_div: + val = (eval_expression (expr->val.binary.left, addr) + / eval_expression (expr->val.binary.right, addr)); + break; + + case exp_mod: + val = (eval_expression (expr->val.binary.left, addr) + % eval_expression (expr->val.binary.right, addr)); + break; + + case exp_plus: + val = (eval_expression (expr->val.binary.left, addr) + + eval_expression (expr->val.binary.right, addr)); + break; + + case exp_minus: + val = (eval_expression (expr->val.binary.left, addr) + - eval_expression (expr->val.binary.right, addr)); + break; + + case exp_and: + val = (eval_expression (expr->val.binary.left, addr) + & eval_expression (expr->val.binary.right, addr)); + break; + + case exp_or: + val = (eval_expression (expr->val.binary.left, addr) + | eval_expression (expr->val.binary.right, addr)); + break; + + case exp_align: + val = eval_expression (expr->val.child, addr); + if ((val & (val - 1)) != 0) + error (EXIT_FAILURE, 0, gettext ("argument '%" PRIuMAX "' of ALIGN in address computation expression is no power of two"), + (uintmax_t) val); + val = (addr + val - 1) & ~(val - 1); + break; + } + + return val; +} + + +/* Find a good as possible size for the hash table so that all the + non-zero entries in HASHCODES don't collide too much and the table + isn't too large. There is no exact formular for this so we use a + heuristic. Depending on the optimization level the search is + longer or shorter. */ +static size_t +optimal_bucket_size (Elf32_Word *hashcodes, size_t maxcnt, int optlevel) +{ + size_t minsize; + size_t maxsize; + size_t bestsize; + uint64_t bestcost; + size_t size; + uint32_t *counts; + uint32_t *lengths; + + if (maxcnt == 0) + return 0; + + /* When we are not optimizing we run only very few tests. */ + if (optlevel <= 0) + { + minsize = maxcnt; + maxsize = maxcnt + 10000 / maxcnt; + } + else + { + /* Does not make much sense to start with a smaller table than + one which has at least four collisions. */ + minsize = MAX (1, maxcnt / 4); + /* We look for a best fit in the range of up to eigth times the + number of elements. */ + maxsize = 2 * maxcnt + (6 * MIN (optlevel, 100) * maxcnt) / 100; + } + bestsize = maxcnt; + bestcost = UINT_MAX; + + /* Array for counting the collisions and chain lengths. */ + counts = (uint32_t *) xmalloc ((maxcnt + 1 + maxsize) * sizeof (uint32_t)); + lengths = &counts[maxcnt + 1]; + + for (size = minsize; size <= maxsize; ++size) + { + size_t inner; + uint64_t cost; + uint32_t maxlength; + uint64_t success; + uint32_t acc; + double factor; + + memset (lengths, '\0', size * sizeof (uint32_t)); + memset (counts, '\0', (maxcnt + 1) * sizeof (uint32_t)); + + /* Determine how often each hash bucket is used. */ + for (inner = 0; inner < maxcnt; ++inner) + ++lengths[hashcodes[inner] % size]; + + /* Determine the lengths. */ + maxlength = 0; + for (inner = 0; inner < size; ++inner) + { + ++counts[lengths[inner]]; + + if (lengths[inner] > maxlength) + maxlength = lengths[inner]; + } + + /* Determine successful lookup length. */ + acc = 0; + success = 0; + for (inner = 0; inner <= maxlength; ++inner) + { + acc += inner; + success += counts[inner] * acc; + } + + /* We can compute two factors now: the average length of a + positive search and the average length of a negative search. + We count the number of comparisons which have to look at the + names themselves. Recognizing that the chain ended is not + accounted for since it's almost for free. + + Which lookup is more important depends on the kind of DSO. + If it is a system DSO like libc it is expected that most + lookups succeed. Otherwise most lookups fail. */ + if (ld_state.is_system_library) + factor = (1.0 * (double) success / (double) maxcnt + + 0.3 * (double) maxcnt / (double) size); + else + factor = (0.3 * (double) success / (double) maxcnt + + 1.0 * (double) maxcnt / (double) size); + + /* Combine the lookup cost factor. The 1/16th addend adds + penalties for too large table sizes. */ + cost = (2 + maxcnt + size) * (factor + 1.0 / 16.0); + +#if 0 + printf ("maxcnt = %d, size = %d, cost = %Ld, success = %g, fail = %g, factor = %g\n", + maxcnt, size, cost, (double) success / (double) maxcnt, (double) maxcnt / (double) size, factor); +#endif + + /* Compare with current best results. */ + if (cost < bestcost) + { + bestcost = cost; + bestsize = size; + } + } + + free (counts); + + return bestsize; +} + + +static XElf_Addr +find_entry_point (void) +{ + XElf_Addr result; + + if (ld_state.entry != NULL) + { + struct symbol search = { .name = ld_state.entry }; + struct symbol *syment; + + syment = ld_symbol_tab_find (&ld_state.symbol_tab, + elf_hash (ld_state.entry), &search); + if (syment != NULL && syment->defined) + { + /* We found the symbol. */ + Elf_Data *data = elf_getdata (elf_getscn (ld_state.outelf, + ld_state.symscnidx), NULL); + + XElf_Sym_vardef (sym); + + sym = NULL; + if (data != NULL) + xelf_getsym (data, ld_state.dblindirect[syment->outsymidx], sym); + + if (sym == NULL && ld_state.need_dynsym && syment->outdynsymidx != 0) + { + /* Use the dynamic symbol table if available. */ + data = elf_getdata (elf_getscn (ld_state.outelf, + ld_state.dynsymscnidx), NULL); + + sym = NULL; + if (data != NULL) + xelf_getsym (data, syment->outdynsymidx, sym); + } + + if (sym != NULL) + return sym->st_value; + + /* XXX What to do if the output has no non-dynamic symbol + table and the dynamic symbol table does not contain the + symbol? */ + assert (ld_state.need_symtab); + assert (ld_state.symscnidx != 0); + } + } + + /* We couldn't find the symbol or none was given. Use the first + address of the ".text" section then. */ + + + result = 0; + + /* In DSOs this is no fatal error. They usually have no entry + points. In this case we set the entry point to zero, which makes + sure it will always fail. */ + if (ld_state.file_type == executable_file_type) + { + if (ld_state.entry != NULL) + error (0, 0, gettext ("\ +cannot find entry symbol \"%s\": defaulting to %#0*" PRIx64), + ld_state.entry, + xelf_getclass (ld_state.outelf) == ELFCLASS32 ? 10 : 18, + (uint64_t) result); + else + error (0, 0, gettext ("\ +no entry symbol specified: defaulting to %#0*" PRIx64), + xelf_getclass (ld_state.outelf) == ELFCLASS32 ? 10 : 18, + (uint64_t) result); + } + + return result; +} + + +static void +fillin_special_symbol (struct symbol *symst, size_t scnidx, size_t nsym, + Elf_Data *symdata, struct Ebl_Strtab *strtab) +{ + assert (ld_state.file_type != relocatable_file_type); + + XElf_Sym_vardef (sym); + xelf_getsym_ptr (symdata, nsym, sym); + + /* The name offset will be filled in later. */ + sym->st_name = 0; + /* Traditionally: globally visible. */ + sym->st_info = XELF_ST_INFO (STB_GLOBAL, symst->type); + /* No special visibility or so. */ + sym->st_other = 0; + /* Reference to the GOT or dynamic section. Since the GOT and + dynamic section are only created for executables and DSOs it + cannot be that the section index is too large. */ + assert (scnidx != 0); + assert (scnidx < SHN_LORESERVE || scnidx == SHN_ABS); + sym->st_shndx = scnidx; + /* We want the beginning of the section. */ + sym->st_value = 0; + + /* Determine the size of the section. */ + if (scnidx != SHN_ABS) + { + Elf_Data *data = elf_getdata (elf_getscn (ld_state.outelf, scnidx), + NULL); + assert (data != NULL); + sym->st_size = data->d_size; + /* Make sure there is no second data block. */ + assert (elf_getdata (elf_getscn (ld_state.outelf, scnidx), data) + == NULL); + } + + /* Insert symbol into the symbol table. Note that we do not have to + use xelf_update_symshdx. */ + (void) xelf_update_sym (symdata, nsym, sym); + + /* Cross-references. */ + ndxtosym[nsym] = symst; + symst->outsymidx = nsym; + + /* Add the name to the string table. */ + symstrent[nsym] = ebl_strtabadd (strtab, symst->name, 0); +} + + +static void +new_dynamic_entry (Elf_Data *data, int idx, XElf_Sxword tag, XElf_Addr val) +{ + XElf_Dyn_vardef (dyn); + xelf_getdyn_ptr (data, idx, dyn); + dyn->d_tag = tag; + dyn->d_un.d_ptr = val; + (void) xelf_update_dyn (data, idx, dyn); +} + + +static void +allocate_version_names (struct usedfiles *runp, struct Ebl_Strtab *dynstrtab) +{ + /* If this DSO has no versions skip it. */ + if (runp->status != opened || runp->verdefdata == NULL) + return; + + /* Add the object name. */ + int offset = 0; + while (1) + { + XElf_Verdef_vardef (def); + XElf_Verdaux_vardef (aux); + + /* Get data at the next offset. */ + xelf_getverdef (runp->verdefdata, offset, def); + assert (def != NULL); + xelf_getverdaux (runp->verdefdata, offset + def->vd_aux, aux); + assert (aux != NULL); + + assert (def->vd_ndx <= runp->nverdef); + if (def->vd_ndx == 1 || runp->verdefused[def->vd_ndx] != 0) + { + runp->verdefent[def->vd_ndx] + = ebl_strtabadd (dynstrtab, elf_strptr (runp->elf, + runp->dynsymstridx, + aux->vda_name), 0); + + if (def->vd_ndx > 1) + runp->verdefused[def->vd_ndx] = ld_state.nextveridx++; + } + + if (def->vd_next == 0) + /* That were all versions. */ + break; + + offset += def->vd_next; + } +} + + +XElf_Off +create_verneed_data (XElf_Off offset, Elf_Data *verneeddata, + struct usedfiles *runp, int *ntotal) +{ + size_t verneed_size = xelf_fsize (ld_state.outelf, ELF_T_VNEED, 1); + size_t vernaux_size = xelf_fsize (ld_state.outelf, ELF_T_VNAUX, 1); + int need_offset; + bool filled = false; + GElf_Verneed verneed; + GElf_Vernaux vernaux; + int ndef = 0; +size_t cnt; + + /* If this DSO has no versions skip it. */ + if (runp->nverdefused == 0) + return offset; + + /* We fill in the Verneed record last. Remember the + offset. */ + need_offset = offset; + offset += verneed_size; + + for (cnt = 2; cnt <= runp->nverdef; ++cnt) + if (runp->verdefused[cnt] != 0) + { + assert (runp->verdefent[cnt] != NULL); + + if (filled) + { + vernaux.vna_next = vernaux_size; + (void) gelf_update_vernaux (verneeddata, offset, + &vernaux); + offset += vernaux_size; + } + + vernaux.vna_hash + = elf_hash (ebl_string (runp->verdefent[cnt])); + vernaux.vna_flags = 0; + vernaux.vna_other = runp->verdefused[cnt]; + vernaux.vna_name = ebl_strtaboffset (runp->verdefent[cnt]); + filled = true; + ++ndef; + } + + assert (filled); + vernaux.vna_next = 0; + (void) gelf_update_vernaux (verneeddata, offset, &vernaux); + offset += vernaux_size; + + verneed.vn_version = VER_NEED_CURRENT; + verneed.vn_cnt = ndef; + verneed.vn_file = ebl_strtaboffset (runp->verdefent[1]); + /* The first auxiliary entry is always found directly + after the verneed entry. */ + verneed.vn_aux = verneed_size; + verneed.vn_next = --*ntotal > 0 ? offset - need_offset : 0; + (void) gelf_update_verneed (verneeddata, need_offset, + &verneed); + + return offset; +} + + +/* Create the output file. + + For relocatable files what basically has to happen is that all + sections from all input files are written into the output file. + Sections with the same name are combined (offsets adjusted + accordingly). The symbol tables are combined in one single table. + When stripping certain symbol table entries are omitted. + + For executables (shared or not) we have to create the program header, + additional sections like the .interp, eventually (in addition) create + a dynamic symbol table and a dynamic section. Also the relocations +have to be processed differently. */ +static int +ld_generic_create_outfile (struct ld_state *statep) +{ + struct scnlist + { + size_t scnidx; + struct scninfo *scninfo; + struct scnlist *next; + }; + struct scnlist *rellist = NULL; + size_t cnt; + Elf_Scn *symscn = NULL; + Elf_Scn *xndxscn = NULL; + Elf_Scn *strscn = NULL; + struct Ebl_Strtab *strtab = NULL; + struct Ebl_Strtab *dynstrtab = NULL; + XElf_Shdr_vardef (shdr); + Elf_Data *data; + Elf_Data *symdata = NULL; + Elf_Data *xndxdata = NULL; + struct usedfiles *file; + size_t nsym; + size_t nsym_local; + size_t nsym_allocated; + size_t nsym_dyn = 0; + Elf32_Word *dblindirect = NULL; +#ifndef NDEBUG + bool need_xndx; +#endif + Elf_Scn *shstrtab_scn; + size_t shstrtab_ndx; + XElf_Ehdr_vardef (ehdr); + struct Ebl_Strent *symtab_ent = NULL; + struct Ebl_Strent *xndx_ent = NULL; + struct Ebl_Strent *strtab_ent = NULL; + struct Ebl_Strent *shstrtab_ent; + struct scngroup *groups; + Elf_Scn *dynsymscn = NULL; + Elf_Data *dynsymdata = NULL; + Elf_Data *dynstrdata = NULL; + Elf32_Word *hashcodes = NULL; + size_t nsym_dyn_allocated = 0; + Elf_Scn *versymscn = NULL; + Elf_Data *versymdata = NULL; + + if (ld_state.need_symtab) + { + /* First create the symbol table. We need the symbol section itself + and the string table for it. */ + symscn = elf_newscn (ld_state.outelf); + ld_state.symscnidx = elf_ndxscn (symscn); + symdata = elf_newdata (symscn); + if (symdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create symbol table for output file: %s"), + elf_errmsg (-1)); + + symdata->d_type = ELF_T_SYM; + /* This is an estimated size, but it will definitely cap the real value. + We might have to adjust the number later. */ + nsym_allocated = (1 + ld_state.nsymtab + ld_state.nplt + ld_state.ngot + + ld_state.nusedsections + ld_state.nlscript_syms); + symdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_SYM, + nsym_allocated); + + /* Optionally the extended section table. */ + /* XXX Is SHN_LORESERVE correct? Do we need some other sections? */ + if (unlikely (ld_state.nusedsections >= SHN_LORESERVE)) + { + xndxscn = elf_newscn (ld_state.outelf); + ld_state.xndxscnidx = elf_ndxscn (xndxscn); + + xndxdata = elf_newdata (xndxscn); + if (xndxdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create symbol table for output file: %s"), + elf_errmsg (-1)); + + /* The following relies on the fact that Elf32_Word and Elf64_Word + have the same size. */ + xndxdata->d_type = ELF_T_WORD; + /* This is an estimated size, but it will definitely cap the + real value. we might have to adjust the number later. */ + xndxdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_WORD, + nsym_allocated); + /* The first entry is left empty, clear it here and now. */ + xndxdata->d_buf = memset (xmalloc (xndxdata->d_size), '\0', + xelf_fsize (ld_state.outelf, ELF_T_WORD, + 1)); + xndxdata->d_off = 0; + /* XXX Should use an ebl function. */ + xndxdata->d_align = sizeof (Elf32_Word); + } + } + else + { + assert (ld_state.need_dynsym); + + /* First create the symbol table. We need the symbol section itself + and the string table for it. */ + symscn = elf_getscn (ld_state.outelf, ld_state.dynsymscnidx); + symdata = elf_newdata (symscn); + if (symdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create symbol table for output file: %s"), + elf_errmsg (-1)); + + symdata->d_version = EV_CURRENT; + symdata->d_type = ELF_T_SYM; + /* This is an estimated size, but it will definitely cap the real value. + We might have to adjust the number later. */ + nsym_allocated = (1 + ld_state.nsymtab + ld_state.nplt + ld_state.ngot + - ld_state.nlocalsymbols + ld_state.nlscript_syms); + symdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_SYM, + nsym_allocated); + } + + /* The first entry is left empty, clear it here and now. */ + symdata->d_buf = memset (xmalloc (symdata->d_size), '\0', + xelf_fsize (ld_state.outelf, ELF_T_SYM, 1)); + symdata->d_off = 0; + /* XXX This is ugly but how else can it be done. */ + symdata->d_align = xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1); + + /* Allocate another array to keep track of the handles for the symbol + names. */ + symstrent = (struct Ebl_Strent **) xcalloc (nsym_allocated, + sizeof (struct Ebl_Strent *)); + + /* By starting at 1 we effectively add a null entry. */ + nsym = 1; + + /* Iteration over all sections. */ + for (cnt = 0; cnt < ld_state.nallsections; ++cnt) + { + struct scnhead *head = ld_state.allsections[cnt]; + Elf_Scn *scn; + struct scninfo *runp; + XElf_Off offset; + Elf32_Word xndx; + + /* Don't handle unused sections at all. */ + if (!head->used) + continue; + + /* Get the section handle. */ + scn = elf_getscn (ld_state.outelf, head->scnidx); + + if (unlikely (head->kind == scn_dot_interp)) + { + Elf_Data *outdata = elf_newdata (scn); + if (outdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + /* This is the string we'll put in the section. */ + const char *interp = ld_state.interp ?: "/lib/ld.so.1"; + + /* Create the section data. */ + outdata->d_buf = (void *) interp; + outdata->d_size = strlen (interp) + 1; + outdata->d_type = ELF_T_BYTE; + outdata->d_off = 0; + outdata->d_align = 1; + outdata->d_version = EV_CURRENT; + + /* Remember the index of this section. */ + ld_state.interpscnidx = head->scnidx; + + continue; + } + + if (unlikely (head->kind == scn_dot_got)) + { + /* Remember the index of this section. */ + ld_state.gotscnidx = elf_ndxscn (scn); + + /* Give the backend the change to initialize the section. */ + INITIALIZE_GOT (&ld_state, scn); + + continue; + } + + if (unlikely (head->kind == scn_dot_dynrel)) + { + Elf_Data *outdata; + + outdata = elf_newdata (scn); + if (outdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + outdata->d_size = ld_state.relsize_total; + outdata->d_buf = xmalloc (outdata->d_size); + outdata->d_type = (REL_TYPE (&ld_state) == DT_REL + ? ELF_T_REL : ELF_T_RELA); + outdata->d_off = 0; + outdata->d_align = xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1); + + /* Remember the index of this section. */ + ld_state.reldynscnidx = elf_ndxscn (scn); + + continue; + } + + if (unlikely (head->kind == scn_dot_dynamic)) + { + /* Only create the data for now. */ + Elf_Data *outdata; + + /* Account for a few more entries we have to add. */ + if (ld_state.dt_flags != 0) + ++ld_state.ndynamic; + if (ld_state.dt_flags_1 != 0) + ++ld_state.ndynamic; + if (ld_state.dt_feature_1 != 0) + ++ld_state.ndynamic; + + outdata = elf_newdata (scn); + if (outdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + /* Create the section data. */ + outdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_DYN, + ld_state.ndynamic); + outdata->d_buf = xcalloc (1, outdata->d_size); + outdata->d_type = ELF_T_DYN; + outdata->d_off = 0; + outdata->d_align = xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1); + + /* Remember the index of this section. */ + ld_state.dynamicscnidx = elf_ndxscn (scn); + + continue; + } + + if (unlikely (head->kind == scn_dot_dynsym)) + { + /* We already know the section index. */ + assert (ld_state.dynsymscnidx == elf_ndxscn (scn)); + + continue; + } + + if (unlikely (head->kind == scn_dot_dynstr)) + { + /* Remember the index of this section. */ + ld_state.dynstrscnidx = elf_ndxscn (scn); + + /* Create the string table. */ + dynstrtab = ebl_strtabinit (true); + + /* XXX TBI + We have to add all the strings which are needed in the + dynamic section here. This means DT_FILTER, + DT_AUXILIARY, ... entries. */ + if (ld_state.ndsofiles > 0) + { + struct usedfiles *frunp = ld_state.dsofiles; + + do + if (! ld_state.ignore_unused_dsos || frunp->used) + frunp->sonameent = ebl_strtabadd (dynstrtab, frunp->soname, + 0); + while ((frunp = frunp->next) != ld_state.dsofiles); + } + + + /* Add the runtime path information. The strings are stored + in the .dynstr section. If both rpath and runpath are defined + the runpath information is used. */ + if (ld_state.runpath != NULL || ld_state.rpath != NULL) + { + struct pathelement *startp; + struct pathelement *prunp; + int tag; + size_t len; + char *str; + char *cp; + + if (ld_state.runpath != NULL) + { + startp = ld_state.runpath; + tag = DT_RUNPATH; + } + else + { + startp = ld_state.rpath; + tag = DT_RPATH; + } + + /* Determine how long the string will be. */ + for (len = 0, prunp = startp; prunp != NULL; prunp = prunp->next) + len += strlen (prunp->pname) + 1; + + cp = str = (char *) obstack_alloc (&ld_state.smem, len); + /* Copy the string. */ + for (prunp = startp; prunp != NULL; prunp = prunp->next) + { + cp = stpcpy (cp, prunp->pname); + *cp++ = ':'; + } + /* Remove the last colon. */ + cp[-1] = '\0'; + + /* Remember the values until we can generate the dynamic + section. */ + ld_state.rxxpath_strent = ebl_strtabadd (dynstrtab, str, len); + ld_state.rxxpath_tag = tag; + } + + continue; + } + + if (unlikely (head->kind == scn_dot_hash)) + { + /* Remember the index of this section. */ + ld_state.hashscnidx = elf_ndxscn (scn); + + continue; + } + + if (unlikely (head->kind == scn_dot_plt)) + { + /* Remember the index of this section. */ + ld_state.pltscnidx = elf_ndxscn (scn); + + /* Give the backend the change to initialize the section. */ + INITIALIZE_PLT (&ld_state, scn); + + continue; + } + + if (unlikely (head->kind == scn_dot_pltrel)) + { + /* Remember the index of this section. */ + ld_state.pltrelscnidx = elf_ndxscn (scn); + + /* Give the backend the change to initialize the section. */ + INITIALIZE_PLTREL (&ld_state, scn); + + continue; + } + + if (unlikely (head->kind == scn_dot_version)) + { + /* Remember the index of this section. */ + ld_state.versymscnidx = elf_ndxscn (scn); + + continue; + } + + if (unlikely (head->kind == scn_dot_version_r)) + { + /* Remember the index of this section. */ + ld_state.verneedscnidx = elf_ndxscn (scn); + + continue; + } + + /* If we come here we must be handling a normal section. */ + assert (head->kind == scn_normal); + + /* Create an STT_SECTION entry in the symbol table. But not for + the symbolic symbol table. */ + if (ld_state.need_symtab) + { + /* XXX Can we be cleverer and do this only if needed? */ + XElf_Sym_vardef (sym); + + /* Optimization ahead: in the native linker we get a pointer + to the final location so that the following code writes + directly in the correct place. Otherwise we write into + the local variable first. */ + xelf_getsym_ptr (symdata, nsym, sym); + + /* Usual section symbol: local, no specific information, + except the section index. The offset here is zero, the + start address will later be added. */ + sym->st_name = 0; + sym->st_info = XELF_ST_INFO (STB_LOCAL, STT_SECTION); + sym->st_other = 0; + sym->st_value = 0; + sym->st_size = 0; + /* In relocatable files the section index can be too big for + the ElfXX_Sym struct. we have to deal with the extended + symbol table. */ + if (likely (head->scnidx < SHN_LORESERVE)) + { + sym->st_shndx = head->scnidx; + xndx = 0; + } + else + { + sym->st_shndx = SHN_XINDEX; + xndx = head->scnidx; + } + /* Commit the change. See the optimization above, this does + not change the symbol table entry. But the extended + section index table entry is always written, if there is + such a table. */ + assert (nsym < nsym_allocated); + xelf_update_symshndx (symdata, xndxdata, nsym, sym, xndx, 0); + + /* Remember the symbol's index in the symbol table. */ + head->scnsymidx = nsym++; + } + + if (head->type == SHT_REL || head->type == SHT_RELA) + { + /* Remember that we have to fill in the symbol table section + index. */ + if (ld_state.file_type == relocatable_file_type) + { + struct scnlist *newp; + + newp = (struct scnlist *) alloca (sizeof (*newp)); + newp->scnidx = head->scnidx; + newp->scninfo = head->last->next; +#ifndef NDEBUG + newp->next = NULL; +#endif + SNGL_LIST_PUSH (rellist, newp); + } + else + { + /* When we create an executable or a DSO we don't simply + copy the existing relocations. Instead many will be + resolved, others will be converted. Create a data buffer + large enough to contain the contents which we will fill + in later. */ + int type = head->type == SHT_REL ? ELF_T_REL : ELF_T_RELA; + + data = elf_newdata (scn); + if (data == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + data->d_size = xelf_fsize (ld_state.outelf, type, head->relsize); + data->d_buf = xcalloc (data->d_size, 1); + data->d_type = type; + data->d_align = xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1); + data->d_off = 0; + + continue; + } + } + + /* Recognize string and merge flag and handle them. */ + if (head->flags & SHF_MERGE) + { + /* We merge the contents of the sections. For this we do + not look at the contents of section directly. Instead we + look at the symbols of the section. */ + Elf_Data *outdata; + + /* Concatenate the lists of symbols for all sections. + + XXX In case any input section has no symbols associated + (this happens for debug sections) we cannot use this + method. Implement parsing the other debug sections and + find the string pointers. For now we don't merge. */ + runp = head->last->next; + if (runp->symbols == NULL) + { + head->flags &= ~SHF_MERGE; + goto no_merge; + } + head->symbols = runp->symbols; + + while ((runp = runp->next) != head->last->next) + { + if (runp->symbols == NULL) + { + head->flags &= ~SHF_MERGE; + head->symbols = NULL; + goto no_merge; + } + + struct symbol *oldhead = head->symbols->next_in_scn; + + head->symbols->next_in_scn = runp->symbols->next_in_scn; + runp->symbols->next_in_scn = oldhead; + head->symbols = runp->symbols; + } + + /* Create the output section. */ + outdata = elf_newdata (scn); + if (outdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + /* We use different merging algorithms for performance + reasons. We can easily handle single-byte and + wchar_t-wide character strings. All other cases (which + really should happen in real life) are handled by the + generic code. */ + if (SCNINFO_SHDR (head->last->shdr).sh_entsize == 1 + && (head->flags & SHF_STRINGS)) + { + /* Simple, single-byte string matching. */ + struct Ebl_Strtab *mergestrtab; + struct symbol *symrunp; + Elf_Data *locsymdata = NULL; + Elf_Data *locdata = NULL; + + mergestrtab = ebl_strtabinit (false); + + symrunp = head->symbols->next_in_scn; + file = NULL; + do + { + /* Accelarate the loop. We cache the file + information since it might very well be the case + that the previous entry was from the same + file. */ + if (symrunp->file != file) + { + /* Remember the file. */ + file = symrunp->file; + /* Symbol table data from that file. */ + locsymdata = file->symtabdata; + /* String section data. */ + locdata = elf_rawdata (file->scninfo[symrunp->scndx].scn, + NULL); + assert (locdata != NULL); + /* While we are at it, remember the output + section. If we don't access the string data + section the section won't be in the output + file. So it is sufficient to do the work + here. */ + file->scninfo[symrunp->scndx].outscnndx = head->scnidx; + } + + /* Get the symbol information. This provides us the + offset into the string data section. */ + XElf_Sym_vardef (sym); + xelf_getsym (locsymdata, symrunp->symidx, sym); + assert (sym != NULL); + + /* Get the data from the file. Note that we access + the raw section data; no endian-ness issues with + single-byte strings. */ + symrunp->merge.handle + = ebl_strtabadd (mergestrtab, + (char *) locdata->d_buf + sym->st_value, + 0); + } + while ((symrunp = symrunp->next_in_scn) + != head->symbols->next_in_scn); + + /* All strings have been added. Create the final table. */ + ebl_strtabfinalize (mergestrtab, outdata); + + /* Compute the final offsets in the section. */ + symrunp = runp->symbols; + do + { + symrunp->merge.value + = ebl_strtaboffset (symrunp->merge.handle); + symrunp->merged = 1; + } + while ((symrunp = symrunp->next_in_scn) != runp->symbols); + + /* We don't need the string table anymore. */ + ebl_strtabfree (mergestrtab); + } + else if (likely (SCNINFO_SHDR (head->last->shdr).sh_entsize + == sizeof (wchar_t)) + && likely (head->flags & SHF_STRINGS)) + { + /* Simple, wchar_t string merging. */ + struct Ebl_WStrtab *mergestrtab; + struct symbol *symrunp; + Elf_Data *locsymdata = NULL; + Elf_Data *locdata = NULL; + + mergestrtab = ebl_wstrtabinit (false); + + symrunp = runp->symbols; + file = NULL; + do + { + /* Accelarate the loop. We cache the file + information since it might very well be the case + that the previous entry was from the same + file. */ + if (symrunp->file != file) + { + /* Remember the file. */ + file = symrunp->file; + /* Symbol table data from that file. */ + locsymdata = file->symtabdata; + /* String section data. */ + locdata = elf_rawdata (file->scninfo[symrunp->scndx].scn, + NULL); + assert (locdata != NULL); + + /* While we are at it, remember the output + section. If we don't access the string data + section the section won't be in the output + file. So it is sufficient to do the work + here. */ + file->scninfo[symrunp->scndx].outscnndx = head->scnidx; + } + + /* Get the symbol information. This provides us the + offset into the string data section. */ + XElf_Sym_vardef (sym); + xelf_getsym (locsymdata, symrunp->symidx, sym); + assert (sym != NULL); + + /* Get the data from the file. Using the raw + section data here is possible since we don't + interpret the string themselves except for + looking for the wide NUL character. The NUL + character has fortunately the same representation + regardless of the byte order. */ + symrunp->merge.handle + = ebl_wstrtabadd (mergestrtab, + (wchar_t *) ((char *) locdata->d_buf + + sym->st_value), 0); + } + while ((symrunp = symrunp->next_in_scn) != runp->symbols); + + /* All strings have been added. Create the final table. */ + ebl_wstrtabfinalize (mergestrtab, outdata); + + /* Compute the final offsets in the section. */ + symrunp = runp->symbols; + do + { + symrunp->merge.value + = ebl_wstrtaboffset (symrunp->merge.handle); + symrunp->merged = 1; + } + while ((symrunp = symrunp->next_in_scn) != runp->symbols); + + /* We don't need the string table anymore. */ + ebl_wstrtabfree (mergestrtab); + } + else + { + /* Non-standard merging. */ + struct Ebl_GStrtab *mergestrtab; + struct symbol *symrunp; + Elf_Data *locsymdata = NULL; + Elf_Data *locdata = NULL; + /* If this is no string section the length of each "string" + is always one. */ + unsigned int len = (head->flags & SHF_STRINGS) ? 0 : 1; + + /* This is the generic string table functionality. Much + slower than the specialized code. */ + mergestrtab + = ebl_gstrtabinit (SCNINFO_SHDR (head->last->shdr).sh_entsize, + false); + + symrunp = runp->symbols; + file = NULL; + do + { + /* Accelarate the loop. We cache the file + information since it might very well be the case + that the previous entry was from the same + file. */ + if (symrunp->file != file) + { + /* Remember the file. */ + file = symrunp->file; + /* Symbol table data from that file. */ + locsymdata = file->symtabdata; + /* String section data. */ + locdata = elf_rawdata (file->scninfo[symrunp->scndx].scn, + NULL); + assert (locdata != NULL); + + /* While we are at it, remember the output + section. If we don't access the string data + section the section won't be in the output + file. So it is sufficient to do the work + here. */ + file->scninfo[symrunp->scndx].outscnndx = head->scnidx; + } + + /* Get the symbol information. This provides us the + offset into the string data section. */ + XElf_Sym_vardef (sym); + xelf_getsym (locsymdata, symrunp->symidx, sym); + assert (sym != NULL); + + /* Get the data from the file. Using the raw + section data here is possible since we don't + interpret the string themselves except for + looking for the wide NUL character. The NUL + character has fortunately the same representation + regardless of the byte order. */ + symrunp->merge.handle + = ebl_gstrtabadd (mergestrtab, + (char *) locdata->d_buf + sym->st_value, + len); + } + while ((symrunp = symrunp->next_in_scn) != runp->symbols); + + /* Create the final table. */ + ebl_gstrtabfinalize (mergestrtab, outdata); + + /* Compute the final offsets in the section. */ + symrunp = runp->symbols; + do + { + symrunp->merge.value + = ebl_gstrtaboffset (symrunp->merge.handle); + symrunp->merged = 1; + } + while ((symrunp = symrunp->next_in_scn) != runp->symbols); + + /* We don't need the string table anymore. */ + ebl_gstrtabfree (mergestrtab); + } + } + else + { + no_merge: + assert (head->scnidx == elf_ndxscn (scn)); + + /* It is important to start with the first list entry (and + not just any one) to add the sections in the correct + order. */ + runp = head->last->next; + offset = 0; + do + { + Elf_Data *outdata = elf_newdata (scn); + if (outdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + /* Exceptional case: if we synthesize a data block SCN + is NULL and the sectio header info must be for a + SHT_NOBITS block and the size and alignment are + filled in. */ + if (likely (runp->scn != NULL)) + { + data = elf_getdata (runp->scn, NULL); + assert (data != NULL); + + /* We reuse the data buffer in the input file. */ + *outdata = *data; + + /* Given that we read the input file from disk we know there + cannot be another data part. */ + assert (elf_getdata (runp->scn, data) == NULL); + } + else + { + /* Must be a NOBITS section. */ + assert (SCNINFO_SHDR (runp->shdr).sh_type == SHT_NOBITS); + + outdata->d_buf = NULL; /* Not needed. */ + outdata->d_type = ELF_T_BYTE; + outdata->d_version = EV_CURRENT; + outdata->d_size = SCNINFO_SHDR (runp->shdr).sh_size; + outdata->d_align = SCNINFO_SHDR (runp->shdr).sh_addralign; + } + + XElf_Off align = MAX (1, outdata->d_align); + assert (powerof2 (align)); + offset = ((offset + align - 1) & ~(align - 1)); + + runp->offset = offset; + runp->outscnndx = head->scnidx; + runp->allsectionsidx = cnt; + + outdata->d_off = offset; + + offset += outdata->d_size; + } + while ((runp = runp->next) != head->last->next); + + /* If necessary add the additional line to the .comment section. */ + if (ld_state.add_ld_comment + && head->flags == 0 + && head->type == SHT_PROGBITS + && strcmp (head->name, ".comment") == 0 + && head->entsize == 0) + { + Elf_Data *outdata = elf_newdata (scn); + + if (outdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + outdata->d_buf = (void *) "\0ld (Red Hat " PACKAGE ") " VERSION; + outdata->d_size = strlen ((char *) outdata->d_buf + 1) + 2; + outdata->d_off = offset; + outdata->d_type = ELF_T_BYTE; + outdata->d_align = 1; + } + /* XXX We should create a .comment section if none exists. + This requires that we early on detect that no such + section exists. This should probably be implemented + together with some merging of the section contents. + Currently identical entries are not merged. */ + } + } + + /* The table we collect the strings in. */ + strtab = ebl_strtabinit (true); + if (strtab == NULL) + error (EXIT_FAILURE, errno, gettext ("cannot create string table")); + + +#ifndef NDEBUG + /* Keep track of the use of the XINDEX. */ + need_xndx = false; +#endif + + /* We we generate a normal symbol table for an executable and the + --export-dynamic option is not given, we need an extra table + which keeps track of the symbol entry belonging to the symbol + table entry. Note that EXPORT_ALL_DYNAMIC is always set if we + generate a DSO so we do not have to test this separately. */ + ndxtosym = (struct symbol **) xcalloc (nsym_allocated, + sizeof (struct symbol)); + + /* Create the special symbol for the GOT section. */ + if (ld_state.got_symbol != NULL) + { + assert (nsym < nsym_allocated); + fillin_special_symbol (ld_state.got_symbol, ld_state.gotscnidx, + nsym++, symdata, strtab); + } + + /* Similarly for the dynamic section symbol. */ + if (ld_state.dyn_symbol != NULL) + { + assert (nsym < nsym_allocated); + fillin_special_symbol (ld_state.dyn_symbol, ld_state.dynamicscnidx, + nsym++, symdata, strtab); + } + + /* Create symbol table entries for the symbols defined in the linker + script. */ + if (ld_state.lscript_syms != NULL) + { + struct symbol *rsym = ld_state.lscript_syms; + do + { + assert (nsym < nsym_allocated); + fillin_special_symbol (rsym, SHN_ABS, nsym++, symdata, strtab); + } + while ((rsym = rsym->next) != NULL); + } + + /* Iterate over all input files to collect the symbols. */ + file = ld_state.relfiles->next; + symdata = elf_getdata (elf_getscn (ld_state.outelf, ld_state.symscnidx), + NULL); + do + { + size_t maxcnt; + Elf_Data *insymdata; + Elf_Data *inxndxdata; + + /* There must be no dynamic symbol table when creating + relocatable files. */ + assert (ld_state.file_type != relocatable_file_type + || file->dynsymtabdata == NULL); + + insymdata = file->symtabdata; + assert (insymdata != NULL); + inxndxdata = file->xndxdata; + + maxcnt = file->nsymtab; + + file->symindirect = (Elf32_Word *) xcalloc (maxcnt, sizeof (Elf32_Word)); + + /* The dynamic symbol table does not contain local symbols. So + we skip those entries. */ + for (cnt = ld_state.need_symtab ? 1 : file->nlocalsymbols; cnt < maxcnt; + ++cnt) + { + XElf_Sym_vardef (sym); + Elf32_Word xndx; + struct symbol *defp = NULL; + + xelf_getsymshndx (insymdata, inxndxdata, cnt, sym, xndx); + assert (sym != NULL); + + if (unlikely (XELF_ST_TYPE (sym->st_info) == STT_SECTION)) + { + /* Section symbols should always be local but who knows... */ + if (ld_state.need_symtab) + { + /* Determine the real section index in the source file. + Use the XINDEX section content if necessary. We don't + add this information to the dynamic symbol table. */ + if (sym->st_shndx != SHN_XINDEX) + xndx = sym->st_shndx; + + assert (file->scninfo[xndx].allsectionsidx + < ld_state.nallsections); + file->symindirect[cnt] = ld_state.allsections[file->scninfo[xndx].allsectionsidx]->scnsymidx; + /* Note that the resulting index can be zero here. There is + no guarantee that the output file will contain all the + sections the input file did. */ + } + continue; + } + + if ((ld_state.strip >= strip_all || !ld_state.need_symtab) + /* XXX Do we need these entries? */ + && XELF_ST_TYPE (sym->st_info) == STT_FILE) + continue; + +#if NATIVE_ELF != 0 + /* Copy old data. */ + XElf_Sym *sym2 = sym; + assert (nsym < nsym_allocated); + xelf_getsym (symdata, nsym, sym); + *sym = *sym2; +#endif + + if (sym->st_shndx != SHN_UNDEF + && (sym->st_shndx < SHN_LORESERVE + || sym->st_shndx == SHN_XINDEX)) + { + /* If we are creating an executable with no normal + symbol table and we do not export all symbols and + this symbol is not defined in a DSO as well, ignore + it. */ + if (!ld_state.export_all_dynamic && !ld_state.need_symtab) + { + assert (cnt >= file->nlocalsymbols); + defp = file->symref[cnt]; + assert (defp != NULL); + + if (!defp->in_dso) + /* Ignore it. */ + continue; + } + + /* Determine the real section index in the source file. Use + the XINDEX section content if necessary. */ + if (sym->st_shndx != SHN_XINDEX) + xndx = sym->st_shndx; + + sym->st_value += file->scninfo[xndx].offset; + + assert (file->scninfo[xndx].outscnndx < SHN_LORESERVE + || file->scninfo[xndx].outscnndx > SHN_HIRESERVE); + if (unlikely (file->scninfo[xndx].outscnndx > SHN_LORESERVE)) + { + /* It is not possible to have an extended section index + table for the dynamic symbol table. */ + if (!ld_state.need_symtab) + error (EXIT_FAILURE, 0, gettext ("\ +section index too large in dynamic symbol table")); + + assert (xndxdata != NULL); + sym->st_shndx = SHN_XINDEX; + xndx = file->scninfo[xndx].outscnndx; +#ifndef NDEBUG + need_xndx = true; +#endif + } + else + { + sym->st_shndx = file->scninfo[xndx].outscnndx; + xndx = 0; + } + } + else if (sym->st_shndx == SHN_COMMON || sym->st_shndx == SHN_UNDEF) + { + /* Check whether we have a (real) definition for this + symbol. If this is the case we skip this symbol + table entry. */ + assert (cnt >= file->nlocalsymbols); + defp = file->symref[cnt]; + assert (defp != NULL); + + assert (sym->st_shndx != SHN_COMMON || defp->defined); + + if ((sym->st_shndx == SHN_COMMON && !defp->common) + || (sym->st_shndx == SHN_UNDEF && defp->defined) + || defp->added) + /* Ignore this symbol table entry, there is a + "better" one or we already added it. */ + continue; + + /* Remember that we already added this symbol. */ + defp->added = 1; + + /* Adjust the section number for common symbols. */ + if (sym->st_shndx == SHN_COMMON) + { + sym->st_value = (ld_state.common_section->offset + + file->symref[cnt]->merge.value); + assert (ld_state.common_section->outscnndx < SHN_LORESERVE); + sym->st_shndx = ld_state.common_section->outscnndx; + xndx = 0; + } + } + else if (unlikely (sym->st_shndx != SHN_ABS)) + { + if (SPECIAL_SECTION_NUMBER_P (&ld_state, sym->st_shndx)) + /* XXX Add code to handle machine specific special + sections. */ + abort (); + } + + /* Add the symbol name to the string table. If the user + chooses the highest level of stripping avoid adding names + for local symbols in the string table. */ + if (sym->st_name != 0 + && (ld_state.strip < strip_everything + || XELF_ST_BIND (sym->st_info) != STB_LOCAL)) + symstrent[nsym] = ebl_strtabadd (strtab, + elf_strptr (file->elf, + file->symstridx, + sym->st_name), 0); + + /* Once we know the name this field will get the correct + offset. For now set it to zero which means no name + associated. */ + sym->st_name = 0; + + /* If we had to merge sections we have a completely new + offset for the symbol. */ + if (file->has_merge_sections && file->symref[cnt] != NULL + && file->symref[cnt]->merged) + sym->st_value = file->symref[cnt]->merge.value; + + /* Create the record in the output sections. */ + assert (nsym < nsym_allocated); + xelf_update_symshndx (symdata, xndxdata, nsym, sym, xndx, 0); + + /* Add the reference to the symbol record in case we need it. + Find the symbol if this has not happened yet. We do + not need the information for local symbols. */ + if (defp == NULL && cnt >= file->nlocalsymbols) + { + defp = file->symref[cnt]; + assert (defp != NULL); + } + + /* Store the reference to the symbol record. The + sorting code will have to keep this array in the + correct order, too. */ + ndxtosym[nsym] = defp; + + /* One more entry finished. */ + if (cnt >= file->nlocalsymbols) + { + assert (file->symref[cnt]->outsymidx == 0); + file->symref[cnt]->outsymidx = nsym; + } + file->symindirect[cnt] = nsym++; + } + } + while ((file = file->next) != ld_state.relfiles->next); + /* Make sure we didn't create the extended section index table for + nothing. */ + assert (xndxdata == NULL || need_xndx); + + + /* Create the version related sections. */ + if (ld_state.verneedscnidx != 0) + { + /* We know the number of input files and total number of + referenced versions. This allows us to allocate the memory + and then we iterate over the DSOs to get the version + information. */ + struct usedfiles *runp; + + runp = ld_state.dsofiles->next; + do + allocate_version_names (runp, dynstrtab); + while ((runp = runp->next) != ld_state.dsofiles->next); + + if (ld_state.needed != NULL) + { + runp = ld_state.needed->next; + do + allocate_version_names (runp, dynstrtab); + while ((runp = runp->next) != ld_state.needed->next); + } + } + + /* At this point we should hide symbols and so on. */ + if (ld_state.default_bind_local || ld_state.version_str_tab.filled > 0) + /* XXX Add one more test when handling of wildcard symbol names + is supported. */ + { + /* Check all non-local symbols whether they are on the export list. */ + bool any_reduced = false; + + for (cnt = 1; cnt < nsym; ++cnt) + { + XElf_Sym_vardef (sym); + + /* Note that we don't have to use 'xelf_getsymshndx' since we + only need the binding and the symbol name. */ + xelf_getsym (symdata, cnt, sym); + assert (sym != NULL); + + if (reduce_symbol_p (sym, symstrent[cnt])) + { + sym->st_info = XELF_ST_INFO (STB_LOCAL, + XELF_ST_TYPE (sym->st_info)); + (void) xelf_update_sym (symdata, cnt, sym); + + /* Show that we don't need this string anymore. */ + if (ld_state.strip == strip_everything) + { + symstrent[cnt] = NULL; + any_reduced = true; + } + } + } + + if (unlikely (any_reduced)) + { + /* Since we will not write names of local symbols in the + output file and we have reduced the binding of some + symbols the string table previously constructed contains + too many string. Correct it. */ + struct Ebl_Strtab *newp = ebl_strtabinit (true); + + for (cnt = 1; cnt < nsym; ++cnt) + if (symstrent[cnt] != NULL) + symstrent[cnt] = ebl_strtabadd (newp, + ebl_string (symstrent[cnt]), 0); + + ebl_strtabfree (strtab); + strtab = newp; + } + } + + /* Add the references to DSOs. We can add these entries this late + (after sorting out versioning) because references to DSOs are not + effected. */ + if (ld_state.from_dso != NULL) + { + struct symbol *runp; + size_t plt_base = nsym + ld_state.nfrom_dso - ld_state.nplt; + size_t plt_idx = 0; + size_t obj_idx = 0; + + assert (ld_state.nfrom_dso >= ld_state.nplt); + runp = ld_state.from_dso; + do + { + // XXX What about functions which are only referenced via + // pointers and not PLT entries? Can we distinguish such uses? + size_t idx; + if (runp->type == STT_FUNC) + { + /* Store the PLT entry number. */ + runp->merge.value = plt_idx + 1; + idx = plt_base + plt_idx++; + } + else + idx = nsym + obj_idx++; + + XElf_Sym_vardef (sym); + xelf_getsym_ptr (symdata, idx, sym); + + sym->st_value = 0; + sym->st_size = runp->size; + sym->st_info = XELF_ST_INFO (runp->weak ? STB_WEAK : STB_GLOBAL, + runp->type); + sym->st_other = STV_DEFAULT; + sym->st_shndx = SHN_UNDEF; + + /* Create the record in the output sections. */ + xelf_update_symshndx (symdata, xndxdata, idx, sym, 0, 0); + + const char *name = runp->name; + size_t namelen = 0; + + if (runp->file->verdefdata != NULL) + { + // XXX Is it useful to add the versym value to struct symbol? + XElf_Versym versym; + + (void) xelf_getversym_copy (runp->file->versymdata, runp->symidx, + versym); + + /* One can only link with the default version. */ + assert ((versym & 0x8000) == 0); + + const char *versname + = ebl_string (runp->file->verdefent[versym]); + + size_t versname_len = strlen (versname) + 1; + namelen = strlen (name) + versname_len + 2; + char *newp = (char *) obstack_alloc (&ld_state.smem, namelen); + memcpy (stpcpy (stpcpy (newp, name), "@@"), + versname, versname_len); + name = newp; + } + + symstrent[idx] = ebl_strtabadd (strtab, name, namelen); + + /* Record the initial index in the symbol table. */ + runp->outsymidx = idx; + + /* Remember the symbol record this ELF symbol came from. */ + ndxtosym[idx] = runp; + } + while ((runp = runp->next) != ld_state.from_dso); + + assert (nsym + obj_idx == plt_base); + assert (plt_idx == ld_state.nplt); + nsym = plt_base + plt_idx; + } + + /* Now we know how many symbols will be in the output file. Adjust + the count in the section data. */ + symdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_SYM, nsym); + if (unlikely (xndxdata != NULL)) + xndxdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_WORD, nsym); + + /* Create the symbol string table section. */ + strscn = elf_newscn (ld_state.outelf); + ld_state.strscnidx = elf_ndxscn (strscn); + data = elf_newdata (strscn); + xelf_getshdr (strscn, shdr); + if (data == NULL || shdr == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + /* Create a compact string table, allocate the memory for it, and + fill in the section data information. */ + ebl_strtabfinalize (strtab, data); + + shdr->sh_type = SHT_STRTAB; + assert (shdr->sh_entsize == 0); + + if (unlikely (xelf_update_shdr (strscn, shdr) == 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot create section for output file: %s"), + elf_errmsg (-1)); + + /* Fill in the offsets of the symbol names. */ + for (cnt = 1; cnt < nsym; ++cnt) + if (symstrent[cnt] != NULL) + { + XElf_Sym_vardef (sym); + + /* Note that we don't have to use 'xelf_getsymshndx' since we don't + modify the section index. */ + xelf_getsym (symdata, cnt, sym); + /* This better worked, we did it before. */ + assert (sym != NULL); + sym->st_name = ebl_strtaboffset (symstrent[cnt]); + (void) xelf_update_sym (symdata, cnt, sym); + } + + /* Since we are going to reorder the symbol table but still have to + be able to find the new position based on the old one (since the + latter is stored in 'symindirect' information of the input file + data structure) we have to create yet another indirection + table. */ + ld_state.dblindirect = dblindirect + = (Elf32_Word *) xmalloc (nsym * sizeof (Elf32_Word)); + + /* Sort the symbol table so that the local symbols come first. */ + /* XXX We don't use stable sorting here. It seems not necessary and + would be more expensive. If it turns out to be necessary this can + be fixed easily. */ + nsym_local = 1; + cnt = nsym - 1; + while (nsym_local < cnt) + { + XElf_Sym_vardef (locsym); + Elf32_Word locxndx; + XElf_Sym_vardef (globsym); + Elf32_Word globxndx; + + do + { + xelf_getsymshndx (symdata, xndxdata, nsym_local, locsym, locxndx); + /* This better works. */ + assert (locsym != NULL); + + if (XELF_ST_BIND (locsym->st_info) != STB_LOCAL + && (ld_state.need_symtab || ld_state.export_all_dynamic)) + { + do + { + xelf_getsymshndx (symdata, xndxdata, cnt, globsym, globxndx); + /* This better works. */ + assert (globsym != NULL); + + if (unlikely (XELF_ST_BIND (globsym->st_info) == STB_LOCAL)) + { + /* We swap the two entries. */ +#if NATIVE_ELF != 0 + /* Since we directly modify the data in the ELF + data structure we have to make a copy of one + of the entries. */ + XElf_Sym locsym_copy = *locsym; + locsym = &locsym_copy; +#endif + xelf_update_symshndx (symdata, xndxdata, nsym_local, + globsym, globxndx, 1); + xelf_update_symshndx (symdata, xndxdata, cnt, + locsym, locxndx, 1); + + /* Also swap the cross references. */ + dblindirect[nsym_local] = cnt; + dblindirect[cnt] = nsym_local; + + /* And the entries for the symbol names. */ + struct Ebl_Strent *strtmp = symstrent[nsym_local]; + symstrent[nsym_local] = symstrent[cnt]; + symstrent[cnt] = strtmp; + + /* And the mapping from symbol table entry to + struct symbol record. */ + struct symbol *symtmp = ndxtosym[nsym_local]; + ndxtosym[nsym_local] = ndxtosym[cnt]; + ndxtosym[cnt] = symtmp; + + /* Go to the next entry. */ + ++nsym_local; + --cnt; + + break; + } + + dblindirect[cnt] = cnt; + } + while (nsym_local < --cnt); + + break; + } + + dblindirect[nsym_local] = nsym_local; + } + while (++nsym_local < cnt); + } + + /* The symbol 'nsym_local' is currently pointing to might be local, + too. Check and increment the variable if this is the case. */ + if (likely (nsym_local < nsym)) + { + XElf_Sym_vardef (locsym); + + /* This entry isn't moved. */ + dblindirect[nsym_local] = nsym_local; + + /* Note that it is OK to not use 'xelf_getsymshndx' here. */ + xelf_getsym (symdata, nsym_local, locsym); + /* This better works. */ + assert (locsym != NULL); + + if (XELF_ST_BIND (locsym->st_info) == STB_LOCAL) + ++nsym_local; + } + + + /* We need the versym array right away to keep track of the version + symbols. */ + if (ld_state.versymscnidx != 0) + { + /* We allocate more memory than we need since the array is morroring + the dynamic symbol table and not the normal symbol table. I.e., + no local symbols are present. */ + versymscn = elf_getscn (ld_state.outelf, ld_state.versymscnidx); + versymdata = elf_newdata (versymscn); + if (versymdata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create versioning section: %s"), + elf_errmsg (-1)); + + versymdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_HALF, + nsym - nsym_local + 1); + versymdata->d_buf = xcalloc (1, versymdata->d_size); + versymdata->d_align = xelf_fsize (ld_state.outelf, ELF_T_HALF, 1); + versymdata->d_off = 0; + versymdata->d_type = ELF_T_HALF; + } + + + /* If we have to construct the dynamic symbol table we must not include + the local symbols. If the normal symbol has to be emitted as well + we haven't done anything else yet and we can construct it from + scratch now. */ + if (unlikely (!ld_state.need_symtab)) + { + /* Note that the following code works even if there is no entry + to remove since the zeroth entry is always local. */ + size_t reduce = xelf_fsize (ld_state.outelf, ELF_T_SYM, nsym_local - 1); + + XElf_Sym_vardef (nullsym); + xelf_getsym_ptr (symdata, nsym_local - 1, nullsym); + + /* Note that we don't have to use 'xelf_update_symshndx' since + this is the dynamic symbol table we write. */ + (void) xelf_update_sym (symdata, nsym_local - 1, + memset (nullsym, '\0', sizeof (*nullsym))); + + /* Update the buffer pointer and size in the output data. */ + symdata->d_buf = (char *) symdata->d_buf + reduce; + symdata->d_size -= reduce; + + /* Add the version symbol information. */ + if (versymdata != NULL) + { + nsym_dyn = 1; + for (cnt = nsym_local; cnt < nsym; ++cnt, ++nsym_dyn) + { + struct symbol *symp = ndxtosym[cnt]; + + if (symp->file->versymdata != NULL) + { + GElf_Versym versym; + + gelf_getversym (symp->file->versymdata, symp->symidx, + &versym); + + (void) gelf_update_versym (versymdata, nsym_dyn, + &symp->file->verdefused[versym]); + } + } + } + + /* Since we only created the dynamic symbol table the number of + dynamic symbols is the total number of symbols. */ + nsym_dyn = nsym - nsym_local + 1; + + /* XXX TBI. Create whatever data structure is missing. */ + abort (); + } + else if (ld_state.need_dynsym) + { + /* Create the dynamic symbol table section data along with the + string table. We look at all non-local symbols we found for + the normal symbol table and add those. */ + dynsymscn = elf_getscn (ld_state.outelf, ld_state.dynsymscnidx); + dynsymdata = elf_newdata (dynsymscn); + + dynstrdata = elf_newdata (elf_getscn (ld_state.outelf, + ld_state.dynstrscnidx)); + if (dynsymdata == NULL || dynstrdata == NULL) + error (EXIT_FAILURE, 0, gettext ("\ +cannot create dynamic symbol table for output file: %s"), + elf_errmsg (-1)); + + nsym_dyn_allocated = nsym - nsym_local + 1; + dynsymdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_SYM, + nsym_dyn_allocated); + dynsymdata->d_buf = memset (xmalloc (dynsymdata->d_size), '\0', + xelf_fsize (ld_state.outelf, ELF_T_SYM, 1)); + dynsymdata->d_type = ELF_T_SYM; + dynsymdata->d_off = 0; + dynsymdata->d_align = xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1); + + /* We need one more array which contains the hash codes of the + symbol names. */ + hashcodes = (Elf32_Word *) xcalloc (nsym_dyn_allocated, + sizeof (Elf32_Word)); + + /* We have and empty entry at the beginning. */ + nsym_dyn = 1; + + /* We don't mix PLT symbols and others. */ + size_t plt_idx = 1; + size_t obj_idx = 1 + ld_state.nplt; + + /* Populate the table. */ + for (cnt = nsym_local; cnt < nsym; ++cnt) + { + XElf_Sym_vardef (sym); + + xelf_getsym (symdata, cnt, sym); + assert (sym != NULL); + + if (sym->st_shndx == SHN_XINDEX) + error (EXIT_FAILURE, 0, gettext ("\ +section index too large in dynamic symbol table")); + + /* We do not add the symbol to the dynamic symbol table if + + - the symbol is for a file + - it is not externally visible (internal, hidden) + - if export_all_dynamic is not set and is only defined in + the executable (i.e., it is defined, but not (also) in + in DSO) + + Set symstrent[cnt] to NULL in case an entry is ignored. */ + if (XELF_ST_TYPE (sym->st_info) == STT_FILE + || XELF_ST_VISIBILITY (sym->st_other) == STV_INTERNAL + || XELF_ST_VISIBILITY (sym->st_other) == STV_HIDDEN + || (!ndxtosym[cnt]->in_dso && ndxtosym[cnt]->defined)) + { + symstrent[cnt] = NULL; + continue; + } + + size_t idx; + if (ndxtosym[cnt]->in_dso && ndxtosym[cnt]->type == STT_FUNC) + { + idx = plt_idx++; + assert (idx < 1 + ld_state.nplt); + } + else + { + idx = obj_idx++; + assert (idx < nsym_dyn_allocated); + } + + /* Add the version information. */ + if (versymdata != NULL) + { + struct symbol *symp = ndxtosym[cnt]; + + if (symp->file->verdefdata != NULL) + { + GElf_Versym versym; + + gelf_getversym (symp->file->versymdata, symp->symidx, + &versym); + + (void) gelf_update_versym (versymdata, idx, + &symp->file->verdefused[versym]); + } + else + { + /* XXX Add support for version definitions. */ + GElf_Versym global = VER_NDX_GLOBAL; + (void) gelf_update_versym (versymdata, idx, &global); + } + } + + /* Store the index of the symbol in the dynamic symbol table. */ + ndxtosym[cnt]->outdynsymidx = idx; + + /* Create a new string table entry. */ + const char *str = ndxtosym[cnt]->name; + symstrent[cnt] = ebl_strtabadd (dynstrtab, str, 0); + hashcodes[idx] = elf_hash (str); + ++nsym_dyn; + } + assert (nsym_dyn == obj_idx); + assert (ld_state.nplt + 1 == plt_idx); + + /* Update the information about the symbol section. */ + if (versymdata != NULL) + { + /* Correct the size now that we know how many entries the + dynamic symbol table has. */ + versymdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_HALF, + nsym_dyn); + + /* Add the reference to the symbol table. */ + xelf_getshdr (versymscn, shdr); + assert (shdr != NULL); + + shdr->sh_link = ld_state.dynsymscnidx; + + (void) xelf_update_shdr (versymscn, shdr); + } + } + + if (ld_state.file_type != relocatable_file_type) + { + size_t nbucket; + Elf32_Word *bucket; + Elf32_Word *chain; + size_t nchain; + Elf_Scn *hashscn; + Elf_Data *hashdata; + + /* Finalize the dynamic string table. */ + ebl_strtabfinalize (dynstrtab, dynstrdata); + + /* Determine the "optimal" bucket size. */ + nbucket = optimal_bucket_size (hashcodes, nsym_dyn, ld_state.optlevel); + + /* Create the .hash section data structures. */ + assert (ld_state.hashscnidx != 0); + hashscn = elf_getscn (ld_state.outelf, ld_state.hashscnidx); + xelf_getshdr (hashscn, shdr); + hashdata = elf_newdata (hashscn); + if (shdr == NULL || hashdata == NULL) + error (EXIT_FAILURE, 0, gettext ("\ +cannot create hash table section for output file: %s"), + elf_errmsg (-1)); + + shdr->sh_link = ld_state.dynsymscnidx; + (void) xelf_update_shdr (hashscn, shdr); + + hashdata->d_size = (2 + nsym_dyn + nbucket) * sizeof (Elf32_Word); + hashdata->d_buf = xcalloc (1, hashdata->d_size); + hashdata->d_align = sizeof (Elf32_Word); + hashdata->d_type = ELF_T_WORD; + hashdata->d_off = 0; + + ((Elf32_Word *) hashdata->d_buf)[0] = nbucket; + ((Elf32_Word *) hashdata->d_buf)[1] = nsym_dyn; + bucket = &((Elf32_Word *) hashdata->d_buf)[2]; + chain = &((Elf32_Word *) hashdata->d_buf)[2 + nbucket]; + + /* Haven't yet filled in any chain value. */ + nchain = 0; + + /* Now put the names in. */ + for (cnt = nsym_local; cnt < nsym; ++cnt) + if (symstrent[cnt] != NULL) + { + XElf_Sym_vardef (sym); + size_t hashidx; + size_t dynidx = ndxtosym[cnt]->outdynsymidx; + +#if NATIVE_ELF != 0 + XElf_Sym *osym; + memcpy (xelf_getsym (dynsymdata, dynidx, sym), + xelf_getsym (symdata, cnt, osym), + sizeof (XElf_Sym)); +#else + xelf_getsym (symdata, cnt, sym); + assert (sym != NULL); +#endif + + sym->st_name = ebl_strtaboffset (symstrent[cnt]); + + (void) xelf_update_sym (dynsymdata, dynidx, sym); + + /* Add to the hash table. */ + hashidx = hashcodes[dynidx] % nbucket; + if (bucket[hashidx] == 0) + bucket[hashidx] = dynidx; + else + { + hashidx = bucket[hashidx]; + while (chain[hashidx] != 0) + hashidx = chain[hashidx]; + + chain[hashidx] = dynidx; + } + } + + free (hashcodes); + + /* We don't need the map from the symbol table index to the symbol + structure anymore. */ + free (ndxtosym); + + /* Create the required version section. */ + if (ld_state.verneedscnidx != 0) + { + Elf_Scn *verneedscn; + Elf_Data *verneeddata; + struct usedfiles *runp; + size_t verneed_size = xelf_fsize (ld_state.outelf, ELF_T_VNEED, 1); + size_t vernaux_size = xelf_fsize (ld_state.outelf, ELF_T_VNAUX, 1); + size_t offset; + int ntotal; + + verneedscn = elf_getscn (ld_state.outelf, ld_state.verneedscnidx); + xelf_getshdr (verneedscn, shdr); + verneeddata = elf_newdata (verneedscn); + if (shdr == NULL || verneeddata == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create versioning data: %s"), + elf_errmsg (-1)); + + verneeddata->d_size = (ld_state.nverdeffile * verneed_size + + ld_state.nverdefused * vernaux_size); + verneeddata->d_buf = xmalloc (verneeddata->d_size); + verneeddata->d_type = ELF_T_VNEED; + verneeddata->d_align = xelf_fsize (ld_state.outelf, ELF_T_WORD, 1); + verneeddata->d_off = 0; + + offset = 0; + ntotal = ld_state.nverdeffile; + runp = ld_state.dsofiles->next; + do + { + offset = create_verneed_data (offset, verneeddata, runp, + &ntotal); + runp = runp->next; + } + while (ntotal > 0 && runp != ld_state.dsofiles->next); + + if (ntotal > 0) + { + runp = ld_state.needed->next; + do + { + offset = create_verneed_data (offset, verneeddata, runp, + &ntotal); + runp = runp->next; + } + while (ntotal > 0 && runp != ld_state.needed->next); + } + + assert (offset == verneeddata->d_size); + + /* Add the needed information to the section header. */ + shdr->sh_link = ld_state.dynstrscnidx; + shdr->sh_info = ld_state.nverdeffile; + (void) xelf_update_shdr (verneedscn, shdr); + } + + /* Adjust the section size. */ + dynsymdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_SYM, nsym_dyn); + if (versymdata != NULL) + versymdata->d_size = xelf_fsize (ld_state.outelf, ELF_T_HALF, + nsym_dyn); + + /* Add the remaining information to the section header. */ + xelf_getshdr (dynsymscn, shdr); + /* There is always exactly one local symbol. */ + shdr->sh_info = 1; + /* Reference the string table. */ + shdr->sh_link = ld_state.dynstrscnidx; + /* Write the updated info back. */ + (void) xelf_update_shdr (dynsymscn, shdr); + } + else + /* We don't need the map from the symbol table index to the symbol + structure anymore. */ + free (ndxtosym); + + /* We don't need the string table anymore. */ + free (symstrent); + + /* Remember the total number of symbols in the dynamic symbol table. */ + ld_state.ndynsym = nsym_dyn; + + /* Fill in the section header information. */ + symscn = elf_getscn (ld_state.outelf, ld_state.symscnidx); + xelf_getshdr (symscn, shdr); + if (shdr == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create symbol table for output file: %s"), + elf_errmsg (-1)); + + shdr->sh_type = SHT_SYMTAB; + shdr->sh_link = ld_state.strscnidx; + shdr->sh_info = nsym_local; + shdr->sh_entsize = xelf_fsize (ld_state.outelf, ELF_T_SYM, 1); + + (void) xelf_update_shdr (symscn, shdr); + + + /* Add names for the generated sections. */ + if (ld_state.symscnidx != 0) + symtab_ent = ebl_strtabadd (ld_state.shstrtab, ".symtab", 8); + if (ld_state.xndxscnidx != 0) + xndx_ent = ebl_strtabadd (ld_state.shstrtab, ".symtab_shndx", 14); + if (ld_state.strscnidx != 0) + strtab_ent = ebl_strtabadd (ld_state.shstrtab, ".strtab", 8); + /* At this point we would have to test for failures in the + allocation. But we skip this. First, the problem will be caught + latter when doing more allocations for the section header table. + Even if this would not be the case all that would happen is that + the section names are empty. The binary would still be usable if + it is an executable or a DSO. Not adding the test here saves + quite a bit of code. */ + + + /* Finally create the section for the section header string table. */ + shstrtab_scn = elf_newscn (ld_state.outelf); + shstrtab_ndx = elf_ndxscn (shstrtab_scn); + if (unlikely (shstrtab_ndx == SHN_UNDEF)) + error (EXIT_FAILURE, 0, + gettext ("cannot create section header string section: %s"), + elf_errmsg (-1)); + + /* Add the name of the section to the string table. */ + shstrtab_ent = ebl_strtabadd (ld_state.shstrtab, ".shstrtab", 10); + if (unlikely (shstrtab_ent == NULL)) + error (EXIT_FAILURE, errno, + gettext ("cannot create section header string section")); + + /* Finalize the section header string table. */ + data = elf_newdata (shstrtab_scn); + if (data == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section header string section: %s"), + elf_errmsg (-1)); + ebl_strtabfinalize (ld_state.shstrtab, data); + + /* Now we know the string offsets for all section names. */ + for (cnt = 0; cnt < ld_state.nallsections; ++cnt) + if (ld_state.allsections[cnt]->scnidx != 0) + { + Elf_Scn *scn; + + scn = elf_getscn (ld_state.outelf, ld_state.allsections[cnt]->scnidx); + + xelf_getshdr (scn, shdr); + assert (shdr != NULL); + + shdr->sh_name = ebl_strtaboffset (ld_state.allsections[cnt]->nameent); + + if (xelf_update_shdr (scn, shdr) == 0) + assert (0); + } + + /* Add the names for the generated sections to the respective + section headers. */ + if (symtab_ent != NULL) + { + Elf_Scn *scn = elf_getscn (ld_state.outelf, ld_state.symscnidx); + + xelf_getshdr (scn, shdr); + /* This cannot fail, we already accessed the header before. */ + assert (shdr != NULL); + + shdr->sh_name = ebl_strtaboffset (symtab_ent); + + (void) xelf_update_shdr (scn, shdr); + } + if (xndx_ent != NULL) + { + Elf_Scn *scn = elf_getscn (ld_state.outelf, ld_state.xndxscnidx); + + xelf_getshdr (scn, shdr); + /* This cannot fail, we already accessed the header before. */ + assert (shdr != NULL); + + shdr->sh_name = ebl_strtaboffset (xndx_ent); + + (void) xelf_update_shdr (scn, shdr); + } + if (strtab_ent != NULL) + { + Elf_Scn *scn = elf_getscn (ld_state.outelf, ld_state.strscnidx); + + xelf_getshdr (scn, shdr); + /* This cannot fail, we already accessed the header before. */ + assert (shdr != NULL); + + shdr->sh_name = ebl_strtaboffset (strtab_ent); + + (void) xelf_update_shdr (scn, shdr); + } + + /* And the section header table section itself. */ + xelf_getshdr (shstrtab_scn, shdr); + if (shdr == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create section header string section: %s"), + elf_errmsg (-1)); + + shdr->sh_name = ebl_strtaboffset (shstrtab_ent); + shdr->sh_type = SHT_STRTAB; + + if (unlikely (xelf_update_shdr (shstrtab_scn, shdr) == 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot create section header string section: %s"), + elf_errmsg (-1)); + + + /* Add the correct section header info to the section group sections. */ + groups = ld_state.groups; + while (groups != NULL) + { + Elf_Scn *scn; + struct scngroup *oldp; + Elf32_Word si; + + scn = elf_getscn (ld_state.outelf, groups->outscnidx); + xelf_getshdr (scn, shdr); + assert (shdr != NULL); + + shdr->sh_name = ebl_strtaboffset (groups->nameent); + shdr->sh_type = SHT_GROUP; + shdr->sh_flags = 0; + shdr->sh_link = ld_state.symscnidx; + shdr->sh_entsize = sizeof (Elf32_Word); + + /* Determine the index for the signature symbol. */ + si = groups->symbol->file->symindirect[groups->symbol->symidx]; + if (si == 0) + { + assert (groups->symbol->file->symref[groups->symbol->symidx] + != NULL); + si = groups->symbol->file->symref[groups->symbol->symidx]->outsymidx; + assert (si != 0); + } + shdr->sh_info = ld_state.dblindirect[si]; + + (void) xelf_update_shdr (scn, shdr); + + oldp = groups; + groups = groups->next; + free (oldp); + } + + + if (ld_state.file_type != relocatable_file_type) + { + size_t nphdr; + XElf_Addr addr; + struct output_segment *segment; + Elf_Scn *scn; + Elf32_Word nsec; + XElf_Phdr_vardef (phdr); + + /* Every executable needs a program header. The number of entries + varies. One exists for each segment. Each SHT_NOTE section gets + one, too. For dynamically linked executables we have to create + one for the program header, the interpreter, and the dynamic + section. First count the number of segments. + + XXX Determine whether the segment is non-empty. */ + nphdr = 0; + segment = ld_state.output_segments; + while (segment != NULL) + { + ++nphdr; + segment = segment->next; + } + + /* Add the number of SHT_NOTE sections. We counted them earlier. */ + nphdr += ld_state.nnotesections; + + /* If we create a DSO or the file is linked against DSOs we have three + more entries: INTERP, PHDR, DYNAMIC. */ + if (dynamically_linked_p ()) + nphdr += 3; + + /* Create the program header structure. */ + if (xelf_newphdr (ld_state.outelf, nphdr) == 0) + error (EXIT_FAILURE, 0, gettext ("cannot create program header: %s"), + elf_errmsg (-1)); + + + /* Determine the section sizes and offsets. We have to do this + to be able to determine the memory layout (which normally + differs from the file layout). */ + if (elf_update (ld_state.outelf, ELF_C_NULL) == -1) + error (EXIT_FAILURE, 0, gettext ("while determining file layout: %s"), + elf_errmsg (-1)); + + + /* Now determine the memory addresses of all the sections and + segments. */ + nsec = 0; + scn = elf_getscn (ld_state.outelf, ld_state.allsections[nsec]->scnidx); + xelf_getshdr (scn, shdr); + assert (shdr != NULL); + + /* The address we start with is the offset of the first (not + zeroth) section. */ + addr = shdr->sh_offset; + + /* The index of the first loadable segment. */ + nphdr = 1 + (dynamically_linked_p () == true) * 2; + + segment = ld_state.output_segments; + while (segment != NULL) + { + struct output_rule *orule; + bool first_section = true; + XElf_Off nobits_size = 0; + XElf_Off memsize = 0; + + /* the minimum alignment is a page size. */ + segment->align = ld_state.pagesize; + + for (orule = segment->output_rules; orule != NULL; + orule = orule->next) + if (orule->tag == output_section) + { + XElf_Off oldoff; + + /* See whether this output rule corresponds to the next + section. Yes, this is a pointer comparison. */ + if (ld_state.allsections[nsec]->name + != orule->val.section.name) + /* No, ignore this output rule. */ + continue; + + /* We assign addresses only in segments which are actually + loaded. */ + if (segment->mode != 0) + { + /* Adjust the offset of the input sections. */ + struct scninfo *isect; + struct scninfo *first; + + isect = first = ld_state.allsections[nsec]->last; + if (isect != NULL) + do + isect->offset += addr; + while ((isect = isect->next) != first); + + /* Set the address of current section. */ + shdr->sh_addr = addr; + + /* Write the result back. */ + (void) xelf_update_shdr (scn, shdr); + + /* Remember the address. */ + ld_state.allsections[nsec]->addr = addr; + } + + if (first_section) + { + /* The first segment starts at offset zero. */ + if (segment == ld_state.output_segments) + { + segment->offset = 0; + segment->addr = addr - shdr->sh_offset; + } + else + { + segment->offset = shdr->sh_offset; + segment->addr = addr; + } + + /* Determine the maximum alignment requirement. */ + segment->align = MAX (segment->align, shdr->sh_addralign); + + first_section = false; + } + + memsize = shdr->sh_offset - segment->offset + shdr->sh_size; + if (nobits_size != 0 && shdr->sh_type != SHT_NOTE) + error (EXIT_FAILURE, 0, gettext ("\ +internal error: nobits section follows nobits section")); + if (shdr->sh_type == SHT_NOBITS) + nobits_size += shdr->sh_size; + + /* Determine the new address which is computed using + the difference of the offsets on the sections. Note + that this assumes that the sections following each + other in the section header table are also + consecutive in the file. This is true here because + libelf constructs files this way. */ + oldoff = shdr->sh_offset; + + if (++nsec >= ld_state.nallsections) + break; + + scn = elf_getscn (ld_state.outelf, + ld_state.allsections[nsec]->scnidx); + xelf_getshdr (scn, shdr); + assert (shdr != NULL); + + /* This is the new address resulting from the offsets + in the file. */ + assert (oldoff <= shdr->sh_offset); + addr += shdr->sh_offset - oldoff; + } + else + { + assert (orule->tag == output_assignment); + + if (strcmp (orule->val.assignment->variable, ".") == 0) + /* This is a change of the address. */ + addr = eval_expression (orule->val.assignment->expression, + addr); + else if (orule->val.assignment->sym != NULL) + { + /* This symbol is used. Update the symbol table + entry. */ + XElf_Sym_vardef (sym); + size_t idx; + + /* Note that we do not have to use + xelf_getsymshndx since we only update the + symbol address, not the section + information. */ + idx = dblindirect[orule->val.assignment->sym->outsymidx]; + xelf_getsym (symdata, idx, sym); + sym->st_value = addr; + (void) xelf_update_sym (symdata, idx, sym); + + idx = orule->val.assignment->sym->outdynsymidx; + if (idx != 0) + { + assert (dynsymdata != NULL); + xelf_getsym (dynsymdata, idx, sym); + sym->st_value = addr; + (void) xelf_update_sym (dynsymdata, idx, sym); + } + } + } + + /* Store the segment parameter for loadable segments. */ + if (segment->mode != 0) + { + xelf_getphdr_ptr (ld_state.outelf, nphdr, phdr); + + phdr->p_type = PT_LOAD; + phdr->p_offset = segment->offset; + phdr->p_vaddr = segment->addr; + phdr->p_paddr = phdr->p_vaddr; + phdr->p_filesz = memsize - nobits_size; + phdr->p_memsz = memsize; + phdr->p_flags = segment->mode; + phdr->p_align = segment->align; + + (void) xelf_update_phdr (ld_state.outelf, nphdr, phdr); + ++nphdr; + } + + segment = segment->next; + } + + /* Create the other program header entries. */ + xelf_getehdr (ld_state.outelf, ehdr); + assert (ehdr != NULL); + + xelf_getphdr_ptr (ld_state.outelf, 1, phdr); + phdr->p_type = PT_PHDR; + phdr->p_offset = ehdr->e_phoff; + phdr->p_vaddr = ld_state.output_segments->addr + phdr->p_offset; + phdr->p_paddr = phdr->p_vaddr; + phdr->p_filesz = ehdr->e_phnum * ehdr->e_phentsize; + phdr->p_memsz = phdr->p_filesz; + phdr->p_flags = 0; /* No need to set PF_R or so. */ + phdr->p_align = xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1); + (void) xelf_update_phdr (ld_state.outelf, 0, phdr); + + + /* Adjust the addresses in the addresses of the symbol according + to the load addresses of the sections. */ + if (ld_state.need_symtab) + for (cnt = 1; cnt < nsym; ++cnt) + { + XElf_Sym_vardef (sym); + Elf32_Word shndx; + + xelf_getsymshndx (symdata, xndxdata, cnt, sym, shndx); + assert (sym != NULL); + + if (sym->st_shndx != SHN_XINDEX) + shndx = sym->st_shndx; + + if ((shndx > SHN_UNDEF && shndx < SHN_LORESERVE) + || shndx > SHN_HIRESERVE) + { + /* Note we subtract 1 from the section index since ALLSECTIONS + does not store the dummy section with offset zero. */ + sym->st_value += ld_state.allsections[shndx - 1]->addr; + + /* We don't have to use 'xelf_update_symshndx' since the + section number doesn't change. */ + (void) xelf_update_sym (symdata, cnt, sym); + } + } + + if (ld_state.need_dynsym) + for (cnt = 1; cnt < nsym_dyn; ++cnt) + { + XElf_Sym_vardef (sym); + + xelf_getsym (dynsymdata, cnt, sym); + assert (sym != NULL); + + if (sym->st_shndx > SHN_UNDEF && sym->st_shndx < SHN_LORESERVE) + { + /* Note we subtract 1 from the section index since ALLSECTIONS + does not store the dummy section with offset zero. */ + sym->st_value += ld_state.allsections[sym->st_shndx - 1]->addr; + + /* We don't have to use 'xelf_update_symshndx' since the + section number doesn't change. */ + (void) xelf_update_sym (dynsymdata, cnt, sym); + } + } + + + /* Now is a good time to determine the values of all the symbols + we encountered. */ + // XXX This loop is very inefficient. The hash tab iterator also + // returns all symbols in DSOs. + struct symbol *se; + void *p = NULL; + while ((se = ld_symbol_tab_iterate (&ld_state.symbol_tab, &p)) != NULL) + if (! se->in_dso) + { + XElf_Sym_vardef (sym); + + addr = 0; + + if (se->outdynsymidx != 0) + { + xelf_getsym (dynsymdata, se->outdynsymidx, sym); + assert (sym != NULL); + addr = sym->st_value; + } + else if (se->outsymidx != 0) + { + assert (dblindirect[se->outsymidx] != 0); + xelf_getsym (symdata, dblindirect[se->outsymidx], sym); + assert (sym != NULL); + addr = sym->st_value; + } + else + abort (); + + se->merge.value = addr; + } + + /* Complete the header of the .rel.dyn/.rela.dyn section. Point + to the symbol table. The sh_info field is left zero since + there is no specific section the contained relocations are + for. */ + if (ld_state.reldynscnidx != 0) + { + assert (ld_state.dynsymscnidx != 0); + scn = elf_getscn (ld_state.outelf, ld_state.reldynscnidx); + xelf_getshdr (scn, shdr); + assert (shdr != NULL); + + shdr->sh_link = ld_state.dynsymscnidx; + + (void) xelf_update_shdr (scn, shdr); + } + + /* Fill in the dynamic segment/section. */ + if (dynamically_linked_p ()) + { + Elf_Scn *outscn; + + assert (ld_state.interpscnidx != 0); + xelf_getshdr (elf_getscn (ld_state.outelf, ld_state.interpscnidx), + shdr); + assert (shdr != NULL); + + /* The interpreter string. */ + // XXX Do we need to support files (DSOs) without interpreters? + xelf_getphdr_ptr (ld_state.outelf, 1, phdr); + phdr->p_type = PT_INTERP; + phdr->p_offset = shdr->sh_offset; + phdr->p_vaddr = shdr->sh_addr; + phdr->p_paddr = phdr->p_vaddr; + phdr->p_filesz = shdr->sh_size; + phdr->p_memsz = phdr->p_filesz; + phdr->p_flags = 0; /* No need to set PF_R or so. */ + phdr->p_align = 1; /* It's a string. */ + + (void) xelf_update_phdr (ld_state.outelf, 1, phdr); + + /* The pointer to the dynamic section. We this we need to + get the information for the dynamic section first. */ + assert (ld_state.dynamicscnidx); + outscn = elf_getscn (ld_state.outelf, ld_state.dynamicscnidx); + xelf_getshdr (outscn, shdr); + assert (shdr != NULL); + + xelf_getphdr_ptr (ld_state.outelf, 2, phdr); + phdr->p_type = PT_DYNAMIC; + phdr->p_offset = shdr->sh_offset; + phdr->p_vaddr = shdr->sh_addr; + phdr->p_paddr = phdr->p_vaddr; + phdr->p_filesz = shdr->sh_size; + phdr->p_memsz = phdr->p_filesz; + phdr->p_flags = 0; /* No need to set PF_R or so. */ + phdr->p_align = shdr->sh_addralign; + + (void) xelf_update_phdr (ld_state.outelf, 2, phdr); + + /* Fill in the reference to the .dynstr section. */ + assert (ld_state.dynstrscnidx != 0); + shdr->sh_link = ld_state.dynstrscnidx; + (void) xelf_update_shdr (outscn, shdr); + + /* And fill the remaining entries. */ + Elf_Data *dyndata = elf_getdata (outscn, NULL); + assert (dyndata != NULL); + + /* Add the DT_NEEDED entries. */ + if (ld_state.ndsofiles > 0) + { + struct usedfiles *runp = ld_state.dsofiles->next; + + do + if (! ld_state.ignore_unused_dsos || runp->used) + { + /* Add the position-dependent flag if necessary. */ + if (runp->lazyload) + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_POSFLAG_1, DF_P1_LAZYLOAD); + + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_NEEDED, + ebl_strtaboffset (runp->sonameent)); + } + while ((runp = runp->next) != ld_state.dsofiles->next); + } + + /* We can finish the DT_RUNPATH/DT_RPATH entries now. */ + if (ld_state.rxxpath_strent != NULL) + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + ld_state.rxxpath_tag, + ebl_strtaboffset (ld_state.rxxpath_strent)); + + /* Reference to initialization and finalization functions. */ + // XXX This code depends on symbol table being relocated. + if (ld_state.init_symbol != NULL) + { + XElf_Sym_vardef (sym); + + if (ld_state.need_symtab) + xelf_getsym (symdata, + dblindirect[ld_state.init_symbol->outsymidx], + sym); + else + xelf_getsym (dynsymdata, ld_state.init_symbol->outdynsymidx, + sym); + assert (sym != NULL); + + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_INIT, sym->st_value); + } + if (ld_state.fini_symbol != NULL) + { + XElf_Sym_vardef (sym); + + if (ld_state.need_symtab) + xelf_getsym (symdata, + dblindirect[ld_state.fini_symbol->outsymidx], + sym); + else + xelf_getsym (dynsymdata, ld_state.fini_symbol->outdynsymidx, + sym); + assert (sym != NULL); + + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_FINI, sym->st_value); + } + // XXX Support init,fini,preinit arrays + + /* The hash table which comes with dynamic symbol table. */ + xelf_getshdr (elf_getscn (ld_state.outelf, ld_state.hashscnidx), + shdr); + assert (shdr != NULL); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, DT_HASH, + shdr->sh_addr); + + /* Reference to the symbol table section. */ + assert (ld_state.dynsymscnidx != 0); + xelf_getshdr (elf_getscn (ld_state.outelf, ld_state.dynsymscnidx), + shdr); + assert (shdr != NULL); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, DT_SYMTAB, + shdr->sh_addr); + + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, DT_SYMENT, + xelf_fsize (ld_state.outelf, ELF_T_SYM, 1)); + + /* And the string table which comes with it. */ + xelf_getshdr (elf_getscn (ld_state.outelf, ld_state.dynstrscnidx), + shdr); + assert (shdr != NULL); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, DT_STRTAB, + shdr->sh_addr); + + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, DT_STRSZ, + shdr->sh_size); + + /* Add the entries related to the .plt. */ + if (ld_state.nplt > 0) + { + xelf_getshdr (elf_getscn (ld_state.outelf, ld_state.gotscnidx), + shdr); + assert (shdr != NULL); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + // XXX This should probably be machine + // dependent. + DT_PLTGOT, shdr->sh_addr); + + xelf_getshdr (elf_getscn (ld_state.outelf, + ld_state.pltrelscnidx), shdr); + assert (shdr != NULL); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_PLTRELSZ, shdr->sh_size); + + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_JMPREL, shdr->sh_addr); + + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_PLTREL, REL_TYPE (statep)); + } + + if (ld_state.relsize_total > 0) + { + int rel = REL_TYPE (statep); + xelf_getshdr (elf_getscn (ld_state.outelf, + ld_state.reldynscnidx), shdr); + assert (shdr != NULL); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + rel, shdr->sh_addr); + + /* Trick ahead. Use arithmetic to get the right tag. + We check the validity of this assumption in the asserts. */ + assert (DT_RELASZ - DT_RELA == 1); + assert (DT_RELSZ - DT_REL == 1); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + rel + 1, shdr->sh_size); + + /* Similar for the entry size tag. */ + assert (DT_RELAENT - DT_RELA == 2); + assert (DT_RELENT - DT_REL == 2); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + rel + 2, + rel == DT_REL + ? xelf_fsize (ld_state.outelf, ELF_T_REL, 1) + : xelf_fsize (ld_state.outelf, ELF_T_RELA, + 1)); + } + + if (ld_state.verneedscnidx != 0) + { + xelf_getshdr (elf_getscn (ld_state.outelf, + ld_state.verneedscnidx), shdr); + assert (shdr != NULL); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_VERNEED, shdr->sh_addr); + + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_VERNEEDNUM, ld_state.nverdeffile); + } + + if (ld_state.versymscnidx != 0) + { + xelf_getshdr (elf_getscn (ld_state.outelf, + ld_state.versymscnidx), shdr); + assert (shdr != NULL); + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_VERSYM, shdr->sh_addr); + } + + /* We always create the DT_DEBUG entry. */ + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, DT_DEBUG, 0); + assert (ld_state.ndynamic_filled < ld_state.ndynamic); + + /* Add the flag words if necessary. */ + if (ld_state.dt_flags != 0) + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, DT_FLAGS, + ld_state.dt_flags); + + /* Create entry for the DT_FLAGS_1 flag. */ + if (ld_state.dt_flags_1 != 0) + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_FLAGS_1, ld_state.dt_flags_1); + + /* Create entry for the DT_FEATURE_1 flag. */ + if (ld_state.dt_feature_1 != 0) + new_dynamic_entry (dyndata, ld_state.ndynamic_filled++, + DT_FEATURE_1, ld_state.dt_feature_1); + + assert (ld_state.ndynamic_filled <= ld_state.ndynamic); + } + } + + + // XXX The following code isn't nice. We use two different + // mechanisms to handle relocations, one for relocatable files, one + // for executables and DSOs. Maybe this is the best method but also + // maybe it can be somewhat unified. + + /* Now that we created the symbol table we can add the reference to + it in the sh_link field of the section headers of the relocation + sections. */ + while (rellist != NULL) + { + assert (ld_state.file_type == relocatable_file_type); + Elf_Scn *outscn; + + outscn = elf_getscn (ld_state.outelf, rellist->scnidx); + xelf_getshdr (outscn, shdr); + /* This must not fail since we did it before. */ + assert (shdr != NULL); + + /* Remember the symbol table which belongs to the relocation section. */ + shdr->sh_link = ld_state.symscnidx; + + /* And the reference to the section which is relocated by this + relocation section. We use the info from the first input + section but all records should have the same information. */ + shdr->sh_info = + rellist->scninfo->fileinfo->scninfo[SCNINFO_SHDR (rellist->scninfo->shdr).sh_info].outscnndx; + + + /* Perform the actual relocations. We only have to adjust + offsets and symbol indices. */ + RELOCATE_SECTION (statep, outscn, rellist->scninfo, dblindirect); + + /* Store the changes. */ + (void) xelf_update_shdr (outscn, shdr); + + /* Up to the next relocation section. */ + rellist = rellist->next; + } + + if (ld_state.rellist != NULL) + { + assert (ld_state.file_type != relocatable_file_type); + /* Create the relocations for the output file. */ + CREATE_RELOCATIONS (statep, dblindirect); + } + + + /* We need the ELF header once more. */ + xelf_getehdr (ld_state.outelf, ehdr); + assert (ehdr != NULL); + + /* Set the section header string table index. */ + if (likely (shstrtab_ndx < SHN_HIRESERVE) + && likely (shstrtab_ndx != SHN_XINDEX)) + ehdr->e_shstrndx = shstrtab_ndx; + else + { + /* We have to put the section index in the sh_link field of the + zeroth section header. */ + Elf_Scn *scn = elf_getscn (ld_state.outelf, 0); + + xelf_getshdr (scn, shdr); + if (unlikely (shdr == NULL)) + error (EXIT_FAILURE, 0, + gettext ("cannot get header of 0th section: %s"), + elf_errmsg (-1)); + + shdr->sh_link = shstrtab_ndx; + + (void) xelf_update_shdr (scn, shdr); + + ehdr->e_shstrndx = SHN_XINDEX; + } + + if (ld_state.file_type != relocatable_file_type) + /* DSOs and executables have to define the entry point symbol. */ + ehdr->e_entry = find_entry_point (); + + if (unlikely (xelf_update_ehdr (ld_state.outelf, ehdr) == 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot update ELF header: %s"), + elf_errmsg (-1)); + + + /* Free the data which we don't need anymore. */ + free (ld_state.dblindirect); + + + /* Finalize the .plt section the what belongs to them. */ + FINALIZE_PLT (statep, nsym, nsym_dyn); + + return 0; +} + + +/* This is a function which must be specified in all backends. */ +static void +ld_generic_relocate_section (struct ld_state *statep, Elf_Scn *outscn, + struct scninfo *firstp, + const Elf32_Word *dblindirect) +{ + error (EXIT_FAILURE, 0, gettext ("\ +linker backend didn't specify function to relocate section")); + /* NOTREACHED */ +} + + +/* Finalize the output file. */ +static int +ld_generic_finalize (struct ld_state *statep) +{ + /* Write out the ELF file data. */ + if (elf_update (ld_state.outelf, ELF_C_WRITE) == -1) + error (EXIT_FAILURE, 0, gettext ("while writing output file: %s"), + elf_errmsg (-1)); + + /* Free the resources. */ + if (elf_end (ld_state.outelf) != 0) + error (EXIT_FAILURE, 0, gettext ("while finishing output file: %s"), + elf_errmsg (-1)); + + /* Get the file status of the temporary file. */ + struct stat temp_st; + if (fstat (ld_state.outfd, &temp_st) != 0) + error (EXIT_FAILURE, errno, gettext ("cannot stat output file")); + + /* Now it's time to rename the file. Remove an old existing file + first. */ + if (rename (ld_state.tempfname, ld_state.outfname) != 0) + /* Something went wrong. */ + error (EXIT_FAILURE, errno, gettext ("cannot rename output file")); + + /* Make sure the output file is really the one we created. */ + struct stat new_st; + if (stat (ld_state.outfname, &new_st) != 0 + || new_st.st_ino != temp_st.st_ino + || new_st.st_dev != temp_st.st_dev) + { + /* Wow, somebody overwrote the output file, probably some intruder. */ + unlink (ld_state.outfname); + error (EXIT_FAILURE, 0, gettext ("\ +WARNING: temporary output file overwritten before linking finished")); + } + + /* Close the file descriptor. */ + (void) close (ld_state.outfd); + + /* Signal the cleanup handler that the file is correctly created. */ + ld_state.tempfname = NULL; + + return 0; +} + + +static bool +ld_generic_special_section_number_p (struct ld_state *statep, size_t number) +{ + /* There are no special section numbers in the gABI. */ + return false; +} + + +static bool +ld_generic_section_type_p (struct ld_state *statep, GElf_Word type) +{ + if (type < SHT_NUM + /* XXX Enable the following two when implemented. */ + // || type == SHT_GNU_LIBLIST + // || type == SHT_CHECKSUM + /* XXX Eventually include SHT_SUNW_move, SHT_SUNW_COMDAT, and + SHT_SUNW_syminfo. */ + || (type >= SHT_GNU_verdef && type <= SHT_GNU_versym)) + return true; + + return false; +} + + +static XElf_Xword +ld_generic_dynamic_section_flags (struct ld_state *statep) +{ + /* By default the .dynamic section is writable (and is of course + loaded). Few architecture differ from this. */ + return SHF_ALLOC | SHF_WRITE; +} + + +static void +ld_generic_initialize_plt (struct ld_state *statep, Elf_Scn *scn) +{ + /* This cannot be implemented generally. There should have been a + machine dependent implementation and we should never have arrived + here. */ + error (EXIT_FAILURE, 0, gettext ("no machine specific '%s' implementation"), + "initialize_plt"); +} + + +static void +ld_generic_initialize_pltrel (struct ld_state *statep, Elf_Scn *scn) +{ + /* This cannot be implemented generally. There should have been a + machine dependent implementation and we should never have arrived + here. */ + error (EXIT_FAILURE, 0, gettext ("no machine specific '%s' implementation"), + "initialize_pltrel"); +} + + +static void +ld_generic_initialize_got (struct ld_state *statep, Elf_Scn *scn) +{ + /* This cannot be implemented generally. There should have been a + machine dependent implementation and we should never have arrived + here. */ + error (EXIT_FAILURE, 0, gettext ("no machine specific '%s' implementation"), + "initialize_got"); +} + + +static void +ld_generic_finalize_plt (struct ld_state *statep, size_t nsym, size_t nsym_dyn) +{ + /* By default we assume that nothing has to be done. */ +} + + +static int +ld_generic_rel_type (struct ld_state *statep) +{ + /* This cannot be implemented generally. There should have been a + machine dependent implementation and we should never have arrived + here. */ + error (EXIT_FAILURE, 0, gettext ("no machine specific '%s' implementation"), + "rel_type"); + /* Just to keep the compiler calm. */ + return 0; +} + + +static void +ld_generic_count_relocations (struct ld_state *statep, struct scninfo *scninfo) +{ + /* This cannot be implemented generally. There should have been a + machine dependent implementation and we should never have arrived + here. */ + error (EXIT_FAILURE, 0, gettext ("no machine specific '%s' implementation"), + "count_relocations"); +} + + +static void +ld_generic_create_relocations (struct ld_state *statep, + const Elf32_Word *dblindirect) +{ + /* This cannot be implemented generally. There should have been a + machine dependent implementation and we should never have arrived + here. */ + error (EXIT_FAILURE, 0, gettext ("no machine specific '%s' implementation"), + "create_relocations"); +} diff --git a/src/ldlex.l b/src/ldlex.l new file mode 100644 index 00000000..9e30a865 --- /dev/null +++ b/src/ldlex.l @@ -0,0 +1,349 @@ +%{ +/* Copyright (C) 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2001. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <assert.h> +#include <ctype.h> +#include <elf.h> +#include <error.h> +#include <inttypes.h> +#include <libintl.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include <system.h> +#include <ld.h> +#include "ldscript.h" + +/* We sure use no threads to read the stream, so use the _unlocked + variants of the functions. */ +#undef getc +#define getc(s) getc_unlocked (s) +#undef ferror +#define ferror(s) ferror_unlocked (s) +#undef fread +#define fread(b, m, n, s) fread_unlocked (b, m, n, s) +#undef fwrite +#define fwrite(b, m, n, s) fwrite_unlocked (b, m, n, s) + +/* ECHO must be redefined since the default implementation ignores + the return value of fwrite_unlocked. */ +#define ECHO do { size_t n__ __attribute__ ((unused)) \ + = fwrite (yytext, yyleng, 1, yyout); } while (0) + +/* Defined in ld.c. */ +extern int ld_scan_version_script; + +#define MAX_PREPDEPTH 20 +static enum prepstate +{ + prep_normal, + skip_if, + skip_to_endif +} prepstate[MAX_PREPDEPTH]; +static int prepdepth; + +static void eat_comment (void); +static void eat_to_eol (bool empty); +static int attrib_convert (int c); +static void push_state (enum prepstate); +static int pop_state (void); +static int handle_ifdef (void); +static void invalid_char (int ch); +%} + +ID [a-zA-Z0-9_.*?]+ +FILENAMECHAR1 [a-zA-Z0-9_/.\\~] +FILENAMECHAR [^][{}[:space:]():;]+ +HEX 0[xX][0-9a-fA-F]+[kKmM]? +OCT 0[0-7]*[kKmM]? +DEC [0-9]+[kKmM]? +WHITE [[:space:]]+ + +%option yylineno +%option never-interactive +%option noyywrap + +%x IGNORE + +%% + if (unlikely (ld_scan_version_script)) + { + ld_scan_version_script = -1; + return kVERSION_SCRIPT; + } + +^"#"ifdef/[[:space:]] { BEGIN (handle_ifdef ()); } +^"#"else/[[:space:]\n] { eat_to_eol (true); + push_state (skip_to_endif); + BEGIN (IGNORE); } +^"#"elifdef/[[:space:]] { eat_to_eol (false); + push_state (skip_to_endif); + BEGIN (IGNORE); } +^"#"endif/[[:space:]\n] { eat_to_eol (true) ; } + +<IGNORE>^"#"ifdef/[[:space:]\n] { eat_to_eol (false); + push_state (skip_to_endif); } +<IGNORE>^"#"else/[[:space:]\n] { eat_to_eol (true); + assert (prepdepth > 0); + if (prepstate[prepdepth - 1] == skip_if) + { + /* Back to normal processing. */ + assert (prepdepth == 1); + BEGIN (pop_state ()); + } + } +<IGNORE>^"#"elifdef/[[:space:]] { assert (prepdepth > 0); + if (prepstate[prepdepth - 1] == skip_if) + { + /* Maybe this symbol is defined. */ + pop_state (); + BEGIN (handle_ifdef ()); + } + } +<IGNORE>^"#"endif/[[:space:]\n] { eat_to_eol (true); + BEGIN (pop_state ()); } +<IGNORE>.|\n { /* nothing */ } + + +"/*" { eat_comment (); } + +ALIGN { return kALIGN; } +AS_NEEDED { return kAS_NEEDED; } +ENTRY { return kENTRY; } +EXCLUDE_FILE { return kEXCLUDE_FILE; } +"global:" { return kGLOBAL; } +GROUP { return kGROUP; } +INPUT { return kINPUT; } +INTERP { return kINTERP; } +KEEP { return kKEEP; } +"local:" { return kLOCAL; } +OUTPUT_FORMAT { return kOUTPUT_FORMAT; } +PAGESIZE { return kPAGESIZE; } +PROVIDE { return kPROVIDE; } +SEARCH_DIR { return kSEARCH_DIR; } +SEGMENT { return kSEGMENT; } +SIZEOF_HEADERS { return kSIZEOF_HEADERS; } +SORT { return kSORT; } +VERSION { return kVERSION; } + +"["([RWX]){0,3}"]" { int cnt = 1 ; + ldlval.num = 0; + while (cnt < yyleng - 1) + ldlval.num |= attrib_convert (yytext[cnt++]); + return kMODE; } + +"{" { return '{'; } +"}" { return '}'; } +"(" { return '('; } +")" { return ')'; } +":" { return ':'; } +";" { return ';'; } +"=" { return '='; } +"+" { ldlval.op = exp_plus; return kADD_OP; } +"-" { ldlval.op = exp_minus; return kADD_OP; } +"*" { return '*'; } +"/" { ldlval.op = exp_div; return kMUL_OP; } +"%" { ldlval.op = exp_mod; return kMUL_OP; } +"&" { return '&'; } +"|" { return '|'; } + +"," { return ','; } + +{HEX}|{OCT}|{DEC} { char *endp; + ldlval.num = strtoumax (yytext, &endp, 0); + if (*endp != '\0') + { + if (tolower (*endp) == 'k') + ldlval.num *= 1024; + else + { + assert (tolower (*endp) == 'm'); + ldlval.num *= 1024 * 1024; + } + } + return kNUM; } + +{ID} { ldlval.str = obstack_strndup (&ld_state.smem, + yytext, yyleng); + return kID; } + +{FILENAMECHAR1}{FILENAMECHAR} { ldlval.str = obstack_strndup (&ld_state.smem, + yytext, yyleng); + return kFILENAME; } + +{WHITE} { /* IGNORE */ } + +. { invalid_char (*yytext); } + +%% + +static void +eat_comment (void) +{ + while (1) + { + int c = input (); + + while (c != '*' && c != EOF) + c = input (); + + if (c == '*') + { + c = input (); + while (c == '*') + c = input (); + if (c == '/') + break; + } + + if (c == EOF) + { + /* XXX Use the setjmp buffer and signal EOF in comment */ + error (0, 0, gettext ("EOF in comment")); + break; + } + } +} + + +static void +eat_to_eol (bool empty) +{ + bool warned = false; + + while (1) + { + int c = input (); + + if (c == EOF) + break; + if (c == '\n') + { + ++yylineno; + break; + } + + if (empty && ! isspace (c) && ! warned) + { + error (0, 0, gettext ("%d: garbage at end of line"), yylineno); + warned = true; + } + } +} + + +static int +attrib_convert (int c) +{ + if (c == 'X') + return PF_X; + if (c == 'W') + return PF_W; + assert (c == 'R'); + return PF_R; +} + + +static void +push_state (enum prepstate state) +{ + if (prepdepth >= MAX_PREPDEPTH) + error (EXIT_FAILURE, 0, gettext ("%d: conditionals nested too deep"), + yylineno); + + prepstate[prepdepth++] = state; +} + + +static int +pop_state (void) +{ + if (prepdepth == 0) + error (0, 0, gettext ("%d: unexpected #endif"), yylineno); + else + --prepdepth; + + return prepdepth == 0 ? INITIAL : IGNORE; +} + + +static int +handle_ifdef (void) +{ + char idbuf[50]; + char *id = idbuf; + size_t idlen = 0; + size_t idmax = sizeof (idbuf); + bool ignore_ws = true; + bool defined = false; + int result; + + while (1) + { + int c = input (); + + if (isspace (c) && ignore_ws) + continue; + + if (c != '_' && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') + && (idlen == 0 || c < '0' || c > '9')) + { + unput (c); + break; + } + + if (idlen == idmax) + { + char *newp = (char *) alloca (idmax *= 2); + id = memcpy (newp, id, idlen); + } + + id[idlen++] = c; + ignore_ws = false; + } + + /* XXX Compare in a better way. */ + if (idlen == 6 && strncmp (id, "SHARED", 6) == 0) + defined = ld_state.file_type == dso_file_type; + + if (defined) + result = INITIAL; + else + { + push_state (skip_if); + result = IGNORE; + } + + return result; +} + + +static void +invalid_char (int ch) +{ + error (0, 0, (isascii (ch) + ? gettext ("invalid character '%c' at line %d; ignored") + : gettext ("invalid character '\\%o' at line %d; ignored")), + ch, yylineno); +} + + +// Local Variables: +// mode: C +// End: diff --git a/src/ldscript.y b/src/ldscript.y new file mode 100644 index 00000000..23741b7d --- /dev/null +++ b/src/ldscript.y @@ -0,0 +1,795 @@ +%{ +/* Parser for linker scripts. + Copyright (C) 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2001. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <assert.h> +#include <error.h> +#include <libintl.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <system.h> +#include <ld.h> + +/* The error handler. */ +static void yyerror (const char *s); + +/* Some helper functions we need to construct the data structures + describing information from the file. */ +static struct expression *new_expr (int tag); +static struct input_section_name *new_input_section_name (const char *name, + bool sort_flag); +static struct input_rule *new_input_rule (int tag); +static struct output_rule *new_output_rule (int tag); +static struct assignment *new_assignment (const char *variable, + struct expression *expression, + bool provide_flag); +static void new_segment (int mode, struct output_rule *output_rule); +static struct filename_list *new_filename_listelem (const char *string); +static void add_inputfiles (struct filename_list *fnames); +static struct id_list *new_id_listelem (const char *str); + static struct filename_list *mark_as_needed (struct filename_list *listp); +static struct version *new_version (struct id_list *local, + struct id_list *global); +static struct version *merge_versions (struct version *one, + struct version *two); +static void add_versions (struct version *versions); + +extern int yylex (void); +%} + +%union { + uintmax_t num; + enum expression_tag op; + char *str; + struct expression *expr; + struct input_section_name *sectionname; + struct filemask_section_name *filemask_section_name; + struct input_rule *input_rule; + struct output_rule *output_rule; + struct assignment *assignment; + struct filename_list *filename_list; + struct version *version; + struct id_list *id_list; +} + +%token kADD_OP +%token kALIGN +%token kAS_NEEDED +%token kENTRY +%token kEXCLUDE_FILE +%token <str> kFILENAME +%token kGLOBAL +%token kGROUP +%token <str> kID +%token kINPUT +%token kINTERP +%token kKEEP +%token kLOCAL +%token <num> kMODE +%token kMUL_OP +%token <num> kNUM +%token kOUTPUT_FORMAT +%token kPAGESIZE +%token kPROVIDE +%token kSEARCH_DIR +%token kSEGMENT +%token kSIZEOF_HEADERS +%token kSORT +%token kVERSION +%token kVERSION_SCRIPT + +%left '|' +%left '&' +%left ADD_OP +%left MUL_OP '*' + +%type <op> kADD_OP +%type <op> kMUL_OP +%type <str> filename_id +%type <str> filename_id_star +%type <str> exclude_opt +%type <expr> expr +%type <sectionname> sort_opt_name +%type <filemask_section_name> sectionname +%type <input_rule> inputsection +%type <input_rule> inputsections +%type <output_rule> outputsection +%type <output_rule> outputsections +%type <assignment> assignment +%type <filename_list> filename_id_list +%type <version> versionlist +%type <version> version +%type <version> version_stmt_list +%type <version> version_stmt +%type <id_list> filename_id_star_list + +%expect 16 + +%% + +script_or_version: + file + | kVERSION_SCRIPT versionlist + { add_versions ($2); } + ; + +file: file content + | content + ; + +content: kENTRY '(' kID ')' ';' + { + if (likely (ld_state.entry == NULL)) + ld_state.entry = $3; + } + | kSEARCH_DIR '(' filename_id ')' ';' + { + ld_new_searchdir ($3); + } + | kPAGESIZE '(' kNUM ')' ';' + { + if (likely (ld_state.pagesize == 0)) + ld_state.pagesize = $3; + } + | kINTERP '(' filename_id ')' ';' + { + if (likely (ld_state.interp == NULL)) + ld_state.interp = $3; + } + | kSEGMENT kMODE '{' outputsections '}' + { + new_segment ($2, $4); + } + | kSEGMENT error '{' outputsections '}' + { + fputs_unlocked (gettext ("mode for segment invalid\n"), + stderr); + new_segment (0, $4); + } + | kGROUP '(' filename_id_list ')' + { + /* First little optimization. If there is only one + file in the group don't do anything. */ + if ($3 != $3->next) + { + $3->next->group_start = 1; + $3->group_end = 1; + } + add_inputfiles ($3); + } + | kINPUT '(' filename_id_list ')' + { add_inputfiles ($3); } + | kAS_NEEDED '(' filename_id_list ')' + { add_inputfiles (mark_as_needed ($3)); } + | kVERSION '{' versionlist '}' + { add_versions ($3); } + | kOUTPUT_FORMAT '(' filename_id ')' + { /* XXX TODO */ } + ; + +outputsections: outputsections outputsection + { + $2->next = $1->next; + $$ = $1->next = $2; + } + | outputsection + { $$ = $1; } + ; + +outputsection: assignment ';' + { + $$ = new_output_rule (output_assignment); + $$->val.assignment = $1; + } + | kID '{' inputsections '}' + { + $$ = new_output_rule (output_section); + $$->val.section.name = $1; + $$->val.section.input = $3->next; + if (ld_state.strip == strip_debug + && ebl_debugscn_p (ld_state.ebl, $1)) + $$->val.section.ignored = true; + else + $$->val.section.ignored = false; + $3->next = NULL; + } + | kID ';' + { + /* This is a short cut for "ID { *(ID) }". */ + $$ = new_output_rule (output_section); + $$->val.section.name = $1; + $$->val.section.input = new_input_rule (input_section); + $$->val.section.input->next = NULL; + $$->val.section.input->val.section = + (struct filemask_section_name *) + obstack_alloc (&ld_state.smem, + sizeof (struct filemask_section_name)); + $$->val.section.input->val.section->filemask = NULL; + $$->val.section.input->val.section->excludemask = NULL; + $$->val.section.input->val.section->section_name = + new_input_section_name ($1, false); + $$->val.section.input->val.section->keep_flag = false; + if (ld_state.strip == strip_debug + && ebl_debugscn_p (ld_state.ebl, $1)) + $$->val.section.ignored = true; + else + $$->val.section.ignored = false; + } + ; + +assignment: kID '=' expr + { $$ = new_assignment ($1, $3, false); } + | kPROVIDE '(' kID '=' expr ')' + { $$ = new_assignment ($3, $5, true); } + ; + +inputsections: inputsections inputsection + { + $2->next = $1->next; + $$ = $1->next = $2; + } + | inputsection + { $$ = $1; } + ; + +inputsection: sectionname + { + $$ = new_input_rule (input_section); + $$->val.section = $1; + } + | kKEEP '(' sectionname ')' + { + $3->keep_flag = true; + + $$ = new_input_rule (input_section); + $$->val.section = $3; + } + | assignment ';' + { + $$ = new_input_rule (input_assignment); + $$->val.assignment = $1; + } + ; + +sectionname: filename_id_star '(' exclude_opt sort_opt_name ')' + { + $$ = (struct filemask_section_name *) + obstack_alloc (&ld_state.smem, sizeof (*$$)); + $$->filemask = $1; + $$->excludemask = $3; + $$->section_name = $4; + $$->keep_flag = false; + } + ; + +sort_opt_name: kID + { $$ = new_input_section_name ($1, false); } + | kSORT '(' kID ')' + { $$ = new_input_section_name ($3, true); } + ; + +exclude_opt: kEXCLUDE_FILE '(' filename_id ')' + { $$ = $3; } + | + { $$ = NULL; } + ; + +expr: kALIGN '(' expr ')' + { + $$ = new_expr (exp_align); + $$->val.child = $3; + } + | '(' expr ')' + { $$ = $2; } + | expr '*' expr + { + $$ = new_expr (exp_mult); + $$->val.binary.left = $1; + $$->val.binary.right = $3; + } + | expr kMUL_OP expr + { + $$ = new_expr ($2); + $$->val.binary.left = $1; + $$->val.binary.right = $3; + } + | expr kADD_OP expr + { + $$ = new_expr ($2); + $$->val.binary.left = $1; + $$->val.binary.right = $3; + } + | expr '&' expr + { + $$ = new_expr (exp_and); + $$->val.binary.left = $1; + $$->val.binary.right = $3; + } + | expr '|' expr + { + $$ = new_expr (exp_or); + $$->val.binary.left = $1; + $$->val.binary.right = $3; + } + | kNUM + { + $$ = new_expr (exp_num); + $$->val.num = $1; + } + | kID + { + $$ = new_expr (exp_id); + $$->val.str = $1; + } + | kSIZEOF_HEADERS + { $$ = new_expr (exp_sizeof_headers); } + | kPAGESIZE + { $$ = new_expr (exp_pagesize); } + ; + +filename_id_list: kGROUP '(' filename_id_list ')' + { + /* First little optimization. If there is only one + file in the group don't do anything. */ + if ($3 != $3->next) + { + $3->next->group_start = 1; + $3->group_end = 1; + } + $$ = $3; + } + | kAS_NEEDED '(' filename_id_list ')' + { $$ = mark_as_needed ($3); } + | filename_id_list comma_opt filename_id + { + struct filename_list *newp = new_filename_listelem ($3); + newp->next = $1->next; + $$ = $1->next = newp; + } + | filename_id + { $$ = new_filename_listelem ($1); } + ; + +comma_opt: ',' + | + ; + +versionlist: versionlist version + { + $2->next = $1->next; + $$ = $1->next = $2; + } + | version + { $$ = $1; } + ; + +version: '{' version_stmt_list '}' ';' + { + $2->versionname = ""; + $2->parentname = NULL; + $$ = $2; + } + | filename_id '{' version_stmt_list '}' ';' + { + $3->versionname = $1; + $3->parentname = NULL; + $$ = $3; + } + | filename_id '{' version_stmt_list '}' filename_id ';' + { + $3->versionname = $1; + $3->parentname = $5; + $$ = $3; + } + ; + +version_stmt_list: + version_stmt_list version_stmt + { $$ = merge_versions ($1, $2); } + | version_stmt + { $$ = $1; } + ; + +version_stmt: kGLOBAL filename_id_star_list + { $$ = new_version (NULL, $2); } + | kLOCAL filename_id_star_list + { $$ = new_version ($2, NULL); } + ; + +filename_id_star_list: + filename_id_star_list filename_id_star ';' + { + struct id_list *newp = new_id_listelem ($2); + newp->next = $1->next; + $$ = $1->next = newp; + } + | filename_id_star ';' + { $$ = new_id_listelem ($1); } + ; + +filename_id: kFILENAME + { $$ = $1; } + | kID + { $$ = $1; } + ; + +filename_id_star: filename_id + { $$ = $1; } + | '*' + { $$ = NULL; } + ; + +%% + +static void +yyerror (const char *s) +{ + error (0, 0, (ld_scan_version_script + ? gettext ("while reading version script '%s': %s at line %d") + : gettext ("while reading linker script '%s': %s at line %d")), + ldin_fname, gettext (s), ldlineno); +} + + +static struct expression * +new_expr (int tag) +{ + struct expression *newp = (struct expression *) + obstack_alloc (&ld_state.smem, sizeof (*newp)); + + newp->tag = tag; + return newp; +} + + +static struct input_section_name * +new_input_section_name (const char *name, bool sort_flag) +{ + struct input_section_name *newp = (struct input_section_name *) + obstack_alloc (&ld_state.smem, sizeof (*newp)); + + newp->name = name; + newp->sort_flag = sort_flag; + return newp; +} + + +static struct input_rule * +new_input_rule (int tag) +{ + struct input_rule *newp = (struct input_rule *) + obstack_alloc (&ld_state.smem, sizeof (*newp)); + + newp->tag = tag; + newp->next = newp; + return newp; +} + + +static struct output_rule * +new_output_rule (int tag) +{ + struct output_rule *newp = (struct output_rule *) + memset (obstack_alloc (&ld_state.smem, sizeof (*newp)), + '\0', sizeof (*newp)); + + newp->tag = tag; + newp->next = newp; + return newp; +} + + +static struct assignment * +new_assignment (const char *variable, struct expression *expression, + bool provide_flag) +{ + struct assignment *newp = (struct assignment *) + obstack_alloc (&ld_state.smem, sizeof (*newp)); + + newp->variable = variable; + newp->expression = expression; + newp->sym = NULL; + newp->provide_flag = provide_flag; + + /* Insert the symbol into a hash table. We will later have to matc*/ + return newp; +} + + +static void +new_segment (int mode, struct output_rule *output_rule) +{ + struct output_segment *newp; + + newp + = (struct output_segment *) obstack_alloc (&ld_state.smem, sizeof (*newp)); + newp->mode = mode; + newp->next = newp; + + newp->output_rules = output_rule->next; + output_rule->next = NULL; + + /* Enqueue the output segment description. */ + if (ld_state.output_segments == NULL) + ld_state.output_segments = newp; + else + { + newp->next = ld_state.output_segments->next; + ld_state.output_segments = ld_state.output_segments->next = newp; + } + + /* If the output file should be stripped of all symbol set the flag + in the structures of all output sections. */ + if (mode == 0 && ld_state.strip == strip_all) + { + struct output_rule *runp; + + for (runp = newp->output_rules; runp != NULL; runp = runp->next) + if (runp->tag == output_section) + runp->val.section.ignored = true; + } +} + + +static struct filename_list * +new_filename_listelem (const char *string) +{ + struct filename_list *newp; + + /* We use calloc and not the obstack since this object can be freed soon. */ + newp = (struct filename_list *) xcalloc (1, sizeof (*newp)); + newp->name = string; + newp->next = newp; + return newp; +} + + +static struct filename_list * +mark_as_needed (struct filename_list *listp) +{ + struct filename_list *runp = listp; + while (runp != NULL) + { + runp->as_needed = true; + runp = runp->next; + } + + return listp; +} + + +static void +add_inputfiles (struct filename_list *fnames) +{ + assert (fnames != NULL); + + if (ld_state.srcfiles == NULL) + ld_state.srcfiles = fnames; + else + { + struct filename_list *first = ld_state.srcfiles->next; + + ld_state.srcfiles->next = fnames->next; + fnames->next = first; + ld_state.srcfiles->next = fnames; + } +} + + +static _Bool +special_char_p (const char *str) +{ + while (*str != '\0') + { + if (__builtin_expect (*str == '*', 0) + || __builtin_expect (*str == '?', 0) + || __builtin_expect (*str == '[', 0)) + return true; + + ++str; + } + + return false; +} + + +static struct id_list * +new_id_listelem (const char *str) +{ + struct id_list *newp; + + newp = (struct id_list *) obstack_alloc (&ld_state.smem, sizeof (*newp)); + if (str == NULL) + newp->u.id_type = id_all; + else if (__builtin_expect (special_char_p (str), false)) + newp->u.id_type = id_wild; + else + newp->u.id_type = id_str; + newp->id = str; + newp->next = newp; + + return newp; +} + + +static struct version * +new_version (struct id_list *local, struct id_list *global) +{ + struct version *newp; + + newp = (struct version *) obstack_alloc (&ld_state.smem, sizeof (*newp)); + newp->next = newp; + newp->local_names = local; + newp->global_names = global; + newp->versionname = NULL; + newp->parentname = NULL; + + return newp; +} + + +static struct version * +merge_versions (struct version *one, struct version *two) +{ + assert (two->local_names == NULL || two->global_names == NULL); + + if (two->local_names != NULL) + { + if (one->local_names == NULL) + one->local_names = two->local_names; + else + { + two->local_names->next = one->local_names->next; + one->local_names = one->local_names->next = two->local_names; + } + } + else + { + if (one->global_names == NULL) + one->global_names = two->global_names; + else + { + two->global_names->next = one->global_names->next; + one->global_names = one->global_names->next = two->global_names; + } + } + + return one; +} + + +static void +add_id_list (const char *versionname, struct id_list *runp, _Bool local) +{ + struct id_list *lastp = runp; + + if (runp == NULL) + /* Nothing to do. */ + return; + + /* Convert into a simple single-linked list. */ + runp = runp->next; + assert (runp != NULL); + lastp->next = NULL; + + do + if (runp->u.id_type == id_str) + { + struct id_list *curp; + struct id_list *defp; + unsigned long int hval = elf_hash (runp->id); + + curp = runp; + runp = runp->next; + + defp = ld_version_str_tab_find (&ld_state.version_str_tab, hval, curp); + if (defp != NULL) + { + /* There is already a version definition for this symbol. */ + while (strcmp (defp->u.s.versionname, versionname) != 0) + { + if (defp->next == NULL) + { + /* No version like this so far. */ + defp->next = curp; + curp->u.s.local = local; + curp->u.s.versionname = versionname; + curp->next = NULL; + defp = NULL; + break; + } + + defp = defp->next; + } + + if (defp != NULL && defp->u.s.local != local) + error (EXIT_FAILURE, 0, versionname[0] == '\0' + ? gettext ("\ +symbol '%s' in declared both local and global for unnamed version") + : gettext ("\ +symbol '%s' in declared both local and global for version '%s'"), + runp->id, versionname); + } + else + { + /* This is the first version definition for this symbol. */ + ld_version_str_tab_insert (&ld_state.version_str_tab, hval, curp); + + curp->u.s.local = local; + curp->u.s.versionname = versionname; + curp->next = NULL; + } + } + else if (runp->u.id_type == id_all) + { + if (local) + { + if (ld_state.default_bind_global) + error (EXIT_FAILURE, 0, + gettext ("default visibility set as local and global")); + ld_state.default_bind_local = true; + } + else + { + if (ld_state.default_bind_local) + error (EXIT_FAILURE, 0, + gettext ("default visibility set as local and global")); + ld_state.default_bind_global = true; + } + + runp = runp->next; + } + else + { + assert (runp->u.id_type == id_wild); + /* XXX TBI */ + abort (); + } + while (runp != NULL); +} + + +static void +add_versions (struct version *versions) +{ + struct version *lastp = versions; + + if (versions == NULL) + return; + + /* Convert into a simple single-linked list. */ + versions = versions->next; + assert (versions != NULL); + lastp->next = NULL; + + do + { + struct version *oldp; + + add_id_list (versions->versionname, versions->local_names, true); + add_id_list (versions->versionname, versions->global_names, false); + + oldp = versions; + versions = versions->next; + } + while (versions != NULL); +} diff --git a/src/libld_elf_i386.map b/src/libld_elf_i386.map new file mode 100644 index 00000000..703af6d8 --- /dev/null +++ b/src/libld_elf_i386.map @@ -0,0 +1,7 @@ +ELFUTILS_1.0 { + global: + elf_i386_ld_init; + + local: + *; +}; diff --git a/src/nm.c b/src/nm.c new file mode 100644 index 00000000..b1e71a33 --- /dev/null +++ b/src/nm.c @@ -0,0 +1,1282 @@ +/* Print symbol information from ELF file in human-readable form. + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2000. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <ar.h> +#include <argp.h> +#include <assert.h> +#include <ctype.h> +#include <dwarf.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <inttypes.h> +#include <libdw.h> +#include <libintl.h> +#include <locale.h> +#include <mcheck.h> +#include <obstack.h> +#include <search.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/param.h> + +#include <system.h> +#include "../libebl/libeblP.h" + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + + +/* Values for the parameters which have no short form. */ +#define OPT_DEFINED 0x100 +#define OPT_MARK_WEAK 0x101 + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Output selection:"), 0 }, + { "debug-syms", 'a', NULL, 0, N_("Display debugger-only symbols"), 0 }, + { "defined-only", OPT_DEFINED, NULL, 0, N_("Display only defined symbols"), + 0 }, + { "dynamic", 'D', NULL, 0, + N_("Display dynamic symbols instead of normal symbols"), 0 }, + { "extern-only", 'g', NULL, 0, N_("Display only external symbols"), 0 }, + { "undefined-only", 'u', NULL, 0, N_("Display only undefined symbols"), 0 }, + { "print-armap", 's', NULL, 0, + N_("Include index for symbols from archive members"), 0 }, + + { NULL, 0, NULL, 0, N_("Output format:"), 0 }, + { "print-file-name", 'A', NULL, 0, + N_("Print name of the input file before every symbol"), 0 }, + { NULL, 'o', NULL, OPTION_HIDDEN, "Same as -A", 0 }, + { "format", 'f', "FORMAT", 0, + N_("Use the output format FORMAT. FORMAT can be `bsd', `sysv' or `posix'. The default is `sysv'"), + 0 }, + { NULL, 'B', NULL, 0, N_("Same as --format=bsd"), 0 }, + { "portability", 'P', NULL, 0, N_("Same as --format=posix"), 0 }, + { "radix", 't', "RADIX", 0, N_("Use RADIX for printing symbol values"), 0 }, + { "mark-weak", OPT_MARK_WEAK, NULL, 0, N_("Mark weak symbols"), 0 }, + { "print-size", 'S', NULL, 0, N_("Print size of defined symbols"), 0 }, + + { NULL, 0, NULL, 0, N_("Output options:"), 0 }, + { "numeric-sort", 'n', NULL, 0, N_("Sort symbols numerically by address"), + 0 }, + { "no-sort", 'p', NULL, 0, N_("Do not sort the symbols"), 0 }, + { "reverse-sort", 'r', NULL, 0, N_("Reverse the sense of the sort"), 0 }, + { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("List symbols from FILEs (a.out by default)."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("[FILE...]"); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, NULL, NULL, NULL +}; + + +/* Print symbols in file named FNAME. */ +static int process_file (const char *fname, bool more_than_one); + +/* Handle content of archive. */ +static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname, + const char *suffix); + +/* Handle ELF file. */ +static int handle_elf (Elf *elf, const char *prefix, const char *fname, + const char *suffix); + + +#define INTERNAL_ERROR(fname) \ + error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s-%s): %s"), \ + fname, __LINE__, VERSION, __DATE__, elf_errmsg (-1)) + + +/* Internal representation of symbols. */ +typedef struct GElf_SymX +{ + GElf_Sym sym; + Elf32_Word xndx; + char *where; +} GElf_SymX; + + +/* User-selectable options. */ + +/* The selected output format. */ +static enum +{ + format_sysv = 0, + format_bsd, + format_posix +} format; + +/* Print defined, undefined, or both? */ +static bool hide_undefined; +static bool hide_defined; + +/* Print local symbols also? */ +static bool hide_local; + +/* Nonzero if full filename should precede every symbol. */ +static bool print_file_name; + +/* If true print size of defined symbols in BSD format. */ +static bool print_size; + +/* If true print archive index. */ +static bool print_armap; + +/* If true reverse sorting. */ +static bool reverse_sort; + +/* Type of the section we are printing. */ +static GElf_Word symsec_type = SHT_SYMTAB; + +/* Sorting selection. */ +static enum +{ + sort_name = 0, + sort_numeric, + sort_nosort +} sort; + +/* Radix for printed numbers. */ +static enum +{ + radix_hex = 0, + radix_decimal, + radix_octal +} radix; + +/* If nonzero weak symbols are distinguished from global symbols by adding + a `*' after the identifying letter for the symbol class and type. */ +static bool mark_weak; + + +int +main (int argc, char *argv[]) +{ + int remaining; + int result = 0; + + /* Make memory leak detection possible. */ + mtrace (); + + /* We use no threads here which can interfere with handling a stream. */ + (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER); + (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER); + (void) __fsetlocking (stderr, FSETLOCKING_BYCALLER); + + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + (void) bindtextdomain (PACKAGE, LOCALEDIR); + + /* Initialize the message catalog. */ + (void) textdomain (PACKAGE); + + /* Parse and process arguments. */ + (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + /* Tell the library which version we are expecting. */ + (void) elf_version (EV_CURRENT); + + if (remaining == argc) + /* The user didn't specify a name so we use a.out. */ + result = process_file ("a.out", false); + else + { + /* Process all the remaining files. */ + const bool more_than_one = remaining + 1 < argc; + + do + result |= process_file (argv[remaining], more_than_one); + while (++remaining < argc); + } + + return result; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "nm (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2005"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case 'a': + /* XXX */ + break; + + case 'f': + if (strcmp (arg, "bsd") == 0) + format = format_bsd; + else if (strcmp (arg, "posix") == 0) + format = format_posix; + else + /* Be bug compatible. The BFD implementation also defaulted to + using the SysV format if nothing else matches. */ + format = format_sysv; + break; + + case 'g': + hide_local = true; + break; + + case 'n': + sort = sort_numeric; + break; + + case 'p': + sort = sort_nosort; + break; + + case 't': + if (strcmp (arg, "10") == 0 || strcmp (arg, "d") == 0) + radix = radix_decimal; + else if (strcmp (arg, "8") == 0 || strcmp (arg, "o") == 0) + radix = radix_octal; + else + radix = radix_hex; + break; + + case 'u': + hide_undefined = false; + hide_defined = true; + break; + + case 'A': + case 'o': + print_file_name = true; + break; + + case 'B': + format = format_bsd; + break; + + case 'D': + symsec_type = SHT_DYNSYM; + break; + + case 'P': + format = format_posix; + break; + + case OPT_DEFINED: + hide_undefined = true; + hide_defined = false; + break; + + case OPT_MARK_WEAK: + mark_weak = true; + break; + + case 'S': + print_size = true; + break; + + case 's': + print_armap = true; + break; + + case 'r': + reverse_sort = true; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +/* Open the file and determine the type. */ +static int +process_file (const char *fname, bool more_than_one) +{ + /* Open the file. */ + int fd = open (fname, O_RDONLY); + if (fd == -1) + { + error (0, errno, gettext ("cannot open '%s'"), fname); + return 1; + } + + /* Now get the ELF descriptor. */ + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (elf != NULL) + { + if (elf_kind (elf) == ELF_K_ELF) + { + int result = handle_elf (elf, more_than_one ? "" : NULL, + fname, NULL); + + if (elf_end (elf) != 0) + INTERNAL_ERROR (fname); + + if (close (fd) != 0) + error (EXIT_FAILURE, errno, gettext ("while close '%s'"), fname); + + return result; + } + else if (elf_kind (elf) == ELF_K_AR) + { + int result = handle_ar (fd, elf, NULL, fname, NULL); + + if (elf_end (elf) != 0) + INTERNAL_ERROR (fname); + + if (close (fd) != 0) + error (EXIT_FAILURE, errno, gettext ("while close '%s'"), fname); + + return result; + } + + /* We cannot handle this type. Close the descriptor anyway. */ + if (elf_end (elf) != 0) + INTERNAL_ERROR (fname); + } + + error (0, 0, gettext ("%s: File format not recognized"), fname); + + return 1; +} + + +static int +handle_ar (int fd, Elf *elf, const char *prefix, const char *fname, + const char *suffix) +{ + size_t fname_len = strlen (fname) + 1; + size_t prefix_len = prefix != NULL ? strlen (prefix) : 0; + char new_prefix[prefix_len + fname_len + 2]; + size_t suffix_len = suffix != NULL ? strlen (suffix) : 0; + char new_suffix[suffix_len + 2]; + Elf *subelf; + Elf_Cmd cmd = ELF_C_READ_MMAP; + int result = 0; + + char *cp = new_prefix; + if (prefix != NULL) + cp = stpcpy (cp, prefix); + cp = stpcpy (cp, fname); + stpcpy (cp, "["); + + cp = new_suffix; + if (suffix != NULL) + cp = stpcpy (cp, suffix); + stpcpy (cp, "]"); + + /* First print the archive index if this is wanted. */ + if (print_armap) + { + Elf_Arsym *arsym = elf_getarsym (elf, NULL); + + if (arsym != NULL) + { + Elf_Arhdr *arhdr = NULL; + size_t arhdr_off = 0; /* Note: 0 is no valid offset. */ + + puts (gettext("\nArchive index:")); + + while (arsym->as_off != 0) + { + if (arhdr_off != arsym->as_off + && (elf_rand (elf, arsym->as_off) != arsym->as_off + || (subelf = elf_begin (fd, cmd, elf)) == NULL + || (arhdr = elf_getarhdr (subelf)) == NULL)) + { + error (0, 0, gettext ("invalid offset %zu for symbol %s"), + arsym->as_off, arsym->as_name); + continue; + } + + printf (gettext ("%s in %s\n"), arsym->as_name, arhdr->ar_name); + + ++arsym; + } + + if (elf_rand (elf, SARMAG) != SARMAG) + { + error (0, 0, + gettext ("cannot reset archive offset to beginning")); + return 1; + } + } + } + + /* Process all the files contained in the archive. */ + while ((subelf = elf_begin (fd, cmd, elf)) != NULL) + { + /* The the header for this element. */ + Elf_Arhdr *arhdr = elf_getarhdr (subelf); + + /* Skip over the index entries. */ + if (strcmp (arhdr->ar_name, "/") != 0 + && strcmp (arhdr->ar_name, "//") != 0) + { + if (elf_kind (subelf) == ELF_K_ELF) + result |= handle_elf (subelf, new_prefix, arhdr->ar_name, + new_suffix); + else if (elf_kind (subelf) == ELF_K_AR) + result |= handle_ar (fd, subelf, new_prefix, arhdr->ar_name, + new_suffix); + else + { + error (0, 0, gettext ("%s%s%s: file format not recognized"), + new_prefix, arhdr->ar_name, new_suffix); + result = 1; + } + } + + /* Get next archive element. */ + cmd = elf_next (subelf); + if (elf_end (subelf) != 0) + INTERNAL_ERROR (fname); + } + + return result; +} + + +/* Mapping of radix and binary class to length. */ +static const int length_map[2][3] = +{ + [ELFCLASS32 - 1] = + { + [radix_hex] = 8, + [radix_decimal] = 10, + [radix_octal] = 11 + }, + [ELFCLASS64 - 1] = + { + [radix_hex] = 16, + [radix_decimal] = 20, + [radix_octal] = 22 + } +}; + + +struct global_name +{ + Dwarf_Global global; + const char *name; +}; + + +static int +global_compare (const void *p1, const void *p2) +{ + const Dwarf_Global *g1 = (const Dwarf_Global *) p1; + const Dwarf_Global *g2 = (const Dwarf_Global *) p2; + + return strcmp (g1->name, g2->name); +} + + +static void *global_root; + + +static int +get_global (Dwarf *dbg __attribute__ ((unused)), Dwarf_Global *global, + void *arg __attribute__ ((unused))) +{ + tsearch (memcpy (xmalloc (sizeof (Dwarf_Global)), global, + sizeof (Dwarf_Global)), + &global_root, global_compare); + + return DWARF_CB_OK; +} + + +struct local_name +{ + const char *name; + const char *file; + Dwarf_Word lineno; + Dwarf_Addr lowpc; + Dwarf_Addr highpc; +}; + + +static int +local_compare (const void *p1, const void *p2) +{ + struct local_name *g1 = (struct local_name *) p1; + struct local_name *g2 = (struct local_name *) p2; + int result; + + result = strcmp (g1->name, g2->name); + if (result == 0) + { + if (g1->lowpc <= g2->lowpc && g1->highpc >= g2->highpc) + { + /* g2 is contained in g1. Update the data. */ + g2->lowpc = g1->lowpc; + g2->highpc = g1->highpc; + result = 0; + } + else if (g2->lowpc <= g1->lowpc && g2->highpc >= g1->highpc) + { + /* g1 is contained in g2. Update the data. */ + g1->lowpc = g2->lowpc; + g1->highpc = g2->highpc; + result = 0; + } + else + result = g1->lowpc < g2->lowpc ? -1 : 1; + } + + return result; +} + + +static int +get_var_range (Dwarf_Die *die, Dwarf_Word *lowpc, Dwarf_Word *highpc) +{ + Dwarf_Attribute locattr_mem; + Dwarf_Attribute *locattr = dwarf_attr (die, DW_AT_location, &locattr_mem); + if (locattr == NULL) + return 1; + + Dwarf_Loc *loc; + size_t nloc; + if (dwarf_getloclist (locattr, &loc, &nloc) != 0) + return 1; + + /* Interpret the location expressions. */ + // XXX For now just the simple one: + if (nloc == 1 && loc[0].atom == DW_OP_addr) + { + *lowpc = *highpc = loc[0].number; + return 0; + } + + return 1; +} + + + +static void *local_root; + + +static void +get_local_names (Dwarf *dbg) +{ + Dwarf_Off offset = 0; + Dwarf_Off old_offset; + size_t hsize; + + while (dwarf_nextcu (dbg, old_offset = offset, &offset, &hsize, NULL, NULL, + NULL) == 0) + { + Dwarf_Die cudie_mem; + Dwarf_Die *cudie = dwarf_offdie (dbg, old_offset + hsize, &cudie_mem); + + /* If we cannot get the CU DIE there is no need to go on with + this CU. */ + if (cudie == NULL) + continue; + /* This better be a CU DIE. */ + if (dwarf_tag (cudie) != DW_TAG_compile_unit) + continue; + + /* Get the line information. */ + Dwarf_Files *files; + size_t nfiles; + if (dwarf_getsrcfiles (cudie, &files, &nfiles) != 0) + continue; + + Dwarf_Die die_mem; + Dwarf_Die *die = &die_mem; + if (dwarf_child (cudie, die) == 0) + /* Iterate over all immediate children of the CU DIE. */ + do + { + int tag = dwarf_tag (die); + if (tag != DW_TAG_subprogram && tag != DW_TAG_variable) + continue; + + /* We are interested in five attributes: name, decl_file, + decl_line, low_pc, and high_pc. */ + Dwarf_Attribute attr_mem; + Dwarf_Attribute *attr = dwarf_attr (die, DW_AT_name, &attr_mem); + const char *name = dwarf_formstring (attr); + if (name == NULL) + continue; + + Dwarf_Word fileidx; + attr = dwarf_attr (die, DW_AT_decl_file, &attr_mem); + if (dwarf_formudata (attr, &fileidx) != 0 || fileidx >= nfiles) + continue; + + Dwarf_Word lineno; + attr = dwarf_attr (die, DW_AT_decl_line, &attr_mem); + if (dwarf_formudata (attr, &lineno) != 0 || lineno == 0) + continue; + + Dwarf_Addr lowpc; + Dwarf_Addr highpc; + if (tag == DW_TAG_subprogram) + { + if (dwarf_lowpc (die, &lowpc) != 0 + || dwarf_highpc (die, &highpc) != 0) + continue; + } + else + { + if (get_var_range (die, &lowpc, &highpc) != 0) + continue; + } + + /* We have all the information. Create a record. */ + struct local_name *newp + = (struct local_name *) xmalloc (sizeof (*newp)); + newp->name = name; + newp->file = dwarf_filesrc (files, fileidx, NULL, NULL); + newp->lineno = lineno; + newp->lowpc = lowpc; + newp->highpc = highpc; + + /* Since we cannot deallocate individual memory we do not test + for duplicates in the tree. This should not happen anyway. */ + if (tsearch (newp, &local_root, local_compare) == NULL) + error (EXIT_FAILURE, errno, + gettext ("cannot create search tree")); + } + while (dwarf_siblingof (die, die) == 0); + } +} + + +/* Show symbols in SysV format. */ +static void +show_symbols_sysv (Ebl *ebl, GElf_Word strndx, + const char *prefix, const char *fname, const char *fullname, + GElf_SymX *syms, size_t nsyms, int longest_name, + int longest_where) +{ + size_t shnum; + if (elf_getshnum (ebl->elf, &shnum) < 0) + INTERNAL_ERROR (fullname); + + bool scnnames_malloced = shnum * sizeof (const char *) > 128 * 1024; + const char **scnnames; + if (scnnames_malloced) + scnnames = (const char **) xmalloc (sizeof (const char *) * shnum); + else + scnnames = (const char **) alloca (sizeof (const char *) * shnum); + /* Get the section header string table index. */ + size_t shstrndx; + if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + /* Cache the section names. */ + Elf_Scn *scn = NULL; + size_t cnt = 1; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + + assert (elf_ndxscn (scn) == cnt++); + + scnnames[elf_ndxscn (scn)] + = elf_strptr (ebl->elf, shstrndx, + gelf_getshdr (scn, &shdr_mem)->sh_name); + } + + int digits = length_map[gelf_getclass (ebl->elf) - 1][radix]; + + /* We always print this prolog. */ + if (prefix == NULL || 1) + printf (gettext ("\n\nSymbols from %s:\n\n"), fullname); + else + printf (gettext ("\n\nSymbols from %s[%s]:\n\n"), prefix, fname); + + /* The header line. */ + printf (gettext ("%*s%-*s %-*s Class Type %-*s %*s Section\n\n"), + print_file_name ? (int) strlen (fullname) + 1: 0, "", + longest_name, sgettext ("sysv|Name"), + /* TRANS: the "sysv|" parts makes the string unique. */ + digits, sgettext ("sysv|Value"), + /* TRANS: the "sysv|" parts makes the string unique. */ + digits, sgettext ("sysv|Size"), + /* TRANS: the "sysv|" parts makes the string unique. */ + longest_where, sgettext ("sysv|Line")); + + /* Which format string to use (different radix for numbers). */ + const char *fmtstr; + if (radix == radix_hex) + fmtstr = "%-*s|%0*" PRIx64 "|%-6s|%-8s|%*" PRIx64 "|%*s|%s\n"; + else if (radix == radix_decimal) + fmtstr = "%-*s|%*" PRId64 "|%-6s|%-8s|%*" PRId64 "|%*s|%s\n"; + else + fmtstr = "%-*s|%0*" PRIo64 "|%-6s|%-8s|%*" PRIo64 "|%*s|%s\n"; + + /* Iterate over all symbols. */ + for (cnt = 0; cnt < nsyms; ++cnt) + { + const char *symstr = elf_strptr (ebl->elf, strndx, + syms[cnt].sym.st_name); + char symbindbuf[50]; + char symtypebuf[50]; + char secnamebuf[1024]; + + /* If we have to precede the line with the file name. */ + if (print_file_name) + { + fputs_unlocked (fullname, stdout); + putchar_unlocked (':'); + } + + /* Print the actual string. */ + printf (fmtstr, + longest_name, symstr, + digits, syms[cnt].sym.st_value, + ebl_symbol_binding_name (ebl, + GELF_ST_BIND (syms[cnt].sym.st_info), + symbindbuf, sizeof (symbindbuf)), + ebl_symbol_type_name (ebl, GELF_ST_TYPE (syms[cnt].sym.st_info), + symtypebuf, sizeof (symtypebuf)), + digits, syms[cnt].sym.st_size, longest_where, syms[cnt].where, + ebl_section_name (ebl, syms[cnt].sym.st_shndx, syms[cnt].xndx, + secnamebuf, sizeof (secnamebuf), scnnames, + shnum)); + } + + if (scnnames_malloced) + free (scnnames); +} + + +static char +class_type_char (GElf_Sym *sym) +{ + int local_p = GELF_ST_BIND (sym->st_info) == STB_LOCAL; + + /* XXX Add support for architecture specific types and classes. */ + if (sym->st_shndx == SHN_ABS) + return local_p ? 'a' : 'A'; + + if (sym->st_shndx == SHN_UNDEF) + /* Undefined symbols must be global. */ + return 'U'; + + char result = "NDTSFB "[GELF_ST_TYPE (sym->st_info)]; + + return local_p ? tolower (result) : result; +} + + +static void +show_symbols_bsd (Elf *elf, GElf_Word strndx, + const char *prefix, const char *fname, const char *fullname, + GElf_SymX *syms, size_t nsyms) +{ + int digits = length_map[gelf_getclass (elf) - 1][radix]; + + if (prefix != NULL && ! print_file_name) + printf ("\n%s:\n", fname); + + static const char *const fmtstrs[] = + { + [radix_hex] = "%0*" PRIx64 " %c%s %s\n", + [radix_decimal] = "%*" PRId64 " %c%s %s\n", + [radix_octal] = "%0*" PRIo64 " %c%s %s\n" + }; + static const char *const sfmtstrs[] = + { + [radix_hex] = "%2$0*1$" PRIx64 " %7$0*6$" PRIx64 " %3$c%4$s %5$s\n", + [radix_decimal] = "%2$*1$" PRId64 " %7$*6$" PRId64 " %3$c%4$s %5$s\n", + [radix_octal] = "%2$0*1$" PRIo64 " %7$0*6$" PRIo64 " %3$c%4$s %5$s\n" + }; + + /* Iterate over all symbols. */ + for (size_t cnt = 0; cnt < nsyms; ++cnt) + { + const char *symstr = elf_strptr (elf, strndx, syms[cnt].sym.st_name); + + /* Printing entries with a zero-length name makes the output + not very well parseable. Since these entries don't carry + much information we leave them out. */ + if (symstr[0] == '\0') + continue; + + /* If we have to precede the line with the file name. */ + if (print_file_name) + { + fputs_unlocked (fullname, stdout); + putchar_unlocked (':'); + } + + if (syms[cnt].sym.st_shndx == SHN_UNDEF) + printf ("%*s U%s %s\n", + digits, "", + mark_weak + ? (GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK + ? "*" : " ") + : "", + elf_strptr (elf, strndx, syms[cnt].sym.st_name)); + else + printf (print_size ? sfmtstrs[radix] : fmtstrs[radix], + digits, syms[cnt].sym.st_value, + class_type_char (&syms[cnt].sym), + mark_weak + ? (GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK + ? "*" : " ") + : "", + elf_strptr (elf, strndx, syms[cnt].sym.st_name), + digits, (uint64_t) syms[cnt].sym.st_size); + } +} + + +static void +show_symbols_posix (Elf *elf, GElf_Word strndx, const char *prefix, + const char *fullname, GElf_SymX *syms, size_t nsyms) +{ + if (prefix != NULL && ! print_file_name) + printf ("%s:\n", fullname); + + const char *fmtstr; + if (radix == radix_hex) + fmtstr = "%s %c%s %0*" PRIx64 " %0*" PRIx64 "\n"; + else if (radix == radix_decimal) + fmtstr = "%s %c%s %*" PRId64 " %*" PRId64 "\n"; + else + fmtstr = "%s %c%s %0*" PRIo64 " %0*" PRIo64 "\n"; + + int digits = length_map[gelf_getclass (elf) - 1][radix]; + + /* Iterate over all symbols. */ + for (size_t cnt = 0; cnt < nsyms; ++cnt) + { + const char *symstr = elf_strptr (elf, strndx, syms[cnt].sym.st_name); + + /* Printing entries with a zero-length name makes the output + not very well parseable. Since these entries don't carry + much information we leave them out. */ + if (symstr[0] == '\0') + continue; + + /* If we have to precede the line with the file name. */ + if (print_file_name) + { + fputs_unlocked (fullname, stdout); + putchar_unlocked (':'); + putchar_unlocked (' '); + } + + printf (fmtstr, + symstr, + class_type_char (&syms[cnt].sym), + mark_weak + ? (GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK ? "*" : " ") + : "", + digits, syms[cnt].sym.st_value, + digits, syms[cnt].sym.st_size); + } +} + + +/* Maximum size of memory we allocate on the stack. */ +#define MAX_STACK_ALLOC 65536 + +static void +show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn, + GElf_Shdr *shdr, const char *prefix, const char *fname, + const char *fullname) +{ + int sort_by_name (const void *p1, const void *p2) + { + GElf_SymX *s1 = (GElf_SymX *) p1; + GElf_SymX *s2 = (GElf_SymX *) p2; + int result; + + result = strcmp (elf_strptr (ebl->elf, shdr->sh_link, s1->sym.st_name), + elf_strptr (ebl->elf, shdr->sh_link, s2->sym.st_name)); + + return reverse_sort ? -result : result; + } + + int sort_by_address (const void *p1, const void *p2) + { + GElf_SymX *s1 = (GElf_SymX *) p1; + GElf_SymX *s2 = (GElf_SymX *) p2; + + int result = (s1->sym.st_value < s2->sym.st_value + ? -1 : (s1->sym.st_value == s2->sym.st_value ? 0 : 1)); + + return reverse_sort ? -result : result; + } + + /* Get the section header string table index. */ + size_t shstrndx; + if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + /* The section is that large. */ + size_t size = shdr->sh_size; + /* One entry is this large. */ + size_t entsize = shdr->sh_entsize; + + /* Consistency checks. */ + if (entsize != gelf_fsize (ebl->elf, ELF_T_SYM, 1, ehdr->e_version)) + error (0, 0, + gettext ("%s: entry size in section `%s' is not what we expect"), + fullname, elf_strptr (ebl->elf, shstrndx, shdr->sh_name)); + else if (size % entsize != 0) + error (0, 0, + gettext ("%s: size of section `%s' is not multiple of entry size"), + fullname, elf_strptr (ebl->elf, shstrndx, shdr->sh_name)); + + /* Compute number of entries. Handle buggy entsize values. */ + size_t nentries = size / (entsize ?: 1); + + +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free + struct obstack whereob; + obstack_init (&whereob); + + /* Get a DWARF debugging descriptor. It's no problem if this isn't + possible. We just won't print any line number information. */ + Dwarf *dbg = NULL; + if (format == format_sysv) + { + dbg = dwarf_begin_elf (ebl->elf, DWARF_C_READ, NULL); + if (dbg != NULL) + { + (void) dwarf_getpubnames (dbg, get_global, NULL, 0); + + get_local_names (dbg); + } + } + + /* Allocate the memory. + + XXX We can use a dirty trick here. Since GElf_Sym == Elf64_Sym we + can use the data memory instead of copying again if what we read + is a 64 bit file. */ + GElf_SymX *sym_mem; + if (nentries * sizeof (GElf_SymX) < MAX_STACK_ALLOC) + sym_mem = (GElf_SymX *) alloca (nentries * sizeof (GElf_SymX)); + else + sym_mem = (GElf_SymX *) xmalloc (nentries * sizeof (GElf_SymX)); + + /* Get the data of the section. */ + Elf_Data *data = elf_getdata (scn, NULL); + Elf_Data *xndxdata = elf_getdata (xndxscn, NULL); + if (data == NULL || (xndxscn != NULL && xndxdata == NULL)) + INTERNAL_ERROR (fullname); + + /* Iterate over all symbols. */ + int longest_name = 4; + int longest_where = 4; + size_t nentries_used = 0; + for (size_t cnt = 0; cnt < nentries; ++cnt) + { + GElf_Sym *sym = gelf_getsymshndx (data, xndxdata, cnt, + &sym_mem[nentries_used].sym, + &sym_mem[nentries_used].xndx); + if (sym == NULL) + INTERNAL_ERROR (fullname); + + /* Filter out administrative symbols without a name and those + deselected by ther user with command line options. */ + if ((hide_undefined && sym->st_shndx == SHN_UNDEF) + || (hide_defined && sym->st_shndx != SHN_UNDEF) + || (hide_local && GELF_ST_BIND (sym->st_info) == STB_LOCAL)) + continue; + + sym_mem[nentries_used].where = ""; + if (format == format_sysv) + { + const char *symstr = elf_strptr (ebl->elf, shdr->sh_link, + sym->st_name); + + longest_name = MAX ((size_t) longest_name, strlen (symstr)); + + if (sym->st_shndx != SHN_UNDEF + && GELF_ST_BIND (sym->st_info) != STB_LOCAL + && global_root != NULL) + { + Dwarf_Global fake = { .name = symstr }; + Dwarf_Global **found = tfind (&fake, &global_root, + global_compare); + if (found != NULL) + { + Dwarf_Die die_mem; + Dwarf_Die *die = dwarf_offdie (dbg, (*found)->die_offset, + &die_mem); + + Dwarf_Die cudie_mem; + Dwarf_Die *cudie = NULL; + + Dwarf_Addr lowpc; + Dwarf_Addr highpc; + if (die != NULL + && dwarf_lowpc (die, &lowpc) == 0 + && lowpc <= sym->st_value + && dwarf_highpc (die, &highpc) == 0 + && highpc > sym->st_value) + cudie = dwarf_offdie (dbg, (*found)->cu_offset, + &cudie_mem); + if (cudie != NULL) + { + Dwarf_Line *line = dwarf_getsrc_die (cudie, + sym->st_value); + if (line != NULL) + { + /* We found the line. */ + int lineno; + (void) dwarf_lineno (line, &lineno); + int n; + n = obstack_printf (&whereob, "%s:%d%c", + basename (dwarf_linesrc (line, + NULL, + NULL)), + lineno, '\0'); + sym_mem[nentries_used].where + = obstack_finish (&whereob); + + /* The return value of obstack_print included the + NUL byte, so subtract one. */ + if (--n > (int) longest_where) + longest_where = (size_t) n; + } + } + } + } + + /* Try to find the symol among the local symbols. */ + if (sym_mem[nentries_used].where[0] == '\0') + { + struct local_name fake = + { + .name = symstr, + .lowpc = sym->st_value, + .highpc = sym->st_value, + }; + struct local_name **found = tfind (&fake, &local_root, + local_compare); + if (found != NULL) + { + /* We found the line. */ + int n = obstack_printf (&whereob, "%s:%" PRIu64 "%c", + basename ((*found)->file), + (*found)->lineno, + '\0'); + sym_mem[nentries_used].where = obstack_finish (&whereob); + + /* The return value of obstack_print included the + NUL byte, so subtract one. */ + if (--n > (int) longest_where) + longest_where = (size_t) n; + } + } + } + + /* We use this entry. */ + ++nentries_used; + } + /* Now we know the exact number. */ + nentries = nentries_used; + + /* Sort the entries according to the users wishes. */ + if (sort == sort_name) + qsort (sym_mem, nentries, sizeof (GElf_SymX), sort_by_name); + else if (sort == sort_numeric) + qsort (sym_mem, nentries, sizeof (GElf_SymX), sort_by_address); + + /* Finally print according to the users selection. */ + switch (format) + { + case format_sysv: + show_symbols_sysv (ebl, shdr->sh_link, prefix, fname, + fullname, sym_mem, nentries, longest_name, + longest_where); + break; + + case format_bsd: + show_symbols_bsd (ebl->elf, shdr->sh_link, prefix, fname, fullname, + sym_mem, nentries); + break; + + case format_posix: + default: + assert (format == format_posix); + show_symbols_posix (ebl->elf, shdr->sh_link, prefix, fullname, sym_mem, + nentries); + break; + } + + /* Free all memory. */ + if (nentries * sizeof (GElf_Sym) >= MAX_STACK_ALLOC) + free (sym_mem); + + obstack_free (&whereob, NULL); + + if (dbg != NULL) + { + tdestroy (global_root, free); + global_root = NULL; + + tdestroy (local_root, free); + local_root = NULL; + + (void) dwarf_end (dbg); + } +} + + +static int +handle_elf (Elf *elf, const char *prefix, const char *fname, + const char *suffix) +{ + size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); + size_t suffix_len = suffix == NULL ? 0 : strlen (suffix); + size_t fname_len = strlen (fname) + 1; + char fullname[prefix_len + 1 + fname_len + suffix_len]; + char *cp = fullname; + Elf_Scn *scn = NULL; + int any = 0; + int result = 0; + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr; + Ebl *ebl; + + /* Get the backend for this object file type. */ + ebl = ebl_openbackend (elf); + + /* We need the ELF header in a few places. */ + ehdr = gelf_getehdr (elf, &ehdr_mem); + if (ehdr == NULL) + INTERNAL_ERROR (fullname); + + /* If we are asked to print the dynamic symbol table and this is + executable or dynamic executable, fail. */ + if (symsec_type == SHT_DYNSYM + && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) + { + /* XXX Add machine specific object file types. */ + error (0, 0, gettext ("%s%s%s%s: Invalid operation"), + prefix ?: "", prefix ? "(" : "", fname, prefix ? ")" : ""); + result = 1; + goto out; + } + + /* Create the full name of the file. */ + if (prefix != NULL) + cp = mempcpy (cp, prefix, prefix_len); + cp = mempcpy (cp, fname, fname_len); + if (suffix != NULL) + memcpy (cp - 1, suffix, suffix_len + 1); + + /* Find the symbol table. + + XXX Can there be more than one? Do we print all? Currently we do. */ + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr == NULL) + INTERNAL_ERROR (fullname); + + if (shdr->sh_type == symsec_type) + { + Elf_Scn *xndxscn = NULL; + + /* We have a symbol table. First make sure we remember this. */ + any = 1; + + /* Look for an extended section index table for this section. */ + if (symsec_type == SHT_SYMTAB) + { + size_t scnndx = elf_ndxscn (scn); + + while ((xndxscn = elf_nextscn (elf, xndxscn)) != NULL) + { + GElf_Shdr xndxshdr_mem; + GElf_Shdr *xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem); + + if (xndxshdr == NULL) + INTERNAL_ERROR (fullname); + + if (xndxshdr->sh_type == SHT_SYMTAB_SHNDX + && xndxshdr->sh_link == scnndx) + break; + } + } + + show_symbols (ebl, ehdr, scn, xndxscn, shdr, prefix, fname, + fullname); + } + } + + if (! any) + { + error (0, 0, gettext ("%s%s%s: no symbols"), + prefix ?: "", prefix ? ":" : "", fname); + result = 1; + } + + out: + /* Close the ELF backend library descriptor. */ + ebl_closebackend (ebl); + + return result; +} diff --git a/src/none_ld.c b/src/none_ld.c new file mode 100644 index 00000000..fb0f0fb2 --- /dev/null +++ b/src/none_ld.c @@ -0,0 +1 @@ +/* Nothing here. This is just a testimony of automake inflexibility. */ diff --git a/src/readelf.c b/src/readelf.c new file mode 100644 index 00000000..95532fa3 --- /dev/null +++ b/src/readelf.c @@ -0,0 +1,4997 @@ +/* Print information from ELF file in human-readable form. + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 1999. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <argp.h> +#include <assert.h> +#include <dwarf.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <inttypes.h> +#include <langinfo.h> +#include <libdw.h> +#include <libintl.h> +#include <locale.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/param.h> + +#include <system.h> +#include "../libebl/libeblP.h" +#include "../libdw/libdwP.h" +#include "../libdw/memory-access.h" + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Output selection:"), 0 }, + { "all", 'a', NULL, 0, N_("Equivalent to: -h -l"), 0 }, + { "dynamic", 'd', NULL, 0, N_("Display the dynamic segment"), 0 }, + { "file-header", 'h', NULL, 0, N_("Display the ELF file header"), 0 }, + { "histogram", 'I', NULL, 0, + N_("Display histogram of bucket list lengths"), 0 }, + { "program-headers", 'l', NULL, 0, N_("Display the program headers"), 0 }, + { "relocs", 'r', NULL, 0, N_("Display relocations"), 0 }, + { "section-headers", 'S', NULL, 0, N_("Display the sections' header"), 0 }, + { "symbols", 's', NULL, 0, N_("Display the symbol table"), 0 }, + { "version-info", 'V', NULL, 0, N_("Display versioning information"), 0 }, + { "debug-dump", 'w', "SECTION", OPTION_ARG_OPTIONAL, + N_("Display DWARF section content. SECTION can be one of abbrev, " + "aranges, frame, info, loc, line, ranges, pubnames, str, or macinfo."), + 0 }, + { "notes", 'n', NULL, 0, N_("Display the core notes"), 0 }, + { "arch-specific", 'A', NULL, 0, + N_("Display architecture specific information (if any)"), 0 }, + + { NULL, 0, NULL, 0, N_("Output control:"), 0 }, + + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("\ +Print information from ELF file in human-readable form."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("FILE..."); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, NULL, NULL, NULL +}; + + +/* Flags set by the option controlling the output. */ + +/* True if dynamic segment should be printed. */ +static bool print_dynamic_table; + +/* True if the file header should be printed. */ +static bool print_file_header; + +/* True if the program headers should be printed. */ +static bool print_program_header; + +/* True if relocations should be printed. */ +static bool print_relocations; + +/* True if the section headers should be printed. */ +static bool print_section_header; + +/* True if the symbol table should be printed. */ +static bool print_symbol_table; + +/* True if the version information should be printed. */ +static bool print_version_info; + +/* True if section groups should be printed. */ +static bool print_section_groups; + +/* True if bucket list length histogram should be printed. */ +static bool print_histogram; + +/* True if the architecture specific data should be printed. */ +static bool print_arch; + +/* True if note section content should be printed. */ +static bool print_notes; + +/* Select printing of debugging sections. */ +static enum section_e +{ + section_abbrev = 1, /* .debug_abbrev */ + section_aranges = 2, /* .debug_aranges */ + section_frame = 4, /* .debug_frame or .eh_frame */ + section_info = 8, /* .debug_info */ + section_line = 16, /* .debug_line */ + section_loc = 32, /* .debug_loc */ + section_pubnames = 64,/* .debug_pubnames */ + section_str = 128, /* .debug_str */ + section_macinfo = 256,/* .debug_macinfo */ + section_ranges = 512, /* .debug_ranges */ + section_all = (section_abbrev | section_aranges | section_frame + | section_info | section_line | section_loc + | section_pubnames | section_str | section_macinfo + | section_ranges) +} print_debug_sections; + +/* Number of sections in the file. */ +static size_t shnum; + + +/* Declarations of local functions. */ +static void process_file (int fd, Elf *elf, const char *prefix, + const char *fname, bool only_one); +static void process_elf_file (Elf *elf, const char *prefix, const char *fname, + bool only_one); +static void print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr); +static void print_shdr (Ebl *ebl, GElf_Ehdr *ehdr); +static void print_phdr (Ebl *ebl, GElf_Ehdr *ehdr); +static void print_scngrp (Ebl *ebl); +static void print_dynamic (Ebl *ebl); +static void print_relocs (Ebl *ebl); +static void handle_relocs_rel (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); +static void handle_relocs_rela (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); +static void print_symtab (Ebl *ebl, int type); +static void handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); +static void print_verinfo (Ebl *ebl); +static void handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); +static void handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); +static void handle_versym (Ebl *ebl, Elf_Scn *scn, + GElf_Shdr *shdr); +static void print_debug (Ebl *ebl, GElf_Ehdr *ehdr); +static void handle_hash (Ebl *ebl); +static void handle_notes (Ebl *ebl, GElf_Ehdr *ehdr); +static void print_liblist (Ebl *ebl); + + +int +main (int argc, char *argv[]) +{ + /* Set locale. */ + setlocale (LC_ALL, ""); + + /* Initialize the message catalog. */ + textdomain (PACKAGE); + + /* Parse and process arguments. */ + int remaining; + argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + /* Before we start tell the ELF library which version we are using. */ + elf_version (EV_CURRENT); + + /* Now process all the files given at the command line. */ + bool only_one = remaining + 1 == argc; + do + { + /* Open the file. */ + int fd = open (argv[remaining], O_RDONLY); + if (fd == -1) + { + error (0, errno, gettext ("cannot open input file")); + continue; + } + + /* Create an `Elf' descriptor. */ + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (elf == NULL) + error (0, 0, gettext ("cannot generate Elf descriptor: %s\n"), + elf_errmsg (-1)); + else + { + process_file (fd, elf, NULL, argv[remaining], only_one); + + /* Now we can close the descriptor. */ + if (elf_end (elf) != 0) + error (0, 0, gettext ("error while closing Elf descriptor: %s"), + elf_errmsg (-1)); + } + + close (fd); + } + while (++remaining < argc); + + return error_message_count != 0; +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + /* True if any of the control options is set. */ + static bool any_control_option; + + switch (key) + { + case 'a': + print_file_header = true; + print_program_header = true; + print_relocations = true; + print_section_header = true; + print_symbol_table = true; + print_version_info = true; + print_dynamic_table = true; + print_section_groups = true; + print_histogram = true; + print_arch = true; + print_notes = true; + any_control_option = true; + break; + case 'A': + print_arch = true; + any_control_option = true; + break; + case 'd': + print_dynamic_table = true; + any_control_option = true; + break; + case 'g': + print_section_groups = true; + any_control_option = true; + break; + case 'h': + print_file_header = true; + any_control_option = true; + break; + case 'I': + print_histogram = true; + any_control_option = true; + break; + case 'l': + print_program_header = true; + any_control_option = true; + break; + case 'n': + print_notes = true; + any_control_option = true; + break; + case 'r': + print_relocations = true; + any_control_option = true; + break; + case 'S': + print_section_header = true; + any_control_option = true; + break; + case 's': + print_symbol_table = true; + any_control_option = true; + break; + case 'V': + print_version_info = true; + any_control_option = true; + break; + case 'w': + if (arg == NULL) + print_debug_sections = section_all; + else if (strcmp (arg, "abbrev") == 0) + print_debug_sections |= section_abbrev; + else if (strcmp (arg, "aranges") == 0) + print_debug_sections |= section_aranges; + else if (strcmp (arg, "ranges") == 0) + print_debug_sections |= section_ranges; + else if (strcmp (arg, "frame") == 0) + print_debug_sections |= section_frame; + else if (strcmp (arg, "info") == 0) + print_debug_sections |= section_info; + else if (strcmp (arg, "loc") == 0) + print_debug_sections |= section_loc; + else if (strcmp (arg, "line") == 0) + print_debug_sections |= section_line; + else if (strcmp (arg, "pubnames") == 0) + print_debug_sections |= section_pubnames; + else if (strcmp (arg, "str") == 0) + print_debug_sections |= section_str; + else if (strcmp (arg, "macinfo") == 0) + print_debug_sections |= section_macinfo; + else + { + fprintf (stderr, gettext ("Unknown DWARF debug section `%s'.\n"), + arg); + argp_help (&argp, stderr, ARGP_HELP_SEE, + program_invocation_short_name); + exit (1); + } + any_control_option = true; + break; + case ARGP_KEY_NO_ARGS: + fputs (gettext ("Missing file name.\n"), stderr); + goto do_argp_help; + case ARGP_KEY_FINI: + if (! any_control_option) + { + fputs (gettext ("No operation specified.\n"), stderr); + do_argp_help: + argp_help (&argp, stderr, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR, + program_invocation_short_name); + exit (1); + } + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "readelf (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2005"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Process one file. */ +static void +process_file (int fd, Elf *elf, const char *prefix, const char *fname, + bool only_one) +{ + /* We can handle two types of files: ELF files and archives. */ + Elf_Kind kind = elf_kind (elf); + struct stat64 st; + + switch (kind) + { + case ELF_K_ELF: + /* Yes! It's an ELF file. */ + process_elf_file (elf, prefix, fname, only_one); + break; + + case ELF_K_AR: + { + Elf *subelf; + Elf_Cmd cmd = ELF_C_READ_MMAP; + size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); + size_t fname_len = strlen (fname) + 1; + char new_prefix[prefix_len + 1 + fname_len]; + char *cp = new_prefix; + + /* Create the full name of the file. */ + if (prefix != NULL) + { + cp = mempcpy (cp, prefix, prefix_len); + *cp++ = ':'; + } + memcpy (cp, fname, fname_len); + + /* It's an archive. We process each file in it. */ + while ((subelf = elf_begin (fd, cmd, elf)) != NULL) + { + kind = elf_kind (subelf); + + /* Call this function recursively. */ + if (kind == ELF_K_ELF || kind == ELF_K_AR) + { + Elf_Arhdr *arhdr = elf_getarhdr (subelf); + assert (arhdr != NULL); + + process_file (fd, subelf, new_prefix, arhdr->ar_name, false); + } + + /* Get next archive element. */ + cmd = elf_next (subelf); + if (elf_end (subelf) != 0) + error (0, 0, + gettext (" error while freeing sub-ELF descriptor: %s\n"), + elf_errmsg (-1)); + } + } + break; + + default: + if (fstat64 (fd, &st) != 0) + error (0, errno, gettext ("cannot stat input file")); + else if (st.st_size == 0) + error (0, 0, gettext ("input file is empty")); + else + /* We cannot do anything. */ + error (0, 0, gettext ("\ +Not an ELF file - it has the wrong magic bytes at the start")); + break; + } +} + + +/* Process one file. */ +static void +process_elf_file (Elf *elf, const char *prefix, const char *fname, + bool only_one) +{ + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); + Ebl *ebl; + + /* Print the file name. */ + if (!only_one) + { + if (prefix != NULL) + printf ("\n%s(%s):\n\n", prefix, fname); + else + printf ("\n%s:\n\n", fname); + } + + if (ehdr == NULL) + { + error (0, 0, gettext ("cannot read ELF header: %s"), elf_errmsg (-1)); + return; + } + + ebl = ebl_openbackend (elf); + if (ebl == NULL) + { + error (0, errno, gettext ("cannot create EBL handle")); + return; + } + + /* Determine the number of sections. */ + if (elf_getshnum (ebl->elf, &shnum) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot determine number of sections: %s"), + elf_errmsg (-1)); + + if (print_file_header) + print_ehdr (ebl, ehdr); + if (print_section_header) + print_shdr (ebl, ehdr); + if (print_program_header) + print_phdr (ebl, ehdr); + if (print_section_groups) + print_scngrp (ebl); + if (print_dynamic_table) + print_dynamic (ebl); + if (print_relocations) + print_relocs (ebl); + if (print_histogram) + handle_hash (ebl); + if (print_symbol_table) + print_symtab (ebl, SHT_DYNSYM); + if (print_version_info) + print_verinfo (ebl); + if (print_symbol_table) + print_symtab (ebl, SHT_SYMTAB); + if (print_arch) + print_liblist (ebl); + if (print_debug_sections != 0) + print_debug (ebl, ehdr); + if (print_notes) + handle_notes (ebl, ehdr); + + ebl_closebackend (ebl); +} + + +/* Print file type. */ +static void +print_file_type (unsigned short int e_type) +{ + if (e_type <= ET_CORE) + { + static const char *knowntypes[] = + { + N_("NONE (None)"), + N_("REL (Relocatable file)"), + N_("EXEC (Executable file)"), + N_("DYN (Shared object file)"), + N_("CORE (Core file)") + }; + puts (gettext (knowntypes[e_type])); + } + else if (e_type >= ET_LOOS && e_type <= ET_HIOS) + printf (gettext ("OS Specific: (%x)\n"), e_type); + else if (e_type >= ET_LOPROC /* && e_type <= ET_HIPROC always true */) + printf (gettext ("Processor Specific: (%x)\n"), e_type); + else + puts ("???"); +} + + +/* Print ELF header. */ +static void +print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr) +{ + char buf[512]; + size_t cnt; + + fputs_unlocked (gettext ("ELF Header:\n Magic: "), stdout); + for (cnt = 0; cnt < EI_NIDENT; ++cnt) + printf (" %02hhx", ehdr->e_ident[cnt]); + + printf (gettext ("\n Class: %s\n"), + ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? "ELF32" + : ehdr->e_ident[EI_CLASS] == ELFCLASS64 ? "ELF64" + : "\?\?\?"); + + printf (gettext (" Data: %s\n"), + ehdr->e_ident[EI_DATA] == ELFDATA2LSB + ? "2's complement, little endian" + : ehdr->e_ident[EI_DATA] == ELFDATA2MSB + ? "2's complement, big endian" : "\?\?\?"); + + printf (gettext (" Version: %hhd %s\n"), + ehdr->e_ident[EI_VERSION], + ehdr->e_ident[EI_VERSION] == EV_CURRENT ? gettext ("(current)") + : "(\?\?\?)"); + + printf (gettext (" OS/ABI: %s\n"), + ebl_osabi_name (ebl, ehdr->e_ident[EI_OSABI], buf, sizeof (buf))); + + printf (gettext (" ABI Version: %hhd\n"), + ehdr->e_ident[EI_ABIVERSION]); + + fputs_unlocked (gettext (" Type: "), stdout); + print_file_type (ehdr->e_type); + + printf (gettext (" Machine: %s\n"), ebl->name); + + printf (gettext (" Version: %d %s\n"), + ehdr->e_version, + ehdr->e_version == EV_CURRENT ? gettext ("(current)") : "(\?\?\?)"); + + printf (gettext (" Entry point address: %#" PRIx64 "\n"), + ehdr->e_entry); + + printf (gettext (" Start of program headers: %" PRId64 " %s\n"), + ehdr->e_phoff, gettext ("(bytes into file)")); + + printf (gettext (" Start of section headers: %" PRId64 " %s\n"), + ehdr->e_shoff, gettext ("(bytes into file)")); + + printf (gettext (" Flags: %s\n"), + ebl_machine_flag_name (ebl, ehdr->e_flags, buf, sizeof (buf))); + + printf (gettext (" Size of this header: %" PRId16 " %s\n"), + ehdr->e_ehsize, gettext ("(bytes)")); + + printf (gettext (" Size of program header entries: %" PRId16 " %s\n"), + ehdr->e_phentsize, gettext ("(bytes)")); + + printf (gettext (" Number of program headers entries: %" PRId16 "\n"), + ehdr->e_phnum); + + printf (gettext (" Size of section header entries: %" PRId16 " %s\n"), + ehdr->e_shentsize, gettext ("(bytes)")); + + printf (gettext (" Number of section headers entries: %" PRId16), + ehdr->e_shnum); + if (ehdr->e_shnum == 0) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr; + + shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); + if (shdr != NULL) + printf (gettext (" (%" PRIu32 " in [0].sh_size)"), + (uint32_t) shdr->sh_size); + else + fputs_unlocked (gettext (" ([0] not available)"), stdout); + } + fputc_unlocked ('\n', stdout); + + if (ehdr->e_shstrndx == SHN_XINDEX) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr; + + shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); + if (shdr != NULL) + /* We managed to get the zeroth section. */ + snprintf (buf, sizeof (buf), gettext (" (%" PRIu32 " in [0].sh_link)"), + (uint32_t) shdr->sh_link); + else + { + strncpy (buf, gettext (" ([0] not available)"), sizeof (buf)); + buf[sizeof (buf) - 1] = '\0'; + } + + printf (gettext (" Section header string table index: XINDEX%s\n\n"), + buf); + } + else + printf (gettext (" Section header string table index: %" PRId16 "\n\n"), + ehdr->e_shstrndx); +} + + +static const char * +get_visibility_type (int value) +{ + switch (value) + { + case STV_DEFAULT: + return "DEFAULT"; + case STV_INTERNAL: + return "INTERNAL"; + case STV_HIDDEN: + return "HIDDEN"; + case STV_PROTECTED: + return "PROTECTED"; + default: + return "???"; + } +} + + +/* Print the section headers. */ +static void +print_shdr (Ebl *ebl, GElf_Ehdr *ehdr) +{ + size_t cnt; + size_t shstrndx; + + if (! print_file_header) + printf (gettext ("\ +There are %d section headers, starting at offset %#" PRIx64 ":\n\ +\n"), + ehdr->e_shnum, ehdr->e_shoff); + + /* Get the section header string table index. */ + if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + puts (gettext ("Section Headers:")); + + if (ehdr->e_ident[EI_CLASS] == ELFCLASS32) + puts (gettext ("[Nr] Name Type Addr Off Size ES Flags Lk Inf Al")); + else + puts (gettext ("[Nr] Name Type Addr Off Size ES Flags Lk Inf Al")); + + for (cnt = 0; cnt < shnum; ++cnt) + { + char buf[128]; + char flagbuf[20]; + char *cp; + Elf_Scn *scn = elf_getscn (ebl->elf, cnt); + GElf_Shdr shdr_mem; + GElf_Shdr *shdr; + + if (scn == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot get section: %s"), + elf_errmsg (-1)); + + /* Get the section header. */ + shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot get section header: %s"), + elf_errmsg (-1)); + + cp = flagbuf; + if (shdr->sh_flags & SHF_WRITE) + *cp++ = 'W'; + if (shdr->sh_flags & SHF_ALLOC) + *cp++ = 'A'; + if (shdr->sh_flags & SHF_EXECINSTR) + *cp++ = 'X'; + if (shdr->sh_flags & SHF_MERGE) + *cp++ = 'M'; + if (shdr->sh_flags & SHF_STRINGS) + *cp++ = 'S'; + if (shdr->sh_flags & SHF_INFO_LINK) + *cp++ = 'I'; + if (shdr->sh_flags & SHF_LINK_ORDER) + *cp++ = 'L'; + if (shdr->sh_flags & SHF_OS_NONCONFORMING) + *cp++ = 'N'; + if (shdr->sh_flags & SHF_GROUP) + *cp++ = 'G'; + if (shdr->sh_flags & SHF_TLS) + *cp++ = 'T'; + if (shdr->sh_flags & SHF_ORDERED) + *cp++ = 'O'; + if (shdr->sh_flags & SHF_EXCLUDE) + *cp++ = 'E'; + *cp = '\0'; + + printf ("[%2zu] %-20s %-12s %0*" PRIx64 " %0*" PRIx64 " %0*" PRIx64 + " %2" PRId64 " %-5s %2" PRId32 " %3" PRId32 + " %2" PRId64 "\n", + cnt, + elf_strptr (ebl->elf, shstrndx, shdr->sh_name) + ?: "<corrupt>", + ebl_section_type_name (ebl, shdr->sh_type, buf, sizeof (buf)), + ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16, shdr->sh_addr, + ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, shdr->sh_offset, + ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, shdr->sh_size, + shdr->sh_entsize, flagbuf, shdr->sh_link, shdr->sh_info, + shdr->sh_addralign); + } + + fputc_unlocked ('\n', stdout); +} + + +/* Print the program header. */ +static void +print_phdr (Ebl *ebl, GElf_Ehdr *ehdr) +{ + size_t cnt; + size_t shstrndx; + + if (ehdr->e_phnum == 0) + /* No program header, this is OK in relocatable objects. */ + return; + + puts (gettext ("Program Headers:")); + if (ehdr->e_ident[EI_CLASS] == ELFCLASS32) + puts (gettext ("\ + Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align")); + else + puts (gettext ("\ + Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align")); + + /* Process all program headers. */ + bool has_relro = false; + GElf_Addr relro_from = 0; + GElf_Addr relro_to = 0; + for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) + { + char buf[128]; + GElf_Phdr mem; + GElf_Phdr *phdr = gelf_getphdr (ebl->elf, cnt, &mem); + + /* If for some reason the header cannot be returned show this. */ + if (phdr == NULL) + { + puts (" ???"); + continue; + } + + printf (" %-14s 0x%06" PRIx64 " 0x%0*" PRIx64 " 0x%0*" PRIx64 + " 0x%06" PRIx64 " 0x%06" PRIx64 " %c%c%c 0x%" PRIx64 "\n", + ebl_segment_type_name (ebl, phdr->p_type, buf, sizeof (buf)), + phdr->p_offset, + ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16, phdr->p_vaddr, + ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16, phdr->p_paddr, + phdr->p_filesz, + phdr->p_memsz, + phdr->p_flags & PF_R ? 'R' : ' ', + phdr->p_flags & PF_W ? 'W' : ' ', + phdr->p_flags & PF_X ? 'E' : ' ', + phdr->p_align); + + if (phdr->p_type == PT_INTERP) + { + /* We can show the user the name of the interpreter. */ + size_t maxsize; + char *filedata = elf_rawfile (ebl->elf, &maxsize); + + if (filedata != NULL && phdr->p_offset < maxsize) + printf (gettext ("\t[Requesting program interpreter: %s]\n"), + filedata + phdr->p_offset); + } + else if (phdr->p_type == PT_GNU_RELRO) + { + has_relro = true; + relro_from = phdr->p_vaddr; + relro_to = relro_from + phdr->p_memsz; + } + } + + /* Get the section header string table index. */ + if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + puts (gettext ("\n Section to Segment mapping:\n Segment Sections...")); + + for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (ebl->elf, cnt, &phdr_mem); + size_t inner; + + /* Print the segment number. */ + printf (" %2.2zu ", cnt); + + /* This must not happen. */ + if (phdr == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot get program header: %s"), + elf_errmsg (-1)); + + /* Iterate over the sections. */ + bool in_relro = false; + bool in_ro = false; + for (inner = 1; inner < shnum; ++inner) + { + Elf_Scn *scn = elf_getscn (ebl->elf, inner); + GElf_Shdr shdr_mem; + GElf_Shdr *shdr; + + /* It should not happen. */ + if (scn == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot get section: %s"), + elf_errmsg (-1)); + + /* Get the section header. */ + shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header: %s"), + elf_errmsg (-1)); + + if (shdr->sh_size > 0 + /* Compare allocated sections by VMA, unallocated + sections by file offset. */ + && (shdr->sh_flags & SHF_ALLOC + ? (shdr->sh_addr >= phdr->p_vaddr + && (shdr->sh_addr + shdr->sh_size + <= phdr->p_vaddr + phdr->p_memsz)) + : (shdr->sh_offset >= phdr->p_offset + && (shdr->sh_offset + shdr->sh_size + <= phdr->p_offset + phdr->p_filesz)))) + { + if (has_relro && !in_relro + && shdr->sh_addr >= relro_from + && shdr->sh_addr + shdr->sh_size <= relro_to) + { + fputs_unlocked (" [RELRO:", stdout); + in_relro = true; + } + else if (has_relro && in_relro && shdr->sh_addr >= relro_to) + { + fputs_unlocked ("]", stdout); + in_relro = false; + } + else if (has_relro && in_relro + && shdr->sh_addr + shdr->sh_size > relro_to) + fputs_unlocked ("] <RELRO:", stdout); + else if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_W) == 0) + { + if (!in_ro) + { + fputs_unlocked (" [RO:", stdout); + in_ro = true; + } + } + else + { + /* Determine the segment this section is part of. */ + size_t cnt2; + GElf_Phdr *phdr2 = NULL; + for (cnt2 = 0; cnt2 < ehdr->e_phnum; ++cnt2) + { + GElf_Phdr phdr2_mem; + phdr2 = gelf_getphdr (ebl->elf, cnt2, &phdr2_mem); + + if (phdr2 != NULL && phdr2->p_type == PT_LOAD + && shdr->sh_addr >= phdr2->p_vaddr + && (shdr->sh_addr + shdr->sh_size + <= phdr2->p_vaddr + phdr2->p_memsz)) + break; + } + + if (cnt2 < ehdr->e_phnum) + { + if ((phdr2->p_flags & PF_W) == 0 && !in_ro) + { + fputs_unlocked (" [RO:", stdout); + in_ro = true; + } + else if ((phdr2->p_flags & PF_W) != 0 && in_ro) + { + fputs_unlocked ("]", stdout); + in_ro = false; + } + } + } + + printf (" %s", + elf_strptr (ebl->elf, shstrndx, shdr->sh_name)); + + /* Signal that this sectin is only partially covered. */ + if (has_relro && in_relro + && shdr->sh_addr + shdr->sh_size > relro_to) + { + fputs_unlocked (">", stdout); + in_relro = false; + } + } + } + if (in_relro || in_ro) + fputs_unlocked ("]", stdout); + + /* Finish the line. */ + fputc_unlocked ('\n', stdout); + } +} + + +static void +handle_scngrp (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) +{ + Elf_Data *data; + Elf32_Word *grpref; + Elf_Scn *symscn; + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr; + Elf_Data *symdata; + GElf_Sym sym_mem; + size_t cnt; + size_t shstrndx; + + /* Get the data of the section. */ + data = elf_getdata (scn, NULL); + + symscn = elf_getscn (ebl->elf, shdr->sh_link); + symshdr = gelf_getshdr (symscn, &symshdr_mem); + symdata = elf_getdata (symscn, NULL); + + if (data == NULL || data->d_size < sizeof (Elf32_Word) || symshdr == NULL + || symdata == NULL) + return; + + /* Get the section header string table index. */ + if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + grpref = (Elf32_Word *) data->d_buf; + + printf ((grpref[0] & GRP_COMDAT) + ? ngettext ("\ +\nCOMDAT section group [%2zu] '%s' with signature '%s' contains %zu entry:\n", + "\ +\nCOMDAT section group [%2zu] '%s' with signature '%s' contains %zu entries:\n", + data->d_size / sizeof (Elf32_Word) - 1) + : ngettext ("\ +\nSection group [%2zu] '%s' with signature '%s' contains %zu entry:\n", "\ +\nSection group [%2zu] '%s' with signature '%s' contains %zu entries:\n", + data->d_size / sizeof (Elf32_Word) - 1), + elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + elf_strptr (ebl->elf, symshdr->sh_link, + gelf_getsym (symdata, shdr->sh_info, &sym_mem)->st_name) + ?: gettext ("<INVALID SYMBOL>"), + data->d_size / sizeof (Elf32_Word) - 1); + + for (cnt = 1; cnt < data->d_size / sizeof (Elf32_Word); ++cnt) + { + GElf_Shdr grpshdr_mem; + GElf_Shdr *grpshdr; + + grpshdr = gelf_getshdr (elf_getscn (ebl->elf, grpref[cnt]), + &grpshdr_mem); + + if (grpshdr == NULL) + printf (gettext (" [%2u] <INVALID SECTION>\n"), grpref[cnt]); + else + printf (" [%2u] %s\n", + grpref[cnt], + elf_strptr (ebl->elf, shstrndx, grpshdr->sh_name) + ?: gettext ("<INVALID SECTION>")); + } +} + + +static void +print_scngrp (Ebl *ebl) +{ + /* Find all relocation sections and handle them. */ + Elf_Scn *scn = NULL; + + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + /* Handle the section if it is a symbol table. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr != NULL && shdr->sh_type == SHT_GROUP) + handle_scngrp (ebl, scn, shdr); + } +} + + +static const struct flags +{ + int mask; + const char *str; +} dt_flags[] = + { + { DF_ORIGIN, "ORIGIN" }, + { DF_SYMBOLIC, "SYMBOLIC" }, + { DF_TEXTREL, "TEXTREL" }, + { DF_BIND_NOW, "BIND_NOW" }, + { DF_STATIC_TLS, "STATIC_TLS" } + }; +static const int ndt_flags = sizeof (dt_flags) / sizeof (dt_flags[0]); + +static const struct flags dt_flags_1[] = + { + { DF_1_NOW, "NOW" }, + { DF_1_GLOBAL, "GLOBAL" }, + { DF_1_GROUP, "GROUP" }, + { DF_1_NODELETE, "NODELETE" }, + { DF_1_LOADFLTR, "LOADFLTR" }, + { DF_1_INITFIRST, "INITFIRST" }, + { DF_1_NOOPEN, "NOOPEN" }, + { DF_1_ORIGIN, "ORIGIN" }, + { DF_1_DIRECT, "DIRECT" }, + { DF_1_TRANS, "TRANS" }, + { DF_1_INTERPOSE, "INTERPOSE" }, + { DF_1_NODEFLIB, "NODEFLIB" }, + { DF_1_NODUMP, "NODUMP" }, + { DF_1_CONFALT, "CONFALT" }, + { DF_1_ENDFILTEE, "ENDFILTEE" }, + { DF_1_DISPRELDNE, "DISPRELDNE" }, + { DF_1_DISPRELPND, "DISPRELPND" }, + }; +static const int ndt_flags_1 = sizeof (dt_flags_1) / sizeof (dt_flags_1[0]); + +static const struct flags dt_feature_1[] = + { + { DTF_1_PARINIT, "PARINIT" }, + { DTF_1_CONFEXP, "CONFEXP" } + }; +static const int ndt_feature_1 = (sizeof (dt_feature_1) + / sizeof (dt_feature_1[0])); + +static const struct flags dt_posflag_1[] = + { + { DF_P1_LAZYLOAD, "LAZYLOAD" }, + { DF_P1_GROUPPERM, "GROUPPERM" } + }; +static const int ndt_posflag_1 = (sizeof (dt_posflag_1) + / sizeof (dt_posflag_1[0])); + + +static void +print_flags (int class, GElf_Xword d_val, const struct flags *flags, + int nflags) +{ + bool first = true; + int cnt; + + for (cnt = 0; cnt < nflags; ++cnt) + if (d_val & flags[cnt].mask) + { + if (!first) + putchar_unlocked (' '); + fputs_unlocked (flags[cnt].str, stdout); + d_val &= ~flags[cnt].mask; + first = false; + } + + if (d_val != 0) + { + if (!first) + putchar_unlocked (' '); + printf ("%#0*" PRIx64, class == ELFCLASS32 ? 10 : 18, d_val); + } + + putchar_unlocked ('\n'); +} + + +static void +print_dt_flags (int class, GElf_Xword d_val) +{ + print_flags (class, d_val, dt_flags, ndt_flags); +} + + +static void +print_dt_flags_1 (int class, GElf_Xword d_val) +{ + print_flags (class, d_val, dt_flags_1, ndt_flags_1); +} + + +static void +print_dt_feature_1 (int class, GElf_Xword d_val) +{ + print_flags (class, d_val, dt_feature_1, ndt_feature_1); +} + + +static void +print_dt_posflag_1 (int class, GElf_Xword d_val) +{ + print_flags (class, d_val, dt_posflag_1, ndt_posflag_1); +} + + +static void +handle_dynamic (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) +{ + int class = gelf_getclass (ebl->elf); + GElf_Shdr glink; + Elf_Data *data; + size_t cnt; + size_t shstrndx; + + /* Get the data of the section. */ + data = elf_getdata (scn, NULL); + if (data == NULL) + return; + + /* Get the section header string table index. */ + if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + printf (ngettext ("\ +\nDynamic segment contains %lu entry:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", + "\ +\nDynamic segment contains %lu entries:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", + shdr->sh_size / shdr->sh_entsize), + (unsigned long int) (shdr->sh_size / shdr->sh_entsize), + class == ELFCLASS32 ? 10 : 18, shdr->sh_addr, + shdr->sh_offset, + (int) shdr->sh_link, + elf_strptr (ebl->elf, shstrndx, + gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &glink)->sh_name)); + fputs_unlocked (gettext (" Type Value\n"), stdout); + + for (cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) + { + char buf[64]; + GElf_Dyn dynmem; + GElf_Dyn *dyn; + + dyn = gelf_getdyn (data, cnt, &dynmem); + if (dyn == NULL) + break; + + printf (" %-17s ", + ebl_dynamic_tag_name (ebl, dyn->d_tag, buf, sizeof (buf))); + + switch (dyn->d_tag) + { + case DT_NULL: + case DT_DEBUG: + case DT_BIND_NOW: + case DT_TEXTREL: + /* No further output. */ + fputc ('\n', stdout); + break; + + case DT_NEEDED: + printf (gettext ("Shared library: [%s]\n"), + elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val)); + break; + + case DT_SONAME: + printf (gettext ("Library soname: [%s]\n"), + elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val)); + break; + + case DT_RPATH: + printf (gettext ("Library rpath: [%s]\n"), + elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val)); + break; + + case DT_RUNPATH: + printf (gettext ("Library runpath: [%s]\n"), + elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val)); + break; + + case DT_PLTRELSZ: + case DT_RELASZ: + case DT_STRSZ: + case DT_RELSZ: + 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: + printf (gettext ("%" PRId64 " (bytes)\n"), dyn->d_un.d_val); + break; + + case DT_VERDEFNUM: + case DT_VERNEEDNUM: + case DT_RELACOUNT: + case DT_RELCOUNT: + printf ("%" PRId64 "\n", dyn->d_un.d_val); + break; + + case DT_PLTREL: + puts (ebl_dynamic_tag_name (ebl, dyn->d_un.d_val, NULL, 0)); + break; + + case DT_FLAGS: + print_dt_flags (class, dyn->d_un.d_val); + break; + + case DT_FLAGS_1: + print_dt_flags_1 (class, dyn->d_un.d_val); + break; + + case DT_FEATURE_1: + print_dt_feature_1 (class, dyn->d_un.d_val); + break; + + case DT_POSFLAG_1: + print_dt_posflag_1 (class, dyn->d_un.d_val); + break; + + default: + printf ("%#0*" PRIx64 "\n", + class == ELFCLASS32 ? 10 : 18, dyn->d_un.d_val); + break; + } + } +} + + +/* Print the dynamic segment. */ +static void +print_dynamic (Ebl *ebl) +{ + /* Find all relocation sections and handle them. */ + Elf_Scn *scn = NULL; + + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + /* Handle the section if it is a symbol table. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC) + { + handle_dynamic (ebl, scn, shdr); + break; + } + } +} + + +/* Print relocations. */ +static void +print_relocs (Ebl *ebl) +{ + /* Find all relocation sections and handle them. */ + Elf_Scn *scn = NULL; + + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + /* Handle the section if it is a symbol table. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr != NULL) + { + if (shdr->sh_type == SHT_REL) + handle_relocs_rel (ebl, scn, shdr); + else if (shdr->sh_type == SHT_RELA) + handle_relocs_rela (ebl, scn, shdr); + } + } +} + + +/* Handle a relocation section. */ +static void +handle_relocs_rel (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) +{ + int class = gelf_getclass (ebl->elf); + int nentries = shdr->sh_size / shdr->sh_entsize; + int cnt; + Elf_Data *data; + Elf_Scn *symscn; + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr; + Elf_Data *symdata; + GElf_Shdr destshdr_mem; + GElf_Shdr *destshdr; + Elf_Scn *xndxscn; + Elf_Data *xndxdata = NULL; + size_t shstrndx; + + /* Get the data of the section. */ + data = elf_getdata (scn, NULL); + if (data == NULL) + return; + + /* Get the symbol table information. */ + symscn = elf_getscn (ebl->elf, shdr->sh_link); + symshdr = gelf_getshdr (symscn, &symshdr_mem); + symdata = elf_getdata (symscn, NULL); + + /* Get the section header of the section the relocations are for. */ + destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info), + &destshdr_mem); + + if (symshdr == NULL || symdata == NULL || destshdr == NULL) + { + printf (gettext ("\nInvalid symbol table at offset %#0" PRIx64 "\n"), + shdr->sh_offset); + return; + } + + /* Search for the optional extended section index table. */ + xndxscn = NULL; + while ((xndxscn = elf_nextscn (ebl->elf, xndxscn)) != NULL) + { + GElf_Shdr xndxshdr_mem; + GElf_Shdr *xndxshdr; + + xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem); + if (xndxshdr != NULL && xndxshdr->sh_type == SHT_SYMTAB_SHNDX + && xndxshdr->sh_link == elf_ndxscn (symscn)) + { + /* Found it. */ + xndxdata = elf_getdata (xndxscn, NULL); + break; + } + } + + /* Get the section header string table index. */ + if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + if (shdr->sh_info != 0) + printf (ngettext ("\ +\nRelocation section [%2u] '%s' for section [%2u] '%s' at offset %#0" PRIx64 " contains %d entry:\n", + "\ +\nRelocation section [%2u] '%s' for section [%2u] '%s' at offset %#0" PRIx64 " contains %d entries:\n", + nentries), + (unsigned int) elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + (unsigned int) shdr->sh_info, + elf_strptr (ebl->elf, shstrndx, destshdr->sh_name), + shdr->sh_offset, + nentries); + else + /* The .rel.dyn section does not refer to a specific section but + instead of section index zero. Do not try to print a section + name. */ + printf (ngettext ("\ +\nRelocation section [%2u] '%s' at offset %#0" PRIx64 " contains %d entry:\n", + "\ +\nRelocation section [%2u] '%s' at offset %#0" PRIx64 " contains %d entries:\n", + nentries), + (unsigned int) elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + shdr->sh_offset, + nentries); + fputs_unlocked (class == ELFCLASS32 + ? gettext ("\ + Offset Type Value Name\n") + : gettext ("\ + Offset Type Value Name\n"), + stdout); + + for (cnt = 0; cnt < nentries; ++cnt) + { + GElf_Rel relmem; + GElf_Rel *rel; + + rel = gelf_getrel (data, cnt, &relmem); + if (rel != NULL) + { + char buf[128]; + GElf_Sym symmem; + GElf_Sym *sym; + Elf32_Word xndx; + + sym = gelf_getsymshndx (symdata, xndxdata, GELF_R_SYM (rel->r_info), + &symmem, &xndx); + if (sym == NULL) + printf (" %#0*" PRIx64 " %-20s <%s %ld>\n", + class == ELFCLASS32 ? 10 : 18, rel->r_offset, + ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + /* Avoid the leading R_ which isn't carrying any + information. */ + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + 2 + : gettext ("<INVALID RELOC>"), + gettext ("INVALID SYMBOL"), + (long int) GELF_R_SYM (rel->r_info)); + else if (GELF_ST_TYPE (sym->st_info) != STT_SECTION) + printf (" %#0*" PRIx64 " %-20s %#0*" PRIx64 " %s\n", + class == ELFCLASS32 ? 10 : 18, rel->r_offset, + ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + /* Avoid the leading R_ which isn't carrying any + information. */ + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + 2 + : gettext ("<INVALID RELOC>"), + class == ELFCLASS32 ? 10 : 18, sym->st_value, + elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name)); + else + { + destshdr = gelf_getshdr (elf_getscn (ebl->elf, + sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx), + &destshdr_mem); + + if (shdr == NULL) + printf (" %#0*" PRIx64 " %-20s <%s %ld>\n", + class == ELFCLASS32 ? 10 : 18, rel->r_offset, + ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + /* Avoid the leading R_ which isn't carrying any + information. */ + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + 2 + : gettext ("<INVALID RELOC>"), + gettext ("INVALID SECTION"), + (long int) (sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx)); + else + printf (" %#0*" PRIx64 " %-20s %#0*" PRIx64 " %s\n", + class == ELFCLASS32 ? 10 : 18, rel->r_offset, + ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + /* Avoid the leading R_ which isn't carrying any + information. */ + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + 2 + : gettext ("<INVALID RELOC>"), + class == ELFCLASS32 ? 10 : 18, sym->st_value, + elf_strptr (ebl->elf, shstrndx, destshdr->sh_name)); + } + } + } +} + + +/* Handle a relocation section. */ +static void +handle_relocs_rela (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) +{ + int class = gelf_getclass (ebl->elf); + int nentries = shdr->sh_size / shdr->sh_entsize; + + /* Get the data of the section. */ + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + return; + + /* Get the symbol table information. */ + Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); + Elf_Data *symdata = elf_getdata (symscn, NULL); + + /* Get the section header of the section the relocations are for. */ + GElf_Shdr destshdr_mem; + GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info), + &destshdr_mem); + + if (symshdr == NULL || symdata == NULL || destshdr == NULL) + { + printf (gettext ("\nInvalid symbol table at offset %#0" PRIx64 "\n"), + shdr->sh_offset); + return; + } + + /* Search for the optional extended section index table. */ + Elf_Data *xndxdata = NULL; + Elf_Scn *xndxscn = NULL; + while ((xndxscn = elf_nextscn (ebl->elf, xndxscn)) != NULL) + { + GElf_Shdr xndxshdr_mem; + GElf_Shdr *xndxshdr; + + xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem); + if (xndxshdr != NULL && xndxshdr->sh_type == SHT_SYMTAB_SHNDX + && xndxshdr->sh_link == elf_ndxscn (symscn)) + { + /* Found it. */ + xndxdata = elf_getdata (xndxscn, NULL); + break; + } + } + + /* Get the section header string table index. */ + size_t shstrndx; + if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + printf (ngettext ("\ +\nRelocation section [%2zu] '%s' for section [%2u] '%s' at offset %#0" PRIx64 " contains %d entry:\n", + "\ +\nRelocation section [%2zu] '%s' for section [%2u] '%s' at offset %#0" PRIx64 " contains %d entries:\n", + nentries), + elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + (unsigned int) shdr->sh_info, + elf_strptr (ebl->elf, shstrndx, destshdr->sh_name), + shdr->sh_offset, + nentries); + fputs_unlocked (class == ELFCLASS32 + ? gettext ("\ + Offset Type Value Addend Name\n") + : gettext ("\ + Offset Type Value Addend Name\n"), + stdout); + + for (int cnt = 0; cnt < nentries; ++cnt) + { + GElf_Rela relmem; + GElf_Rela *rel = gelf_getrela (data, cnt, &relmem); + if (rel != NULL) + { + char buf[64]; + GElf_Sym symmem; + GElf_Sym *sym; + Elf32_Word xndx; + + sym = gelf_getsymshndx (symdata, xndxdata, GELF_R_SYM (rel->r_info), + &symmem, &xndx); + + if (sym == NULL) + printf (" %#0*" PRIx64 " %-15s <%s %ld>\n", + class == ELFCLASS32 ? 10 : 18, rel->r_offset, + ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + /* Avoid the leading R_ which isn't carrying any + information. */ + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + 2 + : gettext ("<INVALID RELOC>"), + gettext ("INVALID SYMBOL"), + (long int) GELF_R_SYM (rel->r_info)); + else if (GELF_ST_TYPE (sym->st_info) != STT_SECTION) + printf ("\ + %#0*" PRIx64 " %-15s %#0*" PRIx64 " +%5" PRId64 " %s\n", + class == ELFCLASS32 ? 10 : 18, rel->r_offset, + ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + /* Avoid the leading R_ which isn't carrying any + information. */ + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + 2 + : gettext ("<INVALID RELOC>"), + class == ELFCLASS32 ? 10 : 18, sym->st_value, + rel->r_addend, + elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name)); + else + { + destshdr = gelf_getshdr (elf_getscn (ebl->elf, + sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx), + &destshdr_mem); + + if (shdr == NULL) + printf (" %#0*" PRIx64 " %-15s <%s %ld>\n", + class == ELFCLASS32 ? 10 : 18, rel->r_offset, + ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + /* Avoid the leading R_ which isn't carrying any + information. */ + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + 2 + : gettext ("<INVALID RELOC>"), + gettext ("INVALID SECTION"), + (long int) (sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx)); + else + printf ("\ + %#0*" PRIx64 " %-15s %#0*" PRIx64 " +%5" PRId64 " %s\n", + class == ELFCLASS32 ? 10 : 18, rel->r_offset, + ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + /* Avoid the leading R_ which isn't carrying any + information. */ + ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), + buf, sizeof (buf)) + 2 + : gettext ("<INVALID RELOC>"), + class == ELFCLASS32 ? 10 : 18, sym->st_value, + rel->r_addend, + elf_strptr (ebl->elf, shstrndx, destshdr->sh_name)); + } + } + } +} + + +/* Print the program header. */ +static void +print_symtab (Ebl *ebl, int type) +{ + /* Find the symbol table(s). For this we have to search through the + section table. */ + Elf_Scn *scn = NULL; + + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + /* Handle the section if it is a symbol table. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr != NULL && shdr->sh_type == (GElf_Word) type) + handle_symtab (ebl, scn, shdr); + } +} + + +static void +handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) +{ + Elf_Data *versym_data = NULL; + Elf_Data *verneed_data = NULL; + Elf_Data *verdef_data = NULL; + Elf_Data *xndx_data = NULL; + Elf_Scn *runscn; + Elf_Data *data; + int class = gelf_getclass (ebl->elf); + unsigned int nsyms; + unsigned int cnt; + Elf32_Word verneed_stridx = 0; + Elf32_Word verdef_stridx = 0; + GElf_Shdr glink; + size_t shstrndx; + + /* Get the data of the section. */ + data = elf_getdata (scn, NULL); + if (data == NULL) + return; + + /* Find out whether we have other sections we might need. */ + runscn = NULL; + while ((runscn = elf_nextscn (ebl->elf, runscn)) != NULL) + { + GElf_Shdr runshdr_mem; + GElf_Shdr *runshdr = gelf_getshdr (runscn, &runshdr_mem); + + if (runshdr != NULL) + { + if (runshdr->sh_type == SHT_GNU_versym + && runshdr->sh_link == elf_ndxscn (scn)) + /* Bingo, found the version information. Now get the data. */ + versym_data = elf_getdata (runscn, NULL); + else if (runshdr->sh_type == SHT_GNU_verneed) + { + /* This is the information about the needed versions. */ + verneed_data = elf_getdata (runscn, NULL); + verneed_stridx = runshdr->sh_link; + } + else if (runshdr->sh_type == SHT_GNU_verdef) + { + /* This is the information about the defined versions. */ + verdef_data = elf_getdata (runscn, NULL); + verdef_stridx = runshdr->sh_link; + } + else if (runshdr->sh_type == SHT_SYMTAB_SHNDX + && runshdr->sh_link == elf_ndxscn (scn)) + /* Extended section index. */ + xndx_data = elf_getdata (runscn, NULL); + } + } + + /* Get the section header string table index. */ + if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + /* Now we can compute the number of entries in the section. */ + nsyms = data->d_size / (class == ELFCLASS32 + ? sizeof (Elf32_Sym) : sizeof (Elf64_Sym)); + + printf (ngettext ("\nSymbol table [%2u] '%s' contains %u entry:\n", + "\nSymbol table [%2u] '%s' contains %u entries:\n", + nsyms), + (unsigned int) elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), nsyms); + printf (ngettext (" %lu local symbol String table: [%2u] '%s'\n", + " %lu local symbols String table: [%2u] '%s'\n", + shdr->sh_info), + (unsigned long int) shdr->sh_info, + (unsigned int) shdr->sh_link, + elf_strptr (ebl->elf, shstrndx, + gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &glink)->sh_name)); + + fputs_unlocked (class == ELFCLASS32 + ? gettext ("\ + Num: Value Size Type Bind Vis Ndx Name\n") + : gettext ("\ + Num: Value Size Type Bind Vis Ndx Name\n"), + stdout); + + for (cnt = 0; cnt < nsyms; ++cnt) + { + char typebuf[64]; + char bindbuf[64]; + char scnbuf[64]; + Elf32_Word xndx; + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsymshndx (data, xndx_data, cnt, &sym_mem, &xndx); + + if (sym == NULL) + continue; + + /* Determine the real section index. */ + if (sym->st_shndx != SHN_XINDEX) + xndx = sym->st_shndx; + + printf (gettext ("\ +%5u: %0*" PRIx64 " %6" PRId64 " %-7s %-6s %-9s %6s %s"), + cnt, + class == ELFCLASS32 ? 8 : 16, + sym->st_value, + sym->st_size, + ebl_symbol_type_name (ebl, GELF_ST_TYPE (sym->st_info), + typebuf, sizeof (typebuf)), + ebl_symbol_binding_name (ebl, GELF_ST_BIND (sym->st_info), + bindbuf, sizeof (bindbuf)), + get_visibility_type (GELF_ST_VISIBILITY (sym->st_other)), + ebl_section_name (ebl, sym->st_shndx, xndx, scnbuf, + sizeof (scnbuf), NULL, shnum), + elf_strptr (ebl->elf, shdr->sh_link, sym->st_name)); + + if (versym_data != NULL) + { + /* Get the version information. */ + GElf_Versym versym_mem; + GElf_Versym *versym; + + versym = gelf_getversym (versym_data, cnt, &versym_mem); + + if (versym != NULL && ((*versym & 0x8000) != 0 || *versym > 1)) + { + bool is_nobits = false; + bool check_def = xndx != SHN_UNDEF; + + if (xndx < SHN_LORESERVE || sym->st_shndx == SHN_XINDEX) + { + GElf_Shdr symshdr_mem; + GElf_Shdr *symshdr = + gelf_getshdr (elf_getscn (ebl->elf, xndx), &symshdr_mem); + + is_nobits = (symshdr != NULL + && symshdr->sh_type == SHT_NOBITS); + } + + if (is_nobits || ! check_def) + { + /* We must test both. */ + GElf_Verneed verneed_mem; + GElf_Verneed *verneed; + GElf_Vernaux vernaux_mem; + GElf_Vernaux *vernaux = NULL; + size_t vn_offset = 0; + + verneed = gelf_getverneed (verneed_data, 0, &verneed_mem); + while (verneed != NULL) + { + size_t vna_offset = vn_offset; + + vernaux = gelf_getvernaux (verneed_data, + vna_offset += verneed->vn_aux, + &vernaux_mem); + while (vernaux != NULL + && vernaux->vna_other != *versym + && vernaux->vna_next != 0) + { + /* Update the offset. */ + vna_offset += vernaux->vna_next; + + vernaux = (vernaux->vna_next == 0 + ? NULL + : gelf_getvernaux (verneed_data, + vna_offset, + &vernaux_mem)); + } + + /* Check whether we found the version. */ + if (vernaux != NULL && vernaux->vna_other == *versym) + /* Found it. */ + break; + + vn_offset += verneed->vn_next; + verneed = (verneed->vn_next == 0 + ? NULL + : gelf_getverneed (verneed_data, vn_offset, + &verneed_mem)); + } + + if (vernaux != NULL && vernaux->vna_other == *versym) + { + printf ("@%s (%u)", + elf_strptr (ebl->elf, verneed_stridx, + vernaux->vna_name), + (unsigned int) vernaux->vna_other); + check_def = 0; + } + else if (! is_nobits) + error (0, 0, gettext ("bad dynamic symbol")); + else + check_def = 1; + } + + if (check_def && *versym != 0x8001) + { + /* We must test both. */ + GElf_Verdef verdef_mem; + GElf_Verdef *verdef; + size_t vd_offset = 0; + + verdef = gelf_getverdef (verdef_data, 0, &verdef_mem); + while (verdef != NULL) + { + if (verdef->vd_ndx == (*versym & 0x7fff)) + /* Found the definition. */ + break; + + vd_offset += verdef->vd_next; + verdef = (verdef->vd_next == 0 + ? NULL + : gelf_getverdef (verdef_data, vd_offset, + &verdef_mem)); + } + + if (verdef != NULL) + { + GElf_Verdaux verdaux_mem; + GElf_Verdaux *verdaux; + + verdaux = gelf_getverdaux (verdef_data, + vd_offset + verdef->vd_aux, + &verdaux_mem); + + if (verdaux != NULL) + printf ((*versym & 0x8000) ? "@%s" : "@@%s", + elf_strptr (ebl->elf, verdef_stridx, + verdaux->vda_name)); + } + } + } + } + + putchar ('\n'); + } +} + + +/* Print version information. */ +static void +print_verinfo (Ebl *ebl) +{ + /* Find the version information sections. For this we have to + search through the section table. */ + Elf_Scn *scn = NULL; + + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + /* Handle the section if it is part of the versioning handling. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr != NULL) + { + if (shdr->sh_type == SHT_GNU_verneed) + handle_verneed (ebl, scn, shdr); + else if (shdr->sh_type == SHT_GNU_verdef) + handle_verdef (ebl, scn, shdr); + else if (shdr->sh_type == SHT_GNU_versym) + handle_versym (ebl, scn, shdr); + } + } +} + + +static const char * +get_ver_flags (unsigned int flags) +{ + static char buf[32]; + char *endp; + + if (flags == 0) + return gettext ("none"); + + if (flags & VER_FLG_BASE) + endp = stpcpy (buf, "BASE "); + else + endp = buf; + + if (flags & VER_FLG_WEAK) + { + if (endp != buf) + endp = stpcpy (endp, "| "); + + endp = stpcpy (endp, "WEAK "); + } + + if (flags & ~(VER_FLG_BASE | VER_FLG_WEAK)) + { + strncpy (endp, gettext ("| <unknown>"), buf + sizeof (buf) - endp); + buf[sizeof (buf) - 1] = '\0'; + } + + return buf; +} + + +static void +handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) +{ + Elf_Data *data; + int class = gelf_getclass (ebl->elf); + GElf_Shdr glink; + int cnt; + unsigned int offset; + size_t shstrndx; + + /* Get the data of the section. */ + data = elf_getdata (scn, NULL); + if (data == NULL) + return; + + /* Get the section header string table index. */ + if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + printf (ngettext ("\ +\nVersion needs section [%2u] '%s' contains %d entry:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", + "\ +\nVersion needs section [%2u] '%s' contains %d entries:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", + shdr->sh_info), + (unsigned int) elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), shdr->sh_info, + class == ELFCLASS32 ? 10 : 18, shdr->sh_addr, + shdr->sh_offset, + (unsigned int) shdr->sh_link, + elf_strptr (ebl->elf, shstrndx, + gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &glink)->sh_name)); + + offset = 0; + for (cnt = shdr->sh_info; --cnt >= 0; ) + { + GElf_Verneed needmem; + GElf_Verneed *need; + unsigned int auxoffset; + int cnt2; + + /* Get the data at the next offset. */ + need = gelf_getverneed (data, offset, &needmem); + if (need == NULL) + break; + + printf (gettext (" %#06x: Version: %hu File: %s Cnt: %hu\n"), + offset, (unsigned short int) need->vn_version, + elf_strptr (ebl->elf, shdr->sh_link, need->vn_file), + (unsigned short int) need->vn_cnt); + + auxoffset = offset + need->vn_aux; + for (cnt2 = need->vn_cnt; --cnt2 >= 0; ) + { + GElf_Vernaux auxmem; + GElf_Vernaux *aux; + + aux = gelf_getvernaux (data, auxoffset, &auxmem); + if (aux == NULL) + break; + + printf (gettext (" %#06x: Name: %s Flags: %s Version: %hu\n"), + auxoffset, + elf_strptr (ebl->elf, shdr->sh_link, aux->vna_name), + get_ver_flags (aux->vna_flags), + (unsigned short int) aux->vna_other); + + auxoffset += aux->vna_next; + } + + /* Find the next offset. */ + offset += need->vn_next; + } +} + + +static void +handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) +{ + Elf_Data *data; + int class = gelf_getclass (ebl->elf); + GElf_Shdr glink; + int cnt; + unsigned int offset; + size_t shstrndx; + + /* Get the data of the section. */ + data = elf_getdata (scn, NULL); + if (data == NULL) + return; + + /* Get the section header string table index. */ + if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + printf (ngettext ("\ +\nVersion definition section [%2u] '%s' contains %d entry:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", + "\ +\nVersion definition section [%2u] '%s' contains %d entries:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", + shdr->sh_info), + (unsigned int) elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + shdr->sh_info, + class == ELFCLASS32 ? 10 : 18, shdr->sh_addr, + shdr->sh_offset, + (unsigned int) shdr->sh_link, + elf_strptr (ebl->elf, shstrndx, + gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &glink)->sh_name)); + + offset = 0; + for (cnt = shdr->sh_info; --cnt >= 0; ) + { + GElf_Verdef defmem; + GElf_Verdef *def; + GElf_Verdaux auxmem; + GElf_Verdaux *aux; + unsigned int auxoffset; + int cnt2; + + /* Get the data at the next offset. */ + def = gelf_getverdef (data, offset, &defmem); + if (def == NULL) + break; + + auxoffset = offset + def->vd_aux; + aux = gelf_getverdaux (data, auxoffset, &auxmem); + if (aux == NULL) + break; + + printf (gettext ("\ + %#06x: Version: %hd Flags: %s Index: %hd Cnt: %hd Name: %s\n"), + offset, def->vd_version, + get_ver_flags (def->vd_flags), + def->vd_ndx, + def->vd_cnt, + elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name)); + + auxoffset += aux->vda_next; + for (cnt2 = 1; cnt2 < def->vd_cnt; ++cnt2) + { + aux = gelf_getverdaux (data, auxoffset, &auxmem); + if (aux == NULL) + break; + + printf (gettext (" %#06x: Parent %d: %s\n"), + auxoffset, cnt2, + elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name)); + + auxoffset += aux->vda_next; + } + + /* Find the next offset. */ + offset += def->vd_next; + } +} + + +static void +handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) +{ + Elf_Data *data; + int class = gelf_getclass (ebl->elf); + Elf_Scn *verscn; + GElf_Shdr glink; + Elf_Scn *defscn; + Elf_Scn *needscn; + const char **vername; + const char **filename; + size_t nvername; + unsigned int cnt; + size_t shstrndx; + + /* Get the data of the section. */ + data = elf_getdata (scn, NULL); + if (data == NULL) + return; + + /* Get the section header string table index. */ + if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + /* We have to find the version definition section and extract the + version names. */ + defscn = NULL; + needscn = NULL; + + verscn = NULL; + while ((verscn = elf_nextscn (ebl->elf, verscn)) != NULL) + { + GElf_Shdr vershdr_mem; + GElf_Shdr *vershdr = gelf_getshdr (verscn, &vershdr_mem); + + if (vershdr != NULL) + { + if (vershdr->sh_type == SHT_GNU_verdef) + defscn = verscn; + else if (vershdr->sh_type == SHT_GNU_verneed) + needscn = verscn; + } + } + + if (defscn != NULL || needscn != NULL) + { + /* We have a version information (better should have). Now get + the version names. First find the maximum version number. */ + nvername = 0; + if (defscn != NULL) + { + /* Run through the version definitions and find the highest + index. */ + unsigned int offset = 0; + Elf_Data *defdata; + GElf_Shdr defshdrmem; + GElf_Shdr *defshdr; + + defdata = elf_getdata (defscn, NULL); + if (defdata == NULL) + return; + + defshdr = gelf_getshdr (defscn, &defshdrmem); + if (defshdr == NULL) + return; + + for (cnt = 0; cnt < defshdr->sh_info; ++cnt) + { + GElf_Verdef defmem; + GElf_Verdef *def; + + /* Get the data at the next offset. */ + def = gelf_getverdef (defdata, offset, &defmem); + if (def == NULL) + break; + + nvername = MAX (nvername, (size_t) (def->vd_ndx & 0x7fff)); + + offset += def->vd_next; + } + } + if (needscn != NULL) + { + unsigned int offset = 0; + Elf_Data *needdata; + GElf_Shdr needshdrmem; + GElf_Shdr *needshdr; + + needdata = elf_getdata (needscn, NULL); + if (needdata == NULL) + return; + + needshdr = gelf_getshdr (needscn, &needshdrmem); + if (needshdr == NULL) + return; + + for (cnt = 0; cnt < needshdr->sh_info; ++cnt) + { + GElf_Verneed needmem; + GElf_Verneed *need; + unsigned int auxoffset; + int cnt2; + + /* Get the data at the next offset. */ + need = gelf_getverneed (needdata, offset, &needmem); + if (need == NULL) + break; + + /* Run through the auxiliary entries. */ + auxoffset = offset + need->vn_aux; + for (cnt2 = need->vn_cnt; --cnt2 >= 0; ) + { + GElf_Vernaux auxmem; + GElf_Vernaux *aux; + + aux = gelf_getvernaux (needdata, auxoffset, &auxmem); + if (aux == NULL) + break; + + nvername = MAX (nvername, + (size_t) (aux->vna_other & 0x7fff)); + + auxoffset += aux->vna_next; + } + + offset += need->vn_next; + } + } + + /* This is the number of versions we know about. */ + ++nvername; + + /* Allocate the array. */ + vername = (const char **) alloca (nvername * sizeof (const char *)); + filename = (const char **) alloca (nvername * sizeof (const char *)); + + /* Run through the data structures again and collect the strings. */ + if (defscn != NULL) + { + /* Run through the version definitions and find the highest + index. */ + unsigned int offset = 0; + Elf_Data *defdata; + GElf_Shdr defshdrmem; + GElf_Shdr *defshdr; + + defdata = elf_getdata (defscn, NULL); + if (defdata == NULL) + return; + + defshdr = gelf_getshdr (defscn, &defshdrmem); + if (defshdr == NULL) + return; + + for (cnt = 0; cnt < defshdr->sh_info; ++cnt) + { + GElf_Verdef defmem; + GElf_Verdef *def; + GElf_Verdaux auxmem; + GElf_Verdaux *aux; + + /* Get the data at the next offset. */ + def = gelf_getverdef (defdata, offset, &defmem); + if (def == NULL) + break; + + aux = gelf_getverdaux (defdata, offset + def->vd_aux, &auxmem); + if (aux == NULL) + break; + + vername[def->vd_ndx & 0x7fff] + = elf_strptr (ebl->elf, defshdr->sh_link, aux->vda_name); + filename[def->vd_ndx & 0x7fff] = NULL; + + offset += def->vd_next; + } + } + if (needscn != NULL) + { + unsigned int offset = 0; + Elf_Data *needdata; + GElf_Shdr needshdrmem; + GElf_Shdr *needshdr; + + needdata = elf_getdata (needscn, NULL); + if (needdata == NULL) + return; + + needshdr = gelf_getshdr (needscn, &needshdrmem); + if (needshdr == NULL) + return; + + for (cnt = 0; cnt < needshdr->sh_info; ++cnt) + { + GElf_Verneed needmem; + GElf_Verneed *need; + unsigned int auxoffset; + int cnt2; + + /* Get the data at the next offset. */ + need = gelf_getverneed (needdata, offset, &needmem); + if (need == NULL) + break; + + /* Run through the auxiliary entries. */ + auxoffset = offset + need->vn_aux; + for (cnt2 = need->vn_cnt; --cnt2 >= 0; ) + { + GElf_Vernaux auxmem; + GElf_Vernaux *aux; + + aux = gelf_getvernaux (needdata, auxoffset, &auxmem); + if (aux == NULL) + break; + + vername[aux->vna_other & 0x7fff] + = elf_strptr (ebl->elf, needshdr->sh_link, aux->vna_name); + filename[aux->vna_other & 0x7fff] + = elf_strptr (ebl->elf, needshdr->sh_link, need->vn_file); + + auxoffset += aux->vna_next; + } + + offset += need->vn_next; + } + } + } + else + { + vername = NULL; + nvername = 1; + filename = NULL; + } + + /* Print the header. */ + printf (ngettext ("\ +\nVersion symbols section [%2u] '%s' contains %d entry:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'", + "\ +\nVersion symbols section [%2u] '%s' contains %d entries:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'", + shdr->sh_size / shdr->sh_entsize), + (unsigned int) elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + (int) (shdr->sh_size / shdr->sh_entsize), + class == ELFCLASS32 ? 10 : 18, shdr->sh_addr, + shdr->sh_offset, + (unsigned int) shdr->sh_link, + elf_strptr (ebl->elf, shstrndx, + gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &glink)->sh_name)); + + /* Now we can finally look at the actual contents of this section. */ + for (cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) + { + GElf_Versym symmem; + GElf_Versym *sym; + ssize_t n; + + if (cnt % 2 == 0) + printf ("\n %4d:", cnt); + + sym = gelf_getversym (data, cnt, &symmem); + if (sym == NULL) + break; + + switch (*sym) + { + case 0: + fputs_unlocked (gettext (" 0 *local* "), + stdout); + break; + + case 1: + fputs_unlocked (gettext (" 1 *global* "), + stdout); + break; + + default: + n = printf ("%4d%c%s", + *sym & 0x7fff, *sym & 0x8000 ? 'h' : ' ', + (unsigned int) (*sym & 0x7fff) < nvername + ? vername[*sym & 0x7fff] : "???"); + if ((unsigned int) (*sym & 0x7fff) < nvername + && filename[*sym & 0x7fff] != NULL) + n += printf ("(%s)", filename[*sym & 0x7fff]); + printf ("%*s", MAX (0, 33 - (int) n), " "); + break; + } + } + putchar ('\n'); +} + + +static void +handle_hash (Ebl *ebl) +{ + /* Find the symbol table(s). For this we have to search through the + section table. */ + Elf_Scn *scn = NULL; + size_t shstrndx; + + /* Get the section header string table index. */ + if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + /* Handle the section if it is a symbol table. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr != NULL && shdr->sh_type == SHT_HASH) + { + Elf_Data *data = elf_getdata (scn, NULL); + Elf32_Word nbucket; + Elf32_Word nchain; + Elf32_Word *bucket; + Elf32_Word *chain; + uint32_t *lengths; + uint32_t *counts; + Elf32_Word cnt; + Elf32_Word maxlength = 0; + Elf32_Word nsyms = 0; + uint64_t nzero_counts = 0; + GElf_Shdr glink; + + if (data == NULL) + { + error (0, 0, gettext ("cannot get data for section %d: %s"), + (int) elf_ndxscn (scn), elf_errmsg (-1)); + continue; + } + + nbucket = ((Elf32_Word *) data->d_buf)[0]; + nchain = ((Elf32_Word *) data->d_buf)[1]; + bucket = &((Elf32_Word *) data->d_buf)[2]; + chain = &((Elf32_Word *) data->d_buf)[2 + nbucket]; + + printf (ngettext ("\ +\nHistogram for bucket list length in section [%2u] '%s' (total of %d bucket):\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", + "\ +\nHistogram for bucket list length in section [%2u] '%s' (total of %d buckets):\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", + nbucket), + (unsigned int) elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + (int) nbucket, + gelf_getclass (ebl->elf) == ELFCLASS32 ? 10 : 18, + shdr->sh_addr, + shdr->sh_offset, + (unsigned int) shdr->sh_link, + elf_strptr (ebl->elf, shstrndx, + gelf_getshdr (elf_getscn (ebl->elf, + shdr->sh_link), + &glink)->sh_name)); + + lengths = (uint32_t *) xcalloc (nbucket, sizeof (uint32_t)); + + for (cnt = 0; cnt < nbucket; ++cnt) + if (bucket[cnt] != 0) + { + Elf32_Word inner; + + inner = bucket[cnt]; + while (inner > 0 && inner < nchain) + { + ++nsyms; + if (maxlength < ++lengths[cnt]) + ++maxlength; + + inner = chain[inner]; + } + } + + counts = (uint32_t *) xcalloc (maxlength + 1, sizeof (uint32_t)); + + for (cnt = 0; cnt < nbucket; ++cnt) + ++counts[lengths[cnt]]; + + if (nbucket > 0) + { + uint64_t success = 0; + Elf32_Word acc; + + puts (gettext (" Length Number % of total Coverage")); + printf (gettext (" 0 %6" PRIu32 " %5.1f%%\n"), + counts[0], (counts[0] * 100.0) / nbucket); + + for (cnt = 1; cnt <= maxlength; ++cnt) + { + nzero_counts += counts[cnt] * cnt; + printf (gettext ("\ +%7d %6" PRIu32 " %5.1f%% %5.1f%%\n"), + (int) cnt, + counts[cnt], (counts[cnt] * 100.0) / nbucket, + (nzero_counts * 100.0) / nsyms); + } + + acc = 0; + for (cnt = 1; cnt <= maxlength; ++cnt) + { + acc += cnt; + success += counts[cnt] * acc; + } + + printf (gettext ("\ + Average number of tests: successful lookup: %f\n\ + unsuccessful lookup: %f\n"), + (double) success / (double) nzero_counts, + (double) nzero_counts / (double) nbucket); + } + + free (counts); + free (lengths); + } + } +} + + +static void +print_liblist (Ebl *ebl) +{ + /* Find the library list sections. For this we have to search + through the section table. */ + Elf_Scn *scn = NULL; + + /* Get the section header string table index. */ + size_t shstrndx; + if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr != NULL && shdr->sh_type == SHT_GNU_LIBLIST) + { + int nentries = shdr->sh_size / shdr->sh_entsize; + printf (ngettext ("\ +\nLibrary list section [%2zu] '%s' at offset %#0" PRIx64 " contains %d entry:\n", + "\ +\nLibrary list section [%2zu] '%s' at offset %#0" PRIx64 " contains %d entries:\n", + nentries), + elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + shdr->sh_offset, + nentries); + + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + return; + + puts (gettext ("\ + Library Time Stamp Checksum Version Flags")); + + for (int cnt = 0; cnt < nentries; ++cnt) + { + GElf_Lib lib_mem; + GElf_Lib *lib = gelf_getlib (data, cnt, &lib_mem); + if (lib == NULL) + continue; + + time_t t = (time_t) lib->l_time_stamp; + struct tm *tm = gmtime (&t); + if (tm == NULL) + continue; + + printf (" [%2d] %-29s %04u-%02u-%02uT%02u:%02u:%02u %08x %-7u %u\n", + cnt, elf_strptr (ebl->elf, shdr->sh_link, lib->l_name), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + (unsigned int) lib->l_checksum, + (unsigned int) lib->l_version, + (unsigned int) lib->l_flags); + } + } + } +} + + +static const char * +dwarf_tag_string (unsigned int tag) +{ + static const char *known_tags[] = + { + [DW_TAG_array_type] = "array_type", + [DW_TAG_class_type] = "class_type", + [DW_TAG_entry_point] = "entry_point", + [DW_TAG_enumeration_type] = "enumeration_type", + [DW_TAG_formal_parameter] = "formal_parameter", + [DW_TAG_imported_declaration] = "imported_declaration", + [DW_TAG_label] = "label", + [DW_TAG_lexical_block] = "lexical_block", + [DW_TAG_member] = "member", + [DW_TAG_pointer_type] = "pointer_type", + [DW_TAG_reference_type] = "reference_type", + [DW_TAG_compile_unit] = "compile_unit", + [DW_TAG_string_type] = "string_type", + [DW_TAG_structure_type] = "structure_type", + [DW_TAG_subroutine_type] = "subroutine_type", + [DW_TAG_typedef] = "typedef", + [DW_TAG_union_type] = "union_type", + [DW_TAG_unspecified_parameters] = "unspecified_parameters", + [DW_TAG_variant] = "variant", + [DW_TAG_common_block] = "common_block", + [DW_TAG_common_inclusion] = "common_inclusion", + [DW_TAG_inheritance] = "inheritance", + [DW_TAG_inlined_subroutine] = "inlined_subroutine", + [DW_TAG_module] = "module", + [DW_TAG_ptr_to_member_type] = "ptr_to_member_type", + [DW_TAG_set_type] = "set_type", + [DW_TAG_subrange_type] = "subrange_type", + [DW_TAG_with_stmt] = "with_stmt", + [DW_TAG_access_declaration] = "access_declaration", + [DW_TAG_base_type] = "base_type", + [DW_TAG_catch_block] = "catch_block", + [DW_TAG_const_type] = "const_type", + [DW_TAG_constant] = "constant", + [DW_TAG_enumerator] = "enumerator", + [DW_TAG_file_type] = "file_type", + [DW_TAG_friend] = "friend", + [DW_TAG_namelist] = "namelist", + [DW_TAG_namelist_item] = "namelist_item", + [DW_TAG_packed_type] = "packed_type", + [DW_TAG_subprogram] = "subprogram", + [DW_TAG_template_type_param] = "template_type_param", + [DW_TAG_template_value_param] = "template_value_param", + [DW_TAG_thrown_type] = "thrown_type", + [DW_TAG_try_block] = "try_block", + [DW_TAG_variant_part] = "variant_part", + [DW_TAG_variable] = "variable", + [DW_TAG_volatile_type] = "volatile_type", + [DW_TAG_dwarf_procedure] = "dwarf_procedure", + [DW_TAG_restrict_type] = "restrict_type", + [DW_TAG_interface_type] = "interface_type", + [DW_TAG_namespace] = "namespace", + [DW_TAG_imported_module] = "imported_module", + [DW_TAG_unspecified_type] = "unspecified_type", + [DW_TAG_partial_unit] = "partial_unit", + [DW_TAG_imported_unit] = "imported_unit", + [DW_TAG_unspecified_type] = "unspecified_type", + [DW_TAG_partial_unit] = "partial_unit", + [DW_TAG_imported_unit] = "imported_unit", + [DW_TAG_mutable_type] = "mutable_type", + }; + const unsigned int nknown_tags = (sizeof (known_tags) + / sizeof (known_tags[0])); + static char buf[40]; + const char *result = NULL; + + if (tag < nknown_tags) + result = known_tags[tag]; + + if (result == NULL) + /* There are a few known extensions. */ + switch (tag) + { + case DW_TAG_MIPS_loop: + result = "MIPS_loop"; + break; + + case DW_TAG_format_label: + result = "format_label"; + break; + + case DW_TAG_function_template: + result = "function_template"; + break; + + case DW_TAG_class_template: + result = "class_template"; + break; + + default: + if (tag < DW_TAG_lo_user) + snprintf (buf, sizeof buf, gettext ("unknown tag %hx"), tag); + else + snprintf (buf, sizeof buf, gettext ("unknown user tag %hx"), tag); + result = buf; + break; + } + + return result; +} + + +static const char * +dwarf_attr_string (unsigned int attrnum) +{ + static const char *known_attrs[] = + { + [DW_AT_sibling] = "sibling", + [DW_AT_location] = "location", + [DW_AT_name] = "name", + [DW_AT_ordering] = "ordering", + [DW_AT_subscr_data] = "subscr_data", + [DW_AT_byte_size] = "byte_size", + [DW_AT_bit_offset] = "bit_offset", + [DW_AT_bit_size] = "bit_size", + [DW_AT_element_list] = "element_list", + [DW_AT_stmt_list] = "stmt_list", + [DW_AT_low_pc] = "low_pc", + [DW_AT_high_pc] = "high_pc", + [DW_AT_language] = "language", + [DW_AT_member] = "member", + [DW_AT_discr] = "discr", + [DW_AT_discr_value] = "discr_value", + [DW_AT_visibility] = "visibility", + [DW_AT_import] = "import", + [DW_AT_string_length] = "string_length", + [DW_AT_common_reference] = "common_reference", + [DW_AT_comp_dir] = "comp_dir", + [DW_AT_const_value] = "const_value", + [DW_AT_containing_type] = "containing_type", + [DW_AT_default_value] = "default_value", + [DW_AT_inline] = "inline", + [DW_AT_is_optional] = "is_optional", + [DW_AT_lower_bound] = "lower_bound", + [DW_AT_producer] = "producer", + [DW_AT_prototyped] = "prototyped", + [DW_AT_return_addr] = "return_addr", + [DW_AT_start_scope] = "start_scope", + [DW_AT_stride_size] = "stride_size", + [DW_AT_upper_bound] = "upper_bound", + [DW_AT_abstract_origin] = "abstract_origin", + [DW_AT_accessibility] = "accessibility", + [DW_AT_address_class] = "address_class", + [DW_AT_artificial] = "artificial", + [DW_AT_base_types] = "base_types", + [DW_AT_calling_convention] = "calling_convention", + [DW_AT_count] = "count", + [DW_AT_data_member_location] = "data_member_location", + [DW_AT_decl_column] = "decl_column", + [DW_AT_decl_file] = "decl_file", + [DW_AT_decl_line] = "decl_line", + [DW_AT_declaration] = "declaration", + [DW_AT_discr_list] = "discr_list", + [DW_AT_encoding] = "encoding", + [DW_AT_external] = "external", + [DW_AT_frame_base] = "frame_base", + [DW_AT_friend] = "friend", + [DW_AT_identifier_case] = "identifier_case", + [DW_AT_macro_info] = "macro_info", + [DW_AT_namelist_items] = "namelist_items", + [DW_AT_priority] = "priority", + [DW_AT_segment] = "segment", + [DW_AT_specification] = "specification", + [DW_AT_static_link] = "static_link", + [DW_AT_type] = "type", + [DW_AT_use_location] = "use_location", + [DW_AT_variable_parameter] = "variable_parameter", + [DW_AT_virtuality] = "virtuality", + [DW_AT_vtable_elem_location] = "vtable_elem_location", + [DW_AT_allocated] = "allocated", + [DW_AT_associated] = "associated", + [DW_AT_data_location] = "data_location", + [DW_AT_stride] = "stride", + [DW_AT_entry_pc] = "entry_pc", + [DW_AT_use_UTF8] = "use_UTF8", + [DW_AT_extension] = "extension", + [DW_AT_ranges] = "ranges", + [DW_AT_trampoline] = "trampoline", + [DW_AT_call_column] = "call_column", + [DW_AT_call_file] = "call_file", + [DW_AT_call_line] = "call_line", + [DW_AT_description] = "description" + }; + const unsigned int nknown_attrs = (sizeof (known_attrs) + / sizeof (known_attrs[0])); + static char buf[40]; + const char *result = NULL; + + if (attrnum < nknown_attrs) + result = known_attrs[attrnum]; + + if (result == NULL) + /* There are a few known extensions. */ + switch (attrnum) + { + case DW_AT_MIPS_fde: + result = "MIPS_fde"; + break; + + case DW_AT_MIPS_loop_begin: + result = "MIPS_loop_begin"; + break; + + case DW_AT_MIPS_tail_loop_begin: + result = "MIPS_tail_loop_begin"; + break; + + case DW_AT_MIPS_epilog_begin: + result = "MIPS_epilog_begin"; + break; + + case DW_AT_MIPS_loop_unroll_factor: + result = "MIPS_loop_unroll_factor"; + break; + + case DW_AT_MIPS_software_pipeline_depth: + result = "MIPS_software_pipeline_depth"; + break; + + case DW_AT_MIPS_linkage_name: + result = "MIPS_linkage_name"; + break; + + case DW_AT_MIPS_stride: + result = "MIPS_stride"; + break; + + case DW_AT_MIPS_abstract_name: + result = "MIPS_abstract_name"; + break; + + case DW_AT_MIPS_clone_origin: + result = "MIPS_clone_origin"; + break; + + case DW_AT_MIPS_has_inlines: + result = "MIPS_has_inlines"; + break; + + case DW_AT_MIPS_stride_byte: + result = "MIPS_stride_byte"; + break; + + case DW_AT_MIPS_stride_elem: + result = "MIPS_stride_elem"; + break; + + case DW_AT_MIPS_ptr_dopetype: + result = "MIPS_ptr_dopetype"; + break; + + case DW_AT_MIPS_allocatable_dopetype: + result = "MIPS_allocatable_dopetype"; + break; + + case DW_AT_MIPS_assumed_shape_dopetype: + result = "MIPS_assumed_shape_dopetype"; + break; + + case DW_AT_MIPS_assumed_size: + result = "MIPS_assumed_size"; + break; + + case DW_AT_sf_names: + result = "sf_names"; + break; + + case DW_AT_src_info: + result = "src_info"; + break; + + case DW_AT_mac_info: + result = "mac_info"; + break; + + case DW_AT_src_coords: + result = "src_coords"; + break; + + case DW_AT_body_begin: + result = "body_begin"; + break; + + case DW_AT_body_end: + result = "body_end"; + break; + + default: + if (attrnum < DW_AT_lo_user) + snprintf (buf, sizeof buf, gettext ("unknown attribute %hx"), + attrnum); + else + snprintf (buf, sizeof buf, gettext ("unknown user attribute %hx"), + attrnum); + result = buf; + break; + } + + return result; +} + + +static const char * +dwarf_form_string (unsigned int form) +{ + static const char *known_forms[] = + { + [DW_FORM_addr] = "addr", + [DW_FORM_block2] = "block2", + [DW_FORM_block4] = "block4", + [DW_FORM_data2] = "data2", + [DW_FORM_data4] = "data4", + [DW_FORM_data8] = "data8", + [DW_FORM_string] = "string", + [DW_FORM_block] = "block", + [DW_FORM_block1] = "block1", + [DW_FORM_data1] = "data1", + [DW_FORM_flag] = "flag", + [DW_FORM_sdata] = "sdata", + [DW_FORM_strp] = "strp", + [DW_FORM_udata] = "udata", + [DW_FORM_ref_addr] = "ref_addr", + [DW_FORM_ref1] = "ref1", + [DW_FORM_ref2] = "ref2", + [DW_FORM_ref4] = "ref4", + [DW_FORM_ref8] = "ref8", + [DW_FORM_ref_udata] = "ref_udata", + [DW_FORM_indirect] = "indirect" + }; + const unsigned int nknown_forms = (sizeof (known_forms) + / sizeof (known_forms[0])); + static char buf[40]; + const char *result = NULL; + + if (form < nknown_forms) + result = known_forms[form]; + + if (result == NULL) + snprintf (buf, sizeof buf, gettext ("unknown form %" PRIx64), + (uint64_t) form); + + return result; +} + + +static const char * +dwarf_lang_string (unsigned int lang) +{ + static const char *known[] = + { + [DW_LANG_C89] = "ISO C89", + [DW_LANG_C] = "C", + [DW_LANG_Ada83] = "Ada83", + [DW_LANG_C_plus_plus ] = "C++", + [DW_LANG_Cobol74] = "Cobol74", + [DW_LANG_Cobol85] = "Cobol85", + [DW_LANG_Fortran77] = "Fortran77", + [DW_LANG_Fortran90] = "Fortran90", + [DW_LANG_Pascal83] = "Pascal83", + [DW_LANG_Modula2] = "Modula2", + [DW_LANG_Java] = "Java", + [DW_LANG_C99] = "ISO C99", + [DW_LANG_Ada95] = "Ada95", + [DW_LANG_Fortran95] = "Fortran95", + [DW_LANG_PL1] = "PL1" + }; + + if (lang < sizeof (known) / sizeof (known[0])) + return known[lang]; + else if (lang == DW_LANG_Mips_Assembler) + /* This language tag is used for assembler in general. */ + return "Assembler"; + + if (lang >= DW_LANG_lo_user && lang <= DW_LANG_hi_user) + { + static char buf[30]; + snprintf (buf, sizeof (buf), "lo_user+%u", lang - DW_LANG_lo_user); + return buf; + } + + return "???"; +} + + +static const char * +dwarf_inline_string (unsigned int code) +{ + static const char *known[] = + { + [DW_INL_not_inlined] = "not_inlined", + [DW_INL_inlined] = "inlined", + [DW_INL_declared_not_inlined] = "declared_not_inlined", + [DW_INL_declared_inlined] = "declared_inlined" + }; + + if (code < sizeof (known) / sizeof (known[0])) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_encoding_string (unsigned int code) +{ + static const char *known[] = + { + [DW_ATE_void] = "void", + [DW_ATE_address] = "address", + [DW_ATE_boolean] = "boolean", + [DW_ATE_complex_float] = "complex_float", + [DW_ATE_float] = "float", + [DW_ATE_signed] = "signed", + [DW_ATE_signed_char] = "signed_char", + [DW_ATE_unsigned] = "unsigned", + [DW_ATE_unsigned_char] = "unsigned_char", + [DW_ATE_imaginary_float] = "imaginary_float" + }; + + if (code < sizeof (known) / sizeof (known[0])) + return known[code]; + + if (code >= DW_ATE_lo_user && code <= DW_ATE_hi_user) + { + static char buf[30]; + snprintf (buf, sizeof (buf), "lo_user+%u", code - DW_ATE_lo_user); + return buf; + } + + return "???"; +} + + +static const char * +dwarf_access_string (unsigned int code) +{ + static const char *known[] = + { + [DW_ACCESS_public] = "public", + [DW_ACCESS_protected] = "protected", + [DW_ACCESS_private] = "private" + }; + + if (code < sizeof (known) / sizeof (known[0])) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_visibility_string (unsigned int code) +{ + static const char *known[] = + { + [DW_VIS_local] = "local", + [DW_VIS_exported] = "exported", + [DW_VIS_qualified] = "qualified" + }; + + if (code < sizeof (known) / sizeof (known[0])) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_virtuality_string (unsigned int code) +{ + static const char *known[] = + { + [DW_VIRTUALITY_none] = "none", + [DW_VIRTUALITY_virtual] = "virtual", + [DW_VIRTUALITY_pure_virtual] = "pure_virtual" + }; + + if (code < sizeof (known) / sizeof (known[0])) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_identifier_case_string (unsigned int code) +{ + static const char *known[] = + { + [DW_ID_case_sensitive] = "sensitive", + [DW_ID_up_case] = "up_case", + [DW_ID_down_case] = "down_case", + [DW_ID_case_insensitive] = "insensitive" + }; + + if (code < sizeof (known) / sizeof (known[0])) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_calling_convention_string (unsigned int code) +{ + static const char *known[] = + { + [DW_CC_normal] = "normal", + [DW_CC_program] = "program", + [DW_CC_nocall] = "nocall", + }; + + if (code < sizeof (known) / sizeof (known[0])) + return known[code]; + + if (code >= DW_CC_lo_user && code <= DW_CC_hi_user) + { + static char buf[30]; + snprintf (buf, sizeof (buf), "lo_user+%u", code - DW_CC_lo_user); + return buf; + } + + return "???"; +} + + +static const char * +dwarf_ordering_string (unsigned int code) +{ + static const char *known[] = + { + [DW_ORD_row_major] = "row_major", + [DW_ORD_col_major] = "col_major" + }; + + if (code < sizeof (known) / sizeof (known[0])) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_discr_list_string (unsigned int code) +{ + static const char *known[] = + { + [DW_DSC_label] = "label", + [DW_DSC_range] = "range" + }; + + if (code < sizeof (known) / sizeof (known[0])) + return known[code]; + + return "???"; +} + + +static void +print_ops (Dwarf *dbg, int indent, int indentrest, + unsigned int addrsize, Dwarf_Word len, const unsigned char *data) +{ + static const char *known[] = + { + [DW_OP_addr] = "addr", + [DW_OP_deref] = "deref", + [DW_OP_const1u] = "const1u", + [DW_OP_const1s] = "const1s", + [DW_OP_const2u] = "const2u", + [DW_OP_const2s] = "const2s", + [DW_OP_const4u] = "const4u", + [DW_OP_const4s] = "const4s", + [DW_OP_const8u] = "const8u", + [DW_OP_const8s] = "const8s", + [DW_OP_constu] = "constu", + [DW_OP_consts] = "consts", + [DW_OP_dup] = "dup", + [DW_OP_drop] = "drop", + [DW_OP_over] = "over", + [DW_OP_pick] = "pick", + [DW_OP_swap] = "swap", + [DW_OP_rot] = "rot", + [DW_OP_xderef] = "xderef", + [DW_OP_abs] = "abs", + [DW_OP_and] = "and", + [DW_OP_div] = "div", + [DW_OP_minus] = "minus", + [DW_OP_mod] = "mod", + [DW_OP_mul] = "mul", + [DW_OP_neg] = "neg", + [DW_OP_not] = "not", + [DW_OP_or] = "or", + [DW_OP_plus] = "plus", + [DW_OP_plus_uconst] = "plus_uconst", + [DW_OP_shl] = "shl", + [DW_OP_shr] = "shr", + [DW_OP_shra] = "shra", + [DW_OP_xor] = "xor", + [DW_OP_bra] = "bra", + [DW_OP_eq] = "eq", + [DW_OP_ge] = "ge", + [DW_OP_gt] = "gt", + [DW_OP_le] = "le", + [DW_OP_lt] = "lt", + [DW_OP_ne] = "ne", + [DW_OP_skip] = "skip", + [DW_OP_lit0] = "lit0", + [DW_OP_lit1] = "lit1", + [DW_OP_lit2] = "lit2", + [DW_OP_lit3] = "lit3", + [DW_OP_lit4] = "lit4", + [DW_OP_lit5] = "lit5", + [DW_OP_lit6] = "lit6", + [DW_OP_lit7] = "lit7", + [DW_OP_lit8] = "lit8", + [DW_OP_lit9] = "lit9", + [DW_OP_lit10] = "lit10", + [DW_OP_lit11] = "lit11", + [DW_OP_lit12] = "lit12", + [DW_OP_lit13] = "lit13", + [DW_OP_lit14] = "lit14", + [DW_OP_lit15] = "lit15", + [DW_OP_lit16] = "lit16", + [DW_OP_lit17] = "lit17", + [DW_OP_lit18] = "lit18", + [DW_OP_lit19] = "lit19", + [DW_OP_lit20] = "lit20", + [DW_OP_lit21] = "lit21", + [DW_OP_lit22] = "lit22", + [DW_OP_lit23] = "lit23", + [DW_OP_lit24] = "lit24", + [DW_OP_lit25] = "lit25", + [DW_OP_lit26] = "lit26", + [DW_OP_lit27] = "lit27", + [DW_OP_lit28] = "lit28", + [DW_OP_lit29] = "lit29", + [DW_OP_lit30] = "lit30", + [DW_OP_lit31] = "lit31", + [DW_OP_reg0] = "reg0", + [DW_OP_reg1] = "reg1", + [DW_OP_reg2] = "reg2", + [DW_OP_reg3] = "reg3", + [DW_OP_reg4] = "reg4", + [DW_OP_reg5] = "reg5", + [DW_OP_reg6] = "reg6", + [DW_OP_reg7] = "reg7", + [DW_OP_reg8] = "reg8", + [DW_OP_reg9] = "reg9", + [DW_OP_reg10] = "reg10", + [DW_OP_reg11] = "reg11", + [DW_OP_reg12] = "reg12", + [DW_OP_reg13] = "reg13", + [DW_OP_reg14] = "reg14", + [DW_OP_reg15] = "reg15", + [DW_OP_reg16] = "reg16", + [DW_OP_reg17] = "reg17", + [DW_OP_reg18] = "reg18", + [DW_OP_reg19] = "reg19", + [DW_OP_reg20] = "reg20", + [DW_OP_reg21] = "reg21", + [DW_OP_reg22] = "reg22", + [DW_OP_reg23] = "reg23", + [DW_OP_reg24] = "reg24", + [DW_OP_reg25] = "reg25", + [DW_OP_reg26] = "reg26", + [DW_OP_reg27] = "reg27", + [DW_OP_reg28] = "reg28", + [DW_OP_reg29] = "reg29", + [DW_OP_reg30] = "reg30", + [DW_OP_reg31] = "reg31", + [DW_OP_breg0] = "breg0", + [DW_OP_breg1] = "breg1", + [DW_OP_breg2] = "breg2", + [DW_OP_breg3] = "breg3", + [DW_OP_breg4] = "breg4", + [DW_OP_breg5] = "breg5", + [DW_OP_breg6] = "breg6", + [DW_OP_breg7] = "breg7", + [DW_OP_breg8] = "breg8", + [DW_OP_breg9] = "breg9", + [DW_OP_breg10] = "breg10", + [DW_OP_breg11] = "breg11", + [DW_OP_breg12] = "breg12", + [DW_OP_breg13] = "breg13", + [DW_OP_breg14] = "breg14", + [DW_OP_breg15] = "breg15", + [DW_OP_breg16] = "breg16", + [DW_OP_breg17] = "breg17", + [DW_OP_breg18] = "breg18", + [DW_OP_breg19] = "breg19", + [DW_OP_breg20] = "breg20", + [DW_OP_breg21] = "breg21", + [DW_OP_breg22] = "breg22", + [DW_OP_breg23] = "breg23", + [DW_OP_breg24] = "breg24", + [DW_OP_breg25] = "breg25", + [DW_OP_breg26] = "breg26", + [DW_OP_breg27] = "breg27", + [DW_OP_breg28] = "breg28", + [DW_OP_breg29] = "breg29", + [DW_OP_breg30] = "breg30", + [DW_OP_breg31] = "breg31", + [DW_OP_regx] = "regx", + [DW_OP_fbreg] = "fbreg", + [DW_OP_bregx] = "bregx", + [DW_OP_piece] = "piece", + [DW_OP_deref_size] = "deref_size", + [DW_OP_xderef_size] = "xderef_size", + [DW_OP_nop] = "nop", + [DW_OP_push_object_address] = "push_object_address", + [DW_OP_call2] = "call2", + [DW_OP_call4] = "call4", + [DW_OP_call_ref] = "call_ref", + }; + + Dwarf_Word offset = 0; + while (len-- > 0) + { + size_t op = *data++; + + switch (op) + { + case DW_OP_call_ref: + case DW_OP_addr:; + /* Address operand. */ + Dwarf_Word addr; + if (addrsize == 4) + addr = read_4ubyte_unaligned (dbg, data); + else + { + assert (addrsize == 8); + addr = read_8ubyte_unaligned (dbg, data); + } + data += addrsize; + len -= addrsize; + + printf ("%*s[%4" PRIuMAX "] %s %" PRIuMAX "\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", (uintmax_t) addr); + offset += 1 + addrsize; + break; + + case DW_OP_deref_size: /* XXX Correct? */ + case DW_OP_xderef_size: /* XXX Correct? */ + case DW_OP_pick: + case DW_OP_const1u: + printf ("%*s[%4" PRIuMAX "] %s %" PRIu8 "\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", *((uint8_t *) data)); + ++data; + --len; + offset += 2; + break; + + case DW_OP_const2u: + printf ("%*s[%4" PRIuMAX "] %s %" PRIu16 "\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", read_2ubyte_unaligned (dbg, data)); + len -= 2; + data += 2; + offset += 3; + break; + + case DW_OP_const4u: + printf ("%*s[%4" PRIuMAX "] %s %" PRIu32 "\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", read_4ubyte_unaligned (dbg, data)); + len -= 4; + data += 4; + offset += 5; + break; + + case DW_OP_const8u: + printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 "\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", read_8ubyte_unaligned (dbg, data)); + len -= 8; + data += 8; + offset += 9; + break; + + case DW_OP_const1s: + printf ("%*s[%4" PRIuMAX "] %s %" PRId8 "\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", *((int8_t *) data)); + ++data; + --len; + offset += 2; + break; + + case DW_OP_const2s: + printf ("%*s[%4" PRIuMAX "] %s %" PRId16 "\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", read_2sbyte_unaligned (dbg, data)); + len -= 2; + data += 2; + offset += 3; + break; + + case DW_OP_const4s: + printf ("%*s[%4" PRIuMAX "] %s %" PRId32 "\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", read_4sbyte_unaligned (dbg, data)); + len -= 4; + data += 4; + offset += 5; + break; + + case DW_OP_const8s: + printf ("%*s[%4" PRIuMAX "] %s %" PRId64 "\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", read_8sbyte_unaligned (dbg, data)); + len -= 8; + data += 8; + offset += 9; + break; + + case DW_OP_piece: /* XXX Correct? */ + case DW_OP_regx: + case DW_OP_plus_uconst: + case DW_OP_constu:; + const unsigned char *start = data; + unsigned int uleb; + get_uleb128 (uleb, data); + printf ("%*s[%4" PRIuMAX "] %s %u\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", uleb); + len -= data - start; + offset += 1 + (data - start); + break; + + case DW_OP_fbreg: + case DW_OP_breg0 ... DW_OP_breg31: + case DW_OP_consts: + start = data; + unsigned int sleb; + get_sleb128 (sleb, data); + printf ("%*s[%4" PRIuMAX "] %s %d\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", sleb); + len -= data - start; + offset += 1 + (data - start); + break; + + case DW_OP_bregx: + start = data; + get_uleb128 (uleb, data); + get_sleb128 (sleb, data); + printf ("%*s[%4" PRIuMAX "] %s %u %d\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", uleb, sleb); + len -= data - start; + offset += 1 + (data - start); + break; + + case DW_OP_call2: + case DW_OP_call4: + case DW_OP_skip: + case DW_OP_bra: + printf ("%*s[%4" PRIuMAX "] %s %" PRIuMAX "\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???", + (uintmax_t) (offset + read_2sbyte_unaligned (dbg, data))); + len -= 2; + data += 2; + offset += 3; + break; + + default: + /* No Operand. */ + printf ("%*s[%4" PRIuMAX "] %s\n", + indent, "", (uintmax_t) offset, + known[op] ?: "???"); + ++offset; + break; + } + + indent = indentrest; + } +} + + +static void +print_debug_abbrev_section (Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn __attribute__ ((unused)), + GElf_Shdr *shdr, Dwarf *dbg) +{ + printf (gettext ("\nDWARF section '%s' at offset %#" PRIx64 ":\n" + " [ Code]\n"), + ".debug_abbrev", (uint64_t) shdr->sh_offset); + + Dwarf_Off offset = 0; + while (offset < shdr->sh_size) + { + printf (gettext ("\nAbbreviation section at offset %" PRIu64 ":\n"), + offset); + + while (1) + { + size_t length; + Dwarf_Abbrev abbrev; + + int res = dwarf_offabbrev (dbg, offset, &length, &abbrev); + if (res != 0) + { + if (res < 0) + { + printf (gettext ("\ + *** error while reading abbreviation: %s\n"), + dwarf_errmsg (-1)); + return; + } + + /* This is the NUL byte at the end of the section. */ + ++offset; + break; + } + + /* We know these calls can never fail. */ + unsigned int code = dwarf_getabbrevcode (&abbrev); + unsigned int tag = dwarf_getabbrevtag (&abbrev); + int has_children = dwarf_abbrevhaschildren (&abbrev); + + printf (gettext (" [%5u] offset: %" PRId64 + ", children: %s, tag: %s\n"), + code, (int64_t) offset, + has_children ? gettext ("yes") : gettext ("no"), + dwarf_tag_string (tag)); + + size_t cnt = 0; + unsigned int name; + unsigned int form; + Dwarf_Off enoffset; + while (dwarf_getabbrevattr (&abbrev, cnt, + &name, &form, &enoffset) == 0) + { + printf (" attr: %s, form: %s, offset: %#" PRIx64 "\n", + dwarf_attr_string (name), dwarf_form_string (form), + (uint64_t) enoffset); + + ++cnt; + } + + offset += length; + } + } +} + + +/* Print content of DWARF .debug_aranges section. We fortunately do + not have to know a bit about the structure of the section, libdwarf + takes care of it. */ +static void +print_debug_aranges_section (Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn __attribute__ ((unused)), + GElf_Shdr *shdr, Dwarf *dbg) +{ + Dwarf_Aranges *aranges; + size_t cnt; + if (dwarf_getaranges (dbg, &aranges, &cnt) != 0) + { + error (0, 0, gettext ("cannot get .debug_aranges content: %s"), + dwarf_errmsg (-1)); + return; + } + + printf (ngettext ("\ +\nDWARF section '%s' at offset %#" PRIx64 " contains %zu entry:\n", + "\ +\nDWARF section '%s' at offset %#" PRIx64 " contains %zu entries:\n", + cnt), + ".debug_aranges", (uint64_t) shdr->sh_offset, cnt); + + /* Compute floor(log16(cnt)). */ + size_t tmp = cnt; + int digits = 1; + while (tmp >= 16) + { + ++digits; + tmp >>= 4; + } + + for (size_t n = 0; n < cnt; ++n) + { + Dwarf_Arange *runp = dwarf_onearange (aranges, n); + if (runp == NULL) + { + printf ("cannot get arange %zu: %s\n", n, dwarf_errmsg (-1)); + return; + } + + Dwarf_Addr start; + Dwarf_Word length; + Dwarf_Off offset; + + if (dwarf_getarangeinfo (runp, &start, &length, &offset) != 0) + printf (gettext (" [%*zu] ???\n"), digits, n); + else + printf (gettext (" [%*zu] start: %0#*" PRIx64 + ", length: %5" PRIu64 ", CU DIE offset: %6" + PRId64 "\n"), + digits, n, ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 10 : 18, + (uint64_t) start, (uint64_t) length, (int64_t) offset); + } +} + +/* Print content of DWARF .debug_ranges section. */ +static void +print_debug_ranges_section (Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr, + Dwarf *dbg) +{ + Elf_Data *data = elf_rawdata (scn, NULL); + + if (data == NULL) + { + error (0, 0, gettext ("cannot get .debug_ranges content: %s"), + elf_errmsg (-1)); + return; + } + + printf (gettext ("\ +\nDWARF section '%s' at offset %#" PRIx64 ":\n"), + ".debug_ranges", (uint64_t) shdr->sh_offset); + + size_t address_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; + + bool first = true; + unsigned char *readp = data->d_buf; + while (readp < (unsigned char *) data->d_buf + data->d_size) + { + ptrdiff_t offset = readp - (unsigned char *) data->d_buf; + + if (data->d_size - offset < address_size * 2) + { + printf (" [%6tx] <INVALID DATA>\n", offset); + break; + } + + Dwarf_Addr begin; + Dwarf_Addr end; + if (address_size == 8) + { + begin = read_8ubyte_unaligned_inc (dbg, readp); + end = read_8ubyte_unaligned_inc (dbg, readp); + } + else + { + begin = read_4ubyte_unaligned_inc (dbg, readp); + end = read_4ubyte_unaligned_inc (dbg, readp); + if (begin == (Dwarf_Addr) (uint32_t) -1) + begin = (Dwarf_Addr) -1l; + } + + if (begin == (Dwarf_Addr) -1l) /* Base address entry. */ + printf (" [%6tx] base address %#0*" PRIxMAX "\n", offset, + 2 + (int) (address_size * 2), (uintmax_t) end); + else if (begin == 0 && end == 0) /* End of list entry. */ + first = true; + else + { + /* We have an address range entry. */ + if (first) /* First address range entry in a list. */ + printf (" [%6tx] %#0*" PRIxMAX "..%#0*" PRIxMAX "\n", offset, + 2 + (int) (address_size * 2), (uintmax_t) begin, + 2 + (int) (address_size * 2), (uintmax_t) end); + else + printf (" %#0*" PRIxMAX "..%#0*" PRIxMAX "\n", + 2 + (int) (address_size * 2), (uintmax_t) begin, + 2 + (int) (address_size * 2), (uintmax_t) end); + + first = false; + } + } +} + + +static void +print_debug_frame_section (Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn __attribute__ ((unused)), + GElf_Shdr *shdr __attribute__ ((unused)), + Dwarf *dbg __attribute__ ((unused))) +{ +} + + +struct attrcb_args +{ + Dwarf *dbg; + int level; + unsigned int addrsize; + Dwarf_Off cu_offset; +}; + + +static int +attr_callback (Dwarf_Attribute *attrp, void *arg) +{ + struct attrcb_args *cbargs = (struct attrcb_args *) arg; + const int level = cbargs->level; + + unsigned int attr = dwarf_whatattr (attrp); + if (unlikely (attr == 0)) + { + error (0, 0, gettext ("cannot get attribute code: %s"), + dwarf_errmsg (-1)); + return DWARF_CB_ABORT; + } + + unsigned int form = dwarf_whatform (attrp); + if (unlikely (form == 0)) + { + error (0, 0, gettext ("cannot get attribute form: %s"), + dwarf_errmsg (-1)); + return DWARF_CB_ABORT; + } + + switch (form) + { + case DW_FORM_addr:; + Dwarf_Addr addr; + if (unlikely (dwarf_formaddr (attrp, &addr) != 0)) + { + attrval_out: + error (0, 0, gettext ("cannot get attribute value: %s"), + dwarf_errmsg (-1)); + return DWARF_CB_ABORT; + } + printf (" %*s%-20s %#0*" PRIxMAX "\n", + (int) (level * 2), "", dwarf_attr_string (attr), + 2 + (int) (cbargs->addrsize * 2), (uintmax_t) addr); + break; + + case DW_FORM_indirect: + case DW_FORM_strp: + case DW_FORM_string:; + const char *str = dwarf_formstring (attrp); + if (unlikely (str == NULL)) + goto attrval_out; + printf (" %*s%-20s \"%s\"\n", + (int) (level * 2), "", dwarf_attr_string (attr), str); + break; + + case DW_FORM_ref_addr: + case DW_FORM_ref_udata: + case DW_FORM_ref8: + case DW_FORM_ref4: + case DW_FORM_ref2: + case DW_FORM_ref1:; + Dwarf_Off ref; + if (unlikely (dwarf_formref (attrp, &ref) != 0)) + goto attrval_out; + + printf (" %*s%-20s [%6" PRIxMAX "]\n", + (int) (level * 2), "", dwarf_attr_string (attr), + (uintmax_t) (ref + cbargs->cu_offset)); + break; + + case DW_FORM_udata: + case DW_FORM_sdata: + case DW_FORM_data8: + case DW_FORM_data4: + case DW_FORM_data2: + case DW_FORM_data1:; + Dwarf_Word num; + if (unlikely (dwarf_formudata (attrp, &num) != 0)) + goto attrval_out; + + const char *valuestr = NULL; + switch (attr) + { + case DW_AT_location: + case DW_AT_data_member_location: + case DW_AT_vtable_elem_location: + case DW_AT_string_length: + case DW_AT_use_location: + case DW_AT_frame_base: + case DW_AT_return_addr: + case DW_AT_static_link: + printf (" %*s%-20s location list [%6" PRIxMAX "]\n", + (int) (level * 2), "", dwarf_attr_string (attr), + (uintmax_t) num); + return DWARF_CB_OK; + + case DW_AT_ranges: + printf (" %*s%-20s range list [%6" PRIxMAX "]\n", + (int) (level * 2), "", dwarf_attr_string (attr), + (uintmax_t) num); + return DWARF_CB_OK; + + case DW_AT_language: + valuestr = dwarf_lang_string (num); + break; + case DW_AT_encoding: + valuestr = dwarf_encoding_string (num); + break; + case DW_AT_accessibility: + valuestr = dwarf_access_string (num); + break; + case DW_AT_visibility: + valuestr = dwarf_visibility_string (num); + break; + case DW_AT_virtuality: + valuestr = dwarf_virtuality_string (num); + break; + case DW_AT_identifier_case: + valuestr = dwarf_identifier_case_string (num); + break; + case DW_AT_calling_convention: + valuestr = dwarf_calling_convention_string (num); + break; + case DW_AT_inline: + valuestr = dwarf_inline_string (num); + break; + case DW_AT_ordering: + valuestr = dwarf_ordering_string (num); + break; + case DW_AT_discr_list: + valuestr = dwarf_discr_list_string (num); + break; + default: + /* Nothing. */ + break; + } + + if (valuestr == NULL) + printf (" %*s%-20s %" PRIuMAX "\n", + (int) (level * 2), "", dwarf_attr_string (attr), + (uintmax_t) num); + else + printf (" %*s%-20s %s (%" PRIuMAX ")\n", + (int) (level * 2), "", dwarf_attr_string (attr), + valuestr, (uintmax_t) num); + break; + + case DW_FORM_flag:; + bool flag; + if (unlikely (dwarf_formflag (attrp, &flag) != 0)) + goto attrval_out; + + printf (" %*s%-20s %s\n", + (int) (level * 2), "", dwarf_attr_string (attr), + nl_langinfo (flag ? YESSTR : NOSTR)); + break; + + case DW_FORM_block4: + case DW_FORM_block2: + case DW_FORM_block1: + case DW_FORM_block:; + Dwarf_Block block; + if (unlikely (dwarf_formblock (attrp, &block) != 0)) + goto attrval_out; + + printf (" %*s%-20s %" PRIxMAX " byte block\n", + (int) (level * 2), "", dwarf_attr_string (attr), + (uintmax_t) block.length); + + switch (attr) + { + case DW_AT_location: + case DW_AT_data_member_location: + case DW_AT_vtable_elem_location: + case DW_AT_string_length: + case DW_AT_use_location: + case DW_AT_frame_base: + case DW_AT_return_addr: + case DW_AT_static_link: + print_ops (cbargs->dbg, 12 + level * 2, 12 + level * 2, + cbargs->addrsize, block.length, block.data); + break; + } + break; + + default: + printf (" %*s%-20s [form: %d] ???\n", + (int) (level * 2), "", dwarf_attr_string (attr), + (int) form); + break; + } + + return DWARF_CB_OK; +} + + +static void +print_debug_info_section (Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn __attribute__ ((unused)), + GElf_Shdr *shdr, Dwarf *dbg) +{ + printf (gettext ("\ +\nDWARF section '%s' at offset %#" PRIx64 ":\n [Offset]\n"), + ".debug_info", (uint64_t) shdr->sh_offset); + + /* If the section is empty we don't have to do anything. */ + if (shdr->sh_size == 0) + return; + + int maxdies = 20; + Dwarf_Die *dies = (Dwarf_Die *) xmalloc (maxdies * sizeof (Dwarf_Die)); + + Dwarf_Off offset = 0; + + /* New compilation unit. */ + size_t cuhl; + //Dwarf_Half version; + Dwarf_Off abbroffset; + uint8_t addrsize; + uint8_t offsize; + Dwarf_Off nextcu; + next_cu: + if (dwarf_nextcu (dbg, offset, &nextcu, &cuhl, &abbroffset, &addrsize, + &offsize) != 0) + goto do_return; + + printf (gettext (" Compilation unit at offset %" PRIu64 ":\n" + " Version: %" PRIu16 ", Abbreviation section offset: %" + PRIu64 ", Address size: %" PRIu8 ", Offset size: %" PRIu8 "\n"), + (uint64_t) offset, /*version*/2, abbroffset, addrsize, offsize); + + + struct attrcb_args args; + args.dbg = dbg; + args.addrsize = addrsize; + args.cu_offset = offset; + + offset += cuhl; + + int level = 0; + + if (unlikely (dwarf_offdie (dbg, offset, &dies[level]) == NULL)) + { + error (0, 0, gettext ("cannot get DIE at offset %" PRIu64 + " in section '%s': %s"), + (uint64_t) offset, ".debug_info", dwarf_errmsg (-1)); + goto do_return; + } + + do + { + offset = dwarf_dieoffset (&dies[level]); + if (offset == ~0ul) + { + error (0, 0, gettext ("cannot get DIE offset: %s"), + dwarf_errmsg (-1)); + goto do_return; + } + + int tag = dwarf_tag (&dies[level]); + if (tag == DW_TAG_invalid) + { + error (0, 0, gettext ("cannot get tag of DIE at offset %" PRIu64 + " in section '%s': %s"), + (uint64_t) offset, ".debug_info", dwarf_errmsg (-1)); + goto do_return; + } + +#if 1 + const char *tagstr = dwarf_tag_string (tag); +#else + static const char *const lowtags[] = + { + [DW_TAG_array_type] = "array_type", + [DW_TAG_class_type] = "class_type", + [DW_TAG_entry_point] = "entry_point", + [DW_TAG_enumeration_type] = "enumeration_type", + [DW_TAG_formal_parameter] = "formal_parameter", + [DW_TAG_imported_declaration] = "imported_declaration", + [DW_TAG_label] = "label", + [DW_TAG_lexical_block] = "lexical_block", + [DW_TAG_member] = "member", + [DW_TAG_pointer_type] = "pointer_type", + [DW_TAG_reference_type] = "reference_type", + [DW_TAG_compile_unit] = "compile_unit", + [DW_TAG_string_type] = "string_type", + [DW_TAG_structure_type] = "structure_type", + [DW_TAG_subroutine_type] = "subroutine_type", + [DW_TAG_typedef] = "typedef", + [DW_TAG_union_type] = "union_type", + [DW_TAG_unspecified_parameters] = "unspecified_parameters", + [DW_TAG_variant] = "variant", + [DW_TAG_common_block] = "common_block", + [DW_TAG_common_inclusion] = "common_inclusion", + [DW_TAG_inheritance] = "inheritance", + [DW_TAG_inlined_subroutine] = "inlined_subroutine", + [DW_TAG_module] = "module", + [DW_TAG_ptr_to_member_type] = "ptr_to_member_type", + [DW_TAG_set_type] = "set_type", + [DW_TAG_subrange_type] = "subrange_type", + [DW_TAG_with_stmt] = "with_stmt", + [DW_TAG_access_declaration] = "access_declaration", + [DW_TAG_base_type] = "base_type", + [DW_TAG_catch_block] = "catch_block", + [DW_TAG_const_type] = "const_type", + [DW_TAG_constant] = "constant", + [DW_TAG_enumerator] = "enumerator", + [DW_TAG_file_type] = "file_type", + [DW_TAG_friend] = "friend", + [DW_TAG_namelist] = "namelist", + [DW_TAG_namelist_item] = "namelist_item", + [DW_TAG_packed_type] = "packed_type", + [DW_TAG_subprogram] = "subprogram", + [DW_TAG_template_type_param] = "template_type_param", + [DW_TAG_template_value_param] = "template_value_param", + [DW_TAG_thrown_type] = "thrown_type", + [DW_TAG_try_block] = "try_block", + [DW_TAG_variant_part] = "variant_part", + [DW_TAG_variable] = "variable", + [DW_TAG_volatile_type] = "volatile_type" + }; + + const char *tagstr; + switch (tag) + { + case DW_TAG_lo_user: + tagstr = "lo_user"; + break; + + case DW_TAG_MIPS_loop: + tagstr = "MIPS_loop"; + break; + + case DW_TAG_format_label: + tagstr = "format_label"; + break; + + case DW_TAG_function_template: + tagstr = "function_template"; + break; + + case DW_TAG_class_template: + tagstr = "class_template"; + break; + case DW_TAG_hi_user: + tagstr = "hi_user"; + break; + + default: + if (tag < (int) (sizeof (lowtags) / sizeof (lowtags[0]))) + tagstr = lowtags[tag]; + else + tagstr = "???"; + break; + } +#endif + + printf (" [%6" PRIx64 "] %*s%s\n", + (uint64_t) offset, (int) (level * 2), "", tagstr); + + /* Print the attribute values. */ + args.level = level; + (void) dwarf_getattrs (&dies[level], attr_callback, &args, 0); + + /* Make room for the next level's DIE. */ + if (level + 1 == maxdies) + dies = (Dwarf_Die *) xrealloc (dies, + (maxdies += 10) + * sizeof (Dwarf_Die)); + + int res = dwarf_child (&dies[level], &dies[level + 1]); + if (res > 0) + { + while ((res = dwarf_siblingof (&dies[level], &dies[level])) == 1) + if (level-- == 0) + break; + + if (res == -1) + { + error (0, 0, gettext ("cannot get next DIE: %s\n"), + dwarf_errmsg (-1)); + goto do_return; + } + } + else if (unlikely (res < 0)) + { + error (0, 0, gettext ("cannot get next DIE: %s"), + dwarf_errmsg (-1)); + goto do_return; + } + else + ++level; + } + while (level >= 0); + + offset = nextcu; + if (offset != 0) + goto next_cu; + + do_return: + free (dies); +} + + +static void +print_debug_line_section (Ebl *ebl, GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) +{ + printf (gettext ("\ +\nDWARF section '%s' at offset %#" PRIx64 ":\n"), + ".debug_line", (uint64_t) shdr->sh_offset); + + if (shdr->sh_size == 0) + return; + + /* There is no functionality in libdw to read the information in the + way it is represented here. Hardcode the decoder. */ + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL || data->d_buf == NULL) + { + error (0, 0, gettext ("cannot get line data section data: %s"), + elf_errmsg (-1)); + return; + } + + const unsigned char *linep = (const unsigned char *) data->d_buf; + const unsigned char *lineendp; + + while (linep + < (lineendp = (const unsigned char *) data->d_buf + data->d_size)) + { + size_t start_offset = linep - (const unsigned char *) data->d_buf; + + printf (gettext ("\nTable at offset %Zu:\n"), start_offset); + + Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, linep); + unsigned int length = 4; + if (unlikely (unit_length == 0xffffffff)) + { + if (unlikely (linep + 8 > lineendp)) + { + invalid_data: + error (0, 0, gettext ("invalid data in section [%zu] '%s'"), + elf_ndxscn (scn), ".debug_line"); + return; + } + unit_length = read_8ubyte_unaligned_inc (dbg, linep); + length = 8; + } + + /* Check whether we have enough room in the section. */ + if (unit_length < 2 + length + 5 * 1 + || unlikely (linep + unit_length > lineendp)) + goto invalid_data; + lineendp = linep + unit_length; + + /* The next element of the header is the version identifier. */ + uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep); + + /* Next comes the header length. */ + Dwarf_Word header_length; + if (length == 4) + header_length = read_4ubyte_unaligned_inc (dbg, linep); + else + header_length = read_8ubyte_unaligned_inc (dbg, linep); + //const unsigned char *header_start = linep; + + /* Next the minimum instruction length. */ + uint_fast8_t minimum_instr_len = *linep++; + + /* Then the flag determining the default value of the is_stmt + register. */ + uint_fast8_t default_is_stmt = *linep++; + + /* Now the line base. */ + int_fast8_t line_base = *((const int_fast8_t *) linep); + ++linep; + + /* And the line range. */ + uint_fast8_t line_range = *linep++; + + /* The opcode base. */ + uint_fast8_t opcode_base = *linep++; + + /* Print what we got so far. */ + printf (gettext ("\n" + " Length: %" PRIu64 "\n" + " DWARF version: %" PRIuFAST16 "\n" + " Prologue length: %" PRIu64 "\n" + " Minimum instruction length: %" PRIuFAST8 "\n" + " Initial value if '%s': %" PRIuFAST8 "\n" + " Line base: %" PRIdFAST8 "\n" + " Line range: %" PRIuFAST8 "\n" + " Opcode base: %" PRIuFAST8 "\n" + "\n" + "Opcodes:\n"), + (uint64_t) unit_length, version, (uint64_t) header_length, + minimum_instr_len, "is_stmt", default_is_stmt, line_base, + line_range, opcode_base); + + if (unlikely (linep + opcode_base - 1 >= lineendp)) + goto invalid_data; + int opcode_base_l10 = 1; + unsigned int tmp = opcode_base; + while (tmp > 10) + { + tmp /= 10; + ++opcode_base_l10; + } + const uint8_t *standard_opcode_lengths = linep - 1; + for (uint_fast8_t cnt = 1; cnt < opcode_base; ++cnt) + printf (ngettext (" [%*" PRIuFAST8 "] %hhu argument\n", + " [%*" PRIuFAST8 "] %hhu arguments\n", + (int) linep[cnt - 1]), + opcode_base_l10, cnt, linep[cnt - 1]); + linep += opcode_base - 1; + if (unlikely (linep >= lineendp)) + goto invalid_data; + + puts (gettext ("\nDirectory table:")); + while (*linep != 0) + { + unsigned char *endp = memchr (linep, '\0', lineendp - linep); + if (endp == NULL) + goto invalid_data; + + printf (" %s\n", (char *) linep); + + linep = endp + 1; + } + /* Skip the final NUL byte. */ + ++linep; + + if (unlikely (linep >= lineendp)) + goto invalid_data; + puts (gettext ("\nFile name table:\n" + " Entry Dir Time Size Name")); + for (unsigned int cnt = 1; *linep != 0; ++cnt) + { + /* First comes the file name. */ + char *fname = (char *) linep; + unsigned char *endp = memchr (fname, '\0', lineendp - linep); + if (endp == NULL) + goto invalid_data; + linep = endp + 1; + + /* Then the index. */ + unsigned int diridx; + get_uleb128 (diridx, linep); + + /* Next comes the modification time. */ + unsigned int mtime; + get_uleb128 (mtime, linep); + + /* Finally the length of the file. */ + unsigned int fsize; + get_uleb128 (fsize, linep); + + printf (" %-5u %-5u %-9u %-9u %s\n", + cnt, diridx, mtime, fsize, fname); + } + /* Skip the final NUL byte. */ + ++linep; + + puts (gettext ("\nLine number statements:")); + Dwarf_Word address = 0; + size_t line = 1; + uint_fast8_t is_stmt = default_is_stmt; + + /* Default address value, in case we do not find the CU. */ + size_t address_size + = elf_getident (ebl->elf, NULL)[EI_CLASS] == ELFCLASS32 ? 4 : 8; + + /* Determine the CU this block is for. */ + Dwarf_Off cuoffset; + Dwarf_Off ncuoffset = 0; + size_t hsize; + while (dwarf_nextcu (dbg, cuoffset = ncuoffset, &ncuoffset, &hsize, + NULL, NULL, NULL) == 0) + { + Dwarf_Die cudie; + if (dwarf_offdie (dbg, cuoffset + hsize, &cudie) == NULL) + continue; + Dwarf_Attribute stmt_list; + if (dwarf_attr (&cudie, DW_AT_stmt_list, &stmt_list) == NULL) + continue; + Dwarf_Word lineoff; + if (dwarf_formudata (&stmt_list, &lineoff) != 0) + continue; + if (lineoff == start_offset) + { + /* Found the CU. */ + address_size = cudie.cu->address_size; + break; + } + } + + while (linep < lineendp) + { + unsigned int u128; + int s128; + + /* Read the opcode. */ + unsigned int opcode = *linep++; + + /* Is this a special opcode? */ + if (likely (opcode >= opcode_base)) + { + /* Yes. Handling this is quite easy since the opcode value + is computed with + + opcode = (desired line increment - line_base) + + (line_range * address advance) + opcode_base + */ + int line_increment = (line_base + + (opcode - opcode_base) % line_range); + unsigned int address_increment = (minimum_instr_len + * ((opcode - opcode_base) + / line_range)); + + /* Perform the increments. */ + line += line_increment; + address += address_increment; + + printf (gettext ("\ + special opcode %u: address+%u = %#" PRIx64 ", line%+d = %zu\n"), + opcode, address_increment, (uint64_t) address, + line_increment, line); + } + else if (opcode == 0) + { + /* This an extended opcode. */ + if (unlikely (linep + 2 > lineendp)) + goto invalid_data; + + /* The length. */ + unsigned int len = *linep++; + + if (unlikely (linep + len > lineendp)) + goto invalid_data; + + /* The sub-opcode. */ + opcode = *linep++; + + printf (gettext (" extended opcode %u: "), opcode); + + switch (opcode) + { + case DW_LNE_end_sequence: + puts (gettext ("end of sequence")); + + /* Reset the registers we care about. */ + address = 0; + line = 1; + is_stmt = default_is_stmt; + break; + + case DW_LNE_set_address: + if (address_size == 4) + address = read_4ubyte_unaligned_inc (dbg, linep); + else + address = read_8ubyte_unaligned_inc (dbg, linep); + printf (gettext ("set address to %#" PRIx64 "\n"), + (uint64_t) address); + break; + + case DW_LNE_define_file: + { + char *fname = (char *) linep; + unsigned char *endp = memchr (linep, '\0', + lineendp - linep); + if (endp == NULL) + goto invalid_data; + linep = endp + 1; + + unsigned int diridx; + get_uleb128 (diridx, linep); + Dwarf_Word mtime; + get_uleb128 (mtime, linep); + Dwarf_Word filelength; + get_uleb128 (filelength, linep); + + printf (gettext ("\ +define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"), + diridx, (uint64_t) mtime, (uint64_t) filelength, + fname); + } + break; + + default: + /* Unknown, ignore it. */ + puts (gettext ("unknown opcode")); + linep += len - 1; + break; + } + } + else if (opcode <= DW_LNS_set_epilog_begin) + { + /* This is a known standard opcode. */ + switch (opcode) + { + case DW_LNS_copy: + /* Takes no argument. */ + puts (gettext (" copy")); + break; + + case DW_LNS_advance_pc: + /* Takes one uleb128 parameter which is added to the + address. */ + get_uleb128 (u128, linep); + address += minimum_instr_len * u128; + printf (gettext ("\ + advance address by %u to %#" PRIx64 "\n"), + u128, (uint64_t) address); + break; + + case DW_LNS_advance_line: + /* Takes one sleb128 parameter which is added to the + line. */ + get_sleb128 (s128, linep); + line += s128; + printf (gettext ("\ + advance line by constant %d to %" PRId64 "\n"), + s128, (int64_t) line); + break; + + case DW_LNS_set_file: + /* Takes one uleb128 parameter which is stored in file. */ + get_uleb128 (u128, linep); + printf (gettext (" set file to %" PRIu64 "\n"), + (uint64_t) u128); + break; + + case DW_LNS_set_column: + /* Takes one uleb128 parameter which is stored in column. */ + if (unlikely (standard_opcode_lengths[opcode] != 1)) + goto invalid_data; + + get_uleb128 (u128, linep); + printf (gettext (" set column to %" PRIu64 "\n"), + (uint64_t) u128); + break; + + case DW_LNS_negate_stmt: + /* Takes no argument. */ + is_stmt = 1 - is_stmt; + printf (gettext (" set '%s' to %" PRIuFAST8 "\n"), + "is_stmt", is_stmt); + break; + + case DW_LNS_set_basic_block: + /* Takes no argument. */ + puts (gettext (" set basic block flag")); + break; + + case DW_LNS_const_add_pc: + /* Takes no argument. */ + u128 = (minimum_instr_len + * ((255 - opcode_base) / line_range)); + address += u128; + printf (gettext ("\ + advance address by constant %u to %#" PRIx64 "\n"), + u128, (uint64_t) address); + break; + + case DW_LNS_fixed_advance_pc: + /* Takes one 16 bit parameter which is added to the + address. */ + if (unlikely (standard_opcode_lengths[opcode] != 1)) + goto invalid_data; + + u128 = read_2ubyte_unaligned_inc (dbg, linep); + address += u128; + printf (gettext ("\ + advance address by fixed value %u to %#" PRIx64 "\n"), + u128, (uint64_t) address); + break; + + case DW_LNS_set_prologue_end: + /* Takes no argument. */ + puts (gettext (" set prologue end flag")); + break; + + case DW_LNS_set_epilog_begin: + /* Takes no argument. */ + puts (gettext (" set epilogue begin flag")); + break; + } + } + else + { + /* This is a new opcode the generator but not we know about. + Read the parameters associated with it but then discard + everything. Read all the parameters for this opcode. */ + printf (ngettext (" unknown opcode with %" PRIu8 " parameter:", + " unknown opcode with %" PRIu8 " parameters:", + standard_opcode_lengths[opcode]), + standard_opcode_lengths[opcode]); + for (int n = standard_opcode_lengths[opcode]; n > 0; --n) + { + get_uleb128 (u128, linep); + if (n != standard_opcode_lengths[opcode]) + putc_unlocked (',', stdout); + printf (" %u", u128); + } + + /* Next round, ignore this opcode. */ + continue; + } + } + } + + /* There must only be one data block. */ + assert (elf_getdata (scn, data) == NULL); +} + + +static void +print_debug_loc_section (Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn __attribute__ ((unused)), + GElf_Shdr *shdr, + Dwarf *dbg __attribute__ ((unused))) +{ + Elf_Data *data = elf_rawdata (scn, NULL); + + if (data == NULL) + { + error (0, 0, gettext ("cannot get .debug_loc content: %s"), + elf_errmsg (-1)); + return; + } + + printf (gettext ("\ +\nDWARF section '%s' at offset %#" PRIx64 ":\n"), + ".debug_loc", (uint64_t) shdr->sh_offset); + + size_t address_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; + + bool first = true; + unsigned char *readp = data->d_buf; + while (readp < (unsigned char *) data->d_buf + data->d_size) + { + ptrdiff_t offset = readp - (unsigned char *) data->d_buf; + + if (data->d_size - offset < address_size * 2) + { + printf (" [%6tx] <INVALID DATA>\n", offset); + break; + } + + Dwarf_Addr begin; + Dwarf_Addr end; + if (address_size == 8) + { + begin = read_8ubyte_unaligned_inc (dbg, readp); + end = read_8ubyte_unaligned_inc (dbg, readp); + } + else + { + begin = read_4ubyte_unaligned_inc (dbg, readp); + end = read_4ubyte_unaligned_inc (dbg, readp); + if (begin == (Dwarf_Addr) (uint32_t) -1) + begin = (Dwarf_Addr) -1l; + } + + if (begin == (Dwarf_Addr) -1l) /* Base address entry. */ + printf (" [%6tx] base address %#0*" PRIxMAX "\n", offset, + 2 + (int) (address_size * 2), (uintmax_t) end); + else if (begin == 0 && end == 0) /* End of list entry. */ + first = true; + else + { + /* We have a location expression entry. */ + uint_fast16_t len = read_2ubyte_unaligned_inc (dbg, readp); + + if (first) /* First entry in a list. */ + printf (" [%6tx] %#0*" PRIxMAX "..%#0*" PRIxMAX, + offset, + 2 + (int) (address_size * 2), (uintmax_t) begin, + 2 + (int) (address_size * 2), (uintmax_t) end); + else + printf (" %#0*" PRIxMAX "..%#0*" PRIxMAX, + 2 + (int) (address_size * 2), (uintmax_t) begin, + 2 + (int) (address_size * 2), (uintmax_t) end); + + print_ops (dbg, 1, 18 + (address_size * 4), + address_size, len, readp); + + first = false; + readp += len; + } + } +} + +struct mac_culist +{ + Dwarf_Die die; + Dwarf_Off offset; + Dwarf_Files *files; + struct mac_culist *next; +}; + + +static int +mac_compare (const void *p1, const void *p2) +{ + struct mac_culist *m1 = (struct mac_culist *) p1; + struct mac_culist *m2 = (struct mac_culist *) p2; + + if (m1->offset < m2->offset) + return -1; + if (m1->offset > m2->offset) + return 1; + return 0; +} + + +static void +print_debug_macinfo_section (Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) +{ + printf (gettext ("\ +\nDWARF section '%s' at offset %#" PRIx64 ":\n"), + ".debug_macinfo", (uint64_t) shdr->sh_offset); + putc_unlocked ('\n', stdout); + + /* There is no function in libdw to iterate over the raw content of + the section but it is easy enough to do. */ + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL || data->d_buf == NULL) + { + error (0, 0, gettext ("cannot get macro information section data: %s"), + elf_errmsg (-1)); + return; + } + + /* Get the source file information for all CUs. */ + Dwarf_Off offset; + Dwarf_Off ncu = 0; + size_t hsize; + struct mac_culist *culist = NULL; + size_t nculist = 0; + while (dwarf_nextcu (dbg, offset = ncu, &ncu, &hsize, NULL, NULL, NULL) == 0) + { + Dwarf_Die cudie; + if (dwarf_offdie (dbg, offset + hsize, &cudie) == NULL) + continue; + + Dwarf_Attribute attr; + if (dwarf_attr (&cudie, DW_AT_macro_info, &attr) == NULL) + continue; + + Dwarf_Word macoff; + if (dwarf_formudata (&attr, &macoff) != 0) + continue; + + struct mac_culist *newp = (struct mac_culist *) alloca (sizeof (*newp)); + newp->die = cudie; + newp->offset = macoff; + newp->files = NULL; + newp->next = culist; + culist = newp; + ++nculist; + } + + /* Convert the list into an array for easier consumption. */ + struct mac_culist *cus = (struct mac_culist *) alloca ((nculist + 1) + * sizeof (*cus)); + /* Add sentinel. */ + cus[nculist].offset = data->d_size; + if (nculist > 0) + { + for (size_t cnt = nculist - 1; culist != NULL; --cnt) + { + assert (cnt < nculist); + cus[cnt] = *culist; + culist = culist->next; + } + + /* Sort the array according to the offset in the .debug_macinfo + section. Note we keep the sentinel at the end. */ + qsort (cus, nculist, sizeof (*cus), mac_compare); + } + + const unsigned char *readp = (const unsigned char *) data->d_buf; + const unsigned char *readendp = readp + data->d_size; + int level = 1; + + while (readp < readendp) + { + unsigned int opcode = *readp++; + unsigned int u128; + unsigned int u128_2; + const unsigned char *endp; + + switch (opcode) + { + case DW_MACINFO_define: + case DW_MACINFO_undef: + case DW_MACINFO_vendor_ext: + /* For the first two opcodes the parameters are + line, string + For the latter + number, string. + We can treat these cases together. */ + get_uleb128 (u128, readp); + + endp = memchr (readp, '\0', readendp - readp); + if (endp == NULL) + { + printf (gettext ("\ +%*s*** non-terminated string at end of section"), + level, ""); + return; + } + + if (opcode == DW_MACINFO_define) + printf ("%*s#define %s, line %u\n", + level, "", (char *) readp, u128); + else if (opcode == DW_MACINFO_undef) + printf ("%*s#undef %s, line %u\n", + level, "", (char *) readp, u128); + else + printf (" #vendor-ext %s, number %u\n", (char *) readp, u128); + + readp = endp + 1; + break; + + case DW_MACINFO_start_file: + /* The two parameters are line and file index, in this order. */ + get_uleb128 (u128, readp); + get_uleb128 (u128_2, readp); + + /* Find the CU DIE for this file. */ + size_t macoff = readp - (const unsigned char *) data->d_buf; + const char *fname = "???"; + if (macoff >= cus[0].offset) + { + while (macoff >= cus[1].offset) + ++cus; + + if (cus[0].files == NULL + && dwarf_getsrcfiles (&cus[0].die, &cus[0].files, NULL) != 0) + cus[0].files = (Dwarf_Files *) -1l; + + if (cus[0].files != (Dwarf_Files *) -1l) + fname = (dwarf_filesrc (cus[0].files, u128_2, NULL, NULL) + ?: "???"); + } + + printf ("%*sstart_file %u, [%u] %s\n", + level, "", u128, u128_2, fname); + ++level; + break; + + case DW_MACINFO_end_file: + --level; + printf ("%*send_file\n", level, ""); + /* Nothing more to do. */ + break; + + default: + // XXX gcc seems to generate files with a trailing zero. + if (opcode != 0 || readp != readendp) + printf ("%*s*** invalid opcode %u\n", level, "", opcode); + break; + } + } +} + + +/* Callback for printing global names. */ +static int +print_pubnames (Dwarf *dbg __attribute__ ((unused)), Dwarf_Global *global, + void *arg) +{ + int *np = (int *) arg; + + printf (gettext (" [%5d] DIE offset: %6" PRId64 + ", CU DIE offset: %6" PRId64 ", name: %s\n"), + (*np)++, global->die_offset, global->cu_offset, global->name); + + return 0; +} + + +/* Print the known exported symbols in the DWARF section '.debug_pubnames'. */ +static void +print_debug_pubnames_section (Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn __attribute__ ((unused)), + GElf_Shdr *shdr, Dwarf *dbg) +{ + printf (gettext ("\nDWARF section '%s' at offset %#" PRIx64 ":\n"), + ".debug_pubnames", (uint64_t) shdr->sh_offset); + + int n = 0; + (void) dwarf_getpubnames (dbg, print_pubnames, &n, 0); +} + +/* Print the content of the DWARF string section '.debug_str'. */ +static void +print_debug_str_section (Ebl *ebl __attribute__ ((unused)), + GElf_Ehdr *ehdr __attribute__ ((unused)), + Elf_Scn *scn __attribute__ ((unused)), + GElf_Shdr *shdr, Dwarf *dbg) +{ + /* Compute floor(log16(shdr->sh_size)). */ + GElf_Addr tmp = shdr->sh_size; + int digits = 1; + while (tmp >= 16) + { + ++digits; + tmp >>= 4; + } + digits = MAX (4, digits); + + printf (gettext ("\nDWARF section '%s' at offset %#" PRIx64 ":\n" + " %*s String\n"), + ".debug_str", (uint64_t) shdr->sh_offset, + /* TRANS: the debugstr| prefix makes the string unique. */ + digits + 2, sgettext ("debugstr|Offset")); + + Dwarf_Off offset = 0; + while (offset < shdr->sh_size) + { + size_t len; + const char *str = dwarf_getstring (dbg, offset, &len); + if (str == NULL) + { + printf (gettext (" *** error while reading strings: %s\n"), + dwarf_errmsg (-1)); + break; + } + + printf (" [%*" PRIx64 "] \"%s\"\n", digits, (uint64_t) offset, str); + + offset += len + 1; + } +} + + +static void +print_debug (Ebl *ebl, GElf_Ehdr *ehdr) +{ + /* Find the version information sections. For this we have to + search through the section table. */ + Dwarf *dbg; + Elf_Scn *scn; + size_t shstrndx; + + /* Before we start the real work get a debug context descriptor. */ + dbg = dwarf_begin_elf (ebl->elf, DWARF_C_READ, NULL); + if (dbg == NULL) + { + error (0, 0, gettext ("cannot get debug context descriptor: %s"), + dwarf_errmsg (-1)); + return; + } + + /* Get the section header string table index. */ + if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + scn = NULL; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + /* Handle the section if it is part of the versioning handling. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr != NULL || shdr->sh_type != SHT_PROGBITS) + { + static const struct + { + const char *name; + enum section_e bitmask; + void (*fp) (Ebl *, GElf_Ehdr *, Elf_Scn *, GElf_Shdr *, Dwarf *); + } debug_sections[] = + { +#define NEW_SECTION(name) \ + { ".debug_" #name, section_##name, print_debug_##name##_section } + NEW_SECTION (abbrev), + NEW_SECTION (aranges), + NEW_SECTION (frame), + NEW_SECTION (info), + NEW_SECTION (line), + NEW_SECTION (loc), + NEW_SECTION (pubnames), + NEW_SECTION (str), + NEW_SECTION (macinfo), + NEW_SECTION (ranges), + { ".eh_frame", section_frame, print_debug_frame_section } + }; + const int ndebug_sections = (sizeof (debug_sections) + / sizeof (debug_sections[0])); + const char *name = elf_strptr (ebl->elf, shstrndx, + shdr->sh_name); + int n; + + for (n = 0; n < ndebug_sections; ++n) + if (strcmp (name, debug_sections[n].name) == 0) + { + if (print_debug_sections & debug_sections[n].bitmask) + debug_sections[n].fp (ebl, ehdr, scn, shdr, dbg); + break; + } + } + } + + /* We are done with the DWARF handling. */ + dwarf_end (dbg); +} + + +static void +handle_notes (Ebl *ebl, GElf_Ehdr *ehdr) +{ + int class = gelf_getclass (ebl->elf); + size_t cnt; + + /* We have to look through the program header to find the note + sections. There can be more than one. */ + for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) + { + GElf_Phdr mem; + GElf_Phdr *phdr = gelf_getphdr (ebl->elf, cnt, &mem); + + if (phdr == NULL || phdr->p_type != PT_NOTE) + /* Not what we are looking for. */ + continue; + + printf (gettext ("\ +\nNote segment of %" PRId64 " bytes at offset %#0" PRIx64 ":\n"), + phdr->p_filesz, phdr->p_offset); + + char *notemem = gelf_rawchunk (ebl->elf, phdr->p_offset, phdr->p_filesz); + if (notemem == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot get content of note section: %s"), + elf_errmsg (-1)); + + fputs_unlocked (gettext (" Owner Data size Type\n"), stdout); + + + /* Handle the note section content. It consists of one or more + entries each of which consists of five parts: + + - a 32-bit name length + - a 32-bit descriptor length + - a 32-bit type field + - the NUL-terminated name, length as specified in the first field + - the descriptor, length as specified in the second field + + The variable sized fields are padded to 32- or 64-bits + depending on whether the file is a 32- or 64-bit ELF file. + */ + // XXX Which 64-bit archs need 8-byte alignment? x86-64 does not. + size_t align = class == ELFCLASS32 ? 4 : 4; // XXX 8; +#define ALIGNED_LEN(len) (((len) + align - 1) & ~(align - 1)) + + size_t idx = 0; + while (idx < phdr->p_filesz) + { + /* XXX Handle 64-bit note section entries correctly. */ + struct + { + uint32_t namesz; + uint32_t descsz; + uint32_t type; + char name[0]; + } *noteentry = (__typeof (noteentry)) (notemem + idx); + + if (idx + 12 > phdr->p_filesz + || (idx + 12 + ALIGNED_LEN (noteentry->namesz) + + ALIGNED_LEN (noteentry->descsz) > phdr->p_filesz)) + /* This entry isn't completely contained in the note + section. Ignore it. */ + break; + + char buf[100]; + char buf2[100]; + printf (gettext (" %-13.*s %9" PRId32 " %s\n"), + (int) noteentry->namesz, noteentry->name, + noteentry->descsz, + ehdr->e_type == ET_CORE + ? ebl_core_note_type_name (ebl, noteentry->type, + buf, sizeof (buf)) + : ebl_object_note_type_name (ebl, noteentry->type, + buf2, sizeof (buf2))); + + /* Filter out invalid entries. */ + if (memchr (noteentry->name, '\0', noteentry->namesz) != NULL + /* XXX For now help broken Linux kernels. */ + || 1) + { + if (ehdr->e_type == ET_CORE) + ebl_core_note (ebl, noteentry->name, noteentry->type, + noteentry->descsz, + ¬eentry->name[ALIGNED_LEN (noteentry->namesz)]); + else + ebl_object_note (ebl, noteentry->name, noteentry->type, + noteentry->descsz, + ¬eentry->name[ALIGNED_LEN (noteentry->namesz)]); + } + + /* Move to the next entry. */ + idx += (12 + ALIGNED_LEN (noteentry->namesz) + + ALIGNED_LEN (noteentry->descsz)); + } + + gelf_freechunk (ebl->elf, notemem); + } +} diff --git a/src/sectionhash.c b/src/sectionhash.c new file mode 100644 index 00000000..4641c7cb --- /dev/null +++ b/src/sectionhash.c @@ -0,0 +1,69 @@ +/* Section hash table implementation. + Copyright (C) 2001, 2002, 2005 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2001. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <string.h> + +#include <elf-knowledge.h> +#include <ld.h> + + +/* Comparison function for sections. */ +static int +scnhead_compare (struct scnhead *one, struct scnhead *two) +{ + int result = strcmp (one->name, two->name); + + if (result == 0) + { + result = one->type - two->type; + + if (result == 0) + { + GElf_Sxword diff = (SH_FLAGS_IMPORTANT (one->flags) + - SH_FLAGS_IMPORTANT (two->flags)); + result = diff < 0 ? -1 : diff == 0 ? 0 : 1; + + if (result == 0) + { + result = one->entsize - two->entsize; + + if (result == 0) + { + result = (one->grp_signature == NULL + ? (two->grp_signature == NULL ? 0 : -1) + : (two->grp_signature == NULL + ? 1 : strcmp (one->grp_signature, + two->grp_signature))); + + if (result == 0) + result = one->kind - two->kind; + } + } + } + } + + return result; +} + +/* Definitions for the section hash table. */ +#define TYPE struct scnhead * +#define NAME ld_section_tab +#define ITERATE 1 +#define COMPARE(a, b) scnhead_compare (a, b) + +#include "../lib/dynamicsizehash.c" diff --git a/src/sectionhash.h b/src/sectionhash.h new file mode 100644 index 00000000..50bcb9c8 --- /dev/null +++ b/src/sectionhash.h @@ -0,0 +1,23 @@ +/* Copyright (C) 2001, 2002 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2001. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifndef SECTIONHASH_H +#define SECTIONHASH_H 1 + +/* Definitions for the section hash table. */ +#define TYPE struct scnhead * +#define NAME ld_section_tab +#define ITERATE 1 +#include <dynamicsizehash.h> + +#endif /* sectionhash.h */ diff --git a/src/size.c b/src/size.c new file mode 100644 index 00000000..4dc7baf3 --- /dev/null +++ b/src/size.c @@ -0,0 +1,682 @@ +/* Print size information from ELF file. + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2000. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <argp.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <inttypes.h> +#include <libelf.h> +#include <libintl.h> +#include <locale.h> +#include <mcheck.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/param.h> + +#include <system.h> + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + + +/* Values for the parameters which have no short form. */ +#define OPT_FORMAT 0x100 +#define OPT_RADIX 0x101 + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Output format:"), 0 }, + { "format", OPT_FORMAT, "FORMAT", 0, + N_("Use the output format FORMAT. FORMAT can be `bsd' or `sysv'. " + "The default is `bsd'"), 0 }, + { NULL, 'A', NULL, 0, N_("Same as `--format=sysv'"), 0 }, + { NULL, 'B', NULL, 0, N_("Same as `--format=bsd'"), 0 }, + { "radix", OPT_RADIX, "RADIX", 0, N_("Use RADIX for printing symbol values"), + 0}, + { NULL, 'd', NULL, 0, N_("Same as `--radix=10'"), 0 }, + { NULL, 'o', NULL, 0, N_("Same as `--radix=8'"), 0 }, + { NULL, 'x', NULL, 0, N_("Same as `--radix=16'"), 0 }, + { NULL, 'f', NULL, 0, + N_("Similar to `--format=sysv' output but in one line"), 0 }, + + { NULL, 0, NULL, 0, N_("Output options:"), 0 }, + { NULL, 'F', NULL, 0, + N_("Print size and permission flags for loadable segments"), 0 }, + { "totals", 't', NULL, 0, N_("Display the total sizes (bsd only)"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("\ +List section sizes of FILEs (a.out by default)."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("[FILE...]"); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, NULL, NULL, NULL +}; + + +/* Print symbols in file named FNAME. */ +static int process_file (const char *fname); + +/* Handle content of archive. */ +static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname); + +/* Handle ELF file. */ +static void handle_elf (Elf *elf, const char *fullname, const char *fname); + +/* Show total size. */ +static void show_bsd_totals (void); + +#define INTERNAL_ERROR(fname) \ + error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s-%s): %s"), \ + fname, __LINE__, VERSION, __DATE__, elf_errmsg (-1)) + + +/* User-selectable options. */ + +/* The selected output format. */ +static enum +{ + format_bsd = 0, + format_sysv, + format_sysv_one_line, + format_segments +} format; + +/* Radix for printed numbers. */ +static enum +{ + radix_decimal = 0, + radix_hex, + radix_octal +} radix; + + +/* Mapping of radix and binary class to length. */ +static const int length_map[2][3] = +{ + [ELFCLASS32 - 1] = + { + [radix_hex] = 8, + [radix_decimal] = 10, + [radix_octal] = 11 + }, + [ELFCLASS64 - 1] = + { + [radix_hex] = 16, + [radix_decimal] = 20, + [radix_octal] = 22 + } +}; + +/* True if total sizes should be printed. */ +static bool totals; +/* To print the total sizes in a reasonable format remember the higest + "class" of ELF binaries processed. */ +static int totals_class; + + +int +main (int argc, char *argv[]) +{ + int remaining; + int result = 0; + + /* Make memory leak detection possible. */ + mtrace (); + + /* We use no threads here which can interfere with handling a stream. */ + __fsetlocking (stdin, FSETLOCKING_BYCALLER); + __fsetlocking (stdout, FSETLOCKING_BYCALLER); + __fsetlocking (stderr, FSETLOCKING_BYCALLER); + + /* Set locale. */ + setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + bindtextdomain (PACKAGE, LOCALEDIR); + + /* Initialize the message catalog. */ + textdomain (PACKAGE); + + /* Parse and process arguments. */ + argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + + /* Tell the library which version we are expecting. */ + elf_version (EV_CURRENT); + + if (remaining == argc) + /* The user didn't specify a name so we use a.out. */ + result = process_file ("a.out"); + else + /* Process all the remaining files. */ + do + result |= process_file (argv[remaining]); + while (++remaining < argc); + + /* Print the total sizes but only if the output format is BSD and at + least one file has been correctly read (i.e., we recognized the + class). */ + if (totals && format == format_bsd && totals_class != 0) + show_bsd_totals (); + + return result; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "size (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2005"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case 'd': + radix = radix_decimal; + break; + + case 'f': + format = format_sysv_one_line; + break; + + case 'o': + radix = radix_octal; + break; + + case 'x': + radix = radix_hex; + break; + + case 'A': + format = format_sysv; + break; + + case 'B': + format = format_bsd; + break; + + case 'F': + format = format_segments; + break; + + case OPT_FORMAT: + if (strcmp (arg, "bsd") == 0 || strcmp (arg, "berkeley") == 0) + format = format_bsd; + else if (strcmp (arg, "sysv") == 0) + format = format_sysv; + else + error (EXIT_FAILURE, 0, gettext ("Invalid format: %s"), arg); + break; + + case OPT_RADIX: + if (strcmp (arg, "x") == 0 || strcmp (arg, "16") == 0) + radix = radix_hex; + else if (strcmp (arg, "d") == 0 || strcmp (arg, "10") == 0) + radix = radix_decimal; + else if (strcmp (arg, "o") == 0 || strcmp (arg, "8") == 0) + radix = radix_octal; + else + error (EXIT_FAILURE, 0, gettext ("Invalid radix: %s"), arg); + break; + + case 't': + totals = true; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +static int +process_file (const char *fname) +{ + /* Open the file and determine the type. */ + int fd; + Elf *elf; + + /* Open the file. */ + fd = open (fname, O_RDONLY); + if (fd == -1) + { + error (0, errno, gettext ("cannot open '%s"), fname); + return 1; + } + + /* Now get the ELF descriptor. */ + elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (elf != NULL) + { + if (elf_kind (elf) == ELF_K_ELF) + { + handle_elf (elf, NULL, fname); + + if (elf_end (elf) != 0) + INTERNAL_ERROR (fname); + + if (close (fd) != 0) + error (EXIT_FAILURE, errno, gettext ("while close '%s'"), fname); + + return 0; + } + else + return handle_ar (fd, elf, NULL, fname); + + /* We cannot handle this type. Close the descriptor anyway. */ + if (elf_end (elf) != 0) + INTERNAL_ERROR (fname); + } + + error (0, 0, gettext ("%s: file format not recognized"), fname); + + return 1; +} + + +/* Print the BSD-style header. This is done exactly once. */ +static void +print_header (Elf *elf) +{ + static int done; + + if (! done) + { + int ddigits = length_map[gelf_getclass (elf) - 1][radix_decimal]; + int xdigits = length_map[gelf_getclass (elf) - 1][radix_hex]; + + printf ("%*s %*s %*s %*s %*s %s\n", + ddigits - 2, sgettext ("bsd|text"), + ddigits - 2, sgettext ("bsd|data"), + ddigits - 2, sgettext ("bsd|bss"), + ddigits - 2, sgettext ("bsd|dec"), + xdigits - 2, sgettext ("bsd|hex"), + sgettext ("bsd|filename")); + + done = 1; + } +} + + +static int +handle_ar (int fd, Elf *elf, const char *prefix, const char *fname) +{ + Elf *subelf; + Elf_Cmd cmd = ELF_C_READ_MMAP; + size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); + size_t fname_len = strlen (fname) + 1; + char new_prefix[prefix_len + 1 + fname_len]; + int result = 0; + char *cp = new_prefix; + + /* Create the full name of the file. */ + if (prefix != NULL) + { + cp = mempcpy (cp, prefix, prefix_len); + *cp++ = ':'; + } + memcpy (cp, fname, fname_len); + + /* Process all the files contained in the archive. */ + while ((subelf = elf_begin (fd, cmd, elf)) != NULL) + { + /* The the header for this element. */ + Elf_Arhdr *arhdr = elf_getarhdr (subelf); + + if (elf_kind (subelf) == ELF_K_ELF) + handle_elf (subelf, new_prefix, arhdr->ar_name); + else if (elf_kind (subelf) == ELF_K_AR) + result |= handle_ar (fd, subelf, new_prefix, arhdr->ar_name); + /* else signal error??? */ + + /* Get next archive element. */ + cmd = elf_next (subelf); + if (elf_end (subelf) != 0) + INTERNAL_ERROR (fname); + } + + if (elf_end (elf) != 0) + INTERNAL_ERROR (fname); + + if (close (fd) != 0) + error (EXIT_FAILURE, errno, gettext ("while closing `%s'"), fname); + + return result; +} + + +/* Show sizes in SysV format. */ +static void +show_sysv (Elf *elf, const char *prefix, const char *fname, + const char *fullname) +{ + size_t shstrndx; + Elf_Scn *scn = NULL; + GElf_Shdr shdr_mem; + int maxlen = 10; + int digits = length_map[gelf_getclass (elf) - 1][radix]; + const char *fmtstr; + GElf_Off total = 0; + + /* Get the section header string table index. */ + if (elf_getshstrndx (elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + /* First round over the sections: determine the longest section name. */ + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr == NULL) + INTERNAL_ERROR (fullname); + + /* Ignore all sections which are not used at runtime. */ + if ((shdr->sh_flags & SHF_ALLOC) != 0) + maxlen = MAX (maxlen, + (int) strlen (elf_strptr (elf, shstrndx, + shdr->sh_name))); + } + + fputs_unlocked (fname, stdout); + if (prefix != NULL) + printf (gettext (" (ex %s)"), prefix); + printf (":\n%-*s %*s %*s\n", + maxlen, sgettext ("sysv|section"), + digits - 2, sgettext ("sysv|size"), + digits, sgettext ("sysv|addr")); + + if (radix == radix_hex) + fmtstr = "%-*s %*" PRIx64 " %*" PRIx64 "\n"; + else if (radix == radix_decimal) + fmtstr = "%-*s %*" PRId64 " %*" PRId64 "\n"; + else + fmtstr = "%-*s %*" PRIo64 " %*" PRIo64 "\n"; + + /* Iterate over all sections. */ + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + /* Ignore all sections which are not used at runtime. */ + if ((shdr->sh_flags & SHF_ALLOC) != 0) + { + printf (fmtstr, + maxlen, elf_strptr (elf, shstrndx, shdr->sh_name), + digits - 2, shdr->sh_size, + digits, shdr->sh_addr); + + total += shdr->sh_size; + } + } + + if (radix == radix_hex) + printf ("%-*s %*" PRIx64 "\n\n\n", maxlen, sgettext ("sysv|Total"), + digits - 2, total); + else if (radix == radix_decimal) + printf ("%-*s %*" PRId64 "\n\n\n", maxlen, sgettext ("sysv|Total"), + digits - 2, total); + else + printf ("%-*s %*" PRIo64 "\n\n\n", maxlen, sgettext ("sysv|Total"), + digits - 2, total); +} + + +/* Show sizes in SysV format in one line. */ +static void +show_sysv_one_line (Elf *elf) +{ + size_t shstrndx; + Elf_Scn *scn = NULL; + GElf_Shdr shdr_mem; + const char *fmtstr; + GElf_Off total = 0; + int first = 1; + + /* Get the section header string table index. */ + if (elf_getshstrndx (elf, &shstrndx) < 0) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + if (radix == radix_hex) + fmtstr = "%" PRIx64 "(%s)"; + else if (radix == radix_decimal) + fmtstr = "%" PRId64 "(%s)"; + else + fmtstr = "%" PRIo64 "(%s)"; + + /* Iterate over all sections. */ + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + /* Ignore all sections which are not used at runtime. */ + if ((shdr->sh_flags & SHF_ALLOC) == 0) + continue; + + if (! first) + fputs_unlocked (" + ", stdout); + first = 0; + + printf (fmtstr, shdr->sh_size, + elf_strptr (elf, shstrndx, shdr->sh_name)); + + total += shdr->sh_size; + } + + if (radix == radix_hex) + printf (" = %#" PRIx64 "\n", total); + else if (radix == radix_decimal) + printf (" = %" PRId64 "\n", total); + else + printf (" = %" PRIo64 "\n", total); +} + + +/* Variables to add up the sizes of all files. */ +static uintmax_t total_textsize; +static uintmax_t total_datasize; +static uintmax_t total_bsssize; + + +/* Show sizes in BSD format. */ +static void +show_bsd (Elf *elf, const char *prefix, const char *fname, + const char *fullname) +{ + Elf_Scn *scn = NULL; + GElf_Shdr shdr_mem; + GElf_Off textsize = 0; + GElf_Off datasize = 0; + GElf_Off bsssize = 0; + int ddigits = length_map[gelf_getclass (elf) - 1][radix_decimal]; + int xdigits = length_map[gelf_getclass (elf) - 1][radix_hex]; + + /* Iterate over all sections. */ + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr == NULL) + INTERNAL_ERROR (fullname); + + /* Ignore all sections which are not marked as loaded. */ + if ((shdr->sh_flags & SHF_ALLOC) == 0) + continue; + + if ((shdr->sh_flags & SHF_WRITE) == 0) + textsize += shdr->sh_size; + else if (shdr->sh_type == SHT_NOBITS) + bsssize += shdr->sh_size; + else + datasize += shdr->sh_size; + } + + printf ("%*" PRId64 " %*" PRId64 " %*" PRId64 " %*" PRId64 " %*" + PRIx64 " %s", + ddigits - 2, textsize, + ddigits - 2, datasize, + ddigits - 2, bsssize, + ddigits - 2, textsize + datasize + bsssize, + xdigits - 2, textsize + datasize + bsssize, + fname); + if (prefix != NULL) + printf (gettext (" (ex %s)"), prefix); + fputs_unlocked ("\n", stdout); + + total_textsize += textsize; + total_datasize += datasize; + total_bsssize += bsssize; + + totals_class = MAX (totals_class, gelf_getclass (elf)); +} + + +/* Show total size. */ +static void +show_bsd_totals (void) +{ + int ddigits = length_map[totals_class - 1][radix_decimal]; + int xdigits = length_map[totals_class - 1][radix_hex]; + + printf ("%*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*" + PRIxMAX " %s", + ddigits - 2, total_textsize, + ddigits - 2, total_datasize, + ddigits - 2, total_bsssize, + ddigits - 2, total_textsize + total_datasize + total_bsssize, + xdigits - 2, total_textsize + total_datasize + total_bsssize, + gettext ("(TOTALS)\n")); +} + + +/* Show size and permission of loadable segments. */ +static void +show_segments (Elf *elf, const char *fullname) +{ + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr; + size_t cnt; + GElf_Off total = 0; + int first = 1; + + ehdr = gelf_getehdr (elf, &ehdr_mem); + if (ehdr == NULL) + INTERNAL_ERROR (fullname); + + for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr; + + phdr = gelf_getphdr (elf, cnt, &phdr_mem); + if (phdr == NULL) + INTERNAL_ERROR (fullname); + + if (phdr->p_type != PT_LOAD) + /* Only load segments. */ + continue; + + if (! first) + fputs_unlocked (" + ", stdout); + first = 0; + + printf (radix == radix_hex ? "%" PRIx64 "(%c%c%c)" + : (radix == radix_decimal ? "%" PRId64 "(%c%c%c)" + : "%" PRIo64 "(%c%c%c)"), + phdr->p_memsz, + (phdr->p_flags & PF_R) == 0 ? '-' : 'r', + (phdr->p_flags & PF_W) == 0 ? '-' : 'w', + (phdr->p_flags & PF_X) == 0 ? '-' : 'x'); + + total += phdr->p_memsz; + } + + if (radix == radix_hex) + printf (" = %#" PRIx64 "\n", total); + else if (radix == radix_decimal) + printf (" = %" PRId64 "\n", total); + else + printf (" = %" PRIo64 "\n", total); +} + + +static void +handle_elf (Elf *elf, const char *prefix, const char *fname) +{ + size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); + size_t fname_len = strlen (fname) + 1; + char fullname[prefix_len + 1 + fname_len]; + char *cp = fullname; + + /* Create the full name of the file. */ + if (prefix != NULL) + { + cp = mempcpy (cp, prefix, prefix_len); + *cp++ = ':'; + } + memcpy (cp, fname, fname_len); + + if (format == format_sysv) + show_sysv (elf, prefix, fname, fullname); + else if (format == format_sysv_one_line) + show_sysv_one_line (elf); + else if (format == format_segments) + show_segments (elf, fullname); + else + { + print_header (elf); + + show_bsd (elf, prefix, fname, fullname); + } +} diff --git a/src/strip.c b/src/strip.c new file mode 100644 index 00000000..214513c4 --- /dev/null +++ b/src/strip.c @@ -0,0 +1,1750 @@ +/* Discard section not used at runtime from object files. + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2000. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <argp.h> +#include <assert.h> +#include <byteswap.h> +#include <endian.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <libelf.h> +#include <libintl.h> +#include <locale.h> +#include <mcheck.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/param.h> +#include <sys/time.h> + +#include <elf-knowledge.h> +#include <libebl.h> +#include <system.h> + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + + +/* Values for the parameters which have no short form. */ +#define OPT_REMOVE_COMMENT 0x100 +#define OPT_PERMISSIVE 0x101 + + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Output selection:"), 0 }, + { NULL, 'o', "FILE", 0, N_("Place stripped output into FILE"), 0 }, + { NULL, 'f', "FILE", 0, N_("Extract the removed sections into FILE"), 0 }, + { NULL, 'F', "FILE", 0, N_("Embed name FILE instead of -f argument"), 0 }, + + { NULL, 0, NULL, 0, N_("Output options:"), 0 }, + { "strip-debug", 'g', NULL, 0, N_("Remove all debugging symbols"), 0 }, + { "preserve-dates", 'p', NULL, 0, + N_("Copy modified/access timestamps to the output"), 0 }, + { "remove-comment", OPT_REMOVE_COMMENT, NULL, 0, + N_("Remove .comment section"), 0 }, + { "permissive", OPT_PERMISSIVE, NULL, 0, + N_("Relax a few rules to handle slightly broken ELF files"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("Discard symbols from object files."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("[FILE...]"); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, NULL, NULL, NULL +}; + + +/* Print symbols in file named FNAME. */ +static int process_file (const char *fname); + +/* Handle one ELF file. */ +static int handle_elf (int fd, Elf *elf, const char *prefix, + const char *fname, mode_t mode, struct timeval tvp[2]); + +/* Handle all files contained in the archive. */ +static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname, + struct timeval tvp[2]); + +#define INTERNAL_ERROR(fname) \ + error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s-%s): %s"), \ + fname, __LINE__, VERSION, __DATE__, elf_errmsg (-1)) + + +/* Name of the output file. */ +static const char *output_fname; + +/* Name of the debug output file. */ +static const char *debug_fname; + +/* Name to pretend the debug output file has. */ +static const char *debug_fname_embed; + +/* If true output files shall have same date as the input file. */ +static bool preserve_dates; + +/* If true .comment sections will be removed. */ +static bool remove_comment; + +/* If true remove all debug sections. */ +static bool remove_debug; + +/* If true relax some ELF rules for input files. */ +static bool permissive; + + +int +main (int argc, char *argv[]) +{ + int remaining; + int result = 0; + + /* Make memory leak detection possible. */ + mtrace (); + + /* We use no threads here which can interfere with handling a stream. */ + __fsetlocking (stdin, FSETLOCKING_BYCALLER); + __fsetlocking (stdout, FSETLOCKING_BYCALLER); + __fsetlocking (stderr, FSETLOCKING_BYCALLER); + + /* Set locale. */ + setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + bindtextdomain (PACKAGE, LOCALEDIR); + + /* Initialize the message catalog. */ + textdomain (PACKAGE); + + /* Parse and process arguments. */ + if (argp_parse (&argp, argc, argv, 0, &remaining, NULL) != 0) + return EXIT_FAILURE; + + /* Tell the library which version we are expecting. */ + elf_version (EV_CURRENT); + + if (remaining == argc) + /* The user didn't specify a name so we use a.out. */ + result = process_file ("a.out"); + else + { + /* If we have seen the '-o' or '-f' option there must be exactly one + input file. */ + if ((output_fname != NULL || debug_fname != NULL) + && remaining + 1 < argc) + error (EXIT_FAILURE, 0, gettext ("\ +Only one input file allowed together with '-o' and '-f'")); + + /* Process all the remaining files. */ + do + result |= process_file (argv[remaining]); + while (++remaining < argc); + } + + return result; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "strip (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2005"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case 'f': + if (debug_fname != NULL) + { + error (0, 0, gettext ("-f option specified twice")); + return EINVAL; + } + debug_fname = arg; + break; + + case 'F': + if (debug_fname_embed != NULL) + { + error (0, 0, gettext ("-F option specified twice")); + return EINVAL; + } + debug_fname_embed = arg; + break; + + case 'o': + if (output_fname != NULL) + { + error (0, 0, gettext ("-o option specified twice")); + return EINVAL; + } + output_fname = arg; + break; + + case 'p': + preserve_dates = true; + break; + + case OPT_REMOVE_COMMENT: + remove_comment = true; + break; + + case 'g': + remove_debug = true; + break; + + case OPT_PERMISSIVE: + permissive = true; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +static int +process_file (const char *fname) +{ + /* If we have to preserve the modify and access timestamps get them + now. We cannot use fstat() after opening the file since the open + would change the access time. */ + struct stat64 pre_st; + struct timeval tv[2]; + again: + if (preserve_dates) + { + if (stat64 (fname, &pre_st) != 0) + { + error (0, errno, gettext ("cannot stat input file \"%s\""), fname); + return 1; + } + + /* If we have to preserve the timestamp, we need it in the + format utimes() understands. */ + TIMESPEC_TO_TIMEVAL (&tv[0], &pre_st.st_atim); + TIMESPEC_TO_TIMEVAL (&tv[1], &pre_st.st_mtim); + } + + /* Open the file. */ + int fd = open (fname, O_RDWR); + if (fd == -1) + { + error (0, errno, gettext ("while opening \"%s\""), fname); + return 1; + } + + /* We always use fstat() even if we called stat() before. This is + done to make sure the information returned by stat() is for the + same file. */ + struct stat64 st; + if (fstat64 (fd, &st) != 0) + { + error (0, errno, gettext ("cannot stat input file \"%s\""), fname); + return 1; + } + /* Paranoid mode on. */ + if (preserve_dates + && (st.st_ino != pre_st.st_ino || st.st_dev != pre_st.st_dev)) + { + /* We detected a race. Try again. */ + close (fd); + goto again; + } + + /* Now get the ELF descriptor. */ + Elf *elf = elf_begin (fd, ELF_C_RDWR, NULL); + int result; + switch (elf_kind (elf)) + { + case ELF_K_ELF: + result = handle_elf (fd, elf, NULL, fname, st.st_mode & ACCESSPERMS, + preserve_dates ? tv : NULL); + break; + + case ELF_K_AR: + /* It is not possible to strip the content of an archive direct + the output to a specific file. */ + if (unlikely (output_fname != NULL)) + { + error (0, 0, gettext ("%s: cannot use -o when stripping archive"), + fname); + result = 1; + } + else + result = handle_ar (fd, elf, NULL, fname, preserve_dates ? tv : NULL); + break; + + default: + error (0, 0, gettext ("%s: File format not recognized"), fname); + result = 1; + break; + } + + if (unlikely (elf_end (elf) != 0)) + INTERNAL_ERROR (fname); + + close (fd); + + return result; +} + + +/* Maximum size of array allocated on stack. */ +#define MAX_STACK_ALLOC (400 * 1024) + +static int +handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, + mode_t mode, struct timeval tvp[2]) +{ + size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); + size_t fname_len = strlen (fname) + 1; + char *fullname = alloca (prefix_len + 1 + fname_len); + char *cp = fullname; + Elf *newelf; + Elf *debugelf = NULL; + char *tmp_debug_fname = NULL; + int result = 0; + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr; + size_t shstrndx; + size_t shnum; + struct shdr_info + { + Elf_Scn *scn; + GElf_Shdr shdr; + Elf_Data *data; + const char *name; + Elf32_Word idx; /* Index in new file. */ + Elf32_Word old_sh_link; /* Original value of shdr.sh_link. */ + Elf32_Word symtab_idx; + Elf32_Word version_idx; + Elf32_Word group_idx; + Elf32_Word group_cnt; + Elf_Scn *newscn; + struct Ebl_Strent *se; + Elf32_Word *newsymidx; + } *shdr_info = NULL; + Elf_Scn *scn; + size_t cnt; + size_t idx; + bool changes; + GElf_Ehdr newehdr_mem; + GElf_Ehdr *newehdr; + GElf_Ehdr debugehdr_mem; + GElf_Ehdr *debugehdr; + struct Ebl_Strtab *shst = NULL; + Elf_Data debuglink_crc_data; + bool any_symtab_changes = false; + Elf_Data *shstrtab_data = NULL; + + /* Create the full name of the file. */ + if (prefix != NULL) + { + cp = mempcpy (cp, prefix, prefix_len); + *cp++ = ':'; + } + memcpy (cp, fname, fname_len); + + /* If we are not replacing the input file open a new file here. */ + if (output_fname != NULL) + { + fd = open (output_fname, O_RDWR | O_CREAT, mode); + if (unlikely (fd == -1)) + { + error (0, errno, gettext ("cannot open '%s'"), output_fname); + return 1; + } + } + + int debug_fd = -1; + + /* Get the EBL handling. The -g option is currently the only reason + we need EBL so dont open the backend unless necessary. */ + Ebl *ebl = NULL; + if (remove_debug) + { + ebl = ebl_openbackend (elf); + if (ebl == NULL) + { + error (0, errno, gettext ("cannot open EBL backend")); + result = 1; + goto fail; + } + } + + /* Open the additional file the debug information will be stored in. */ + if (debug_fname != NULL) + { + /* Create a temporary file name. We do not want to overwrite + the debug file if the file would not contain any + information. */ + size_t debug_fname_len = strlen (debug_fname); + tmp_debug_fname = (char *) alloca (debug_fname_len + sizeof (".XXXXXX")); + strcpy (mempcpy (tmp_debug_fname, debug_fname, debug_fname_len), + ".XXXXXX"); + + debug_fd = mkstemp (tmp_debug_fname); + if (unlikely (debug_fd == -1)) + { + error (0, errno, gettext ("cannot open '%s'"), debug_fname); + result = 1; + goto fail; + } + } + + /* Get the information from the old file. */ + ehdr = gelf_getehdr (elf, &ehdr_mem); + if (ehdr == NULL) + INTERNAL_ERROR (fname); + + /* Get the section header string table index. */ + if (unlikely (elf_getshstrndx (elf, &shstrndx) < 0)) + error (EXIT_FAILURE, 0, + gettext ("cannot get section header string table index")); + + /* We now create a new ELF descriptor for the same file. We + construct it almost exactly in the same way with some information + dropped. */ + if (output_fname != NULL) + newelf = elf_begin (fd, ELF_C_WRITE_MMAP, NULL); + else + newelf = elf_clone (elf, ELF_C_EMPTY); + + if (unlikely (gelf_newehdr (newelf, gelf_getclass (elf)) == 0) + || (ehdr->e_type != ET_REL + && unlikely (gelf_newphdr (newelf, ehdr->e_phnum) == 0))) + { + error (0, 0, gettext ("cannot create new file '%s': %s"), + output_fname, elf_errmsg (-1)); + goto fail; + } + + /* Copy over the old program header if needed. */ + if (ehdr->e_type != ET_REL) + for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr; + + phdr = gelf_getphdr (elf, cnt, &phdr_mem); + if (phdr == NULL + || unlikely (gelf_update_phdr (newelf, cnt, phdr) == 0)) + INTERNAL_ERROR (fname); + } + + if (debug_fname != NULL) + { + /* Also create an ELF descriptor for the debug file */ + debugelf = elf_begin (debug_fd, ELF_C_WRITE_MMAP, NULL); + if (unlikely (gelf_newehdr (debugelf, gelf_getclass (elf)) == 0) + || (ehdr->e_type != ET_REL + && unlikely (gelf_newphdr (debugelf, ehdr->e_phnum) == 0))) + { + error (0, 0, gettext ("cannot create new file '%s': %s"), + debug_fname, elf_errmsg (-1)); + goto fail_close; + } + + /* Copy over the old program header if needed. */ + if (ehdr->e_type != ET_REL) + for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr; + + phdr = gelf_getphdr (elf, cnt, &phdr_mem); + if (phdr == NULL + || unlikely (gelf_update_phdr (debugelf, cnt, phdr) == 0)) + INTERNAL_ERROR (fname); + } + } + + /* Number of sections. */ + if (unlikely (elf_getshnum (elf, &shnum) < 0)) + { + error (0, 0, gettext ("cannot determine number of sections: %s"), + elf_errmsg (-1)); + goto fail_close; + } + + /* Storage for section information. We leave room for two more + entries since we unconditionally create a section header string + table. Maybe some weird tool created an ELF file without one. + The other one is used for the debug link section. */ + if ((shnum + 2) * sizeof (struct shdr_info) > MAX_STACK_ALLOC) + shdr_info = (struct shdr_info *) xcalloc (shnum + 2, + sizeof (struct shdr_info)); + else + { + shdr_info = (struct shdr_info *) alloca ((shnum + 2) + * sizeof (struct shdr_info)); + memset (shdr_info, '\0', (shnum + 2) * sizeof (struct shdr_info)); + } + + /* Prepare section information data structure. */ + scn = NULL; + cnt = 1; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + /* This should always be true (i.e., there should not be any + holes in the numbering). */ + assert (elf_ndxscn (scn) == cnt); + + shdr_info[cnt].scn = scn; + + /* Get the header. */ + if (gelf_getshdr (scn, &shdr_info[cnt].shdr) == NULL) + INTERNAL_ERROR (fname); + + /* Get the name of the section. */ + shdr_info[cnt].name = elf_strptr (elf, shstrndx, + shdr_info[cnt].shdr.sh_name); + if (shdr_info[cnt].name == NULL) + { + error (0, 0, gettext ("illformed file '%s'"), fname); + goto fail_close; + } + + /* Mark them as present but not yet investigated. */ + shdr_info[cnt].idx = 1; + + /* Remember the shdr.sh_link value. */ + shdr_info[cnt].old_sh_link = shdr_info[cnt].shdr.sh_link; + + /* Sections in files other than relocatable object files which + are not loaded can be freely moved by us. In relocatable + object files everything can be moved. */ + if (ehdr->e_type == ET_REL + || (shdr_info[cnt].shdr.sh_flags & SHF_ALLOC) == 0) + shdr_info[cnt].shdr.sh_offset = 0; + + /* If this is an extended section index table store an + appropriate reference. */ + if (unlikely (shdr_info[cnt].shdr.sh_type == SHT_SYMTAB_SHNDX)) + { + assert (shdr_info[shdr_info[cnt].shdr.sh_link].symtab_idx == 0); + shdr_info[shdr_info[cnt].shdr.sh_link].symtab_idx = cnt; + } + else if (unlikely (shdr_info[cnt].shdr.sh_type == SHT_GROUP)) + { + Elf32_Word *grpref; + size_t inner; + + /* Cross-reference the sections contained in the section + group. */ + shdr_info[cnt].data = elf_getdata (shdr_info[cnt].scn, NULL); + if (shdr_info[cnt].data == NULL) + INTERNAL_ERROR (fname); + + /* XXX Fix for unaligned access. */ + grpref = (Elf32_Word *) shdr_info[cnt].data->d_buf; + for (inner = 1; + inner < shdr_info[cnt].data->d_size / sizeof (Elf32_Word); + ++inner) + shdr_info[grpref[inner]].group_idx = cnt; + + if (inner == 1 || (inner == 2 && (grpref[0] & GRP_COMDAT) == 0)) + /* If the section group contains only one element and this + is n COMDAT section we can drop it right away. */ + shdr_info[cnt].idx = 0; + else + shdr_info[cnt].group_cnt = inner - 1; + } + else if (unlikely (shdr_info[cnt].shdr.sh_type == SHT_GNU_versym)) + { + assert (shdr_info[shdr_info[cnt].shdr.sh_link].version_idx == 0); + shdr_info[shdr_info[cnt].shdr.sh_link].version_idx = cnt; + } + + /* If this section is part of a group make sure it is not + discarded right away. */ + if ((shdr_info[cnt].shdr.sh_flags & SHF_GROUP) != 0) + { + assert (shdr_info[cnt].group_idx != 0); + + if (shdr_info[shdr_info[cnt].group_idx].idx == 0) + { + /* The section group section will be removed. */ + shdr_info[cnt].group_idx = 0; + shdr_info[cnt].shdr.sh_flags &= ~SHF_GROUP; + } + } + + /* Increment the counter. */ + ++cnt; + } + + /* Now determine which sections can go away. The general rule is that + all sections which are not used at runtime are stripped out. But + there are a few exceptions: + + - special sections named ".comment" and ".note" are kept + - OS or architecture specific sections are kept since we might not + know how to handle them + - if a section is referred to from a section which is not removed + in the sh_link or sh_info element it cannot be removed either + */ + for (cnt = 1; cnt < shnum; ++cnt) + /* Check whether the section can be removed. */ + if (ebl_section_strip_p (ebl, ehdr, &shdr_info[cnt].shdr, + shdr_info[cnt].name, remove_comment, + remove_debug)) + { + /* For now assume this section will be removed. */ + shdr_info[cnt].idx = 0; + + idx = shdr_info[cnt].group_idx; + while (idx != 0) + { + /* If the references section group is a normal section + group and has one element remaining, or if it is an + empty COMDAT section group it is removed. */ + bool is_comdat; + + /* The section group data is already loaded. */ + assert (shdr_info[idx].data != NULL); + + is_comdat = (((Elf32_Word *) shdr_info[idx].data->d_buf)[0] + & GRP_COMDAT) != 0; + + --shdr_info[idx].group_cnt; + if ((!is_comdat && shdr_info[idx].group_cnt == 1) + || (is_comdat && shdr_info[idx].group_cnt == 0)) + { + shdr_info[idx].idx = 0; + /* Continue recursively. */ + idx = shdr_info[idx].group_idx; + } + else + break; + } + } + + /* Mark the SHT_NULL section as handled. */ + shdr_info[0].idx = 2; + + + /* Handle exceptions: section groups and cross-references. We might + have to repeat this a few times since the resetting of the flag + might propagate. */ + do + { + changes = false; + + for (cnt = 1; cnt < shnum; ++cnt) + { + if (shdr_info[cnt].idx == 0) + { + /* If a relocation section is marked as being removed make + sure the section it is relocating is removed, too. */ + if ((shdr_info[cnt].shdr.sh_type == SHT_REL + || shdr_info[cnt].shdr.sh_type == SHT_RELA) + && shdr_info[shdr_info[cnt].shdr.sh_info].idx != 0) + shdr_info[cnt].idx = 1; + } + + if (shdr_info[cnt].idx == 1) + { + /* The content of symbol tables we don't remove must not + reference any section which we do remove. Otherwise + we cannot remove the section. */ + if (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM + || shdr_info[cnt].shdr.sh_type == SHT_SYMTAB) + { + Elf_Data *symdata; + Elf_Data *xndxdata; + size_t elsize; + + /* Make sure the data is loaded. */ + if (shdr_info[cnt].data == NULL) + { + shdr_info[cnt].data + = elf_getdata (shdr_info[cnt].scn, NULL); + if (shdr_info[cnt].data == NULL) + INTERNAL_ERROR (fname); + } + symdata = shdr_info[cnt].data; + + /* If there is an extended section index table load it + as well. */ + if (shdr_info[cnt].symtab_idx != 0 + && shdr_info[shdr_info[cnt].symtab_idx].data == NULL) + { + assert (shdr_info[cnt].shdr.sh_type == SHT_SYMTAB); + + shdr_info[shdr_info[cnt].symtab_idx].data + = elf_getdata (shdr_info[shdr_info[cnt].symtab_idx].scn, + NULL); + if (shdr_info[shdr_info[cnt].symtab_idx].data == NULL) + INTERNAL_ERROR (fname); + } + xndxdata = shdr_info[shdr_info[cnt].symtab_idx].data; + + /* Go through all symbols and make sure the section they + reference is not removed. */ + elsize = gelf_fsize (elf, ELF_T_SYM, 1, ehdr->e_version); + + for (size_t inner = 0; + inner < shdr_info[cnt].data->d_size / elsize; + ++inner) + { + GElf_Sym sym_mem; + Elf32_Word xndx; + GElf_Sym *sym; + size_t scnidx; + + sym = gelf_getsymshndx (symdata, xndxdata, inner, + &sym_mem, &xndx); + if (sym == NULL) + INTERNAL_ERROR (fname); + + scnidx = sym->st_shndx; + if (scnidx == SHN_UNDEF || scnidx >= shnum + || (scnidx >= SHN_LORESERVE + && scnidx <= SHN_HIRESERVE + && scnidx != SHN_XINDEX) + /* Don't count in the section symbols. */ + || GELF_ST_TYPE (sym->st_info) == STT_SECTION) + /* This is no section index, leave it alone. */ + continue; + else if (scnidx == SHN_XINDEX) + scnidx = xndx; + + if (shdr_info[scnidx].idx == 0) + { + /* Mark this section as used. */ + shdr_info[scnidx].idx = 1; + changes |= scnidx < cnt; + } + } + } + + /* Cross referencing happens: + - for the cases the ELF specification says. That are + + SHT_DYNAMIC in sh_link to string table + + SHT_HASH in sh_link to symbol table + + SHT_REL and SHT_RELA in sh_link to symbol table + + SHT_SYMTAB and SHT_DYNSYM in sh_link to string table + + SHT_GROUP in sh_link to symbol table + + SHT_SYMTAB_SHNDX in sh_link to symbol table + Other (OS or architecture-specific) sections might as + well use this field so we process it unconditionally. + - references inside section groups + - specially marked references in sh_info if the SHF_INFO_LINK + flag is set + */ + + if (shdr_info[shdr_info[cnt].shdr.sh_link].idx == 0) + { + shdr_info[shdr_info[cnt].shdr.sh_link].idx = 1; + changes |= shdr_info[cnt].shdr.sh_link < cnt; + } + + /* Handle references through sh_info. */ + if (SH_INFO_LINK_P (&shdr_info[cnt].shdr) + && shdr_info[shdr_info[cnt].shdr.sh_info].idx == 0) + { + shdr_info[shdr_info[cnt].shdr.sh_info].idx = 1; + changes |= shdr_info[cnt].shdr.sh_info < cnt; + } + + /* Mark the section as investigated. */ + shdr_info[cnt].idx = 2; + } + } + } + while (changes); + + /* Copy the removed sections to the debug output file. + The ones that are not removed in the stripped file are SHT_NOBITS. */ + if (debug_fname != NULL) + { + for (cnt = 1; cnt < shnum; ++cnt) + { + Elf_Data *debugdata; + GElf_Shdr debugshdr; + bool discard_section; + + scn = elf_newscn (debugelf); + if (scn == NULL) + error (EXIT_FAILURE, 0, + gettext ("while generating output file: %s"), + elf_errmsg (-1)); + + discard_section = shdr_info[cnt].idx > 0 && cnt != ehdr->e_shstrndx; + + /* Set the section header in the new file. */ + debugshdr = shdr_info[cnt].shdr; + if (discard_section) + debugshdr.sh_type = SHT_NOBITS; + + if (unlikely (gelf_update_shdr (scn, &debugshdr)) == 0) + /* There cannot be any overflows. */ + INTERNAL_ERROR (fname); + + /* Get the data from the old file if necessary. */ + if (shdr_info[cnt].data == NULL) + { + shdr_info[cnt].data = elf_getdata (shdr_info[cnt].scn, NULL); + if (shdr_info[cnt].data == NULL) + INTERNAL_ERROR (fname); + } + + /* Set the data. This is done by copying from the old file. */ + debugdata = elf_newdata (scn); + if (debugdata == NULL) + INTERNAL_ERROR (fname); + + /* Copy the structure. This data may be modified in place + before we write out the file. */ + *debugdata = *shdr_info[cnt].data; + if (discard_section) + debugdata->d_buf = NULL; + } + + /* Finish the ELF header. Fill in the fields not handled by + libelf from the old file. */ + debugehdr = gelf_getehdr (debugelf, &debugehdr_mem); + if (debugehdr == NULL) + INTERNAL_ERROR (fname); + + memcpy (debugehdr->e_ident, ehdr->e_ident, EI_NIDENT); + debugehdr->e_type = ehdr->e_type; + debugehdr->e_machine = ehdr->e_machine; + debugehdr->e_version = ehdr->e_version; + debugehdr->e_entry = ehdr->e_entry; + debugehdr->e_flags = ehdr->e_flags; + debugehdr->e_shstrndx = ehdr->e_shstrndx; + + if (unlikely (gelf_update_ehdr (debugelf, debugehdr)) == 0) + { + error (0, 0, gettext ("%s: error while creating ELF header: %s"), + debug_fname, elf_errmsg (-1)); + result = 1; + goto fail_close; + } + } + + /* Mark the section header string table as unused, we will create + a new one. */ + shdr_info[shstrndx].idx = 0; + + /* We need a string table for the section headers. */ + shst = ebl_strtabinit (true); + if (shst == NULL) + error (EXIT_FAILURE, errno, gettext ("while preparing output for '%s'"), + output_fname ?: fname); + + /* Assign new section numbers. */ + shdr_info[0].idx = 0; + for (cnt = idx = 1; cnt < shnum; ++cnt) + if (shdr_info[cnt].idx > 0) + { + shdr_info[cnt].idx = idx++; + + /* Create a new section. */ + shdr_info[cnt].newscn = elf_newscn (newelf); + if (shdr_info[cnt].newscn == NULL) + error (EXIT_FAILURE, 0, gettext ("while generating output file: %s"), + elf_errmsg (-1)); + + assert (elf_ndxscn (shdr_info[cnt].newscn) == shdr_info[cnt].idx); + + /* Add this name to the section header string table. */ + shdr_info[cnt].se = ebl_strtabadd (shst, shdr_info[cnt].name, 0); + } + + /* Test whether we are doing anything at all. */ + if (cnt == idx) + /* Nope, all removable sections are already gone. */ + goto fail_close; + + /* Create the reference to the file with the debug info. */ + if (debug_fname != NULL) + { + char *debug_basename; + off_t crc_offset; + + /* Add the section header string table section name. */ + shdr_info[cnt].se = ebl_strtabadd (shst, ".gnu_debuglink", 15); + shdr_info[cnt].idx = idx++; + + /* Create the section header. */ + shdr_info[cnt].shdr.sh_type = SHT_PROGBITS; + shdr_info[cnt].shdr.sh_flags = 0; + shdr_info[cnt].shdr.sh_addr = 0; + shdr_info[cnt].shdr.sh_link = SHN_UNDEF; + shdr_info[cnt].shdr.sh_info = SHN_UNDEF; + shdr_info[cnt].shdr.sh_entsize = 0; + shdr_info[cnt].shdr.sh_addralign = 4; + /* We set the offset to zero here. Before we write the ELF file the + field must have the correct value. This is done in the final + loop over all section. Then we have all the information needed. */ + shdr_info[cnt].shdr.sh_offset = 0; + + /* Create the section. */ + shdr_info[cnt].newscn = elf_newscn (newelf); + if (shdr_info[cnt].newscn == NULL) + error (EXIT_FAILURE, 0, + gettext ("while create section header section: %s"), + elf_errmsg (-1)); + assert (elf_ndxscn (shdr_info[cnt].newscn) == shdr_info[cnt].idx); + + shdr_info[cnt].data = elf_newdata (shdr_info[cnt].newscn); + if (shdr_info[cnt].data == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot allocate section data: %s"), + elf_errmsg (-1)); + + debug_basename = basename (debug_fname_embed ?: debug_fname); + crc_offset = strlen (debug_basename) + 1; + /* Align to 4 byte boundary */ + crc_offset = ((crc_offset - 1) & ~3) + 4; + + shdr_info[cnt].data->d_align = 4; + shdr_info[cnt].shdr.sh_size = shdr_info[cnt].data->d_size + = crc_offset + 4; + shdr_info[cnt].data->d_buf = xcalloc (1, shdr_info[cnt].data->d_size); + + strcpy (shdr_info[cnt].data->d_buf, debug_basename); + + /* Cache this Elf_Data describing the CRC32 word in the section. + We'll fill this in when we have written the debug file. */ + debuglink_crc_data = *shdr_info[cnt].data; + debuglink_crc_data.d_buf = ((char *) debuglink_crc_data.d_buf + + crc_offset); + debuglink_crc_data.d_size = 4; + + /* One more section done. */ + ++cnt; + } + + /* Index of the section header table in the shdr_info array. */ + size_t shdridx = cnt; + + /* Add the section header string table section name. */ + shdr_info[cnt].se = ebl_strtabadd (shst, ".shstrtab", 10); + shdr_info[cnt].idx = idx; + + /* Create the section header. */ + shdr_info[cnt].shdr.sh_type = SHT_STRTAB; + shdr_info[cnt].shdr.sh_flags = 0; + shdr_info[cnt].shdr.sh_addr = 0; + shdr_info[cnt].shdr.sh_link = SHN_UNDEF; + shdr_info[cnt].shdr.sh_info = SHN_UNDEF; + shdr_info[cnt].shdr.sh_entsize = 0; + /* We set the offset to zero here. Before we write the ELF file the + field must have the correct value. This is done in the final + loop over all section. Then we have all the information needed. */ + shdr_info[cnt].shdr.sh_offset = 0; + shdr_info[cnt].shdr.sh_addralign = 1; + + /* Create the section. */ + shdr_info[cnt].newscn = elf_newscn (newelf); + if (shdr_info[cnt].newscn == NULL) + error (EXIT_FAILURE, 0, + gettext ("while create section header section: %s"), + elf_errmsg (-1)); + assert (elf_ndxscn (shdr_info[cnt].newscn) == idx); + + /* Finalize the string table and fill in the correct indices in the + section headers. */ + shstrtab_data = elf_newdata (shdr_info[cnt].newscn); + if (shstrtab_data == NULL) + error (EXIT_FAILURE, 0, + gettext ("while create section header string table: %s"), + elf_errmsg (-1)); + ebl_strtabfinalize (shst, shstrtab_data); + + /* We have to set the section size. */ + shdr_info[cnt].shdr.sh_size = shstrtab_data->d_size; + + /* Update the section information. */ + GElf_Off lastoffset = 0; + for (cnt = 1; cnt <= shdridx; ++cnt) + if (shdr_info[cnt].idx > 0) + { + Elf_Data *newdata; + + scn = elf_getscn (newelf, shdr_info[cnt].idx); + assert (scn != NULL); + + /* Update the name. */ + shdr_info[cnt].shdr.sh_name = ebl_strtaboffset (shdr_info[cnt].se); + + /* Update the section header from the input file. Some fields + might be section indeces which now have to be adjusted. */ + if (shdr_info[cnt].shdr.sh_link != 0) + shdr_info[cnt].shdr.sh_link = + shdr_info[shdr_info[cnt].shdr.sh_link].idx; + + if (shdr_info[cnt].shdr.sh_type == SHT_GROUP) + { + assert (shdr_info[cnt].data != NULL); + + Elf32_Word *grpref = (Elf32_Word *) shdr_info[cnt].data->d_buf; + for (size_t inner = 0; + inner < shdr_info[cnt].data->d_size / sizeof (Elf32_Word); + ++inner) + grpref[inner] = shdr_info[grpref[inner]].idx; + } + + /* Handle the SHT_REL, SHT_RELA, and SHF_INFO_LINK flag. */ + if (SH_INFO_LINK_P (&shdr_info[cnt].shdr)) + 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. */ + if (cnt < shnum) + { + if (shdr_info[cnt].data == NULL) + { + shdr_info[cnt].data = elf_getdata (shdr_info[cnt].scn, NULL); + if (shdr_info[cnt].data == NULL) + INTERNAL_ERROR (fname); + } + + /* Set the data. This is done by copying from the old file. */ + newdata = elf_newdata (scn); + if (newdata == NULL) + INTERNAL_ERROR (fname); + + /* Copy the structure. */ + *newdata = *shdr_info[cnt].data; + + /* We know the size. */ + shdr_info[cnt].shdr.sh_size = shdr_info[cnt].data->d_size; + + /* We have to adjust symtol tables. The st_shndx member might + have to be updated. */ + if (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM + || shdr_info[cnt].shdr.sh_type == SHT_SYMTAB) + { + Elf_Data *versiondata = NULL; + Elf_Data *shndxdata = NULL; + + size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, + ehdr->e_version); + + if (shdr_info[cnt].symtab_idx != 0) + { + assert (shdr_info[cnt].shdr.sh_type == SHT_SYMTAB_SHNDX); + /* This section has extended section information. + We have to modify that information, too. */ + shndxdata = elf_getdata (shdr_info[shdr_info[cnt].symtab_idx].scn, + NULL); + + assert ((versiondata->d_size / sizeof (Elf32_Word)) + >= shdr_info[cnt].data->d_size / elsize); + } + + if (shdr_info[cnt].version_idx != 0) + { + assert (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM); + /* This section has associated version + information. We have to modify that + information, too. */ + versiondata = elf_getdata (shdr_info[shdr_info[cnt].version_idx].scn, + NULL); + + assert ((versiondata->d_size / sizeof (GElf_Versym)) + >= shdr_info[cnt].data->d_size / elsize); + } + + shdr_info[cnt].newsymidx + = (Elf32_Word *) xcalloc (shdr_info[cnt].data->d_size + / elsize, sizeof (Elf32_Word)); + + bool last_was_local = true; + size_t destidx; + size_t inner; + for (destidx = inner = 1; + inner < shdr_info[cnt].data->d_size / elsize; + ++inner) + { + Elf32_Word sec; + GElf_Sym sym_mem; + Elf32_Word xshndx; + GElf_Sym *sym = gelf_getsymshndx (shdr_info[cnt].data, + shndxdata, inner, + &sym_mem, &xshndx); + if (sym == NULL) + INTERNAL_ERROR (fname); + + if (sym->st_shndx == SHN_UNDEF + || (sym->st_shndx >= shnum + && sym->st_shndx != SHN_XINDEX)) + { + /* This is no section index, leave it alone + unless it is moved. */ + if (destidx != inner + && gelf_update_symshndx (shdr_info[cnt].data, + shndxdata, + destidx, sym, + xshndx) == 0) + INTERNAL_ERROR (fname); + + shdr_info[cnt].newsymidx[inner] = destidx++; + + if (last_was_local + && GELF_ST_BIND (sym->st_info) != STB_LOCAL) + { + last_was_local = false; + shdr_info[cnt].shdr.sh_info = destidx - 1; + } + + continue; + } + + /* Get the full section index, if necessary from the + XINDEX table. */ + if (sym->st_shndx != SHN_XINDEX) + sec = shdr_info[sym->st_shndx].idx; + else + { + assert (shndxdata != NULL); + + sec = shdr_info[xshndx].idx; + } + + if (sec != 0) + { + GElf_Section nshndx; + Elf32_Word nxshndx; + + if (sec < SHN_LORESERVE) + { + nshndx = sec; + nxshndx = 0; + } + else + { + nshndx = SHN_XINDEX; + nxshndx = sec; + } + + assert (sec < SHN_LORESERVE || shndxdata != NULL); + + if ((inner != destidx || nshndx != sym->st_shndx + || (shndxdata != NULL && nxshndx != xshndx)) + && (sym->st_shndx = nshndx, + gelf_update_symshndx (shdr_info[cnt].data, + shndxdata, + destidx, sym, + nxshndx) == 0)) + INTERNAL_ERROR (fname); + + shdr_info[cnt].newsymidx[inner] = destidx++; + + if (last_was_local + && GELF_ST_BIND (sym->st_info) != STB_LOCAL) + { + last_was_local = false; + shdr_info[cnt].shdr.sh_info = destidx - 1; + } + } + else + /* This is a section symbol for a section which has + been removed. */ + assert (GELF_ST_TYPE (sym->st_info) == STT_SECTION); + } + + if (destidx != inner) + { + /* The size of the symbol table changed. */ + shdr_info[cnt].shdr.sh_size = newdata->d_size + = destidx * elsize; + any_symtab_changes = true; + } + else + { + /* The symbol table didn't really change. */ + free (shdr_info[cnt].newsymidx); + shdr_info[cnt].newsymidx = NULL; + } + } + } + + /* If we have to, compute the offset of the section. */ + if (shdr_info[cnt].shdr.sh_offset == 0) + shdr_info[cnt].shdr.sh_offset + = ((lastoffset + shdr_info[cnt].shdr.sh_addralign - 1) + & ~((GElf_Off) (shdr_info[cnt].shdr.sh_addralign - 1))); + + /* Set the section header in the new file. */ + if (unlikely (gelf_update_shdr (scn, &shdr_info[cnt].shdr) == 0)) + /* There cannot be any overflows. */ + INTERNAL_ERROR (fname); + + /* Remember the last section written so far. */ + GElf_Off filesz = (shdr_info[cnt].shdr.sh_type != SHT_NOBITS + ? shdr_info[cnt].shdr.sh_size : 0); + if (lastoffset < shdr_info[cnt].shdr.sh_offset + filesz) + lastoffset = shdr_info[cnt].shdr.sh_offset + filesz; + } + + /* Adjust symbol references if symbol tables changed. */ + if (any_symtab_changes) + { + /* Find all relocation sections which use this + symbol table. */ + for (cnt = 1; cnt <= shdridx; ++cnt) + { + if (shdr_info[cnt].idx == 0 && debug_fname == NULL) + /* Ignore sections which are discarded. When we are saving a + relocation section in a separate debug file, we must fix up + the symbol table references. */ + continue; + + if (shdr_info[cnt].shdr.sh_type == SHT_REL + || shdr_info[cnt].shdr.sh_type == SHT_RELA) + { + /* If the symbol table hasn't changed, do not do anything. */ + if (shdr_info[shdr_info[cnt].old_sh_link].newsymidx == NULL) + continue; + + Elf32_Word *newsymidx + = shdr_info[shdr_info[cnt].old_sh_link].newsymidx; + Elf_Data *d = elf_getdata (shdr_info[cnt].idx == 0 + ? elf_getscn (debugelf, cnt) + : elf_getscn (newelf, + shdr_info[cnt].idx), + NULL); + assert (d != NULL); + size_t nrels = (shdr_info[cnt].shdr.sh_size + / shdr_info[cnt].shdr.sh_entsize); + + if (shdr_info[cnt].shdr.sh_type == SHT_REL) + for (size_t relidx = 0; relidx < nrels; ++relidx) + { + GElf_Rel rel_mem; + if (gelf_getrel (d, relidx, &rel_mem) == NULL) + INTERNAL_ERROR (fname); + + size_t symidx = GELF_R_SYM (rel_mem.r_info); + if (newsymidx[symidx] != symidx) + { + rel_mem.r_info + = GELF_R_INFO (newsymidx[symidx], + GELF_R_TYPE (rel_mem.r_info)); + + if (gelf_update_rel (d, relidx, &rel_mem) == 0) + INTERNAL_ERROR (fname); + } + } + else + for (size_t relidx = 0; relidx < nrels; ++relidx) + { + GElf_Rela rel_mem; + if (gelf_getrela (d, relidx, &rel_mem) == NULL) + INTERNAL_ERROR (fname); + + size_t symidx = GELF_R_SYM (rel_mem.r_info); + if (newsymidx[symidx] != symidx) + { + rel_mem.r_info + = GELF_R_INFO (newsymidx[symidx], + GELF_R_TYPE (rel_mem.r_info)); + + if (gelf_update_rela (d, relidx, &rel_mem) == 0) + INTERNAL_ERROR (fname); + } + } + } + else if (shdr_info[cnt].shdr.sh_type == SHT_HASH) + { + /* We have to recompute the hash table. */ + Elf32_Word symtabidx = shdr_info[cnt].old_sh_link; + + /* We do not have to do anything if the symbol table was + not changed. */ + if (shdr_info[symtabidx].newsymidx == NULL) + continue; + + assert (shdr_info[cnt].idx > 0); + + /* The hash section in the new file. */ + scn = elf_getscn (newelf, shdr_info[cnt].idx); + + /* The symbol table data. */ + Elf_Data *symd = elf_getdata (elf_getscn (newelf, + shdr_info[symtabidx].idx), + NULL); + assert (symd != NULL); + + /* The hash table data. */ + Elf_Data *hashd = elf_getdata (scn, NULL); + assert (hashd != NULL); + + if (shdr_info[cnt].shdr.sh_entsize == sizeof (Elf32_Word)) + { + /* Sane arches first. */ + Elf32_Word *bucket = (Elf32_Word *) hashd->d_buf; + + size_t strshndx = shdr_info[symtabidx].old_sh_link; + size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, + ehdr->e_version); + + /* Adjust the nchain value. The symbol table size + changed. We keep the same size for the bucket array. */ + bucket[1] = symd->d_size / elsize; + Elf32_Word nbucket = bucket[0]; + bucket += 2; + Elf32_Word *chain = bucket + nbucket; + + /* New size of the section. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + shdr->sh_size = hashd->d_size + = (2 + symd->d_size / elsize + nbucket) + * sizeof (Elf32_Word); + (void) gelf_update_shdr (scn, shdr); + + /* Clear the arrays. */ + memset (bucket, '\0', + (symd->d_size / elsize + nbucket) + * sizeof (Elf32_Word)); + + for (size_t inner = shdr_info[symtabidx].shdr.sh_info; + inner < symd->d_size / elsize; ++inner) + { + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symd, inner, &sym_mem); + assert (sym != NULL); + + const char *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; + } + } + } + else + { + /* Alpha and S390 64-bit use 64-bit SHT_HASH entries. */ + assert (shdr_info[cnt].shdr.sh_entsize + == sizeof (Elf64_Xword)); + + Elf64_Xword *bucket = (Elf64_Xword *) hashd->d_buf; + + size_t strshndx = shdr_info[symtabidx].old_sh_link; + size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, + ehdr->e_version); + + /* Adjust the nchain value. The symbol table size + changed. We keep the same size for the bucket array. */ + bucket[1] = symd->d_size / elsize; + Elf64_Xword nbucket = bucket[0]; + bucket += 2; + Elf64_Xword *chain = bucket + nbucket; + + /* New size of the section. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + shdr->sh_size = hashd->d_size + = (2 + symd->d_size / elsize + nbucket) + * sizeof (Elf64_Xword); + (void) gelf_update_shdr (scn, shdr); + + /* Clear the arrays. */ + memset (bucket, '\0', + (symd->d_size / elsize + nbucket) + * sizeof (Elf64_Xword)); + + for (size_t inner = shdr_info[symtabidx].shdr.sh_info; + inner < symd->d_size / elsize; ++inner) + { + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symd, inner, &sym_mem); + assert (sym != NULL); + + const char *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; + } + } + } + } + else if (shdr_info[cnt].shdr.sh_type == SHT_GNU_versym) + { + /* If the symbol table changed we have to adjust the + entries. */ + Elf32_Word symtabidx = shdr_info[cnt].old_sh_link; + + /* We do not have to do anything if the symbol table was + not changed. */ + if (shdr_info[symtabidx].newsymidx == NULL) + continue; + + assert (shdr_info[cnt].idx > 0); + + /* The symbol version section in the new file. */ + scn = elf_getscn (newelf, shdr_info[cnt].idx); + + /* The symbol table data. */ + Elf_Data *symd = elf_getdata (elf_getscn (newelf, + shdr_info[symtabidx].idx), + NULL); + assert (symd != NULL); + + /* The version symbol data. */ + Elf_Data *verd = elf_getdata (scn, NULL); + assert (verd != NULL); + + /* The symbol version array. */ + GElf_Half *verstab = (GElf_Half *) verd->d_buf; + + /* New indices of the symbols. */ + Elf32_Word *newsymidx = shdr_info[symtabidx].newsymidx; + + /* Walk through the list and */ + size_t elsize = gelf_fsize (elf, verd->d_type, 1, + ehdr->e_version); + for (size_t inner = 1; inner < verd->d_size / elsize; ++inner) + if (newsymidx[inner] != 0) + /* Overwriting the same array works since the + reordering can only move entries to lower indices + in the array. */ + verstab[newsymidx[inner]] = verstab[inner]; + + /* New size of the section. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + shdr->sh_size = verd->d_size + = gelf_fsize (newelf, verd->d_type, + symd->d_size / gelf_fsize (elf, symd->d_type, 1, + ehdr->e_version), + ehdr->e_version); + (void) gelf_update_shdr (scn, shdr); + } + else if (shdr_info[cnt].shdr.sh_type == SHT_GROUP) + { + /* Check whether the associated symbol table changed. */ + if (shdr_info[shdr_info[cnt].old_sh_link].newsymidx != NULL) + { + /* Yes the symbol table changed. Update the section + header of the section group. */ + scn = elf_getscn (newelf, shdr_info[cnt].idx); + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + assert (shdr != NULL); + + size_t stabidx = shdr_info[cnt].old_sh_link; + shdr->sh_info = shdr_info[stabidx].newsymidx[shdr->sh_info]; + + (void) gelf_update_shdr (scn, shdr); + } + } + } + } + + /* Now that we have done all adjustments to the data, + we can actually write out the debug file. */ + if (debug_fname != NULL) + { + uint32_t debug_crc; + Elf_Data debug_crc_data = + { + .d_type = ELF_T_WORD, + .d_buf = &debug_crc, + .d_size = sizeof (debug_crc), + .d_version = EV_CURRENT + }; + + /* Finally write the file. */ + if (unlikely (elf_update (debugelf, ELF_C_WRITE)) == -1) + { + error (0, 0, gettext ("while writing '%s': %s"), + debug_fname, elf_errmsg (-1)); + result = 1; + goto fail_close; + } + + /* Create the real output file. First rename, then change the + mode. */ + if (rename (tmp_debug_fname, debug_fname) != 0 + || fchmod (debug_fd, mode) != 0) + { + error (0, errno, gettext ("while creating '%s'"), debug_fname); + result = 1; + goto fail_close; + } + + /* The temporary file does not exist anymore. */ + tmp_debug_fname = NULL; + + /* Compute the checksum which we will add to the executable. */ + if (crc32_file (debug_fd, &debug_crc) != 0) + { + error (0, errno, + gettext ("while computing checksum for debug information")); + unlink (debug_fname); + result = 1; + goto fail_close; + } + + /* Store it in the debuglink section data. */ + if (unlikely (gelf_xlatetof (newelf, &debuglink_crc_data, + &debug_crc_data, ehdr->e_ident[EI_DATA]) + != &debuglink_crc_data)) + INTERNAL_ERROR (fname); + } + + /* Finally finish the ELF header. Fill in the fields not handled by + libelf from the old file. */ + newehdr = gelf_getehdr (newelf, &newehdr_mem); + if (newehdr == NULL) + INTERNAL_ERROR (fname); + + 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 = ((shdr_info[shdridx].shdr.sh_offset + + shdr_info[shdridx].shdr.sh_size + offsize - 1) + & ~((GElf_Off) (offsize - 1))); + newehdr->e_shentsize = gelf_fsize (elf, ELF_T_SHDR, 1, EV_CURRENT); + + /* The new section header string table index. */ + if (likely (idx < SHN_HIRESERVE) && likely (idx != SHN_XINDEX)) + newehdr->e_shstrndx = idx; + else + { + /* The index does not fit in the ELF header field. */ + shdr_info[0].scn = elf_getscn (elf, 0); + + if (gelf_getshdr (shdr_info[0].scn, &shdr_info[0].shdr) == NULL) + INTERNAL_ERROR (fname); + + shdr_info[0].shdr.sh_link = idx; + (void) gelf_update_shdr (shdr_info[0].scn, &shdr_info[0].shdr); + + newehdr->e_shstrndx = SHN_XINDEX; + } + + if (gelf_update_ehdr (newelf, newehdr) == 0) + { + error (0, 0, gettext ("%s: error while creating ELF header: %s"), + fname, elf_errmsg (-1)); + return 1; + } + + /* We have everything from the old file. */ + if (elf_cntl (elf, ELF_C_FDDONE) != 0) + { + error (0, 0, gettext ("%s: error while reading the file: %s"), + fname, elf_errmsg (-1)); + return 1; + } + + /* The ELF library better follows our layout when this is not a + relocatable object file. */ + elf_flagelf (newelf, ELF_C_SET, + (ehdr->e_type != ET_REL ? ELF_F_LAYOUT : 0) + | (permissive ? ELF_F_PERMISSIVE : 0)); + + /* Finally write the file. */ + if (elf_update (newelf, ELF_C_WRITE) == -1) + { + error (0, 0, gettext ("while writing '%s': %s"), + fname, elf_errmsg (-1)); + result = 1; + } + + fail_close: + if (shdr_info != NULL) + { + /* For some sections we might have created an table to map symbol + table indices. */ + if (any_symtab_changes) + for (cnt = 1; cnt <= shdridx; ++cnt) + free (shdr_info[cnt].newsymidx); + + /* Free the memory. */ + if ((shnum + 2) * sizeof (struct shdr_info) > MAX_STACK_ALLOC) + free (shdr_info); + } + + /* Free other resources. */ + if (shstrtab_data != NULL) + free (shstrtab_data->d_buf); + if (shst != NULL) + ebl_strtabfree (shst); + + /* That was it. Close the descriptors. */ + if (elf_end (newelf) != 0) + { + error (0, 0, gettext ("error while finishing '%s': %s"), fname, + elf_errmsg (-1)); + result = 1; + } + + if (debugelf != NULL && elf_end (debugelf) != 0) + { + error (0, 0, gettext ("error while finishing '%s': %s"), debug_fname, + elf_errmsg (-1)); + result = 1; + } + + fail: + /* Close the EBL backend. */ + if (ebl != NULL) + ebl_closebackend (ebl); + + /* Close debug file descriptor, if opened */ + if (debug_fd >= 0) + { + if (tmp_debug_fname != NULL) + unlink (tmp_debug_fname); + close (debug_fd); + } + + /* If requested, preserve the timestamp. */ + if (tvp != NULL) + { + if (futimes (fd, tvp) != 0) + { + error (0, errno, gettext ("\ +cannot set access and modification date of '%s'"), + output_fname ?: fname); + result = 1; + } + } + + /* Close the file descriptor if we created a new file. */ + if (output_fname != NULL) + close (fd); + + return result; +} + + +static int +handle_ar (int fd, Elf *elf, const char *prefix, const char *fname, + struct timeval tvp[2]) +{ + size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); + size_t fname_len = strlen (fname) + 1; + char new_prefix[prefix_len + 1 + fname_len]; + char *cp = new_prefix; + + /* Create the full name of the file. */ + if (prefix != NULL) + { + cp = mempcpy (cp, prefix, prefix_len); + *cp++ = ':'; + } + memcpy (cp, fname, fname_len); + + + /* Process all the files contained in the archive. */ + Elf *subelf; + Elf_Cmd cmd = ELF_C_RDWR; + int result = 0; + while ((subelf = elf_begin (fd, cmd, elf)) != NULL) + { + /* The the header for this element. */ + Elf_Arhdr *arhdr = elf_getarhdr (subelf); + + if (elf_kind (subelf) == ELF_K_ELF) + result |= handle_elf (fd, subelf, new_prefix, arhdr->ar_name, 0, NULL); + else if (elf_kind (subelf) == ELF_K_AR) + result |= handle_ar (fd, subelf, new_prefix, arhdr->ar_name, NULL); + + /* Get next archive element. */ + cmd = elf_next (subelf); + if (unlikely (elf_end (subelf) != 0)) + INTERNAL_ERROR (fname); + } + + if (tvp != NULL) + { + if (unlikely (futimes (fd, tvp) != 0)) + { + error (0, errno, gettext ("\ +cannot set access and modification date of '%s'"), fname); + result = 1; + } + } + + if (unlikely (close (fd) != 0)) + error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname); + + return result; +} diff --git a/src/symbolhash.c b/src/symbolhash.c new file mode 100644 index 00000000..da2ae6f6 --- /dev/null +++ b/src/symbolhash.c @@ -0,0 +1,29 @@ +/* Symbol hash table implementation. + Copyright (C) 2001, 2002 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2001. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <string.h> + +#include <ld.h> + +/* Definitions for the symbol hash table. */ +#define TYPE struct symbol * +#define NAME ld_symbol_tab +#define ITERATE 1 +#define COMPARE(a, b) strcmp ((a)->name, (b)->name) + +#include "../lib/dynamicsizehash.c" diff --git a/src/symbolhash.h b/src/symbolhash.h new file mode 100644 index 00000000..a8798c2a --- /dev/null +++ b/src/symbolhash.h @@ -0,0 +1,24 @@ +/* Copyright (C) 2001, 2002 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2001. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifndef SYMBOLHASH_H +#define SYMBOLHASH_H 1 + +/* Definitions for the symbol hash table. */ +#define TYPE struct symbol * +#define NAME ld_symbol_tab +#define ITERATE 1 +#define COMPARE(a, b) strcmp ((a)->name, (b)->name) +#include <dynamicsizehash.h> + +#endif /* symbolhash.h */ diff --git a/src/unaligned.h b/src/unaligned.h new file mode 100644 index 00000000..524b35c8 --- /dev/null +++ b/src/unaligned.h @@ -0,0 +1,98 @@ +/* Unaligned memory access functionality. + Copyright (C) 2000, 2001, 2002, 2003 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2001. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifndef _UNALIGNED_H +#define _UNALIGNED_H 1 + +#include <byteswap.h> +#include <endian.h> + + +#ifndef UNALIGNED_ACCESS_CLASS +# error "UNALIGNED_ACCESS_CLASS must be defined" +#endif + + +/* Macros to convert from the host byte order to that of the object file. */ +#if UNALIGNED_ACCESS_CLASS == BYTE_ORDER +# define target_bswap_16(n) (n) +# define target_bswap_32(n) (n) +# define target_bswap_64(n) (n) +#else +# define target_bswap_16(n) bswap_16 (n) +# define target_bswap_32(n) bswap_32 (n) +# define target_bswap_64(n) bswap_64 (n) +#endif + + +union u_2ubyte_unaligned +{ + uint16_t u; + char c[2]; +} __attribute__((packed)); + +union u_4ubyte_unaligned +{ + uint32_t u; + char c[4]; +} __attribute__((packed)); + +union u_8ubyte_unaligned +{ + uint64_t u; + char c[8]; +} __attribute__((packed)); + + +/* Macros to store value at unaligned address. */ +#define store_2ubyte_unaligned(ptr, value) \ + (void) (((union u_2ubyte_unaligned *) (ptr))->u = target_bswap_16 (value)) +#define store_4ubyte_unaligned(ptr, value) \ + (void) (((union u_4ubyte_unaligned *) (ptr))->u = target_bswap_32 (value)) +#define store_8ubyte_unaligned(ptr, value) \ + (void) (((union u_8ubyte_unaligned *) (ptr))->u = target_bswap_64 (value)) + + +/* Macros to add value to unaligned address. This is a bit more + complicated since the value must be read from memory and eventually + converted twice. */ +#if UNALIGNED_ACCESS_CLASS == BYTE_ORDER +# define add_2ubyte_unaligned(ptr, value) \ + (void) (((union u_2ubyte_unaligned *) (ptr))->u += value) +# define add_4ubyte_unaligned(ptr, value) \ + (void) (((union u_4ubyte_unaligned *) (ptr))->u += value) +# define add_8ubyte_unaligned(ptr, value) \ + (void) (((union u_8ubyte_unaligned *) (ptr))->u += value) +#else +# define add_2ubyte_unaligned(ptr, value) \ + do { \ + union u_2ubyte_unaligned *_ptr = (ptr); \ + uint16_t _val = bswap_16 (_ptr->u) + (value); \ + _ptr->u = bswap_16 (_val); \ + } while (0) +# define add_4ubyte_unaligned(ptr, value) \ + do { \ + union u_4ubyte_unaligned *_ptr = (ptr); \ + uint32_t _val = bswap_32 (_ptr->u) + (value); \ + _ptr->u = bswap_32 (_val); \ + } while (0) +# define add_8ubyte_unaligned(ptr, value) \ + do { \ + union u_8ubyte_unaligned *_ptr = (ptr); \ + uint64_t _val = bswap_64 (_ptr->u) + (value); \ + _ptr->u = bswap_64 (_val); \ + } while (0) +#endif + +#endif /* unaligned.h */ diff --git a/src/versionhash.c b/src/versionhash.c new file mode 100644 index 00000000..79b2e105 --- /dev/null +++ b/src/versionhash.c @@ -0,0 +1,28 @@ +/* Version symbol hash table implementation. + Copyright (C) 2001, 2002 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2001. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <string.h> + +#include <ld.h> + +/* Definitions for the symbol hash table. */ +#define TYPE struct id_list * +#define NAME ld_version_str_tab +#define COMPARE(a, b) strcmp ((a)->id, (b)->id) + +#include "../lib/dynamicsizehash.c" diff --git a/src/versionhash.h b/src/versionhash.h new file mode 100644 index 00000000..243aeeb5 --- /dev/null +++ b/src/versionhash.h @@ -0,0 +1,22 @@ +/* Copyright (C) 2001, 2002 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2001. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifndef VERSIONHASH_H +#define VERSIONHASH_H 1 + +/* Definitions for the symbol hash table. */ +#define TYPE struct id_list * +#define NAME ld_version_str_tab +#include <dynamicsizehash.h> + +#endif /* versionhash.h */ diff --git a/src/xelf.h b/src/xelf.h new file mode 100644 index 00000000..ab36e002 --- /dev/null +++ b/src/xelf.h @@ -0,0 +1,387 @@ +/* Macros to enable writing native and generic ELF access code. + Copyright (C) 2003 Red Hat, Inc. + Written by Ulrich Drepper <[email protected]>, 2003. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <libebl.h> + + +/* By default the linker is handling all architectures. But it can + be configured to be a native-only linker. */ +#if NATIVE_ELF == 32 +/* 32-bit only. */ +# define XElf_Ehdr Elf32_Ehdr +# define XElf_Shdr Elf32_Shdr +# define XElf_Off Elf32_Off +# define XElf_Addr Elf32_Addr +# define XElf_Half Elf32_Half +# define XElf_Word Elf32_Word +# define XElf_Xword Elf32_Word +# define XElf_Sxword Elf32_Sword +# define XElf_Versym Elf32_Versym +# define XElf_Sym Elf32_Sym +# define XElf_Rel Elf32_Rel +# define XElf_Rela Elf32_Rela + +# define XElf_Ehdr_vardef(name) Elf32_Ehdr *name +# define xelf_getehdr(elf, name) name = elf32_getehdr (elf) +# define xelf_getehdr_copy(elf, name, copy) \ + (copy) = *(name = elf32_getehdr (elf)) +# define xelf_newehdr(elf, klass) elf32_newehdr (elf) +# define xelf_update_ehdr(elf, ehdr) \ + /* nothing */ ((void) (elf), (void) (ehdr), 1) + +# define xelf_getclass(elf) ELFCLASS32 + +# define XElf_Phdr_vardef(name) Elf32_Phdr *name +# define xelf_newphdr(elf, n) elf32_newphdr (elf, n) +# define xelf_getphdr(elf, idx, name) name = elf32_getphdr (elf) + idx +# define xelf_getphdr_ptr(elf, idx, name) name = elf32_getphdr (elf) + idx +# define xelf_update_phdr(elf, idx, phdr) \ + /* nothing */ ((void) (elf), (void) (idx), (void) (phdr), 1) + +# define XElf_Shdr_vardef(name) Elf32_Shdr *name +# define xelf_getshdr(scn, name) name = elf32_getshdr (scn) +# define xelf_getshdr_copy(scn, name, copy) \ + (copy) = *(name = elf32_getshdr (scn)) +# define xelf_update_shdr(scn, shdr) \ + /* nothing */ ((void) (scn), (void) (shdr), 1) + +# define XElf_Sym_vardef(name) Elf32_Sym *name +# define xelf_getsym(data, idx, name) \ + name = &((Elf32_Sym *) (data)->d_buf)[idx] +# define xelf_getsym_ptr(data, idx, name) \ + name = &((Elf32_Sym *) (data)->d_buf)[idx] +# define xelf_getsymshndx(data, ndxdata, idx, name1, name2) \ + (name1 = &((Elf32_Sym *) ((data)->d_buf))[idx]); \ + name2 = (unlikely ((ndxdata) != NULL) \ + ? ((Elf32_Word *) ((ndxdata)->d_buf))[idx] : 0) +# define xelf_update_sym(data, idx, sym) \ + /* nothing */ ((void) (data), (void) (idx), (void) (sym), 1) +# define xelf_update_symshndx(data, ndxdata, idx, name1, name2, datachanged) \ + if (datachanged) \ + ((Elf32_Sym *) ((data)->d_buf))[idx] = *name1; \ + if (unlikely (ndxdata != NULL)) \ + ((Elf32_Word *) ((ndxdata)->d_buf))[idx] = name2 + +# define XElf_Versym_vardef(name) Elf32_Versym name +# define xelf_getversym_copy(data, idx, name) \ + (name = ((Elf32_Versym *) ((data)->d_buf))[idx], &name) + +# define XElf_Dyn_vardef(name) Elf32_Dyn *name +# define xelf_getdyn(data, idx, name) \ + name = &((Elf32_Dyn *) ((data)->d_buf))[idx] +# define xelf_getdyn_ptr(data, idx, name) \ + name = &((Elf32_Dyn *) ((data)->d_buf))[idx] +# define xelf_update_dyn(data, idx, name) \ + /* nothing */ ((void) (data), (void) (idx), (void) (name), 1) + +# define XElf_Rel_vardef(name) Elf32_Rel *name +# define xelf_getrel(data, idx, name) \ + name = &((Elf32_Rel *) ((data)->d_buf))[idx] +# define xelf_getrel_ptr(data, idx, name) \ + name = &((Elf32_Rel *) ((data)->d_buf))[idx] +# define xelf_update_rel(data, idx, name) \ + /* nothing */ ((void) (data), (void) (idx), (void) (name), 1) + +# define XElf_Rela_vardef(name) Elf32_Rela *name +# define xelf_getrela(data, idx, name) \ + name = &((Elf32_Rela *) ((data)->d_buf))[idx] +# define xelf_getrela_ptr(data, idx, name) \ + name = &((Elf32_Rela *) ((data)->d_buf))[idx] +# define xelf_update_rela(data, idx, name) \ + /* nothing */ ((void) (data), (void) (idx), (void) (name), 1) + +# define XElf_Verdef_vardef(name) Elf32_Verdef *name +# define xelf_getverdef(data, offset, name) \ + name = ((Elf32_Verdef *) ((char *) ((data)->d_buf) + (offset))) + +# define XElf_Verdaux_vardef(name) Elf32_Verdaux *name +# define xelf_getverdaux(data, offset, name) \ + name = ((Elf32_Verdaux *) ((char *) ((data)->d_buf) + (offset))) + +# define XELF_ST_TYPE(info) ELF32_ST_TYPE (info) +# define XELF_ST_BIND(info) ELF32_ST_BIND (info) +# define XELF_ST_INFO(bind, type) ELF32_ST_INFO (bind, type) +# define XELF_ST_VISIBILITY(info) ELF32_ST_VISIBILITY (info) + +# define XELF_R_SYM(info) ELF32_R_SYM (info) +# define XELF_R_TYPE(info) ELF32_R_TYPE (info) +# define XELF_R_INFO(sym, type) ELF32_R_INFO (sym, type) + +# define xelf_fsize(elf, type, cnt) \ + (__builtin_constant_p (type) \ + ? ({ size_t fsize; \ + switch (type) \ + { \ + case ELF_T_BYTE: fsize = 1; break; \ + case ELF_T_ADDR: fsize = sizeof (Elf32_Addr); break; \ + case ELF_T_DYN: fsize = sizeof (Elf32_Dyn); break; \ + case ELF_T_EHDR: fsize = sizeof (Elf32_Ehdr); break; \ + case ELF_T_HALF: fsize = sizeof (Elf32_Half); break; \ + case ELF_T_OFF: fsize = sizeof (Elf32_Off); break; \ + case ELF_T_PHDR: fsize = sizeof (Elf32_Phdr); break; \ + case ELF_T_RELA: fsize = sizeof (Elf32_Rela); break; \ + case ELF_T_REL: fsize = sizeof (Elf32_Rel); break; \ + case ELF_T_SHDR: fsize = sizeof (Elf32_Shdr); break; \ + case ELF_T_SWORD: fsize = sizeof (Elf32_Sword); break; \ + case ELF_T_SYM: fsize = sizeof (Elf32_Sym); break; \ + case ELF_T_WORD: fsize = sizeof (Elf32_Word); break; \ + case ELF_T_XWORD: fsize = sizeof (Elf32_Xword); break; \ + case ELF_T_SXWORD: fsize = sizeof (Elf32_Sxword); break; \ + case ELF_T_VDEF: fsize = sizeof (Elf32_Verdef); break; \ + case ELF_T_VDAUX: fsize = sizeof (Elf32_Verdaux); break; \ + case ELF_T_VNEED: fsize = sizeof (Elf32_Verneed); break; \ + case ELF_T_VNAUX: fsize = sizeof (Elf32_Vernaux); break; \ + case ELF_T_NHDR: fsize = sizeof (Elf32_Nhdr); break; \ + case ELF_T_SYMINFO: fsize = sizeof (Elf32_Syminfo); break; \ + case ELF_T_MOVE: fsize = sizeof (Elf32_Move); break; \ + default: fsize = 0; break; \ + } \ + fsize * (cnt); }) \ + : gelf_fsize (elf, type, cnt, EV_CURRENT)) +#elif NATIVE_ELF == 64 +/* 64-bit only. */ +# define XElf_Ehdr Elf64_Ehdr +# define XElf_Shdr Elf64_Shdr +# define XElf_Addr Elf64_Addr +# define XElf_Half Elf64_Half +# define XElf_Off Elf64_Off +# define XElf_Word Elf64_Word +# define XElf_Xword Elf64_Xword +# define XElf_Sxword Elf64_Sxword +# define XElf_Versym Elf64_Versym +# define XElf_Sym Elf64_Sym +# define XElf_Rel Elf64_Rel +# define XElf_Rela Elf64_Rela + +# define XElf_Ehdr_vardef(name) Elf64_Ehdr *name +# define xelf_getehdr(elf, name) name = elf64_getehdr (elf) +# define xelf_getehdr_copy(elf, name, copy) \ + (copy) = *(name = elf64_getehdr (elf)) +# define xelf_newehdr(elf, klass) elf64_newehdr (elf) +# define xelf_update_ehdr(elf, ehdr) \ + /* nothing */ ((void) (elf), (void) (ehdr), 1) + +# define xelf_getclass(elf) ELFCLASS32 + +# define XElf_Phdr_vardef(name) Elf64_Phdr *name +# define xelf_newphdr(elf, n) elf64_newphdr (elf, n) +# define xelf_getphdr(elf, idx, name) name = elf64_getphdr (elf) + idx +# define xelf_getphdr_ptr(elf, idx, name) name = elf64_getphdr (elf) + idx +# define xelf_update_phdr(elf, idx, phdr) \ + /* nothing */ ((void) (elf), (void) (idx), (void) (phdr), 1) + +# define XElf_Shdr_vardef(name) Elf64_Shdr *name +# define xelf_getshdr(scn, name) name = elf64_getshdr (scn) +# define xelf_getshdr_copy(scn, name, copy) \ + (copy) = *(name = elf64_getshdr (scn)) +# define xelf_update_shdr(scn, shdr) \ + /* nothing */ ((void) (scn), (void) (shdr), 1) + +# define XElf_Sym_vardef(name) Elf64_Sym *name +# define xelf_getsym(data, idx, name) \ + name = &((Elf64_Sym *) (data)->d_buf)[idx] +# define xelf_getsym_ptr(data, idx, name) \ + name = &((Elf64_Sym *) (data)->d_buf)[idx] +# define xelf_getsymshndx(data, ndxdata, idx, name1, name2) \ + (name1 = &((Elf64_Sym *) ((data)->d_buf))[idx]); \ + name2 = (unlikely ((ndxdata) != NULL) \ + ? ((Elf32_Word *) ((ndxdata)->d_buf))[idx] : 0) +# define xelf_update_sym(data, idx, sym) \ + /* nothing */ ((void) (data), (void) (idx), (void) (sym), 1) +# define xelf_update_symshndx(data, ndxdata, idx, name1, name2, datachanged) \ + if (datachanged) \ + ((Elf64_Sym *) ((data)->d_buf))[idx] = *name1; \ + if (ndxdata != NULL) \ + (((Elf32_Word *) ((ndxdata)->d_buf))[idx] = name2) + +# define XElf_Versym_vardef(name) Elf64_Versym name +# define xelf_getversym_copy(data, idx, name) \ + (name = ((Elf64_Versym *) ((data)->d_buf))[idx], (&name)) + +# define XElf_Dyn_vardef(name) Elf64_Dyn *name +# define xelf_getdyn(data, idx, name) \ + name = &((Elf64_Dyn *) ((data)->d_buf))[idx] +# define xelf_getdyn_ptr(data, idx, name) \ + name = &((Elf64_Dyn *) ((data)->d_buf))[idx] +# define xelf_update_dyn(data, idx, name) \ + /* nothing */ ((void) (data), (void) (idx), (void) (name), 1) + +# define XElf_Rel_vardef(name) Elf64_Rel *name +# define xelf_getrel(data, idx, name) \ + name = &((Elf64_Rel *) ((data)->d_buf))[idx] +# define xelf_getrel_ptr(data, idx, name) \ + name = &((Elf64_Rel *) ((data)->d_buf))[idx] +# define xelf_update_rel(data, idx, name) \ + /* nothing */ ((void) (data), (void) (idx), (void) (name), 1) + +# define XElf_Rela_vardef(name) Elf64_Rela *name +# define xelf_getrela(data, idx, name) \ + name = &((Elf64_Rela *) ((data)->d_buf))[idx] +# define xelf_getrela_ptr(data, idx, name) \ + name = &((Elf64_Rela *) ((data)->d_buf))[idx] +# define xelf_update_rela(data, idx, name) \ + /* nothing */ ((void) (data), (void) (idx), (void) (name), 1) + +# define XElf_Verdef_vardef(name) Elf64_Verdef *name +# define xelf_getverdef(data, offset, name) \ + name = ((Elf64_Verdef *) ((char *) ((data)->d_buf) + (offset))) + +# define XElf_Verdaux_vardef(name) Elf64_Verdaux *name +# define xelf_getverdaux(data, offset, name) \ + name = ((Elf64_Verdaux *) ((char *) ((data)->d_buf) + (offset))) + +# define XELF_ST_TYPE(info) ELF64_ST_TYPE (info) +# define XELF_ST_BIND(info) ELF64_ST_BIND (info) +# define XELF_ST_INFO(bind, type) ELF64_ST_INFO (bind, type) +# define XELF_ST_VISIBILITY(info) ELF64_ST_VISIBILITY (info) + +# define XELF_R_SYM(info) ELF64_R_SYM (info) +# define XELF_R_TYPE(info) ELF64_R_TYPE (info) +# define XELF_R_INFO(sym, type) ELF64_R_INFO (sym, type) + +# define xelf_fsize(elf, type, cnt) \ + (__builtin_constant_p (type) \ + ? ({ size_t fsize; \ + switch (type) \ + { \ + case ELF_T_BYTE: fsize = 1; break; \ + case ELF_T_ADDR: fsize = sizeof (Elf64_Addr); break; \ + case ELF_T_DYN: fsize = sizeof (Elf64_Dyn); break; \ + case ELF_T_EHDR: fsize = sizeof (Elf64_Ehdr); break; \ + case ELF_T_HALF: fsize = sizeof (Elf64_Half); break; \ + case ELF_T_OFF: fsize = sizeof (Elf64_Off); break; \ + case ELF_T_PHDR: fsize = sizeof (Elf64_Phdr); break; \ + case ELF_T_RELA: fsize = sizeof (Elf64_Rela); break; \ + case ELF_T_REL: fsize = sizeof (Elf64_Rel); break; \ + case ELF_T_SHDR: fsize = sizeof (Elf64_Shdr); break; \ + case ELF_T_SWORD: fsize = sizeof (Elf64_Sword); break; \ + case ELF_T_SYM: fsize = sizeof (Elf64_Sym); break; \ + case ELF_T_WORD: fsize = sizeof (Elf64_Word); break; \ + case ELF_T_XWORD: fsize = sizeof (Elf64_Xword); break; \ + case ELF_T_SXWORD: fsize = sizeof (Elf64_Sxword); break; \ + case ELF_T_VDEF: fsize = sizeof (Elf64_Verdef); break; \ + case ELF_T_VDAUX: fsize = sizeof (Elf64_Verdaux); break; \ + case ELF_T_VNEED: fsize = sizeof (Elf64_Verneed); break; \ + case ELF_T_VNAUX: fsize = sizeof (Elf64_Vernaux); break; \ + case ELF_T_NHDR: fsize = sizeof (Elf64_Nhdr); break; \ + case ELF_T_SYMINFO: fsize = sizeof (Elf64_Syminfo); break; \ + case ELF_T_MOVE: fsize = sizeof (Elf64_Move); break; \ + default: fsize = 0; break; \ + } \ + fsize * (cnt); }) \ + : gelf_fsize (elf, type, cnt, EV_CURRENT)) +#else +# include <gelf.h> + +/* Generic linker. */ +# define XElf_Ehdr GElf_Ehdr +# define XElf_Shdr GElf_Shdr +# define XElf_Addr GElf_Addr +# define XElf_Half GElf_Half +# define XElf_Off GElf_Off +# define XElf_Word GElf_Word +# define XElf_Xword GElf_Xword +# define XElf_Sxword GElf_Sxword +# define XElf_Versym GElf_Versym +# define XElf_Sym GElf_Sym +# define XElf_Rel GElf_Rel +# define XElf_Rela GElf_Rela + +# define XElf_Ehdr_vardef(name) GElf_Ehdr name##_mem; GElf_Ehdr *name +# define xelf_getehdr(elf, name) name = gelf_getehdr (elf, &name##_mem) +# define xelf_getehdr_copy(elf, name, copy) \ + name = gelf_getehdr (elf, &(copy)) +# define xelf_newehdr(elf, klass) gelf_newehdr (elf, klass) +# define xelf_update_ehdr(elf, ehdr) gelf_update_ehdr (elf, ehdr) + +# define xelf_getclass(elf) gelf_getclass (elf) + +# define XElf_Phdr_vardef(name) GElf_Phdr name##_mem; GElf_Phdr *name +# define xelf_newphdr(elf, n) gelf_newphdr (elf, n) +# define xelf_getphdr(elf, idx, name) \ + name = gelf_getphdr (elf, idx, &name##_mem) +# define xelf_getphdr_ptr(elf, idx, name) \ + name = &name##_mem +# define xelf_update_phdr(elf, idx, phdr) \ + gelf_update_phdr (elf, idx, phdr) + +# define XElf_Shdr_vardef(name) GElf_Shdr name##_mem; GElf_Shdr *name +# define xelf_getshdr(scn, name) name = gelf_getshdr (scn, &name##_mem) +# define xelf_getshdr_copy(scn, name, copy) \ + name = gelf_getshdr (scn, &(copy)) +# define xelf_update_shdr(scn, shdr) gelf_update_shdr (scn, shdr) + +# define XElf_Sym_vardef(name) GElf_Sym name##_mem; GElf_Sym *name +# define xelf_getsym(data, idx, name) \ + name = gelf_getsym (data, idx, &name##_mem) +# define xelf_getsym_ptr(data, idx, name) \ + name = &name##_mem +# define xelf_getsymshndx(data, ndxdata, idx, name1, name2) \ + name1 = gelf_getsymshndx (data, ndxdata, idx, &name1##_mem, &(name2)) +# define xelf_update_sym(data, idx, sym) gelf_update_sym (data, idx, sym) +# define xelf_update_symshndx(data, ndxdata, idx, name1, name2, datachanged) \ + gelf_update_symshndx (data, ndxdata, idx, name1, name2) + +# define XElf_Versym_vardef(name) GElf_Versym name +# define xelf_getversym_copy(data, idx, name) \ + gelf_getversym (data, idx, &name) + +# define XElf_Dyn_vardef(name) GElf_Dyn name##_mem; GElf_Dyn *name +# define xelf_getdyn(data, idx, name) \ + name = gelf_getdyn (data, idx, &name##_mem) +# define xelf_getdyn_ptr(data, idx, name) \ + name = &name##_mem +# define xelf_update_dyn(data, idx, name) \ + gelf_update_dyn (data, idx, name) + +# define XElf_Rel_vardef(name) GElf_Rel name##_mem; GElf_Rel *name +# define xelf_getrel(data, idx, name) \ + name = gelf_getrel (data, idx, &name##_mem) +# define xelf_getrel_ptr(data, idx, name) \ + name = &name##_mem +# define xelf_update_rel(data, idx, name) \ + gelf_update_rel (data, idx, name) + +# define XElf_Rela_vardef(name) GElf_Rela name##_mem; GElf_Rela *name +# define xelf_getrela(data, idx, name) \ + name = gelf_getrela (data, idx, &name##_mem) +# define xelf_getrela_ptr(data, idx, name) \ + name = &name##_mem +# define xelf_update_rela(data, idx, name) \ + gelf_update_rela (data, idx, name) + +# define XElf_Verdef_vardef(name) GElf_Verdef name##_mem; GElf_Verdef *name +# define xelf_getverdef(data, offset, name) \ + name = gelf_getverdef (data, offset, &name##_mem) + +# define XElf_Verdaux_vardef(name) GElf_Verdaux name##_mem; GElf_Verdaux *name +# define xelf_getverdaux(data, offset, name) \ + name = gelf_getverdaux (data, offset, &name##_mem) + +# define XELF_ST_TYPE(info) GELF_ST_TYPE (info) +# define XELF_ST_BIND(info) GELF_ST_BIND (info) +# define XELF_ST_INFO(bind, type) GELF_ST_INFO (bind, type) +# define XELF_ST_VISIBILITY(info) GELF_ST_VISIBILITY (info) + +# define XELF_R_SYM(info) GELF_R_SYM (info) +# define XELF_R_TYPE(info) GELF_R_TYPE (info) +# define XELF_R_INFO(sym, type) GELF_R_INFO (sym, type) + +# define xelf_fsize(elf, type, cnt) \ + gelf_fsize (elf, type, cnt, EV_CURRENT) +#endif diff --git a/src/ylwrap b/src/ylwrap new file mode 100644 index 00000000..e8abf827 --- /dev/null +++ b/src/ylwrap @@ -0,0 +1,154 @@ +#! /bin/sh +# ylwrap - wrapper for lex/yacc invocations. +# Copyright 1996, 1997, 1998, 1999, 2001 Free Software Foundation, Inc. +# Written by Tom Tromey <[email protected]>. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Usage: +# ylwrap INPUT [OUTPUT DESIRED]... -- PROGRAM [ARGS]... +# * INPUT is the input file +# * OUTPUT is file PROG generates +# * DESIRED is file we actually want +# * PROGRAM is program to run +# * ARGS are passed to PROG +# Any number of OUTPUT,DESIRED pairs may be used. + +# The input. +input="$1" +shift +case "$input" in + [\\/]* | ?:[\\/]*) + # Absolute path; do nothing. + ;; + *) + # Relative path. Make it absolute. + input="`pwd`/$input" + ;; +esac + +pairlist= +while test "$#" -ne 0; do + if test "$1" = "--"; then + shift + break + fi + pairlist="$pairlist $1" + shift +done + +# The program to run. +prog="$1" +shift +# Make any relative path in $prog absolute. +case "$prog" in + [\\/]* | ?:[\\/]*) ;; + *[\\/]*) prog="`pwd`/$prog" ;; +esac + +# FIXME: add hostname here for parallel makes that run commands on +# other machines. But that might take us over the 14-char limit. +dirname=ylwrap$$ +trap "cd `pwd`; rm -rf $dirname > /dev/null 2>&1" 1 2 3 15 +mkdir $dirname || exit 1 + +cd $dirname + +$prog ${1+"$@"} "$input" +status=$? + +if test $status -eq 0; then + set X $pairlist + shift + first=yes + # Since DOS filename conventions don't allow two dots, + # the DOS version of Bison writes out y_tab.c instead of y.tab.c + # and y_tab.h instead of y.tab.h. Test to see if this is the case. + y_tab_nodot="no" + if test -f y_tab.c || test -f y_tab.h; then + y_tab_nodot="yes" + fi + + # The directory holding the input. + input_dir=`echo "$input" | sed -e 's,\([\\/]\)[^\\/]*$,\1,'` + # Quote $INPUT_DIR so we can use it in a regexp. + # FIXME: really we should care about more than `.' and `\'. + input_rx=`echo "$input_dir" | sed 's,\\\\,\\\\\\\\,g;s,\\.,\\\\.,g'` + + while test "$#" -ne 0; do + from="$1" + # Handle y_tab.c and y_tab.h output by DOS + if test $y_tab_nodot = "yes"; then + if test $from = "y.tab.c"; then + from="y_tab.c" + else + if test $from = "y.tab.h"; then + from="y_tab.h" + fi + fi + fi + if test -f "$from"; then + # If $2 is an absolute path name, then just use that, + # otherwise prepend `../'. + case "$2" in + [\\/]* | ?:[\\/]*) target="$2";; + *) target="../$2";; + esac + + # Edit out `#line' or `#' directives. + # + # We don't want the resulting debug information to point at + # an absolute srcdir; it is better for it to just mention the + # .y file with no path. + # + # We want to use the real output file name, not yy.lex.c for + # instance. + # + # We want the include guards to be adjusted too. + FROM=`echo "$from" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\ + -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'` + TARGET=`echo "$2" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\ + -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'` + sed "/^#/{s,$input_rx,,;s,$from,$2,;s,$FORM,$TO,;}" "$from" >"$target" || + status=$? + else + # A missing file is only an error for the first file. This + # is a blatant hack to let us support using "yacc -d". If -d + # is not specified, we don't want an error when the header + # file is "missing". + if test $first = yes; then + status=1 + fi + fi + shift + shift + first=no + done +else + status=$? +fi + +# Remove the directory. +cd .. +rm -rf $dirname + +exit $status |