summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorUlrich Drepper <[email protected]>2005-07-26 05:00:05 +0000
committerUlrich Drepper <[email protected]>2005-07-26 05:00:05 +0000
commitb08d5a8fb42f4586d756068065186b5af7e48dad (patch)
tree9f05f86be7877ed461b4dc05f53b29ea4fc0d2a1 /src
Adjust for monotone.
Diffstat (limited to 'src')
-rw-r--r--src/.cvsignore1
-rw-r--r--src/ChangeLog413
-rw-r--r--src/Makefile.am144
-rw-r--r--src/addr2line.c372
-rw-r--r--src/elf32-i386.script215
-rw-r--r--src/elfcmp.c611
-rw-r--r--src/elflint.c2617
-rw-r--r--src/findtextrel.c596
-rw-r--r--src/i386_ld.c894
-rw-r--r--src/ld.c1506
-rw-r--r--src/ld.h1074
-rw-r--r--src/ldgeneric.c6376
-rw-r--r--src/ldlex.l349
-rw-r--r--src/ldscript.y795
-rw-r--r--src/libld_elf_i386.map7
-rw-r--r--src/nm.c1282
-rw-r--r--src/none_ld.c1
-rw-r--r--src/readelf.c4997
-rw-r--r--src/sectionhash.c69
-rw-r--r--src/sectionhash.h23
-rw-r--r--src/size.c682
-rw-r--r--src/strip.c1750
-rw-r--r--src/symbolhash.c29
-rw-r--r--src/symbolhash.h24
-rw-r--r--src/unaligned.h98
-rw-r--r--src/versionhash.c28
-rw-r--r--src/versionhash.h22
-rw-r--r--src/xelf.h387
-rw-r--r--src/ylwrap154
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,
+ &noteentry->name[ALIGNED_LEN (noteentry->namesz)]);
+ else
+ ebl_object_note (ebl, noteentry->name, noteentry->type,
+ noteentry->descsz,
+ &noteentry->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