aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:29:28 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:29:28 -0800
commit441f72d43a9b550baa779fc82f70816da5f74f0e (patch)
tree477ac2b54c1ef5b8e25e4255b27bca9469ed3911 /src
parent7a80622f69812ca3262d2027c07a4ed0c0242945 (diff)
downloadplatform_external_elfutils-donut-release2.tar.gz
platform_external_elfutils-donut-release2.tar.bz2
platform_external_elfutils-donut-release2.zip
Diffstat (limited to 'src')
-rw-r--r--src/ChangeLog188
-rw-r--r--src/Makefile638
-rw-r--r--src/Makefile.am130
-rw-r--r--src/Makefile.in638
-rw-r--r--src/elf32-i386.script215
-rw-r--r--src/elflint.c2578
-rw-r--r--src/i386_ld.c891
-rw-r--r--src/ld.c1520
-rw-r--r--src/ld.h1073
-rw-r--r--src/ldgeneric.c6376
-rw-r--r--src/ldlex.c2663
-rw-r--r--src/ldlex.l343
-rw-r--r--src/ldscript.c2220
-rw-r--r--src/ldscript.h116
-rw-r--r--src/ldscript.y764
-rw-r--r--src/libld_elf_i386.map7
-rw-r--r--src/nm.c1302
-rw-r--r--src/none_ld.c1
-rw-r--r--src/readelf.c4541
-rw-r--r--src/sectionhash.c69
-rw-r--r--src/sectionhash.h23
-rw-r--r--src/size.c698
-rw-r--r--src/strip.c1769
-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
30 files changed, 29505 insertions, 0 deletions
diff --git a/src/ChangeLog b/src/ChangeLog
new file mode 100644
index 00000000..e5b44049
--- /dev/null
+++ b/src/ChangeLog
@@ -0,0 +1,188 @@
+2004-09-25 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c: Make compile with gcc 4.0.
+ * strip.c: Likewise.
+
+2004-08-16 Ulrich Drepper <drepper@redhat.com>
+
+ * strip.c (handle_elf): Rewrite dynamic memory handling to use of
+ allocate to work around gcc 3.4 bug.
+
+2004-01-25 Ulrich Drepper <drepper@redhat.com>
+
+ * ldlex.l (invalid_char): Better error message.
+
+2004-01-23 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c: Print SHT_GNU_LIBLIST sections.
+
+ * none_ld.c: New file.
+
+2004-01-21 Ulrich Drepper <drepper@redhat.com>
+
+ * Makefile.am: Enable building of machine specific linker.
+
+2004-01-20 Ulrich Drepper <drepper@redhat.com>
+
+ * 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 <drepper@redhat.com>
+
+ * 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 <drepper@redhat.com>
+
+ * readelf.c (print_debug_line_section): Implement.
+
+2004-01-17 Ulrich Drepper <drepper@redhat.com>
+
+ * 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 <drepper@redhat.com>
+
+ * readelf.c (print_debug_info_section): Free dies memory.
+
+ * readelf.c: Print .debug_info section content.
+
+2004-01-13 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (print_shdr): Add support for SHF_ORDERED and SHF_EXCLUDE.
+
+2004-01-12 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (print_debug_aranges): Implement using libdw.
+
+2004-01-11 Ulrich Drepper <drepper@redhat.com>
+
+ * 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 <drepper@redhat.com>
+
+ * nm.c (get_local_names): Adjust call to dwarf_nextcu.
+
+ * nm.c: Implement getting information about local variables.
+
+2004-01-07 Ulrich Drepper <drepper@redhat.com>
+
+ * nm.c: Read also debug information for local symbols.
+
+2004-01-05 Ulrich Drepper <drepper@redhat.com>
+
+ * 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 <drepper@redhat.com>
+
+ * strip.c (process_file): Close file before returning.
+
+2003-11-19 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (handle_dynamic): Make column for tag name wider.
+
+2003-09-29 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (handle_dynamic): Always terminate tag name with a space.
+
+2003-09-25 Ulrich Drepper <drepper@redhat.com>
+
+ * 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 <jakub@redhat.com>
+
+ * 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 <drepper@redhat.com>
+
+ * 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 <drepper@redhat.com>
+
+ * ldgeneric.c (check_definition): Don't add symbol on dso_list if
+ the reference is from another DSO.
+
+2003-08-15 Ulrich Drepper <drepper@redhat.com>
+
+ * 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 <drepper@redhat.com>
+
+ * 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 <drepper@redhat.com>
+
+ * 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 <drepper@redhat.com>
+
+ * 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 <drepper@redhat.com>
+
+ * Moved to CVS archive.
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 00000000..f147eee2
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,638 @@
+# Makefile.in generated by automake 1.9.2 from Makefile.am.
+# src/Makefile. Generated from Makefile.in by configure.
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+
+
+
+SOURCES = $(libld_elf_a_SOURCES) $(libld_elf_i386_pic_a_SOURCES) elflint.c $(ld_SOURCES) $(libld_elf_i386_so_SOURCES) nm.c readelf.c size.c strip.c
+
+srcdir = .
+top_srcdir = ..
+
+pkgdatadir = $(datadir)/elfutils
+pkglibdir = $(libdir)/elfutils
+pkgincludedir = $(includedir)/elfutils
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = /usr/bin/install -c
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = i686-pc-linux-gnu
+host_triplet = i686-pc-linux-gnu
+bin_PROGRAMS = readelf$(EXEEXT) nm$(EXEEXT) size$(EXEEXT) \
+ strip$(EXEEXT) ld$(EXEEXT) elflint$(EXEEXT)
+noinst_PROGRAMS = $(am__EXEEXT_1)
+#am__append_1 = libld_elf.a
+subdir = src
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in ChangeLog ldlex.c ldscript.c ylwrap
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+AR = ar
+ARFLAGS = cru
+libld_elf_a_AR = $(AR) $(ARFLAGS)
+libld_elf_a_LIBADD =
+am_libld_elf_a_OBJECTS = $(base_cpu)_ld.$(OBJEXT)
+libld_elf_a_OBJECTS = $(am_libld_elf_a_OBJECTS)
+libld_elf_i386_pic_a_AR = $(AR) $(ARFLAGS)
+libld_elf_i386_pic_a_LIBADD =
+libld_elf_i386_pic_a_OBJECTS = $(am_libld_elf_i386_pic_a_OBJECTS)
+am__installdirs = "$(DESTDIR)$(bindir)"
+binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+am__EXEEXT_1 = libld_elf_i386.so$(EXEEXT)
+PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
+elflint_SOURCES = elflint.c
+elflint_OBJECTS = elflint.$(OBJEXT)
+am__DEPENDENCIES_1 = ../libebl/libebl.a
+am__DEPENDENCIES_2 = ../libelf/libelf.so
+#am__DEPENDENCIES_2 = ../libelf/libelf.a
+am__DEPENDENCIES_3 = ../lib/libeu.a
+elflint_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \
+ $(am__DEPENDENCIES_3)
+am_ld_OBJECTS = ld.$(OBJEXT) ldgeneric.$(OBJEXT) ldlex.$(OBJEXT) \
+ ldscript.$(OBJEXT) symbolhash.$(OBJEXT) sectionhash.$(OBJEXT) \
+ versionhash.$(OBJEXT)
+ld_OBJECTS = $(am_ld_OBJECTS)
+#am__DEPENDENCIES_4 = libld_elf.a
+ld_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \
+ $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_4)
+am_libld_elf_i386_so_OBJECTS =
+libld_elf_i386_so_OBJECTS = $(am_libld_elf_i386_so_OBJECTS)
+libld_elf_i386_so_LDADD = $(LDADD)
+nm_SOURCES = nm.c
+nm_OBJECTS = nm.$(OBJEXT)
+am__DEPENDENCIES_5 = ../libdw/libdw.so
+#am__DEPENDENCIES_5 = ../libdw/libdw.a
+nm_DEPENDENCIES = $(am__DEPENDENCIES_5) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3)
+readelf_SOURCES = readelf.c
+readelf_OBJECTS = readelf.$(OBJEXT)
+readelf_DEPENDENCIES = $(am__DEPENDENCIES_5) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3)
+size_SOURCES = size.c
+size_OBJECTS = size.$(OBJEXT)
+size_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3)
+strip_SOURCES = strip.c
+strip_OBJECTS = strip.$(OBJEXT)
+strip_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \
+ $(am__DEPENDENCIES_3)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/config/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+LEXCOMPILE = $(LEX) $(LFLAGS) $(AM_LFLAGS)
+YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS)
+SOURCES = $(libld_elf_a_SOURCES) $(libld_elf_i386_pic_a_SOURCES) \
+ elflint.c $(ld_SOURCES) $(libld_elf_i386_so_SOURCES) nm.c \
+ readelf.c size.c strip.c
+DIST_SOURCES = $(libld_elf_a_SOURCES) $(libld_elf_i386_pic_a_SOURCES) \
+ elflint.c $(ld_SOURCES) $(libld_elf_i386_so_SOURCES) nm.c \
+ readelf.c size.c strip.c
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = ${SHELL} /work/elfutils/stock/elfutils-0.97/config/missing --run aclocal-1.9
+AMDEP_FALSE = #
+AMDEP_TRUE =
+AMTAR = ${SHELL} /work/elfutils/stock/elfutils-0.97/config/missing --run tar
+AUTOCONF = ${SHELL} /work/elfutils/stock/elfutils-0.97/config/missing --run autoconf
+AUTOHEADER = ${SHELL} /work/elfutils/stock/elfutils-0.97/config/missing --run autoheader
+AUTOMAKE = ${SHELL} /work/elfutils/stock/elfutils-0.97/config/missing --run automake-1.9
+AWK = gawk
+CC = gcc
+CCDEPMODE = depmode=gcc3
+CFLAGS = -g -O2
+CPP = gcc -E
+CPPFLAGS =
+CYGPATH_W = echo
+DATADIRNAME = ${prefix}/share
+DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H $(YYDEBUG) \
+ -DSRCDIR=\"$(shell cd $(srcdir);pwd)\" -DOBJDIR=\"$(shell pwd)\"
+
+DEPDIR = .deps
+ECHO_C =
+ECHO_N = -n
+ECHO_T =
+EGREP = grep -E
+EXEEXT =
+GMSGFMT = /usr/bin/msgfmt
+INSTALL_DATA = ${INSTALL} -m 644
+INSTALL_PROGRAM = ${INSTALL}
+INSTALL_SCRIPT = ${INSTALL}
+INSTALL_STRIP_PROGRAM = ${SHELL} $(install_sh) -c -s
+INTLLIBS =
+LDFLAGS =
+LEX = flex
+LEXLIB = -lfl
+LEX_OUTPUT_ROOT = lex.yy
+LIBICONV = -liconv
+LIBINTL =
+LIBOBJS =
+LIBS =
+LOCALEDIR = ${prefix}/share
+LTLIBICONV = -liconv
+LTLIBINTL =
+LTLIBOBJS =
+MAKEINFO = ${SHELL} /work/elfutils/stock/elfutils-0.97/config/missing --run makeinfo
+MKINSTALLDIRS = config/mkinstalldirs
+MSGFMT = /usr/bin/msgfmt
+MSGMERGE = /usr/bin/msgmerge
+MUDFLAP_FALSE =
+MUDFLAP_TRUE = #
+NATIVE_LD_FALSE =
+NATIVE_LD_TRUE = #
+OBJEXT = o
+PACKAGE = elfutils
+PACKAGE_BUGREPORT = http://bugzilla.redhat.com/bugzilla/
+PACKAGE_NAME = Red Hat elfutils
+PACKAGE_STRING = Red Hat elfutils 0.97
+PACKAGE_TARNAME = elfutils
+PACKAGE_VERSION = 0.97
+PATH_SEPARATOR = :
+POSUB = po
+RANLIB = ranlib
+SET_MAKE =
+SHELL = /bin/sh
+STRIP =
+USE_NLS = yes
+VERSION = 0.97
+XGETTEXT = /usr/bin/xgettext
+YACC = bison -y -d
+ac_ct_CC = gcc
+ac_ct_RANLIB = ranlib
+ac_ct_STRIP =
+am__fastdepCC_FALSE = #
+am__fastdepCC_TRUE =
+am__include = include
+am__leading_dot = .
+am__quote =
+am__tar = ${AMTAR} chof - "$$tardir"
+am__untar = ${AMTAR} xf -
+base_cpu = none
+bindir = ${exec_prefix}/bin
+build = i686-pc-linux-gnu
+build_alias =
+build_cpu = i686
+build_os = linux-gnu
+build_vendor = pc
+datadir = ${prefix}/share
+exec_prefix = ${prefix}
+host = i686-pc-linux-gnu
+host_alias =
+host_cpu = i686
+host_os = linux-gnu
+host_vendor = pc
+includedir = ${prefix}/include
+infodir = ${prefix}/info
+install_sh = /work/elfutils/stock/elfutils-0.97/config/install-sh
+libdir = ${exec_prefix}/lib
+libexecdir = ${exec_prefix}/libexec
+localstatedir = ${prefix}/var
+mandir = ${prefix}/man
+mkdir_p = mkdir -p --
+oldincludedir = /usr/include
+prefix = /work/elfutils/google/linux-install-elfutils-0.97/
+program_transform_name = s,x,x,
+sbindir = ${exec_prefix}/sbin
+sharedstatedir = ${prefix}/com
+sysconfdir = ${prefix}/etc
+target_alias =
+AM_CFLAGS = -Wall -Wshadow -std=gnu99 \
+ $(if $($(*F)_no_Werror),,-Werror) $(native_ld_cflags)
+
+#AM_CFLAGS = -Wall -Wshadow -std=gnu99 \
+# $(native_ld_cflags)
+
+#AM_LDFLAGS = -fmudflap
+INCLUDES = -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl -I$(srcdir)/../lib -I$(srcdir)/../libdw -I..
+AM_YFLAGS = -pld
+AM_LFLAGS = -Pld -olex.yy.c
+native_ld = @native_ld@
+ld_dsos = libld_elf_i386_pic.a
+noinst_LIBRARIES = libld_elf.a $(ld_dsos)
+#noinst_LIBRARIES = libld_elf.a
+#native_ld_cflags = -DBASE_ELF_NAME=elf_$(base_cpu)
+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
+libdw = ../libdw/libdw.so
+#libdw = ../libdw/libdw.a
+libelf = ../libelf/libelf.so
+#libelf = ../libelf/libelf.a
+libebl = ../libebl/libebl.a
+libeu = ../lib/libeu.a
+readelf_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) -ldl
+nm_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) -ldl
+size_LDADD = $(libelf) $(libeu)
+strip_LDADD = $(libebl) $(libelf) $(libeu) -ldl
+ld_LDADD = $(libebl) $(libelf) $(libeu) -ldl $(am__append_1)
+ld_LDFLAGS = -rdynamic
+elflint_LDADD = $(libebl) $(libelf) $(libeu) -ldl
+ldlex_no_Werror = yes
+
+# 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 =
+CLEANFILES = none_ld.os $(ld_modules:.c=.os)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .l .o .obj .y
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnits src/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnits src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libld_elf.a: $(libld_elf_a_OBJECTS) $(libld_elf_a_DEPENDENCIES)
+ -rm -f libld_elf.a
+ $(libld_elf_a_AR) libld_elf.a $(libld_elf_a_OBJECTS) $(libld_elf_a_LIBADD)
+ $(RANLIB) libld_elf.a
+libld_elf_i386_pic.a: $(libld_elf_i386_pic_a_OBJECTS) $(libld_elf_i386_pic_a_DEPENDENCIES)
+ -rm -f libld_elf_i386_pic.a
+ $(libld_elf_i386_pic_a_AR) libld_elf_i386_pic.a $(libld_elf_i386_pic_a_OBJECTS) $(libld_elf_i386_pic_a_LIBADD)
+ $(RANLIB) libld_elf_i386_pic.a
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(bindir)/$$f"; \
+ done
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+clean-noinstPROGRAMS:
+ -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
+elflint$(EXEEXT): $(elflint_OBJECTS) $(elflint_DEPENDENCIES)
+ @rm -f elflint$(EXEEXT)
+ $(LINK) $(elflint_LDFLAGS) $(elflint_OBJECTS) $(elflint_LDADD) $(LIBS)
+ld$(EXEEXT): $(ld_OBJECTS) $(ld_DEPENDENCIES)
+ @rm -f ld$(EXEEXT)
+ $(LINK) $(ld_LDFLAGS) $(ld_OBJECTS) $(ld_LDADD) $(LIBS)
+nm$(EXEEXT): $(nm_OBJECTS) $(nm_DEPENDENCIES)
+ @rm -f nm$(EXEEXT)
+ $(LINK) $(nm_LDFLAGS) $(nm_OBJECTS) $(nm_LDADD) $(LIBS)
+readelf$(EXEEXT): $(readelf_OBJECTS) $(readelf_DEPENDENCIES)
+ @rm -f readelf$(EXEEXT)
+ $(LINK) $(readelf_LDFLAGS) $(readelf_OBJECTS) $(readelf_LDADD) $(LIBS)
+size$(EXEEXT): $(size_OBJECTS) $(size_DEPENDENCIES)
+ @rm -f size$(EXEEXT)
+ $(LINK) $(size_LDFLAGS) $(size_OBJECTS) $(size_LDADD) $(LIBS)
+strip$(EXEEXT): $(strip_OBJECTS) $(strip_DEPENDENCIES)
+ @rm -f strip$(EXEEXT)
+ $(LINK) $(strip_LDFLAGS) $(strip_OBJECTS) $(strip_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+include ./$(DEPDIR)/$(base_cpu)_ld.Po
+include ./$(DEPDIR)/elflint.Po
+include ./$(DEPDIR)/ld.Po
+include ./$(DEPDIR)/ldgeneric.Po
+include ./$(DEPDIR)/ldlex.Po
+include ./$(DEPDIR)/ldscript.Po
+include ./$(DEPDIR)/nm.Po
+include ./$(DEPDIR)/readelf.Po
+include ./$(DEPDIR)/sectionhash.Po
+include ./$(DEPDIR)/size.Po
+include ./$(DEPDIR)/strip.Po
+include ./$(DEPDIR)/symbolhash.Po
+include ./$(DEPDIR)/versionhash.Po
+
+.c.o:
+ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+# source='$<' object='$@' libtool=no \
+# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
+# $(COMPILE) -c $<
+
+.c.obj:
+ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+# source='$<' object='$@' libtool=no \
+# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
+# $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.l.c:
+ $(LEXCOMPILE) $<
+ sed '/^#/ s|$(LEX_OUTPUT_ROOT)\.c|$@|' $(LEX_OUTPUT_ROOT).c >$@
+ rm -f $(LEX_OUTPUT_ROOT).c
+
+.y.c:
+ $(YACCCOMPILE) $<
+ if test -f y.tab.h; then \
+ to=`echo "$*_H" | sed \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \
+ -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`; \
+ sed -e "/^#/!b" -e "s/Y_TAB_H/$$to/g" -e "s|y\.tab\.h|$*.h|" \
+ y.tab.h >$*.ht; \
+ rm -f y.tab.h; \
+ if cmp -s $*.ht $*.h; then \
+ rm -f $*.ht ;\
+ else \
+ mv $*.ht $*.h; \
+ fi; \
+ fi
+ if test -f y.output; then \
+ mv y.output $*.output; \
+ fi
+ sed '/^#/ s|y\.tab\.c|$@|' y.tab.c >$@t && mv $@t $@
+ rm -f y.tab.c
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES) $(PROGRAMS) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -rm -f ldlex.c
+ -rm -f ldscript.c
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-noinstLIBRARIES \
+ clean-noinstPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am: installcheck-binPROGRAMS
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic clean-noinstLIBRARIES clean-noinstPROGRAMS ctags \
+ distclean distclean-compile distclean-generic distclean-tags \
+ distdir dvi dvi-am html html-am info info-am install \
+ install-am install-binPROGRAMS install-data install-data-am \
+ install-exec install-exec-am install-info install-info-am \
+ install-man install-strip installcheck installcheck-am \
+ installcheck-binPROGRAMS installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-binPROGRAMS uninstall-info-am
+
+
+ldlex.o: ldscript.c
+ldscript.h: ldscript.c
+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
+
+%.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
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 00000000..7b957eb5
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,130 @@
+## Process this file with automake to create Makefile.in
+## Configure input file for elfutils.
+##
+## Copyright (C) 1996-2002, 2003, 2004 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 -std=gnu99 \
+ $(native_ld_cflags)
+else
+AM_CFLAGS = -Wall -Wshadow -std=gnu99 \
+ $(if $($(*F)_no_Werror),,-Werror) $(native_ld_cflags)
+endif
+if MUDFLAP
+AM_LDFLAGS = -fmudflap
+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
+
+
+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
+
+
+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
+
+readelf_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) -ldl
+nm_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) -ldl
+size_LDADD = $(libelf) $(libeu)
+strip_LDADD = $(libebl) $(libelf) $(libeu) -ldl
+ld_LDADD = $(libebl) $(libelf) $(libeu) -ldl
+if NATIVE_LD
+ld_LDADD += libld_elf.a
+endif
+ld_LDFLAGS = -rdynamic
+elflint_LDADD = $(libebl) $(libelf) $(libeu) -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
+
+
+%.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/Makefile.in b/src/Makefile.in
new file mode 100644
index 00000000..0eb72237
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,638 @@
+# Makefile.in generated by automake 1.9.2 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+SOURCES = $(libld_elf_a_SOURCES) $(libld_elf_i386_pic_a_SOURCES) elflint.c $(ld_SOURCES) $(libld_elf_i386_so_SOURCES) nm.c readelf.c size.c strip.c
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = readelf$(EXEEXT) nm$(EXEEXT) size$(EXEEXT) \
+ strip$(EXEEXT) ld$(EXEEXT) elflint$(EXEEXT)
+@NATIVE_LD_FALSE@noinst_PROGRAMS = $(am__EXEEXT_1)
+@NATIVE_LD_TRUE@am__append_1 = libld_elf.a
+subdir = src
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in ChangeLog ldlex.c ldscript.c ylwrap
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+AR = ar
+ARFLAGS = cru
+libld_elf_a_AR = $(AR) $(ARFLAGS)
+libld_elf_a_LIBADD =
+am_libld_elf_a_OBJECTS = $(base_cpu)_ld.$(OBJEXT)
+libld_elf_a_OBJECTS = $(am_libld_elf_a_OBJECTS)
+libld_elf_i386_pic_a_AR = $(AR) $(ARFLAGS)
+libld_elf_i386_pic_a_LIBADD =
+libld_elf_i386_pic_a_OBJECTS = $(am_libld_elf_i386_pic_a_OBJECTS)
+am__installdirs = "$(DESTDIR)$(bindir)"
+binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+am__EXEEXT_1 = libld_elf_i386.so$(EXEEXT)
+PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
+elflint_SOURCES = elflint.c
+elflint_OBJECTS = elflint.$(OBJEXT)
+am__DEPENDENCIES_1 = ../libebl/libebl.a
+@MUDFLAP_FALSE@am__DEPENDENCIES_2 = ../libelf/libelf.so
+@MUDFLAP_TRUE@am__DEPENDENCIES_2 = ../libelf/libelf.a
+am__DEPENDENCIES_3 = ../lib/libeu.a
+elflint_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \
+ $(am__DEPENDENCIES_3)
+am_ld_OBJECTS = ld.$(OBJEXT) ldgeneric.$(OBJEXT) ldlex.$(OBJEXT) \
+ ldscript.$(OBJEXT) symbolhash.$(OBJEXT) sectionhash.$(OBJEXT) \
+ versionhash.$(OBJEXT)
+ld_OBJECTS = $(am_ld_OBJECTS)
+@NATIVE_LD_TRUE@am__DEPENDENCIES_4 = libld_elf.a
+ld_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \
+ $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_4)
+am_libld_elf_i386_so_OBJECTS =
+libld_elf_i386_so_OBJECTS = $(am_libld_elf_i386_so_OBJECTS)
+libld_elf_i386_so_LDADD = $(LDADD)
+nm_SOURCES = nm.c
+nm_OBJECTS = nm.$(OBJEXT)
+@MUDFLAP_FALSE@am__DEPENDENCIES_5 = ../libdw/libdw.so
+@MUDFLAP_TRUE@am__DEPENDENCIES_5 = ../libdw/libdw.a
+nm_DEPENDENCIES = $(am__DEPENDENCIES_5) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3)
+readelf_SOURCES = readelf.c
+readelf_OBJECTS = readelf.$(OBJEXT)
+readelf_DEPENDENCIES = $(am__DEPENDENCIES_5) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3)
+size_SOURCES = size.c
+size_OBJECTS = size.$(OBJEXT)
+size_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3)
+strip_SOURCES = strip.c
+strip_OBJECTS = strip.$(OBJEXT)
+strip_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \
+ $(am__DEPENDENCIES_3)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/config/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+LEXCOMPILE = $(LEX) $(LFLAGS) $(AM_LFLAGS)
+YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS)
+SOURCES = $(libld_elf_a_SOURCES) $(libld_elf_i386_pic_a_SOURCES) \
+ elflint.c $(ld_SOURCES) $(libld_elf_i386_so_SOURCES) nm.c \
+ readelf.c size.c strip.c
+DIST_SOURCES = $(libld_elf_a_SOURCES) $(libld_elf_i386_pic_a_SOURCES) \
+ elflint.c $(ld_SOURCES) $(libld_elf_i386_so_SOURCES) nm.c \
+ readelf.c size.c strip.c
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H $(YYDEBUG) \
+ -DSRCDIR=\"$(shell cd $(srcdir);pwd)\" -DOBJDIR=\"$(shell pwd)\"
+
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GMSGFMT = @GMSGFMT@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LOCALEDIR = @LOCALEDIR@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGMERGE = @MSGMERGE@
+MUDFLAP_FALSE = @MUDFLAP_FALSE@
+MUDFLAP_TRUE = @MUDFLAP_TRUE@
+NATIVE_LD_FALSE = @NATIVE_LD_FALSE@
+NATIVE_LD_TRUE = @NATIVE_LD_TRUE@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+YACC = @YACC@ -d
+ac_ct_CC = @ac_ct_CC@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+base_cpu = @base_cpu@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+@MUDFLAP_FALSE@AM_CFLAGS = -Wall -Wshadow -std=gnu99 \
+@MUDFLAP_FALSE@ $(if $($(*F)_no_Werror),,-Werror) $(native_ld_cflags)
+
+@MUDFLAP_TRUE@AM_CFLAGS = -Wall -Wshadow -std=gnu99 \
+@MUDFLAP_TRUE@ $(native_ld_cflags)
+
+@MUDFLAP_TRUE@AM_LDFLAGS = -fmudflap
+INCLUDES = -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl -I$(srcdir)/../lib -I$(srcdir)/../libdw -I..
+AM_YFLAGS = -pld
+AM_LFLAGS = -Pld -olex.yy.c
+native_ld = @native_ld@
+ld_dsos = libld_elf_i386_pic.a
+@NATIVE_LD_FALSE@noinst_LIBRARIES = libld_elf.a $(ld_dsos)
+@NATIVE_LD_TRUE@noinst_LIBRARIES = libld_elf.a
+@NATIVE_LD_TRUE@native_ld_cflags = -DBASE_ELF_NAME=elf_$(base_cpu)
+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
+@MUDFLAP_FALSE@libdw = ../libdw/libdw.so
+@MUDFLAP_TRUE@libdw = ../libdw/libdw.a
+@MUDFLAP_FALSE@libelf = ../libelf/libelf.so
+@MUDFLAP_TRUE@libelf = ../libelf/libelf.a
+libebl = ../libebl/libebl.a
+libeu = ../lib/libeu.a
+readelf_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) -ldl
+nm_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) -ldl
+size_LDADD = $(libelf) $(libeu)
+strip_LDADD = $(libebl) $(libelf) $(libeu) -ldl
+ld_LDADD = $(libebl) $(libelf) $(libeu) -ldl $(am__append_1)
+ld_LDFLAGS = -rdynamic
+elflint_LDADD = $(libebl) $(libelf) $(libeu) -ldl
+ldlex_no_Werror = yes
+
+# 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 =
+CLEANFILES = none_ld.os $(ld_modules:.c=.os)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .l .o .obj .y
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnits src/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnits src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libld_elf.a: $(libld_elf_a_OBJECTS) $(libld_elf_a_DEPENDENCIES)
+ -rm -f libld_elf.a
+ $(libld_elf_a_AR) libld_elf.a $(libld_elf_a_OBJECTS) $(libld_elf_a_LIBADD)
+ $(RANLIB) libld_elf.a
+libld_elf_i386_pic.a: $(libld_elf_i386_pic_a_OBJECTS) $(libld_elf_i386_pic_a_DEPENDENCIES)
+ -rm -f libld_elf_i386_pic.a
+ $(libld_elf_i386_pic_a_AR) libld_elf_i386_pic.a $(libld_elf_i386_pic_a_OBJECTS) $(libld_elf_i386_pic_a_LIBADD)
+ $(RANLIB) libld_elf_i386_pic.a
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(bindir)/$$f"; \
+ done
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+clean-noinstPROGRAMS:
+ -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
+elflint$(EXEEXT): $(elflint_OBJECTS) $(elflint_DEPENDENCIES)
+ @rm -f elflint$(EXEEXT)
+ $(LINK) $(elflint_LDFLAGS) $(elflint_OBJECTS) $(elflint_LDADD) $(LIBS)
+ld$(EXEEXT): $(ld_OBJECTS) $(ld_DEPENDENCIES)
+ @rm -f ld$(EXEEXT)
+ $(LINK) $(ld_LDFLAGS) $(ld_OBJECTS) $(ld_LDADD) $(LIBS)
+nm$(EXEEXT): $(nm_OBJECTS) $(nm_DEPENDENCIES)
+ @rm -f nm$(EXEEXT)
+ $(LINK) $(nm_LDFLAGS) $(nm_OBJECTS) $(nm_LDADD) $(LIBS)
+readelf$(EXEEXT): $(readelf_OBJECTS) $(readelf_DEPENDENCIES)
+ @rm -f readelf$(EXEEXT)
+ $(LINK) $(readelf_LDFLAGS) $(readelf_OBJECTS) $(readelf_LDADD) $(LIBS)
+size$(EXEEXT): $(size_OBJECTS) $(size_DEPENDENCIES)
+ @rm -f size$(EXEEXT)
+ $(LINK) $(size_LDFLAGS) $(size_OBJECTS) $(size_LDADD) $(LIBS)
+strip$(EXEEXT): $(strip_OBJECTS) $(strip_DEPENDENCIES)
+ @rm -f strip$(EXEEXT)
+ $(LINK) $(strip_LDFLAGS) $(strip_OBJECTS) $(strip_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/$(base_cpu)_ld.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elflint.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ld.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldgeneric.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldlex.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldscript.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readelf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sectionhash.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/size.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strip.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symbolhash.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/versionhash.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.l.c:
+ $(LEXCOMPILE) $<
+ sed '/^#/ s|$(LEX_OUTPUT_ROOT)\.c|$@|' $(LEX_OUTPUT_ROOT).c >$@
+ rm -f $(LEX_OUTPUT_ROOT).c
+
+.y.c:
+ $(YACCCOMPILE) $<
+ if test -f y.tab.h; then \
+ to=`echo "$*_H" | sed \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \
+ -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`; \
+ sed -e "/^#/!b" -e "s/Y_TAB_H/$$to/g" -e "s|y\.tab\.h|$*.h|" \
+ y.tab.h >$*.ht; \
+ rm -f y.tab.h; \
+ if cmp -s $*.ht $*.h; then \
+ rm -f $*.ht ;\
+ else \
+ mv $*.ht $*.h; \
+ fi; \
+ fi
+ if test -f y.output; then \
+ mv y.output $*.output; \
+ fi
+ sed '/^#/ s|y\.tab\.c|$@|' y.tab.c >$@t && mv $@t $@
+ rm -f y.tab.c
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES) $(PROGRAMS) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -rm -f ldlex.c
+ -rm -f ldscript.c
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-noinstLIBRARIES \
+ clean-noinstPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am: installcheck-binPROGRAMS
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic clean-noinstLIBRARIES clean-noinstPROGRAMS ctags \
+ distclean distclean-compile distclean-generic distclean-tags \
+ distdir dvi dvi-am html html-am info info-am install \
+ install-am install-binPROGRAMS install-data install-data-am \
+ install-exec install-exec-am install-info install-info-am \
+ install-man install-strip installcheck installcheck-am \
+ installcheck-binPROGRAMS installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-binPROGRAMS uninstall-info-am
+
+
+ldlex.o: ldscript.c
+ldscript.h: ldscript.c
+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
+
+%.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
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
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/elflint.c b/src/elflint.c
new file mode 100644
index 00000000..d66cdd13
--- /dev/null
+++ b/src/elflint.c
@@ -0,0 +1,2578 @@
+/* Pedantic checking of ELF files compliance with gABI/psABI spec.
+ Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc.
+ Written by Ulrich Drepper <drepper@redhat.com>, 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 <libebl.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>
+
+
+/* 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;
+
+
+#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.") },
+ { "quiet", 'q', NULL, 0, N_("Do not print anything if successful") },
+ { "gnu-ld", ARGP_gnuld, NULL, 0,
+ N_("Binary has been created with GNU ld and is therefore known to be \
+broken in certain ways") },
+ { NULL, 0, NULL, 0, NULL }
+};
+
+/* 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);
+
+/* Function to print some extra text in the help message. */
+static char *more_help (int key, const char *text, void *input);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, args_doc, doc, NULL, more_help
+};
+
+
+/* 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 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 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[])
+{
+ int remaining;
+ bool only_one;
+
+ /* Set locale. */
+ setlocale (LC_ALL, "");
+
+ /* Initialize the message catalog. */
+ textdomain (PACKAGE);
+
+ /* Parse and process arguments. */
+ argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+ /* If no ELF file is given punt. */
+ if (remaining >= argc)
+ {
+ argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
+ program_invocation_short_name);
+ exit (1);
+ }
+
+ /* 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. */
+ only_one = remaining + 1 == argc;
+ do
+ {
+ int fd;
+ Elf *elf;
+
+ /* Open the file. */
+ fd = open (argv[remaining], O_RDONLY);
+ if (fd == -1)
+ {
+ error (0, errno, gettext ("cannot open input file"));
+ continue;
+ }
+
+ /* Create an `Elf' descriptor. */
+ 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, struct argp_state *state)
+{
+ switch (key)
+ {
+ case ARGP_strict:
+ be_strict = true;
+ break;
+
+ case 'q':
+ be_quiet = true;
+ break;
+
+ case ARGP_gnuld:
+ gnuld = true;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+static char *
+more_help (int key, const char *text, void *input)
+{
+ char *buf;
+
+ switch (key)
+ {
+ case ARGP_KEY_HELP_EXTRA:
+ /* We print some extra information. */
+ if (asprintf (&buf, gettext ("Please report bugs to %s.\n"),
+ PACKAGE_BUGREPORT) < 0)
+ buf = NULL;
+ return buf;
+
+ default:
+ break;
+ }
+ return (char *) text;
+}
+
+
+/* Print the version information. */
+static void
+print_version (FILE *stream, struct argp_state *state)
+{
+ 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\
+"), "2004");
+ 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, GElf_Ehdr *ehdr, 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, GElf_Ehdr *ehdr, 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, ehdr, idx));
+ else
+ ERROR (gettext ("\
+section [%2d] '%s': section group [%2zu] '%s' does not preceed group member\n"),
+ idx, section_name (ebl, ehdr, idx),
+ cnt, section_name (ebl, ehdr, 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, ehdr, 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, ehdr, shdr->sh_link),
+ idx, section_name (ebl, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, idx), "st_name");
+ if (sym->st_value != 0)
+ ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"),
+ idx, section_name (ebl, ehdr, idx), "st_value");
+ if (sym->st_size != 0)
+ ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"),
+ idx, section_name (ebl, ehdr, idx), "st_size");
+ if (sym->st_info != 0)
+ ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"),
+ idx, section_name (ebl, ehdr, idx), "st_info");
+ if (sym->st_other != 0)
+ ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"),
+ idx, section_name (ebl, ehdr, idx), "st_other");
+ if (sym->st_shndx != 0)
+ ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"),
+ idx, section_name (ebl, ehdr, idx), "st_shndx");
+ if (xndxdata != NULL && xndx != 0)
+ ERROR (gettext ("\
+section [%2d] '%s': XINDEX for zeroth entry not zero\n"),
+ xndxscnidx, section_name (ebl, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, idx), cnt);
+ if (cnt < shdr->sh_info)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: local COMMON symbols are nonsense\n"),
+ idx, section_name (ebl, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, idx), cnt,
+ (int) xndx, section_name (ebl, ehdr, 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, ehdr, idx), cnt,
+ (int) xndx, section_name (ebl, ehdr, 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, ehdr, idx), cnt,
+ (int) xndx, section_name (ebl, ehdr, 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, ehdr, idx), cnt,
+ (int) xndx, section_name (ebl, ehdr, 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, ehdr, 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, ehdr, idx), cnt,
+ (int) xndx, section_name (ebl, ehdr, 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, ehdr, idx), cnt,
+ (int) xndx, section_name (ebl, ehdr, 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, ehdr, idx), cnt,
+ (int) xndx, section_name (ebl, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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;
+
+ 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 && strcmp (sname, ".got") == 0)
+ {
+ /* Found it. */
+ if (sym->st_value != gshdr->sh_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, ehdr, idx),
+ (uint64_t) sym->st_value,
+ (uint64_t) gshdr->sh_addr);
+
+ if (sym->st_size != gshdr->sh_size)
+ ERROR (gettext ("\
+section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol size %" PRIu64 " does not match .got section size %" PRIu64 "\n"),
+ idx, section_name (ebl, ehdr, idx),
+ (uint64_t) sym->st_size,
+ (uint64_t) gshdr->sh_size);
+
+ break;
+ }
+ }
+
+ if (gscn == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol present, but no .got section\n"),
+ idx, section_name (ebl, ehdr, 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, ehdr, idx),
+ (uint64_t) sym->st_value,
+ (uint64_t) phdr->p_vaddr);
+
+ if (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, ehdr, 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, ehdr, 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);
+ int 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, idx), cnt);
+ }
+ }
+}
+
+
+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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, idx), cnt);
+ }
+ }
+}
+
+
+/* Number of dynamic sections. */
+static int ndynamic;
+
+
+static void
+check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, 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, ehdr, 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, ehdr, shdr->sh_link),
+ idx, section_name (ebl, ehdr, 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, ehdr, idx));
+
+ if (shdr->sh_info != 0)
+ ERROR (gettext ("section [%2d] '%s': sh_info not zero\n"),
+ idx, section_name (ebl, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, idx), cnt);
+
+ if (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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, idx),
+ "DT_REL", "DT_RELSZ", "DT_RELENT");
+}
+
+
+static void
+check_symtab_shndx (Ebl *ebl, GElf_Ehdr *ehdr, 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, ehdr, 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, ehdr, 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, ehdr, idx));
+
+ if (shdr->sh_info != 0)
+ ERROR (gettext ("section [%2d] '%s': sh_info not zero\n"),
+ idx, section_name (ebl, ehdr, 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, ehdr, idx),
+ cnt, section_name (ebl, ehdr, 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, GElf_Ehdr *ehdr, 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, ehdr, 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, ehdr, 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, ehdr, idx));
+
+ if ((shdr->sh_flags & SHF_ALLOC) == 0)
+ ERROR (gettext ("section [%2d] '%s': not marked to be allocated\n"),
+ idx, section_name (ebl, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, idx), cnt - 2 - nbucket);
+ }
+}
+
+
+static void
+check_null (Ebl *ebl, GElf_Ehdr *ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, idx));
+
+ if (shdr->sh_flags != 0)
+ ERROR (gettext ("section [%2d] '%s': sh_flags not zero\n"),
+ idx, section_name (ebl, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, idx));
+
+ if (data->d_size < elsize)
+ ERROR (gettext ("\
+section [%2d] '%s': section group without flags word\n"),
+ idx, section_name (ebl, ehdr, 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, ehdr, idx));
+ else if (data->d_size < 3 * elsize)
+ ERROR (gettext ("\
+section [%2d] '%s': section group with only one member\n"),
+ idx, section_name (ebl, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, idx),
+ val, section_name (ebl, ehdr, 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, ehdr, idx), cnt / elsize,
+ val, section_name (ebl, ehdr, val));
+ }
+
+ if (++scnref[val] == 2)
+ ERROR (gettext ("\
+section [%2d] '%s' is contained in more than one section group\n"),
+ val, section_name (ebl, ehdr, 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_Ehdr *ehdr, 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, ehdr, idx),
+ shdr->sh_link, section_name (ebl, ehdr, 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, ehdr, idx),
+ shdr->sh_link, section_name (ebl, ehdr, 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, ehdr, 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)
+ 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, cnt));
+
+ if (shdr->sh_flags & SHF_GROUP)
+ check_scn_group (ebl, ehdr, 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, ehdr, cnt), pcnt);
+
+ if (shdr->sh_type == SHT_NOBITS)
+ {
+ if (shdr->sh_offset < phdr->p_offset + phdr->p_filesz)
+ 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, 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, ehdr, cnt);
+ break;
+
+ case SHT_SYMTAB_SHNDX:
+ check_symtab_shndx (ebl, ehdr, cnt);
+ break;
+
+ case SHT_HASH:
+ check_hash (ebl, ehdr, cnt);
+ break;
+
+ case SHT_NULL:
+ check_null (ebl, ehdr, shdr, cnt);
+ break;
+
+ case SHT_GROUP:
+ check_group (ebl, ehdr, shdr, cnt);
+ break;
+
+ case SHT_GNU_versym:
+ check_versym (ebl, ehdr, 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);
+
+ 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)
+ 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, cnt, &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/i386_ld.c b/src/i386_ld.c
new file mode 100644
index 00000000..28304ca0
--- /dev/null
+++ b/src/i386_ld.c
@@ -0,0 +1,891 @@
+/* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc.
+ Written by Ulrich Drepper <drepper@redhat.com>, 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, int klass,
+ int data)
+{
+ /* 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, 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)
+{
+ 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)
+ {
+ int 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)
+{
+ /* 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..a6b664f7
--- /dev/null
+++ b/src/ld.c
@@ -0,0 +1,1520 @@
+/* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc.
+ Written by Ulrich Drepper <drepper@redhat.com>, 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;
+
+
+/* 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.") },
+ { "no-whole-archive", ARGP_no_whole_archive, NULL, 0,
+ N_("Stop including the whole arhives in the output.") },
+
+ { "output", 'o', N_("FILE"), 0, N_("Place output in FILE.") },
+
+ { NULL, 'O', N_("LEVEL"), OPTION_ARG_OPTIONAL,
+ N_("Set optimization level to LEVEL.") },
+
+ { "verbose", 'v', NULL, 0, N_("Verbose messages.") },
+ { "trace", 't', NULL, 0, N_("Trace file opens.") },
+ { "conserve-memory", ARGP_conserve, NULL, 0,
+ N_("Trade speed for less memory usage") },
+
+ { NULL, 'z', "KEYWORD", OPTION_HIDDEN, NULL },
+ { "-z nodefaultlib", '\0', NULL, OPTION_DOC,
+ N_("Object is marked to not use default search path at runtime.") },
+ { "-z allextract", '\0', NULL, OPTION_DOC,
+ N_("Same as --whole-archive.") },
+ { "-z defaultextract", '\0', NULL, OPTION_DOC, N_("\
+Default rules of extracting from archive; weak references are not enough.") },
+ { "-z weakextract", '\0', NULL, OPTION_DOC,
+ N_("Weak references cause extraction from archive.") },
+ { "-z muldefs", '\0', NULL, OPTION_DOC,
+ N_("Allow multiple definitions; first is used.") },
+ { "-z defs | nodefs", '\0', NULL, OPTION_DOC,
+ N_("Disallow/allow undefined symbols in DSOs.") },
+ { "no-undefined", ARGP_no_undefined, NULL, OPTION_HIDDEN, NULL },
+ { "-z origin", '\0', NULL, OPTION_DOC,
+ N_("Object requires immediate handling of $ORIGIN.") },
+ { "-z now", '\0', NULL, OPTION_DOC,
+ N_("Relocation will not be processed lazily.") },
+ { "-z nodelete", '\0', NULL, OPTION_DOC,
+ N_("Object cannot be unloaded at runtime.") },
+ { "-z initfirst", '\0', NULL, OPTION_DOC,
+ N_("Mark object to be initialized first.") },
+ { "-z lazyload | nolazyload", '\0', NULL, OPTION_DOC,
+ N_("Enable/disable lazy-loading flag for following dependencies.") },
+ { "-z nodlopen", '\0', NULL, OPTION_DOC,
+ N_("Mark object as not loadable with 'dlopen'.") },
+ { "-z ignore | record", '\0', NULL, OPTION_DOC,
+ N_("Ignore/record dependencies on unused DSOs.") },
+ { "-z systemlibrary", '\0', NULL, OPTION_DOC,
+ N_("Generated DSO will be a system library.") },
+
+ { NULL, 'l', N_("FILE"), OPTION_HIDDEN, NULL },
+
+ { NULL, '(', NULL, 0, N_("Start a group.") },
+ { NULL, ')', NULL, 0, N_("End a group.") },
+
+ { NULL, 'L', N_("PATH"), 0,
+ N_("Add PATH to list of directories files are searched in.") },
+
+ { NULL, 'c', N_("FILE"), 0, N_("Use linker script in FILE.") },
+
+ { "entry", 'e', N_("ADDRESS"), 0, N_("Set entry point address.") },
+
+ { "static", ARGP_static, NULL, OPTION_HIDDEN, NULL },
+ { "-B static", ARGP_static, NULL, OPTION_DOC,
+ N_("Do not link against shared libraries.") },
+ { "dynamic", ARGP_dynamic, NULL, OPTION_HIDDEN, NULL },
+ { "-B dynamic", ARGP_dynamic, NULL, OPTION_DOC,
+ N_("Prefer linking against shared libraries.") },
+
+ { "export-dynamic", 'E', NULL, 0, N_("Export all dynamic symbols.") },
+
+ { "strip-all", 's', NULL, 0, N_("Strip all symbols.") },
+ { "strip-debug", 'S', NULL, 0, N_("Strip debugging symbols.") },
+
+ { "pagesize", ARGP_pagesize, "SIZE", 0,
+ N_("Assume pagesize for the target system to be SIZE.") },
+
+ { "rpath", ARGP_rpath, "PATH", OPTION_HIDDEN, NULL },
+ { "rpath-link", ARGP_rpath_link, "PATH", OPTION_HIDDEN, NULL },
+
+ { "runpath", ARGP_runpath, "PATH", 0, N_("Set runtime DSO search path.") },
+ { "runpath-link", ARGP_runpath_link, "PATH", 0,
+ N_("Set link time DSO search path.") },
+
+ { NULL, 'i', NULL, 0, N_("Ignore LD_LIBRARY_PATH environment variable.") },
+
+ { "version-script", ARGP_version_script, "FILE", 0,
+ N_("Read version information from FILE.") },
+
+ { "emulation", 'm', "NAME", 0, N_("Set emulation to NAME.") },
+
+ { "shared", 'G', NULL, 0, N_("Generate dynamic shared object.") },
+ { NULL, 'r', NULL, 0L, N_("Generate relocatable object.") },
+
+ { NULL, 'B', "KEYWORD", OPTION_HIDDEN, "" },
+ { "-B local", 'B', NULL, OPTION_DOC,
+ N_("Causes symbol not assigned to a version be reduced to local.") },
+
+ { "gc-sections", ARGP_gc_sections, NULL, 0, N_("Remove unused sections.") },
+ { "no-gc-sections", ARGP_no_gc_sections, NULL, 0,
+ N_("Don't remove unused sections.") },
+
+ { "soname", 'h', "NAME", 0, N_("Set soname of shared object.") },
+ { "dynamic-linker", 'I', "NAME", 0, N_("Set the dynamic linker name.") },
+
+ { NULL, 'Q', "YN", OPTION_HIDDEN, NULL },
+ { "-Q y | n", 'Q', NULL, OPTION_DOC,
+ N_("Add/suppress addition indentifying link-editor to .comment section") },
+
+#if YYDEBUG
+ { "yydebug", ARGP_yydebug, NULL, 0,
+ N_("Select to get parser debug information") },
+#endif
+
+ { NULL, 0, NULL, 0, NULL }
+};
+
+/* 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);
+
+/* Function to print some extra text in the help message. */
+static char *more_help (int key, const char *text, void *input);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp_1st =
+{
+ options, parse_opt_1st, args_doc, doc, NULL, more_help
+};
+static struct argp argp_2nd =
+{
+ options, parse_opt_2nd, args_doc, doc, NULL, more_help
+};
+
+
+/* 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);
+}
+
+
+static char *
+more_help (int key, const char *text, void *input)
+{
+ char *buf;
+
+ switch (key)
+ {
+ case ARGP_KEY_HELP_EXTRA:
+ /* We print some extra information. */
+ if (asprintf (&buf, gettext ("Please report bugs to %s.\n"),
+ PACKAGE_BUGREPORT) < 0)
+ buf = NULL;
+ return buf;
+
+ default:
+ break;
+ }
+ return (char *) text;
+}
+
+
+/* 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)
+{
+ 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)
+{
+ 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)
+{
+ 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\
+"), "2004");
+ 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..760ff125
--- /dev/null
+++ b/src/ld.h
@@ -0,0 +1,1073 @@
+/* Copyright (C) 2001, 2002, 2003 Red Hat, Inc.
+ Written by Ulrich Drepper <drepper@redhat.com>, 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;
+};
+
+
+/* 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, int symidx);
+extern inline bool
+linked_from_dso_p (struct scninfo *scninfo, int 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..a33e9fcc
--- /dev/null
+++ b/src/ldgeneric.c
@@ -0,0 +1,6376 @@
+/* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc.
+ Written by Ulrich Drepper <drepper@redhat.com>, 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, int 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, int 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 = ~0;
+ 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 == ~0)
+ 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_NULL && 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.c b/src/ldlex.c
new file mode 100644
index 00000000..e0bf0a5f
--- /dev/null
+++ b/src/ldlex.c
@@ -0,0 +1,2663 @@
+#define yy_create_buffer ld_create_buffer
+#define yy_delete_buffer ld_delete_buffer
+#define yy_scan_buffer ld_scan_buffer
+#define yy_scan_string ld_scan_string
+#define yy_scan_bytes ld_scan_bytes
+#define yy_flex_debug ld_flex_debug
+#define yy_init_buffer ld_init_buffer
+#define yy_flush_buffer ld_flush_buffer
+#define yy_load_buffer_state ld_load_buffer_state
+#define yy_switch_to_buffer ld_switch_to_buffer
+#define yyin ldin
+#define yyleng ldleng
+#define yylex ldlex
+#define yyout ldout
+#define yyrestart ldrestart
+#define yytext ldtext
+#define yylineno ldlineno
+
+#line 20 "ldlex.c"
+/* A lexical scanner generated by flex */
+
+/* Scanner skeleton version:
+ * $Header: /home/daffy/u0/vern/flex/RCS/flex.skl,v 2.91 96/09/10 16:58:48 vern Exp $
+ */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+
+#include <stdio.h>
+#include <unistd.h>
+
+
+/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+
+
+#ifdef __cplusplus
+
+#include <stdlib.h>
+
+/* Use prototypes in function declarations. */
+#define YY_USE_PROTOS
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef __TURBOC__
+ #pragma warn -rch
+ #pragma warn -use
+#include <io.h>
+#include <stdlib.h>
+#define YY_USE_CONST
+#define YY_USE_PROTOS
+#endif
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+
+#ifdef YY_USE_PROTOS
+#define YY_PROTO(proto) proto
+#else
+#define YY_PROTO(proto) ()
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#define YY_BUF_SIZE 16384
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+extern int yyleng;
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+/* The funky do-while in the following #define is used to turn the definition
+ * int a single C statement (which needs a semi-colon terminator). This
+ * avoids problems with code like:
+ *
+ * if ( condition_holds )
+ * yyless( 5 );
+ * else
+ * do_something_else();
+ *
+ * Prior to using the do-while the compiler would get upset at the
+ * "else" because it interpreted the "if" statement as being all
+ * done when it reached the ';' after the yyless() call.
+ */
+
+/* Return all but the first 'n' matched characters back to the input stream. */
+
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ *yy_cp = yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yytext_ptr )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+typedef unsigned int yy_size_t;
+
+
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+ };
+
+static YY_BUFFER_STATE yy_current_buffer = 0;
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ */
+#define YY_CURRENT_BUFFER yy_current_buffer
+
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+
+
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart YY_PROTO(( FILE *input_file ));
+
+void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer ));
+void yy_load_buffer_state YY_PROTO(( void ));
+YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size ));
+void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file ));
+void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer )
+
+YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size ));
+YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str ));
+YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len ));
+
+static void *yy_flex_alloc YY_PROTO(( yy_size_t ));
+static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t ));
+static void yy_flex_free YY_PROTO(( void * ));
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! yy_current_buffer ) \
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ yy_current_buffer->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! yy_current_buffer ) \
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ yy_current_buffer->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (yy_current_buffer->yy_at_bol)
+
+
+#define YY_USES_REJECT
+
+#define yywrap() 1
+#define YY_SKIP_YYWRAP
+typedef unsigned char YY_CHAR;
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+typedef int yy_state_type;
+extern int yylineno;
+int yylineno = 1;
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state YY_PROTO(( void ));
+static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state ));
+static int yy_get_next_buffer YY_PROTO(( void ));
+static void yy_fatal_error YY_PROTO(( yyconst char msg[] ));
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yytext_ptr = yy_bp; \
+ yyleng = (int) (yy_cp - yy_bp); \
+ yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 49
+#define YY_END_OF_BUFFER 50
+static yyconst short int yy_acclist[373] =
+ { 0,
+ 50, 48, 49, 47, 48, 49, 47, 49, 40, 48,
+ 49, 41, 48, 49, 31, 48, 49, 32, 48, 49,
+ 38, 45, 48, 49, 36, 48, 49, 43, 48, 49,
+ 37, 48, 49, 45, 48, 49, 39, 48, 49, 44,
+ 45, 48, 49, 44, 45, 48, 49, 33, 48, 49,
+ 34, 48, 49, 35, 48, 49, 45, 48, 49, 45,
+ 48, 49, 45, 48, 49, 45, 48, 49, 45, 48,
+ 49, 45, 48, 49, 45, 48, 49, 45, 48, 49,
+ 45, 48, 49, 45, 48, 49, 48, 49, 48, 49,
+ 45, 48, 49, 45, 48, 49, 29, 48, 49, 42,
+
+ 48, 49, 30, 48, 49, 48, 49, 9, 49, 9,
+ 49, 47, 45, 46, 45, 46, 10, 46, 44, 45,
+ 46, 44, 45, 46, 44, 45, 46, 45, 46, 44,
+ 45, 46, 45, 46, 45, 46, 45, 46, 45, 46,
+ 45, 46, 45, 46, 45, 46, 45, 46, 45, 46,
+ 45, 46, 45, 46, 45, 46, 45, 46, 28, 45,
+ 46, 45, 46, 44, 45, 46, 45, 46, 45, 46,
+ 45, 46, 45, 46, 45, 46, 45, 46, 45, 46,
+ 45, 46, 45, 46, 45, 46, 45, 46, 45, 46,
+ 45, 46, 45, 46, 45, 46, 45, 46, 45, 46,
+
+ 44, 45, 46, 45, 46, 45, 46, 45, 46, 45,
+ 46, 45, 46, 45, 46, 18, 45, 46, 45, 46,
+ 45, 46, 45, 46, 45, 46, 45, 46, 45, 46,
+ 26, 45, 46, 45, 46, 45, 46, 45, 46, 11,
+ 45, 46, 12, 45, 46, 45, 46, 15, 45, 46,
+ 16, 45, 46, 45, 46, 45, 46, 45, 46, 45,
+ 46, 45, 46, 45, 46, 45, 46, 45, 46, 45,
+ 46, 45, 46, 45, 46, 17, 45, 46, 45, 46,
+ 45, 46, 45, 46, 45, 46, 45, 46, 45, 46,
+ 45, 46, 45, 46, 19, 2, 6, 45, 46, 45,
+
+ 46, 45, 46, 22, 45, 46, 45, 46, 24, 45,
+ 46, 45, 46, 27, 45, 46, 14, 4, 1, 8,
+ 5, 45, 46, 45, 46, 21, 45, 46, 45, 46,
+ 45, 46, 45, 46, 45, 46, 45, 46, 45, 46,
+ 3, 7, 45, 46, 45, 46, 23, 45, 46, 45,
+ 46, 45, 46, 45, 46, 45, 46, 13, 45, 46,
+ 45, 46, 45, 46, 20, 45, 46, 45, 46, 25,
+ 45, 46
+ } ;
+
+static yyconst short int yy_accept[212] =
+ { 0,
+ 1, 1, 1, 1, 1, 2, 4, 7, 9, 12,
+ 15, 18, 21, 25, 28, 31, 34, 37, 40, 44,
+ 48, 51, 54, 57, 60, 63, 66, 69, 72, 75,
+ 78, 81, 84, 87, 89, 91, 94, 97, 100, 103,
+ 106, 108, 110, 112, 113, 114, 115, 117, 119, 122,
+ 125, 128, 130, 133, 135, 137, 139, 141, 143, 145,
+ 147, 149, 151, 153, 155, 157, 159, 159, 160, 162,
+ 164, 164, 164, 164, 164, 167, 169, 171, 173, 175,
+ 177, 179, 181, 183, 185, 187, 189, 191, 193, 195,
+ 197, 197, 199, 201, 201, 201, 201, 201, 201, 201,
+
+ 204, 206, 208, 210, 212, 214, 216, 219, 221, 223,
+ 225, 227, 229, 231, 234, 236, 236, 238, 240, 240,
+ 240, 240, 240, 240, 240, 240, 240, 243, 246, 248,
+ 251, 254, 256, 258, 260, 262, 264, 266, 268, 270,
+ 272, 274, 274, 274, 274, 274, 274, 274, 274, 274,
+ 276, 279, 281, 283, 285, 287, 289, 291, 293, 295,
+ 296, 296, 297, 297, 297, 297, 298, 298, 298, 300,
+ 302, 304, 307, 309, 312, 314, 317, 318, 318, 319,
+ 320, 320, 321, 322, 324, 326, 329, 331, 333, 333,
+ 333, 335, 337, 339, 341, 342, 343, 345, 347, 350,
+
+ 352, 354, 356, 358, 361, 363, 365, 368, 370, 373,
+ 373
+ } ;
+
+static yyconst int yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 2, 2, 2, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 1, 4, 1, 5, 6, 1, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 16,
+ 16, 16, 16, 16, 16, 17, 17, 18, 19, 1,
+ 20, 1, 21, 1, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 13, 31, 32, 33, 34, 35, 36,
+ 13, 37, 38, 39, 40, 41, 42, 43, 44, 45,
+ 46, 47, 48, 1, 49, 1, 50, 51, 52, 53,
+
+ 54, 55, 56, 13, 57, 13, 58, 59, 58, 60,
+ 61, 13, 13, 13, 62, 13, 13, 13, 13, 63,
+ 13, 13, 64, 65, 66, 47, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst int yy_meta[67] =
+ { 0,
+ 1, 2, 2, 1, 1, 1, 2, 2, 3, 1,
+ 1, 1, 3, 1, 3, 3, 3, 2, 2, 1,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 2, 1, 2, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 2, 1, 2
+ } ;
+
+static yyconst short int yy_base[215] =
+ { 0,
+ 0, 218, 0, 216, 219, 2141, 65, 67, 2141, 2141,
+ 2141, 2141, 0, 2141, 2141, 2141, 70, 208, 135, 62,
+ 2141, 2141, 2141, 0, 70, 127, 158, 171, 192, 211,
+ 260, 309, 215, 46, 0, 269, 273, 2141, 2141, 2141,
+ 42, 2141, 43, 70, 0, 0, 330, 0, 48, 322,
+ 333, 382, 385, 434, 437, 458, 471, 520, 507, 529,
+ 558, 564, 613, 609, 616, 647, 67, 2141, 666, 670,
+ 47, 160, 59, 158, 90, 703, 722, 725, 756, 760,
+ 789, 793, 822, 828, 851, 862, 873, 886, 907, 926,
+ 89, 945, 964, 65, 159, 158, 67, 157, 153, 985,
+
+ 1004, 1008, 1039, 1042, 1073, 1076, 1107, 1111, 1130, 1161,
+ 1165, 1169, 1198, 1205, 1227, 155, 1234, 1256, 144, 143,
+ 139, 140, 137, 127, 112, 113, 1263, 1285, 1292, 1296,
+ 1325, 1329, 1336, 1365, 1386, 1389, 1420, 1441, 1444, 1465,
+ 1494, 106, 151, 83, 79, 77, 154, 61, 59, 1500,
+ 1503, 1534, 1553, 1556, 1587, 1590, 1609, 1640, 1644, 2141,
+ 58, 2141, 160, 162, 54, 2141, 169, 174, 1648, 1677,
+ 1698, 1701, 1722, 1735, 1757, 1766, 2141, 43, 2141, 2141,
+ 37, 2141, 2141, 1779, 1788, 1801, 1810, 1832, 177, 181,
+ 1839, 1861, 1868, 1892, 2141, 2141, 1901, 1905, 1936, 1945,
+
+ 1958, 1980, 1989, 2002, 2011, 2024, 2047, 2055, 2068, 2141,
+ 2131, 76, 2134, 2137
+ } ;
+
+static yyconst short int yy_def[215] =
+ { 0,
+ 210, 1, 211, 211, 210, 210, 210, 210, 210, 210,
+ 210, 210, 212, 210, 210, 210, 213, 214, 213, 19,
+ 210, 210, 210, 212, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 210, 214, 19, 19, 210, 210, 210,
+ 210, 210, 210, 210, 212, 214, 19, 214, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 210, 210, 19, 19,
+ 210, 210, 210, 210, 52, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 210, 19, 19, 210, 210, 210, 210, 210, 210, 19,
+
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 210, 19, 19, 210, 210,
+ 210, 210, 210, 210, 210, 210, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 210, 210, 210, 210, 210, 210, 210, 210, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 210,
+ 210, 210, 210, 210, 210, 210, 210, 210, 19, 19,
+ 19, 19, 19, 19, 19, 19, 210, 210, 210, 210,
+ 210, 210, 210, 19, 19, 19, 19, 19, 210, 210,
+ 19, 19, 19, 19, 210, 210, 19, 19, 19, 19,
+
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 0,
+ 210, 210, 210, 210
+ } ;
+
+static yyconst short int yy_nxt[2208] =
+ { 0,
+ 6, 7, 8, 6, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 20, 21, 22, 23,
+ 24, 25, 17, 17, 17, 26, 17, 27, 17, 28,
+ 29, 17, 17, 17, 30, 31, 17, 32, 17, 17,
+ 33, 17, 17, 17, 17, 34, 35, 6, 17, 17,
+ 17, 17, 17, 17, 17, 36, 17, 17, 37, 17,
+ 17, 17, 17, 38, 39, 40, 44, 44, 44, 44,
+ 46, 44, 44, 46, 46, 46, 50, 50, 45, 46,
+ 46, 46, 67, 46, 47, 47, 47, 67, 67, 46,
+ 47, 190, 53, 68, 53, 71, 73, 189, 72, 74,
+
+ 47, 54, 47, 91, 47, 94, 95, 181, 91, 91,
+ 47, 178, 47, 168, 68, 167, 46, 97, 98, 53,
+ 100, 119, 100, 123, 47, 116, 120, 47, 124, 165,
+ 116, 116, 47, 164, 46, 46, 68, 163, 46, 46,
+ 46, 47, 47, 47, 46, 46, 46, 100, 46, 49,
+ 49, 50, 162, 162, 46, 166, 166, 47, 161, 47,
+ 55, 179, 179, 180, 180, 51, 149, 51, 148, 56,
+ 182, 182, 47, 47, 47, 183, 183, 52, 195, 195,
+ 147, 46, 196, 196, 47, 47, 47, 47, 47, 47,
+ 47, 146, 51, 145, 57, 144, 143, 52, 142, 46,
+
+ 47, 47, 68, 47, 58, 126, 47, 47, 47, 125,
+ 122, 121, 99, 47, 96, 47, 48, 59, 210, 43,
+ 47, 41, 47, 210, 47, 47, 47, 47, 47, 47,
+ 47, 47, 210, 47, 47, 210, 210, 210, 210, 210,
+ 66, 47, 210, 47, 210, 47, 210, 47, 210, 47,
+ 60, 210, 210, 47, 47, 210, 210, 47, 210, 210,
+ 210, 210, 210, 210, 210, 210, 210, 210, 47, 210,
+ 210, 210, 47, 47, 47, 47, 47, 47, 210, 210,
+ 210, 61, 210, 47, 47, 47, 210, 47, 47, 47,
+ 47, 210, 47, 210, 210, 210, 62, 210, 210, 47,
+
+ 210, 47, 47, 47, 210, 47, 210, 210, 210, 210,
+ 210, 47, 210, 210, 210, 47, 210, 47, 210, 210,
+ 210, 210, 47, 47, 47, 47, 47, 69, 210, 210,
+ 47, 47, 210, 70, 63, 47, 50, 50, 64, 47,
+ 210, 47, 210, 65, 47, 47, 47, 47, 47, 47,
+ 210, 47, 53, 210, 53, 210, 210, 210, 210, 210,
+ 47, 210, 47, 47, 47, 47, 47, 210, 210, 210,
+ 210, 47, 47, 210, 210, 47, 210, 210, 210, 53,
+ 210, 210, 210, 210, 47, 210, 210, 47, 210, 210,
+ 47, 210, 47, 210, 210, 47, 75, 75, 75, 47,
+
+ 47, 47, 210, 75, 75, 75, 75, 75, 75, 210,
+ 210, 210, 47, 210, 47, 47, 210, 47, 210, 210,
+ 210, 210, 210, 210, 47, 210, 210, 47, 210, 210,
+ 210, 75, 75, 75, 75, 75, 75, 210, 210, 47,
+ 210, 210, 47, 210, 47, 210, 210, 47, 47, 47,
+ 47, 47, 47, 47, 210, 210, 210, 210, 210, 210,
+ 210, 210, 210, 76, 47, 210, 47, 47, 210, 47,
+ 210, 210, 47, 47, 47, 77, 47, 210, 210, 47,
+ 210, 78, 210, 210, 210, 47, 47, 47, 47, 210,
+ 47, 47, 210, 210, 47, 210, 47, 210, 210, 47,
+
+ 47, 47, 210, 47, 210, 79, 210, 210, 210, 210,
+ 210, 210, 210, 47, 210, 47, 210, 210, 210, 210,
+ 47, 47, 47, 47, 210, 210, 210, 210, 47, 210,
+ 210, 210, 82, 47, 47, 47, 47, 47, 210, 47,
+ 210, 210, 210, 47, 47, 47, 210, 210, 210, 47,
+ 47, 210, 47, 210, 210, 80, 210, 210, 81, 47,
+ 210, 47, 47, 210, 47, 210, 210, 83, 210, 47,
+ 210, 47, 47, 47, 47, 210, 210, 47, 47, 47,
+ 47, 210, 47, 210, 210, 84, 47, 210, 47, 210,
+ 47, 47, 210, 210, 47, 210, 47, 210, 85, 210,
+
+ 47, 210, 210, 210, 210, 210, 47, 210, 210, 210,
+ 210, 210, 210, 210, 210, 47, 210, 210, 210, 210,
+ 47, 47, 210, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 210, 86, 210, 210, 210, 210, 47,
+ 87, 47, 210, 47, 210, 47, 47, 210, 47, 210,
+ 210, 47, 89, 88, 210, 47, 210, 210, 47, 210,
+ 210, 47, 47, 47, 210, 210, 47, 210, 210, 210,
+ 47, 47, 210, 47, 210, 47, 210, 47, 47, 47,
+ 47, 47, 47, 90, 47, 47, 47, 210, 210, 47,
+ 210, 210, 210, 210, 210, 210, 47, 210, 47, 210,
+
+ 47, 210, 47, 210, 47, 210, 210, 210, 47, 47,
+ 210, 210, 47, 210, 210, 210, 210, 47, 47, 47,
+ 210, 93, 210, 47, 210, 210, 92, 47, 47, 210,
+ 101, 210, 47, 47, 210, 47, 47, 47, 47, 47,
+ 47, 47, 210, 210, 210, 47, 210, 210, 210, 210,
+ 210, 210, 47, 210, 47, 47, 103, 47, 102, 210,
+ 47, 210, 210, 210, 47, 47, 210, 47, 210, 210,
+ 47, 47, 47, 210, 47, 47, 47, 210, 210, 47,
+ 210, 210, 47, 210, 47, 210, 47, 47, 47, 210,
+ 47, 210, 47, 210, 210, 104, 210, 210, 47, 105,
+
+ 210, 210, 47, 47, 47, 47, 210, 47, 47, 47,
+ 210, 210, 210, 47, 106, 210, 210, 47, 47, 47,
+ 210, 47, 47, 47, 210, 47, 210, 210, 107, 210,
+ 210, 47, 210, 210, 210, 47, 47, 47, 47, 210,
+ 210, 210, 47, 47, 47, 210, 47, 210, 210, 210,
+ 47, 47, 47, 109, 47, 47, 210, 108, 47, 210,
+ 47, 210, 210, 210, 47, 47, 47, 47, 210, 210,
+ 47, 210, 210, 210, 210, 210, 47, 47, 47, 47,
+ 210, 47, 210, 47, 47, 47, 210, 47, 47, 47,
+ 47, 110, 47, 47, 47, 210, 210, 210, 111, 210,
+
+ 47, 47, 47, 47, 47, 112, 210, 210, 47, 210,
+ 210, 113, 210, 47, 210, 47, 47, 210, 47, 47,
+ 210, 47, 47, 47, 47, 210, 210, 210, 47, 210,
+ 47, 210, 210, 210, 210, 47, 210, 47, 210, 47,
+ 47, 47, 47, 47, 210, 114, 210, 210, 47, 47,
+ 210, 210, 210, 210, 210, 210, 47, 210, 47, 47,
+ 47, 47, 210, 115, 47, 210, 210, 210, 47, 47,
+ 210, 210, 210, 210, 210, 47, 210, 47, 47, 47,
+ 47, 210, 210, 47, 210, 210, 210, 47, 47, 210,
+ 210, 210, 210, 210, 47, 117, 47, 210, 210, 47,
+
+ 47, 47, 47, 210, 210, 210, 47, 47, 210, 210,
+ 210, 210, 210, 118, 210, 47, 210, 47, 47, 47,
+ 47, 47, 47, 47, 47, 210, 47, 47, 210, 210,
+ 210, 210, 210, 210, 47, 210, 47, 127, 47, 210,
+ 47, 210, 47, 210, 210, 210, 47, 47, 210, 210,
+ 47, 128, 210, 47, 47, 47, 47, 47, 47, 210,
+ 210, 47, 210, 210, 210, 47, 47, 210, 210, 47,
+ 47, 47, 47, 210, 47, 210, 210, 130, 129, 210,
+ 210, 47, 210, 210, 47, 210, 210, 47, 47, 47,
+ 47, 47, 47, 210, 210, 210, 47, 210, 210, 47,
+
+ 210, 47, 210, 47, 47, 47, 47, 210, 47, 210,
+ 210, 131, 132, 210, 210, 47, 210, 210, 47, 210,
+ 210, 47, 47, 47, 210, 47, 47, 47, 210, 210,
+ 47, 210, 210, 47, 210, 47, 210, 47, 47, 47,
+ 210, 47, 210, 47, 47, 47, 47, 210, 210, 47,
+ 133, 210, 210, 47, 210, 210, 210, 210, 210, 210,
+ 47, 210, 47, 210, 47, 210, 210, 134, 47, 47,
+ 210, 210, 47, 47, 210, 47, 47, 47, 210, 47,
+ 47, 47, 210, 47, 47, 47, 210, 47, 136, 210,
+ 135, 47, 47, 47, 137, 47, 210, 47, 210, 47,
+
+ 210, 47, 210, 47, 210, 210, 210, 47, 210, 210,
+ 210, 47, 47, 47, 47, 210, 210, 210, 47, 47,
+ 47, 47, 47, 47, 210, 210, 47, 47, 47, 210,
+ 47, 47, 138, 210, 210, 47, 210, 47, 210, 210,
+ 47, 47, 47, 47, 210, 210, 210, 47, 47, 47,
+ 47, 210, 210, 210, 210, 47, 139, 47, 210, 47,
+ 47, 210, 47, 210, 47, 210, 47, 47, 210, 47,
+ 47, 47, 47, 210, 210, 210, 47, 47, 47, 47,
+ 210, 210, 210, 140, 47, 210, 47, 210, 47, 47,
+ 210, 47, 210, 47, 210, 47, 47, 210, 47, 47,
+
+ 47, 47, 210, 210, 210, 47, 47, 47, 47, 210,
+ 47, 47, 47, 47, 141, 47, 150, 47, 47, 210,
+ 47, 210, 47, 210, 47, 47, 47, 47, 47, 210,
+ 210, 210, 210, 210, 47, 210, 210, 210, 47, 47,
+ 47, 47, 47, 47, 47, 47, 210, 47, 210, 47,
+ 47, 47, 47, 47, 47, 47, 210, 47, 47, 47,
+ 210, 47, 210, 210, 151, 210, 47, 47, 47, 210,
+ 210, 47, 210, 210, 152, 210, 210, 210, 47, 47,
+ 47, 47, 47, 210, 210, 210, 47, 47, 210, 210,
+ 210, 47, 210, 47, 153, 47, 210, 47, 47, 210,
+
+ 47, 47, 47, 47, 47, 47, 210, 47, 210, 210,
+ 154, 210, 210, 210, 210, 210, 47, 155, 47, 47,
+ 210, 47, 47, 210, 210, 210, 210, 47, 47, 210,
+ 210, 47, 210, 210, 47, 47, 47, 210, 210, 210,
+ 210, 210, 210, 47, 210, 210, 47, 210, 47, 210,
+ 47, 47, 47, 156, 210, 47, 47, 47, 47, 47,
+ 47, 210, 47, 210, 210, 210, 210, 157, 210, 210,
+ 210, 47, 210, 47, 47, 210, 47, 47, 158, 47,
+ 47, 47, 47, 47, 210, 210, 47, 210, 210, 210,
+ 210, 210, 210, 210, 210, 47, 210, 47, 47, 210,
+
+ 210, 47, 210, 47, 210, 210, 47, 47, 47, 47,
+ 47, 160, 210, 210, 47, 47, 47, 47, 47, 47,
+ 210, 210, 47, 159, 47, 169, 47, 47, 210, 210,
+ 47, 210, 47, 47, 210, 47, 47, 210, 210, 210,
+ 210, 210, 47, 210, 210, 47, 210, 210, 47, 47,
+ 47, 47, 210, 210, 210, 210, 47, 47, 210, 210,
+ 47, 210, 47, 210, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 210, 210, 210, 47, 210, 210, 210,
+ 210, 172, 170, 47, 210, 47, 47, 210, 47, 210,
+ 210, 47, 210, 210, 210, 47, 47, 171, 47, 210,
+
+ 210, 47, 47, 47, 47, 47, 47, 210, 210, 210,
+ 47, 210, 210, 47, 210, 47, 210, 47, 47, 47,
+ 47, 210, 47, 47, 47, 47, 210, 210, 174, 47,
+ 210, 210, 47, 210, 210, 173, 210, 210, 210, 47,
+ 210, 47, 210, 210, 47, 210, 210, 47, 210, 47,
+ 210, 47, 47, 210, 47, 47, 47, 175, 47, 47,
+ 47, 177, 47, 47, 47, 210, 47, 210, 210, 210,
+ 47, 47, 47, 176, 47, 210, 47, 210, 47, 210,
+ 47, 210, 47, 210, 210, 210, 47, 210, 210, 210,
+ 47, 47, 47, 47, 210, 210, 184, 47, 210, 210,
+
+ 210, 47, 47, 185, 210, 47, 47, 47, 210, 47,
+ 47, 210, 47, 47, 47, 47, 47, 47, 210, 47,
+ 210, 210, 210, 186, 210, 210, 210, 210, 47, 210,
+ 47, 47, 210, 47, 47, 210, 47, 47, 47, 47,
+ 47, 210, 210, 47, 210, 210, 187, 210, 210, 47,
+ 47, 47, 47, 210, 47, 47, 210, 210, 47, 210,
+ 47, 210, 210, 47, 47, 47, 210, 47, 210, 210,
+ 210, 47, 47, 47, 210, 210, 210, 47, 210, 47,
+ 47, 47, 47, 210, 47, 188, 210, 47, 210, 47,
+ 210, 210, 47, 47, 47, 47, 47, 47, 47, 47,
+
+ 210, 210, 47, 47, 47, 191, 210, 210, 47, 47,
+ 210, 47, 210, 210, 47, 47, 47, 47, 47, 47,
+ 47, 47, 192, 47, 47, 47, 47, 210, 47, 210,
+ 47, 47, 210, 47, 210, 210, 47, 210, 210, 193,
+ 47, 47, 47, 47, 210, 47, 47, 47, 47, 210,
+ 47, 210, 47, 47, 47, 47, 210, 194, 47, 210,
+ 210, 210, 47, 47, 47, 210, 210, 47, 197, 47,
+ 210, 47, 47, 210, 47, 47, 47, 47, 210, 210,
+ 210, 47, 47, 47, 47, 210, 210, 210, 210, 47,
+ 210, 47, 210, 47, 47, 210, 47, 198, 47, 210,
+
+ 47, 47, 210, 47, 199, 210, 47, 47, 47, 210,
+ 47, 210, 210, 200, 210, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 210, 210, 210, 210,
+ 47, 47, 201, 47, 47, 47, 210, 202, 210, 210,
+ 210, 210, 210, 47, 210, 210, 210, 47, 210, 47,
+ 47, 47, 47, 210, 47, 210, 210, 210, 47, 47,
+ 47, 47, 47, 47, 210, 210, 47, 47, 47, 203,
+ 210, 210, 47, 47, 47, 47, 210, 47, 47, 210,
+ 210, 210, 210, 204, 210, 210, 210, 47, 47, 210,
+ 47, 210, 210, 47, 47, 47, 47, 210, 47, 210,
+
+ 47, 205, 47, 47, 47, 47, 210, 47, 210, 210,
+ 47, 210, 47, 210, 206, 47, 47, 47, 47, 47,
+ 47, 47, 47, 210, 210, 47, 47, 47, 210, 210,
+ 210, 47, 47, 210, 47, 210, 210, 47, 47, 47,
+ 47, 47, 47, 47, 47, 210, 47, 210, 210, 207,
+ 210, 47, 210, 47, 47, 210, 47, 210, 210, 47,
+ 208, 47, 47, 47, 47, 210, 47, 210, 47, 47,
+ 47, 47, 210, 47, 210, 210, 210, 47, 210, 47,
+ 210, 47, 47, 47, 47, 47, 47, 47, 210, 47,
+ 210, 210, 209, 210, 210, 210, 210, 47, 47, 210,
+
+ 47, 210, 210, 210, 47, 210, 210, 210, 210, 47,
+ 47, 210, 47, 210, 210, 210, 210, 47, 210, 210,
+ 210, 210, 210, 210, 210, 47, 210, 210, 210, 210,
+ 47, 42, 42, 42, 47, 210, 47, 46, 210, 46,
+ 5, 210, 210, 210, 210, 210, 210, 210, 210, 210,
+ 210, 210, 210, 210, 210, 210, 210, 210, 210, 210,
+ 210, 210, 210, 210, 210, 210, 210, 210, 210, 210,
+ 210, 210, 210, 210, 210, 210, 210, 210, 210, 210,
+ 210, 210, 210, 210, 210, 210, 210, 210, 210, 210,
+ 210, 210, 210, 210, 210, 210, 210, 210, 210, 210,
+
+ 210, 210, 210, 210, 210, 210, 210
+ } ;
+
+static yyconst short int yy_chk[2208] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 7, 7, 8, 8,
+ 17, 44, 44, 17, 17, 17, 20, 20, 212, 17,
+ 17, 17, 34, 17, 25, 25, 25, 34, 34, 17,
+ 49, 181, 20, 34, 20, 41, 43, 178, 41, 43,
+
+ 25, 25, 25, 67, 20, 71, 71, 165, 67, 67,
+ 49, 161, 25, 149, 67, 148, 17, 73, 73, 20,
+ 75, 94, 75, 97, 20, 91, 94, 25, 97, 146,
+ 91, 91, 25, 145, 17, 19, 91, 144, 19, 19,
+ 19, 26, 26, 26, 19, 19, 19, 75, 19, 19,
+ 19, 19, 143, 143, 19, 147, 147, 26, 142, 26,
+ 26, 163, 163, 164, 164, 19, 126, 19, 125, 26,
+ 167, 167, 27, 27, 27, 168, 168, 19, 189, 189,
+ 124, 19, 190, 190, 26, 28, 28, 28, 27, 26,
+ 27, 123, 19, 122, 27, 121, 120, 19, 119, 19,
+
+ 27, 28, 116, 28, 28, 99, 29, 29, 29, 98,
+ 96, 95, 74, 28, 72, 27, 18, 29, 5, 4,
+ 27, 2, 29, 0, 29, 30, 30, 30, 28, 33,
+ 33, 33, 0, 28, 29, 0, 0, 0, 0, 0,
+ 33, 30, 0, 30, 0, 33, 0, 33, 0, 29,
+ 30, 0, 0, 30, 29, 0, 0, 33, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 30, 0,
+ 0, 0, 33, 30, 31, 31, 31, 33, 0, 0,
+ 0, 31, 0, 36, 36, 36, 0, 37, 37, 37,
+ 31, 0, 31, 0, 0, 0, 31, 0, 0, 36,
+
+ 0, 36, 31, 37, 0, 37, 0, 0, 0, 0,
+ 0, 36, 0, 0, 0, 37, 0, 31, 0, 0,
+ 0, 0, 31, 32, 32, 32, 36, 36, 0, 0,
+ 37, 36, 0, 37, 32, 37, 50, 50, 32, 32,
+ 0, 32, 0, 32, 47, 47, 47, 51, 51, 51,
+ 0, 32, 50, 0, 50, 0, 0, 0, 0, 0,
+ 47, 0, 47, 51, 50, 51, 32, 0, 0, 0,
+ 0, 32, 47, 0, 0, 51, 0, 0, 0, 50,
+ 0, 0, 0, 0, 50, 0, 0, 47, 0, 0,
+ 51, 0, 47, 0, 0, 51, 52, 52, 52, 53,
+
+ 53, 53, 0, 52, 52, 52, 52, 52, 52, 0,
+ 0, 0, 52, 0, 52, 53, 0, 53, 0, 0,
+ 0, 0, 0, 0, 52, 0, 0, 53, 0, 0,
+ 0, 52, 52, 52, 52, 52, 52, 0, 0, 52,
+ 0, 0, 53, 0, 52, 0, 0, 53, 54, 54,
+ 54, 55, 55, 55, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 54, 54, 0, 54, 55, 0, 55,
+ 0, 0, 56, 56, 56, 55, 54, 0, 0, 55,
+ 0, 56, 0, 0, 0, 57, 57, 57, 56, 0,
+ 56, 54, 0, 0, 55, 0, 54, 0, 0, 55,
+
+ 56, 57, 0, 57, 0, 57, 0, 0, 0, 0,
+ 0, 0, 0, 57, 0, 56, 0, 0, 0, 0,
+ 56, 59, 59, 59, 0, 0, 0, 0, 57, 0,
+ 0, 0, 59, 57, 58, 58, 58, 59, 0, 59,
+ 0, 0, 0, 60, 60, 60, 0, 0, 0, 59,
+ 58, 0, 58, 0, 0, 58, 0, 0, 58, 60,
+ 0, 60, 58, 0, 59, 0, 0, 60, 0, 59,
+ 0, 60, 61, 61, 61, 0, 0, 58, 62, 62,
+ 62, 0, 58, 0, 0, 61, 60, 0, 61, 0,
+ 61, 60, 0, 0, 62, 0, 62, 0, 62, 0,
+
+ 61, 0, 0, 0, 0, 0, 62, 0, 0, 0,
+ 0, 0, 0, 0, 0, 61, 0, 0, 0, 0,
+ 61, 62, 0, 64, 64, 64, 62, 63, 63, 63,
+ 65, 65, 65, 0, 63, 0, 0, 0, 0, 64,
+ 63, 64, 0, 63, 0, 63, 65, 0, 65, 0,
+ 0, 64, 65, 64, 0, 63, 0, 0, 65, 0,
+ 0, 66, 66, 66, 0, 0, 64, 0, 0, 0,
+ 63, 64, 0, 65, 0, 63, 0, 66, 65, 66,
+ 69, 69, 69, 66, 70, 70, 70, 0, 0, 66,
+ 0, 0, 0, 0, 0, 0, 69, 0, 69, 0,
+
+ 70, 0, 70, 0, 66, 0, 0, 0, 69, 66,
+ 0, 0, 70, 0, 0, 0, 0, 76, 76, 76,
+ 0, 70, 0, 69, 0, 0, 69, 70, 69, 0,
+ 76, 0, 70, 76, 0, 76, 77, 77, 77, 78,
+ 78, 78, 0, 0, 0, 76, 0, 0, 0, 0,
+ 0, 0, 77, 0, 77, 78, 78, 78, 77, 0,
+ 76, 0, 0, 0, 77, 76, 0, 78, 0, 0,
+ 79, 79, 79, 0, 80, 80, 80, 0, 0, 77,
+ 0, 0, 78, 0, 77, 0, 79, 78, 79, 0,
+ 80, 0, 80, 0, 0, 79, 0, 0, 79, 80,
+
+ 0, 0, 80, 81, 81, 81, 0, 82, 82, 82,
+ 0, 0, 0, 79, 81, 0, 0, 80, 79, 81,
+ 0, 81, 80, 82, 0, 82, 0, 0, 82, 0,
+ 0, 81, 0, 0, 0, 82, 83, 83, 83, 0,
+ 0, 0, 84, 84, 84, 0, 81, 0, 0, 0,
+ 82, 81, 83, 84, 83, 82, 0, 83, 84, 0,
+ 84, 0, 0, 0, 83, 85, 85, 85, 0, 0,
+ 84, 0, 0, 0, 0, 0, 86, 86, 86, 83,
+ 0, 85, 0, 85, 83, 84, 0, 87, 87, 87,
+ 84, 85, 86, 85, 86, 0, 0, 0, 86, 0,
+
+ 88, 88, 88, 87, 86, 87, 0, 0, 85, 0,
+ 0, 88, 0, 85, 0, 87, 88, 0, 88, 86,
+ 0, 89, 89, 89, 86, 0, 0, 0, 88, 0,
+ 87, 0, 0, 0, 0, 87, 0, 89, 0, 89,
+ 90, 90, 90, 88, 0, 89, 0, 0, 88, 89,
+ 0, 0, 0, 0, 0, 0, 90, 0, 90, 92,
+ 92, 92, 0, 90, 89, 0, 0, 0, 90, 89,
+ 0, 0, 0, 0, 0, 92, 0, 92, 93, 93,
+ 93, 0, 0, 90, 0, 0, 0, 92, 90, 0,
+ 0, 0, 0, 0, 93, 92, 93, 0, 0, 100,
+
+ 100, 100, 92, 0, 0, 0, 93, 92, 0, 0,
+ 0, 0, 0, 93, 0, 100, 0, 100, 101, 101,
+ 101, 93, 102, 102, 102, 0, 93, 100, 0, 0,
+ 0, 0, 0, 0, 101, 0, 101, 101, 102, 0,
+ 102, 0, 100, 0, 0, 0, 101, 100, 0, 0,
+ 102, 102, 0, 103, 103, 103, 104, 104, 104, 0,
+ 0, 101, 0, 0, 0, 102, 101, 0, 0, 103,
+ 102, 103, 104, 0, 104, 0, 0, 104, 103, 0,
+ 0, 103, 0, 0, 104, 0, 0, 105, 105, 105,
+ 106, 106, 106, 0, 0, 0, 103, 0, 0, 104,
+
+ 0, 103, 0, 105, 104, 105, 106, 0, 106, 0,
+ 0, 105, 106, 0, 0, 105, 0, 0, 106, 0,
+ 0, 107, 107, 107, 0, 108, 108, 108, 0, 0,
+ 105, 0, 0, 106, 0, 105, 0, 107, 106, 107,
+ 0, 108, 0, 108, 109, 109, 109, 0, 0, 107,
+ 108, 0, 0, 108, 0, 0, 0, 0, 0, 0,
+ 109, 0, 109, 0, 107, 0, 0, 109, 108, 107,
+ 0, 0, 109, 108, 0, 110, 110, 110, 0, 111,
+ 111, 111, 0, 112, 112, 112, 0, 109, 111, 0,
+ 110, 110, 109, 110, 112, 111, 0, 111, 0, 112,
+
+ 0, 112, 0, 110, 0, 0, 0, 111, 0, 0,
+ 0, 112, 113, 113, 113, 0, 0, 0, 110, 114,
+ 114, 114, 111, 110, 0, 0, 112, 111, 113, 0,
+ 113, 112, 113, 0, 0, 114, 0, 114, 0, 0,
+ 113, 115, 115, 115, 0, 0, 0, 114, 117, 117,
+ 117, 0, 0, 0, 0, 113, 115, 115, 0, 115,
+ 113, 0, 114, 0, 117, 0, 117, 114, 0, 115,
+ 118, 118, 118, 0, 0, 0, 117, 127, 127, 127,
+ 0, 0, 0, 117, 115, 0, 118, 0, 118, 115,
+ 0, 117, 0, 127, 0, 127, 117, 0, 118, 128,
+
+ 128, 128, 0, 0, 0, 127, 129, 129, 129, 0,
+ 130, 130, 130, 118, 118, 128, 129, 128, 118, 0,
+ 127, 0, 129, 0, 129, 127, 130, 128, 130, 0,
+ 0, 0, 0, 0, 129, 0, 0, 0, 130, 131,
+ 131, 131, 128, 132, 132, 132, 0, 128, 0, 129,
+ 133, 133, 133, 130, 129, 131, 0, 131, 130, 132,
+ 0, 132, 0, 0, 132, 0, 133, 131, 133, 0,
+ 0, 132, 0, 0, 133, 0, 0, 0, 133, 134,
+ 134, 134, 131, 0, 0, 0, 132, 131, 0, 0,
+ 0, 132, 0, 133, 134, 134, 0, 134, 133, 0,
+
+ 135, 135, 135, 136, 136, 136, 0, 134, 0, 0,
+ 135, 0, 0, 0, 0, 0, 135, 136, 135, 136,
+ 0, 136, 134, 0, 0, 0, 0, 134, 135, 0,
+ 0, 136, 0, 0, 137, 137, 137, 0, 0, 0,
+ 0, 0, 0, 135, 0, 0, 136, 0, 135, 0,
+ 137, 136, 137, 137, 0, 138, 138, 138, 139, 139,
+ 139, 0, 137, 0, 0, 0, 0, 138, 0, 0,
+ 0, 138, 0, 138, 139, 0, 139, 137, 139, 140,
+ 140, 140, 137, 138, 0, 0, 139, 0, 0, 0,
+ 0, 0, 0, 0, 0, 140, 0, 140, 138, 0,
+
+ 0, 139, 0, 138, 0, 0, 139, 140, 141, 141,
+ 141, 141, 0, 0, 150, 150, 150, 151, 151, 151,
+ 0, 0, 140, 140, 141, 150, 141, 140, 0, 0,
+ 150, 0, 150, 151, 0, 151, 141, 0, 0, 0,
+ 0, 0, 150, 0, 0, 151, 0, 0, 152, 152,
+ 152, 141, 0, 0, 0, 0, 141, 150, 0, 0,
+ 151, 0, 150, 0, 152, 151, 152, 153, 153, 153,
+ 154, 154, 154, 0, 0, 0, 152, 0, 0, 0,
+ 0, 154, 152, 153, 0, 153, 154, 0, 154, 0,
+ 0, 152, 0, 0, 0, 153, 152, 153, 154, 0,
+
+ 0, 155, 155, 155, 156, 156, 156, 0, 0, 0,
+ 153, 0, 0, 154, 0, 153, 0, 155, 154, 155,
+ 156, 0, 156, 157, 157, 157, 0, 0, 156, 155,
+ 0, 0, 156, 0, 0, 155, 0, 0, 0, 157,
+ 0, 157, 0, 0, 155, 0, 0, 156, 0, 155,
+ 0, 157, 156, 0, 158, 158, 158, 157, 159, 159,
+ 159, 159, 169, 169, 169, 0, 157, 0, 0, 0,
+ 158, 157, 158, 158, 159, 0, 159, 0, 169, 0,
+ 169, 0, 158, 0, 0, 0, 159, 0, 0, 0,
+ 169, 170, 170, 170, 0, 0, 169, 158, 0, 0,
+
+ 0, 159, 158, 170, 0, 169, 159, 170, 0, 170,
+ 169, 0, 171, 171, 171, 172, 172, 172, 0, 170,
+ 0, 0, 0, 171, 0, 0, 0, 0, 171, 0,
+ 171, 172, 0, 172, 170, 0, 173, 173, 173, 170,
+ 171, 0, 0, 172, 0, 0, 173, 0, 0, 174,
+ 174, 174, 173, 0, 173, 171, 0, 0, 172, 0,
+ 171, 0, 0, 172, 173, 174, 0, 174, 0, 0,
+ 0, 175, 175, 175, 0, 0, 0, 174, 0, 173,
+ 176, 176, 176, 0, 173, 175, 0, 175, 0, 175,
+ 0, 0, 174, 184, 184, 184, 176, 174, 176, 175,
+
+ 0, 0, 185, 185, 185, 184, 0, 0, 176, 184,
+ 0, 184, 0, 0, 175, 186, 186, 186, 185, 175,
+ 185, 184, 185, 176, 187, 187, 187, 0, 176, 0,
+ 185, 186, 0, 186, 0, 0, 184, 0, 0, 187,
+ 187, 184, 187, 186, 0, 185, 188, 188, 188, 0,
+ 185, 0, 187, 191, 191, 191, 0, 188, 186, 0,
+ 0, 0, 188, 186, 188, 0, 0, 187, 191, 191,
+ 0, 191, 187, 0, 188, 192, 192, 192, 0, 0,
+ 0, 191, 193, 193, 193, 0, 0, 0, 0, 188,
+ 0, 192, 0, 192, 188, 0, 191, 192, 193, 0,
+
+ 193, 191, 0, 192, 193, 0, 194, 194, 194, 0,
+ 193, 0, 0, 194, 0, 197, 197, 197, 192, 198,
+ 198, 198, 194, 192, 194, 193, 0, 0, 0, 0,
+ 193, 197, 197, 197, 194, 198, 0, 198, 0, 0,
+ 0, 0, 0, 197, 0, 0, 0, 198, 0, 194,
+ 199, 199, 199, 0, 194, 0, 0, 0, 197, 200,
+ 200, 200, 198, 197, 0, 0, 199, 198, 199, 200,
+ 0, 0, 201, 201, 201, 200, 0, 200, 199, 0,
+ 0, 0, 0, 201, 0, 0, 0, 200, 201, 0,
+ 201, 0, 0, 199, 202, 202, 202, 0, 199, 0,
+
+ 201, 202, 200, 203, 203, 203, 0, 200, 0, 0,
+ 202, 0, 202, 0, 203, 201, 204, 204, 204, 203,
+ 201, 203, 202, 0, 0, 205, 205, 205, 0, 0,
+ 0, 203, 204, 0, 204, 0, 0, 202, 206, 206,
+ 206, 205, 202, 205, 204, 0, 203, 0, 0, 205,
+ 0, 203, 0, 205, 206, 0, 206, 0, 0, 204,
+ 206, 207, 207, 207, 204, 0, 206, 0, 205, 208,
+ 208, 208, 0, 205, 0, 0, 0, 207, 0, 207,
+ 0, 206, 209, 209, 209, 208, 206, 208, 0, 207,
+ 0, 0, 208, 0, 0, 0, 0, 208, 209, 0,
+
+ 209, 0, 0, 0, 207, 0, 0, 0, 0, 207,
+ 209, 0, 208, 0, 0, 0, 0, 208, 0, 0,
+ 0, 0, 0, 0, 0, 209, 0, 0, 0, 0,
+ 209, 211, 211, 211, 213, 0, 213, 214, 0, 214,
+ 210, 210, 210, 210, 210, 210, 210, 210, 210, 210,
+ 210, 210, 210, 210, 210, 210, 210, 210, 210, 210,
+ 210, 210, 210, 210, 210, 210, 210, 210, 210, 210,
+ 210, 210, 210, 210, 210, 210, 210, 210, 210, 210,
+ 210, 210, 210, 210, 210, 210, 210, 210, 210, 210,
+ 210, 210, 210, 210, 210, 210, 210, 210, 210, 210,
+
+ 210, 210, 210, 210, 210, 210, 210
+ } ;
+
+static yy_state_type yy_state_buf[YY_BUF_SIZE + 2], *yy_state_ptr;
+static char *yy_full_match;
+static int yy_lp;
+#define REJECT \
+{ \
+*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ \
+yy_cp = yy_full_match; /* restore poss. backed-over text */ \
+++yy_lp; \
+goto find_rule; \
+}
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "/home/drepper/gnu/elfutils/src/ldlex.l"
+#define INITIAL 0
+#line 2 "/home/drepper/gnu/elfutils/src/ldlex.l"
+/* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc.
+ Written by Ulrich Drepper <drepper@redhat.com>, 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)
+
+/* 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);
+#define YY_NEVER_INTERACTIVE 1
+#define IGNORE 1
+
+#line 1061 "ldlex.c"
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap YY_PROTO(( void ));
+#else
+extern int yywrap YY_PROTO(( void ));
+#endif
+#endif
+
+#ifndef YY_NO_UNPUT
+static void yyunput YY_PROTO(( int c, char *buf_ptr ));
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int ));
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen YY_PROTO(( yyconst char * ));
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput YY_PROTO(( void ));
+#else
+static int input YY_PROTO(( void ));
+#endif
+#endif
+
+#if YY_STACK_USED
+static int yy_start_stack_ptr = 0;
+static int yy_start_stack_depth = 0;
+static int *yy_start_stack = 0;
+#ifndef YY_NO_PUSH_STATE
+static void yy_push_state YY_PROTO(( int new_state ));
+#endif
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state YY_PROTO(( void ));
+#endif
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state YY_PROTO(( void ));
+#endif
+
+#else
+#define YY_NO_PUSH_STATE 1
+#define YY_NO_POP_STATE 1
+#define YY_NO_TOP_STATE 1
+#endif
+
+#ifdef YY_MALLOC_DECL
+YY_MALLOC_DECL
+#else
+#if __STDC__
+#ifndef __cplusplus
+#include <stdlib.h>
+#endif
+#else
+/* Just try to get by without declaring the routines. This will fail
+ * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int)
+ * or sizeof(void*) != sizeof(int).
+ */
+#endif
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( yy_current_buffer->yy_is_interactive ) \
+ { \
+ int c = '*', n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \
+ && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" );
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL int yylex YY_PROTO(( void ))
+#endif
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ if ( yyleng > 0 ) \
+ yy_current_buffer->yy_at_bol = \
+ (yytext[yyleng - 1] == '\n'); \
+ YY_USER_ACTION
+
+YY_DECL
+ {
+ register yy_state_type yy_current_state;
+ register char *yy_cp = NULL, *yy_bp = NULL;
+ register int yy_act;
+
+#line 79 "/home/drepper/gnu/elfutils/src/ldlex.l"
+
+ if (unlikely (ld_scan_version_script))
+ {
+ ld_scan_version_script = -1;
+ return kVERSION_SCRIPT;
+ }
+
+#line 1223 "ldlex.c"
+
+ if ( yy_init )
+ {
+ yy_init = 0;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! yy_start )
+ yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! yy_current_buffer )
+ yy_current_buffer =
+ yy_create_buffer( yyin, YY_BUF_SIZE );
+
+ yy_load_buffer_state();
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yy_start;
+ yy_current_state += YY_AT_BOL();
+ yy_state_ptr = yy_state_buf;
+ *yy_state_ptr++ = yy_current_state;
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 211 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ *yy_state_ptr++ = yy_current_state;
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 2141 );
+
+yy_find_action:
+ yy_current_state = *--yy_state_ptr;
+ yy_lp = yy_accept[yy_current_state];
+find_rule: /* we branch to this label when backing up */
+ for ( ; ; ) /* until we find what rule we matched */
+ {
+ if ( yy_lp && yy_lp < yy_accept[yy_current_state + 1] )
+ {
+ yy_act = yy_acclist[yy_lp];
+ {
+ yy_full_match = yy_cp;
+ break;
+ }
+ }
+ --yy_cp;
+ yy_current_state = *--yy_state_ptr;
+ yy_lp = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+ if ( yy_act != YY_END_OF_BUFFER )
+ {
+ int yyl;
+ for ( yyl = 0; yyl < yyleng; ++yyl )
+ if ( yytext[yyl] == '\n' )
+ ++yylineno;
+ }
+
+do_action: /* This label is used only to access EOF actions. */
+
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+case 1:
+*yy_cp = yy_hold_char; /* undo effects of setting up yytext */
+yy_c_buf_p = yy_cp = yy_bp + 6;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 86 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ BEGIN (handle_ifdef ()); }
+ YY_BREAK
+case 2:
+*yy_cp = yy_hold_char; /* undo effects of setting up yytext */
+yy_c_buf_p = yy_cp = yy_bp + 5;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 87 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ eat_to_eol (true);
+ push_state (skip_to_endif);
+ BEGIN (IGNORE); }
+ YY_BREAK
+case 3:
+*yy_cp = yy_hold_char; /* undo effects of setting up yytext */
+yy_c_buf_p = yy_cp = yy_bp + 8;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 90 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ eat_to_eol (false);
+ push_state (skip_to_endif);
+ BEGIN (IGNORE); }
+ YY_BREAK
+case 4:
+*yy_cp = yy_hold_char; /* undo effects of setting up yytext */
+yy_c_buf_p = yy_cp = yy_bp + 6;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 93 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ eat_to_eol (true) ; }
+ YY_BREAK
+case 5:
+*yy_cp = yy_hold_char; /* undo effects of setting up yytext */
+yy_c_buf_p = yy_cp = yy_bp + 6;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 95 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ eat_to_eol (false);
+ push_state (skip_to_endif); }
+ YY_BREAK
+case 6:
+*yy_cp = yy_hold_char; /* undo effects of setting up yytext */
+yy_c_buf_p = yy_cp = yy_bp + 5;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 97 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ eat_to_eol (true);
+ assert (prepdepth > 0);
+ if (prepstate[prepdepth - 1] == skip_if)
+ {
+ /* Back to normal processing. */
+ assert (prepdepth == 1);
+ BEGIN (pop_state ());
+ }
+ }
+ YY_BREAK
+case 7:
+*yy_cp = yy_hold_char; /* undo effects of setting up yytext */
+yy_c_buf_p = yy_cp = yy_bp + 8;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 106 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ assert (prepdepth > 0);
+ if (prepstate[prepdepth - 1] == skip_if)
+ {
+ /* Maybe this symbol is defined. */
+ pop_state ();
+ BEGIN (handle_ifdef ());
+ }
+ }
+ YY_BREAK
+case 8:
+*yy_cp = yy_hold_char; /* undo effects of setting up yytext */
+yy_c_buf_p = yy_cp = yy_bp + 6;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+#line 114 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ eat_to_eol (true);
+ BEGIN (pop_state ()); }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 116 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ /* nothing */ }
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 119 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ eat_comment (); }
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 121 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return kALIGN; }
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 122 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return kENTRY; }
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 123 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return kEXCLUDE_FILE; }
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 124 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return kGLOBAL; }
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 125 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return kGROUP; }
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 126 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return kINPUT; }
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 127 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return kINTERP; }
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 128 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return kKEEP; }
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 129 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return kLOCAL; }
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 130 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return kOUTPUT_FORMAT; }
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 131 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return kPAGESIZE; }
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 132 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return kPROVIDE; }
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 133 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return kSEARCH_DIR; }
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 134 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return kSEGMENT; }
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 135 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return kSIZEOF_HEADERS; }
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 136 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return kSORT; }
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 137 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return kVERSION; }
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 139 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ int cnt = 1 ;
+ ldlval.num = 0;
+ while (cnt < yyleng - 1)
+ ldlval.num |= attrib_convert (yytext[cnt++]);
+ return kMODE; }
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 145 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return '{'; }
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 146 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return '}'; }
+ YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 147 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return '('; }
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 148 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return ')'; }
+ YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 149 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return ':'; }
+ YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 150 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return ';'; }
+ YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 151 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return '='; }
+ YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 152 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ ldlval.op = exp_plus; return kADD_OP; }
+ YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 153 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ ldlval.op = exp_minus; return kADD_OP; }
+ YY_BREAK
+case 38:
+YY_RULE_SETUP
+#line 154 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return '*'; }
+ YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 155 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ ldlval.op = exp_div; return kMUL_OP; }
+ YY_BREAK
+case 40:
+YY_RULE_SETUP
+#line 156 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ ldlval.op = exp_mod; return kMUL_OP; }
+ YY_BREAK
+case 41:
+YY_RULE_SETUP
+#line 157 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return '&'; }
+ YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 158 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return '|'; }
+ YY_BREAK
+case 43:
+YY_RULE_SETUP
+#line 160 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ return ','; }
+ YY_BREAK
+case 44:
+YY_RULE_SETUP
+#line 162 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ 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; }
+ YY_BREAK
+case 45:
+YY_RULE_SETUP
+#line 176 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ ldlval.str = obstack_strndup (&ld_state.smem,
+ yytext, yyleng);
+ return kID; }
+ YY_BREAK
+case 46:
+YY_RULE_SETUP
+#line 180 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ ldlval.str = obstack_strndup (&ld_state.smem,
+ yytext, yyleng);
+ return kFILENAME; }
+ YY_BREAK
+case 47:
+YY_RULE_SETUP
+#line 184 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ /* IGNORE */ }
+ YY_BREAK
+case 48:
+YY_RULE_SETUP
+#line 186 "/home/drepper/gnu/elfutils/src/ldlex.l"
+{ invalid_char (*yytext); }
+ YY_BREAK
+case 49:
+YY_RULE_SETUP
+#line 188 "/home/drepper/gnu/elfutils/src/ldlex.l"
+ECHO;
+ YY_BREAK
+#line 1625 "ldlex.c"
+ case YY_STATE_EOF(INITIAL):
+ case YY_STATE_EOF(IGNORE):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between yy_current_buffer and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yy_n_chars = yy_current_buffer->yy_n_chars;
+ yy_current_buffer->yy_input_file = yyin;
+ yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state();
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yy_c_buf_p;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yy_did_buffer_switch_on_eof = 0;
+
+ if ( yywrap() )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p =
+ yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state();
+
+ yy_cp = yy_c_buf_p;
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yy_c_buf_p =
+ &yy_current_buffer->yy_ch_buf[yy_n_chars];
+
+ yy_current_state = yy_get_previous_state();
+
+ yy_cp = yy_c_buf_p;
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+ } /* end of yylex */
+
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+
+static int yy_get_next_buffer()
+ {
+ register char *dest = yy_current_buffer->yy_ch_buf;
+ register char *source = yytext_ptr;
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( yy_current_buffer->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ yy_current_buffer->yy_n_chars = yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ yy_current_buffer->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+#ifdef YY_USES_REJECT
+ YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+#else
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = yy_current_buffer;
+
+ int yy_c_buf_p_offset =
+ (int) (yy_c_buf_p - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yy_flex_realloc( (void *) b->yy_ch_buf,
+ b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = yy_current_buffer->yy_buf_size -
+ number_to_move - 1;
+#endif
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]),
+ yy_n_chars, num_to_read );
+
+ yy_current_buffer->yy_n_chars = yy_n_chars;
+ }
+
+ if ( yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart( yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ yy_current_buffer->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ yy_n_chars += number_to_move;
+ yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yytext_ptr = &yy_current_buffer->yy_ch_buf[0];
+
+ return ret_val;
+ }
+
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+static yy_state_type yy_get_previous_state()
+ {
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = yy_start;
+ yy_current_state += YY_AT_BOL();
+ yy_state_ptr = yy_state_buf;
+ *yy_state_ptr++ = yy_current_state;
+
+ for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 211 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ *yy_state_ptr++ = yy_current_state;
+ }
+
+ return yy_current_state;
+ }
+
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+
+#ifdef YY_USE_PROTOS
+static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state )
+#else
+static yy_state_type yy_try_NUL_trans( yy_current_state )
+yy_state_type yy_current_state;
+#endif
+ {
+ register int yy_is_jam;
+
+ register YY_CHAR yy_c = 1;
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 211 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 210);
+ if ( ! yy_is_jam )
+ *yy_state_ptr++ = yy_current_state;
+
+ return yy_is_jam ? 0 : yy_current_state;
+ }
+
+
+#ifndef YY_NO_UNPUT
+#ifdef YY_USE_PROTOS
+static void yyunput( int c, register char *yy_bp )
+#else
+static void yyunput( c, yy_bp )
+int c;
+register char *yy_bp;
+#endif
+ {
+ register char *yy_cp = yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yy_hold_char;
+
+ if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = yy_n_chars + 2;
+ register char *dest = &yy_current_buffer->yy_ch_buf[
+ yy_current_buffer->yy_buf_size + 2];
+ register char *source =
+ &yy_current_buffer->yy_ch_buf[number_to_move];
+
+ while ( source > yy_current_buffer->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ yy_current_buffer->yy_n_chars =
+ yy_n_chars = yy_current_buffer->yy_buf_size;
+
+ if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ if ( c == '\n' )
+ --yylineno;
+
+ yytext_ptr = yy_bp;
+ yy_hold_char = *yy_cp;
+ yy_c_buf_p = yy_cp;
+ }
+#endif /* ifndef YY_NO_UNPUT */
+
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput()
+#else
+static int input()
+#endif
+ {
+ int c;
+
+ *yy_c_buf_p = yy_hold_char;
+
+ if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+ /* This was really a NUL. */
+ *yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ int offset = yy_c_buf_p - yytext_ptr;
+ ++yy_c_buf_p;
+
+ switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart( yyin );
+
+ /* fall through */
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap() )
+ return EOF;
+
+ if ( ! yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p = yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */
+ *yy_c_buf_p = '\0'; /* preserve yytext */
+ yy_hold_char = *++yy_c_buf_p;
+
+ yy_current_buffer->yy_at_bol = (c == '\n');
+ if ( yy_current_buffer->yy_at_bol )
+ ++yylineno;
+
+ return c;
+ }
+#endif /* YY_NO_INPUT */
+
+#ifdef YY_USE_PROTOS
+void yyrestart( FILE *input_file )
+#else
+void yyrestart( input_file )
+FILE *input_file;
+#endif
+ {
+ if ( ! yy_current_buffer )
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE );
+
+ yy_init_buffer( yy_current_buffer, input_file );
+ yy_load_buffer_state();
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+#else
+void yy_switch_to_buffer( new_buffer )
+YY_BUFFER_STATE new_buffer;
+#endif
+ {
+ if ( yy_current_buffer == new_buffer )
+ return;
+
+ if ( yy_current_buffer )
+ {
+ /* Flush out information for old buffer. */
+ *yy_c_buf_p = yy_hold_char;
+ yy_current_buffer->yy_buf_pos = yy_c_buf_p;
+ yy_current_buffer->yy_n_chars = yy_n_chars;
+ }
+
+ yy_current_buffer = new_buffer;
+ yy_load_buffer_state();
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yy_did_buffer_switch_on_eof = 1;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_load_buffer_state( void )
+#else
+void yy_load_buffer_state()
+#endif
+ {
+ yy_n_chars = yy_current_buffer->yy_n_chars;
+ yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos;
+ yyin = yy_current_buffer->yy_input_file;
+ yy_hold_char = *yy_c_buf_p;
+ }
+
+
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+#else
+YY_BUFFER_STATE yy_create_buffer( file, size )
+FILE *file;
+int size;
+#endif
+ {
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer( b, file );
+
+ return b;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_delete_buffer( YY_BUFFER_STATE b )
+#else
+void yy_delete_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+ {
+ if ( ! b )
+ return;
+
+ if ( b == yy_current_buffer )
+ yy_current_buffer = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yy_flex_free( (void *) b->yy_ch_buf );
+
+ yy_flex_free( (void *) b );
+ }
+
+
+
+#ifdef YY_USE_PROTOS
+void yy_init_buffer( YY_BUFFER_STATE b, FILE *file )
+#else
+void yy_init_buffer( b, file )
+YY_BUFFER_STATE b;
+FILE *file;
+#endif
+
+
+ {
+ yy_flush_buffer( b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+#if YY_ALWAYS_INTERACTIVE
+ b->yy_is_interactive = 1;
+#else
+#if YY_NEVER_INTERACTIVE
+ b->yy_is_interactive = 0;
+#else
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+#endif
+#endif
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_flush_buffer( YY_BUFFER_STATE b )
+#else
+void yy_flush_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+
+ {
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == yy_current_buffer )
+ yy_load_buffer_state();
+ }
+
+
+#ifndef YY_NO_SCAN_BUFFER
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size )
+#else
+YY_BUFFER_STATE yy_scan_buffer( base, size )
+char *base;
+yy_size_t size;
+#endif
+ {
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer( b );
+
+ return b;
+ }
+#endif
+
+
+#ifndef YY_NO_SCAN_STRING
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str )
+#else
+YY_BUFFER_STATE yy_scan_string( yy_str )
+yyconst char *yy_str;
+#endif
+ {
+ int len;
+ for ( len = 0; yy_str[len]; ++len )
+ ;
+
+ return yy_scan_bytes( yy_str, len );
+ }
+#endif
+
+
+#ifndef YY_NO_SCAN_BYTES
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len )
+#else
+YY_BUFFER_STATE yy_scan_bytes( bytes, len )
+yyconst char *bytes;
+int len;
+#endif
+ {
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = len + 2;
+ buf = (char *) yy_flex_alloc( n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < len; ++i )
+ buf[i] = bytes[i];
+
+ buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer( buf, n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+ }
+#endif
+
+
+#ifndef YY_NO_PUSH_STATE
+#ifdef YY_USE_PROTOS
+static void yy_push_state( int new_state )
+#else
+static void yy_push_state( new_state )
+int new_state;
+#endif
+ {
+ if ( yy_start_stack_ptr >= yy_start_stack_depth )
+ {
+ yy_size_t new_size;
+
+ yy_start_stack_depth += YY_START_STACK_INCR;
+ new_size = yy_start_stack_depth * sizeof( int );
+
+ if ( ! yy_start_stack )
+ yy_start_stack = (int *) yy_flex_alloc( new_size );
+
+ else
+ yy_start_stack = (int *) yy_flex_realloc(
+ (void *) yy_start_stack, new_size );
+
+ if ( ! yy_start_stack )
+ YY_FATAL_ERROR(
+ "out of memory expanding start-condition stack" );
+ }
+
+ yy_start_stack[yy_start_stack_ptr++] = YY_START;
+
+ BEGIN(new_state);
+ }
+#endif
+
+
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state()
+ {
+ if ( --yy_start_stack_ptr < 0 )
+ YY_FATAL_ERROR( "start-condition stack underflow" );
+
+ BEGIN(yy_start_stack[yy_start_stack_ptr]);
+ }
+#endif
+
+
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state()
+ {
+ return yy_start_stack[yy_start_stack_ptr - 1];
+ }
+#endif
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+#ifdef YY_USE_PROTOS
+static void yy_fatal_error( yyconst char msg[] )
+#else
+static void yy_fatal_error( msg )
+char msg[];
+#endif
+ {
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+ }
+
+
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ yytext[yyleng] = yy_hold_char; \
+ yy_c_buf_p = yytext + n; \
+ yy_hold_char = *yy_c_buf_p; \
+ *yy_c_buf_p = '\0'; \
+ yyleng = n; \
+ } \
+ while ( 0 )
+
+
+/* Internal utility routines. */
+
+#ifndef yytext_ptr
+#ifdef YY_USE_PROTOS
+static void yy_flex_strncpy( char *s1, yyconst char *s2, int n )
+#else
+static void yy_flex_strncpy( s1, s2, n )
+char *s1;
+yyconst char *s2;
+int n;
+#endif
+ {
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+ }
+#endif
+
+#ifdef YY_NEED_STRLEN
+#ifdef YY_USE_PROTOS
+static int yy_flex_strlen( yyconst char *s )
+#else
+static int yy_flex_strlen( s )
+yyconst char *s;
+#endif
+ {
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+ }
+#endif
+
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_alloc( yy_size_t size )
+#else
+static void *yy_flex_alloc( size )
+yy_size_t size;
+#endif
+ {
+ return (void *) malloc( size );
+ }
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_realloc( void *ptr, yy_size_t size )
+#else
+static void *yy_flex_realloc( ptr, size )
+void *ptr;
+yy_size_t size;
+#endif
+ {
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+ }
+
+#ifdef YY_USE_PROTOS
+static void yy_flex_free( void *ptr )
+#else
+static void yy_flex_free( ptr )
+void *ptr;
+#endif
+ {
+ free( ptr );
+ }
+
+#if YY_MAIN
+int main()
+ {
+ yylex();
+ return 0;
+ }
+#endif
+#line 188 "/home/drepper/gnu/elfutils/src/ldlex.l"
+
+
+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/ldlex.l b/src/ldlex.l
new file mode 100644
index 00000000..06ea6237
--- /dev/null
+++ b/src/ldlex.l
@@ -0,0 +1,343 @@
+%{
+/* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc.
+ Written by Ulrich Drepper <drepper@redhat.com>, 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)
+
+/* 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; }
+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.c b/src/ldscript.c
new file mode 100644
index 00000000..0f70ca95
--- /dev/null
+++ b/src/ldscript.c
@@ -0,0 +1,2220 @@
+/* A Bison parser, made by GNU Bison 1.875c. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* Written by Richard Stallman by simplifying the original so called
+ ``semantic'' parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+/* If NAME_PREFIX is specified substitute the variables and functions
+ names. */
+#define yyparse ldparse
+#define yylex ldlex
+#define yyerror lderror
+#define yylval ldlval
+#define yychar ldchar
+#define yydebug lddebug
+#define yynerrs ldnerrs
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ kADD_OP = 258,
+ kALIGN = 259,
+ kENTRY = 260,
+ kEXCLUDE_FILE = 261,
+ kFILENAME = 262,
+ kGLOBAL = 263,
+ kGROUP = 264,
+ kID = 265,
+ kINPUT = 266,
+ kINTERP = 267,
+ kKEEP = 268,
+ kLOCAL = 269,
+ kMODE = 270,
+ kMUL_OP = 271,
+ kNUM = 272,
+ kOUTPUT_FORMAT = 273,
+ kPAGESIZE = 274,
+ kPROVIDE = 275,
+ kSEARCH_DIR = 276,
+ kSEGMENT = 277,
+ kSIZEOF_HEADERS = 278,
+ kSORT = 279,
+ kVERSION = 280,
+ kVERSION_SCRIPT = 281,
+ ADD_OP = 282,
+ MUL_OP = 283
+ };
+#endif
+#define kADD_OP 258
+#define kALIGN 259
+#define kENTRY 260
+#define kEXCLUDE_FILE 261
+#define kFILENAME 262
+#define kGLOBAL 263
+#define kGROUP 264
+#define kID 265
+#define kINPUT 266
+#define kINTERP 267
+#define kKEEP 268
+#define kLOCAL 269
+#define kMODE 270
+#define kMUL_OP 271
+#define kNUM 272
+#define kOUTPUT_FORMAT 273
+#define kPAGESIZE 274
+#define kPROVIDE 275
+#define kSEARCH_DIR 276
+#define kSEGMENT 277
+#define kSIZEOF_HEADERS 278
+#define kSORT 279
+#define kVERSION 280
+#define kVERSION_SCRIPT 281
+#define ADD_OP 282
+#define MUL_OP 283
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 1 "/home/drepper/gnu/elfutils/src/ldscript.y"
+
+/* Parser for linker scripts.
+ Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc.
+ Written by Ulrich Drepper <drepper@redhat.com>, 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 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);
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+#line 58 "/home/drepper/gnu/elfutils/src/ldscript.y"
+typedef union YYSTYPE {
+ 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;
+} YYSTYPE;
+/* Line 191 of yacc.c. */
+#line 213 "ldscript.c"
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 214 of yacc.c. */
+#line 225 "ldscript.c"
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+# ifndef YYFREE
+# define YYFREE free
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# endif
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# define YYSTACK_ALLOC alloca
+# endif
+# else
+# if defined (alloca) || defined (_ALLOCA_H)
+# define YYSTACK_ALLOC alloca
+# else
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# else
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# endif
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+ && (! defined (__cplusplus) \
+ || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ short yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (short) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined (__GNUC__) && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ register YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (0)
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+ typedef signed char yysigned_char;
+#else
+ typedef short yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 30
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 198
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 39
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 22
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 62
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 146
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 283
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const unsigned char yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 28, 2,
+ 32, 33, 30, 2, 38, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 34,
+ 2, 37, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 35, 27, 36, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 29, 31
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const unsigned char yyprhs[] =
+{
+ 0, 0, 3, 5, 8, 11, 13, 19, 25, 31,
+ 37, 43, 49, 54, 59, 64, 69, 72, 74, 77,
+ 82, 85, 89, 96, 99, 101, 103, 108, 111, 117,
+ 119, 124, 129, 130, 135, 139, 143, 147, 151, 155,
+ 159, 161, 163, 165, 167, 171, 173, 175, 176, 179,
+ 181, 186, 192, 199, 202, 204, 207, 210, 214, 217,
+ 219, 221, 223
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yysigned_char yyrhs[] =
+{
+ 40, 0, -1, 41, -1, 26, 54, -1, 41, 42,
+ -1, 42, -1, 5, 32, 10, 33, 34, -1, 21,
+ 32, 59, 33, 34, -1, 19, 32, 17, 33, 34,
+ -1, 12, 32, 59, 33, 34, -1, 22, 15, 35,
+ 43, 36, -1, 22, 1, 35, 43, 36, -1, 9,
+ 32, 52, 33, -1, 11, 32, 52, 33, -1, 25,
+ 35, 54, 36, -1, 18, 32, 59, 33, -1, 43,
+ 44, -1, 44, -1, 45, 34, -1, 10, 35, 46,
+ 36, -1, 10, 34, -1, 10, 37, 51, -1, 20,
+ 32, 10, 37, 51, 33, -1, 46, 47, -1, 47,
+ -1, 48, -1, 13, 32, 48, 33, -1, 45, 34,
+ -1, 60, 32, 50, 49, 33, -1, 10, -1, 24,
+ 32, 10, 33, -1, 6, 32, 59, 33, -1, -1,
+ 4, 32, 51, 33, -1, 32, 51, 33, -1, 51,
+ 30, 51, -1, 51, 16, 51, -1, 51, 3, 51,
+ -1, 51, 28, 51, -1, 51, 27, 51, -1, 17,
+ -1, 10, -1, 23, -1, 19, -1, 52, 53, 59,
+ -1, 59, -1, 38, -1, -1, 54, 55, -1, 55,
+ -1, 35, 56, 36, 34, -1, 59, 35, 56, 36,
+ 34, -1, 59, 35, 56, 36, 59, 34, -1, 56,
+ 57, -1, 57, -1, 8, 58, -1, 14, 58, -1,
+ 58, 60, 34, -1, 60, 34, -1, 7, -1, 10,
+ -1, 59, -1, 30, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const unsigned short yyrline[] =
+{
+ 0, 128, 128, 129, 133, 134, 137, 142, 146, 151,
+ 156, 160, 166, 177, 179, 181, 185, 190, 194, 199,
+ 211, 235, 237, 241, 246, 250, 255, 262, 269, 280,
+ 282, 286, 289, 292, 297, 299, 305, 311, 317, 323,
+ 329, 334, 339, 341, 345, 351, 355, 356, 359, 364,
+ 368, 374, 380, 389, 391, 395, 397, 402, 408, 412,
+ 414, 418, 420
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE
+/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "kADD_OP", "kALIGN", "kENTRY",
+ "kEXCLUDE_FILE", "kFILENAME", "kGLOBAL", "kGROUP", "kID", "kINPUT",
+ "kINTERP", "kKEEP", "kLOCAL", "kMODE", "kMUL_OP", "kNUM",
+ "kOUTPUT_FORMAT", "kPAGESIZE", "kPROVIDE", "kSEARCH_DIR", "kSEGMENT",
+ "kSIZEOF_HEADERS", "kSORT", "kVERSION", "kVERSION_SCRIPT", "'|'", "'&'",
+ "ADD_OP", "'*'", "MUL_OP", "'('", "')'", "';'", "'{'", "'}'", "'='",
+ "','", "$accept", "script_or_version", "file", "content",
+ "outputsections", "outputsection", "assignment", "inputsections",
+ "inputsection", "sectionname", "sort_opt_name", "exclude_opt", "expr",
+ "filename_id_list", "comma_opt", "versionlist", "version",
+ "version_stmt_list", "version_stmt", "filename_id_star_list",
+ "filename_id", "filename_id_star", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const unsigned short yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 124, 38, 282,
+ 42, 283, 40, 41, 59, 123, 125, 61, 44
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const unsigned char yyr1[] =
+{
+ 0, 39, 40, 40, 41, 41, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 43, 43, 44, 44,
+ 44, 45, 45, 46, 46, 47, 47, 47, 48, 49,
+ 49, 50, 50, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 52, 52, 53, 53, 54, 54,
+ 55, 55, 55, 56, 56, 57, 57, 58, 58, 59,
+ 59, 60, 60
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const unsigned char yyr2[] =
+{
+ 0, 2, 1, 2, 2, 1, 5, 5, 5, 5,
+ 5, 5, 4, 4, 4, 4, 2, 1, 2, 4,
+ 2, 3, 6, 2, 1, 1, 4, 2, 5, 1,
+ 4, 4, 0, 4, 3, 3, 3, 3, 3, 3,
+ 1, 1, 1, 1, 3, 1, 1, 0, 2, 1,
+ 4, 5, 6, 2, 1, 2, 2, 3, 2, 1,
+ 1, 1, 1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const unsigned char yydefact[] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 5, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 59, 60, 0, 3, 49, 0,
+ 1, 4, 0, 47, 45, 47, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 54, 48, 0, 0,
+ 12, 46, 0, 13, 0, 15, 0, 0, 0, 0,
+ 0, 17, 0, 0, 14, 62, 55, 61, 0, 56,
+ 0, 53, 0, 6, 44, 9, 8, 7, 20, 0,
+ 0, 0, 11, 16, 18, 10, 0, 58, 50, 0,
+ 60, 0, 0, 0, 24, 25, 0, 0, 41, 40,
+ 43, 42, 0, 21, 0, 57, 51, 0, 0, 27,
+ 19, 23, 32, 0, 0, 0, 0, 0, 0, 0,
+ 0, 52, 0, 0, 0, 0, 34, 37, 36, 39,
+ 38, 35, 0, 26, 0, 29, 0, 0, 33, 22,
+ 0, 0, 28, 31, 0, 30
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const short yydefgoto[] =
+{
+ -1, 11, 12, 13, 60, 61, 62, 93, 94, 95,
+ 137, 124, 103, 33, 52, 27, 28, 45, 46, 66,
+ 67, 96
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -41
+static const short yypact[] =
+{
+ 107, -28, -20, -13, 34, 77, 85, 88, 91, 33,
+ 38, 123, 125, -41, 117, 52, 52, 52, 52, 114,
+ 52, 100, 103, 38, -41, -41, 96, 38, -41, 110,
+ -41, -41, 115, 64, -41, 67, 116, 118, 120, 127,
+ 1, 1, 28, 84, 84, 36, -41, -41, 96, 128,
+ -41, -41, 52, -41, 129, -41, 130, 131, 105, 134,
+ 75, -41, 133, 79, -41, -41, 84, -41, 135, 84,
+ 136, -41, 41, -41, -41, -41, -41, -41, -41, 83,
+ 48, 151, -41, -41, -41, -41, 137, -41, -41, 44,
+ 138, 140, 139, 17, -41, -41, 142, 144, -41, -41,
+ -41, -41, 48, 54, 141, -41, -41, 143, 84, -41,
+ -41, -41, 162, 48, -2, 48, 48, 48, 48, 48,
+ 48, -41, 146, 148, 97, 6, -41, 54, 54, 58,
+ 53, -1, 13, -41, 52, -41, 149, 150, -41, -41,
+ 152, 172, -41, -41, 153, -41
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const short yypgoto[] =
+{
+ -41, -41, -41, 175, 147, -40, 29, -41, 98, 76,
+ -41, -41, 39, 173, -41, 167, -24, 145, 15, 154,
+ -10, 32
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const unsigned char yytable[] =
+{
+ 29, 115, 115, 47, 14, 34, 34, 36, 37, 115,
+ 39, 58, 15, 29, 116, 116, 115, 29, 47, 16,
+ 83, 59, 116, 83, 24, 117, 118, 90, 119, 116,
+ 91, 126, 29, 117, 118, 24, 119, 59, 25, 138,
+ 117, 118, 74, 119, 43, 24, 139, 65, 25, 43,
+ 44, 24, 97, 110, 25, 44, 115, 115, 98, 24,
+ 71, 115, 25, 26, 64, 99, 17, 100, 23, 116,
+ 116, 101, 70, 26, 116, 68, 68, 89, 106, 107,
+ 102, 117, 118, 119, 119, 58, 118, 71, 119, 58,
+ 24, 24, 21, 90, 25, 59, 91, 50, 86, 59,
+ 53, 86, 51, 59, 43, 51, 22, 135, 92, 18,
+ 44, 82, 1, 65, 65, 85, 2, 19, 3, 4,
+ 20, 136, 92, 30, 140, 5, 6, 32, 7, 8,
+ 1, 38, 9, 10, 2, 40, 3, 4, 41, 78,
+ 79, 114, 80, 5, 6, 48, 7, 8, 49, 54,
+ 9, 55, 125, 56, 127, 128, 129, 130, 131, 132,
+ 57, 104, 73, 75, 76, 77, 81, 84, 123, 87,
+ 88, 105, 108, 109, 112, 80, 113, 121, 120, 133,
+ 134, 141, 144, 142, 122, 143, 145, 31, 63, 35,
+ 42, 111, 0, 72, 0, 0, 0, 0, 69
+};
+
+static const short yycheck[] =
+{
+ 10, 3, 3, 27, 32, 15, 16, 17, 18, 3,
+ 20, 10, 32, 23, 16, 16, 3, 27, 42, 32,
+ 60, 20, 16, 63, 7, 27, 28, 10, 30, 16,
+ 13, 33, 42, 27, 28, 7, 30, 20, 10, 33,
+ 27, 28, 52, 30, 8, 7, 33, 30, 10, 8,
+ 14, 7, 4, 36, 10, 14, 3, 3, 10, 7,
+ 45, 3, 10, 35, 36, 17, 32, 19, 35, 16,
+ 16, 23, 36, 35, 16, 43, 44, 36, 34, 89,
+ 32, 27, 28, 30, 30, 10, 28, 72, 30, 10,
+ 7, 7, 1, 10, 10, 20, 13, 33, 66, 20,
+ 33, 69, 38, 20, 8, 38, 15, 10, 79, 32,
+ 14, 36, 5, 30, 30, 36, 9, 32, 11, 12,
+ 32, 24, 93, 0, 134, 18, 19, 10, 21, 22,
+ 5, 17, 25, 26, 9, 35, 11, 12, 35, 34,
+ 35, 102, 37, 18, 19, 35, 21, 22, 33, 33,
+ 25, 33, 113, 33, 115, 116, 117, 118, 119, 120,
+ 33, 10, 34, 34, 34, 34, 32, 34, 6, 34,
+ 34, 34, 32, 34, 32, 37, 32, 34, 37, 33,
+ 32, 32, 10, 33, 108, 33, 33, 12, 41, 16,
+ 23, 93, -1, 48, -1, -1, -1, -1, 44
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const unsigned char yystos[] =
+{
+ 0, 5, 9, 11, 12, 18, 19, 21, 22, 25,
+ 26, 40, 41, 42, 32, 32, 32, 32, 32, 32,
+ 32, 1, 15, 35, 7, 10, 35, 54, 55, 59,
+ 0, 42, 10, 52, 59, 52, 59, 59, 17, 59,
+ 35, 35, 54, 8, 14, 56, 57, 55, 35, 33,
+ 33, 38, 53, 33, 33, 33, 33, 33, 10, 20,
+ 43, 44, 45, 43, 36, 30, 58, 59, 60, 58,
+ 36, 57, 56, 34, 59, 34, 34, 34, 34, 35,
+ 37, 32, 36, 44, 34, 36, 60, 34, 34, 36,
+ 10, 13, 45, 46, 47, 48, 60, 4, 10, 17,
+ 19, 23, 32, 51, 10, 34, 34, 59, 32, 34,
+ 36, 47, 32, 32, 51, 3, 16, 27, 28, 30,
+ 37, 34, 48, 6, 50, 51, 33, 51, 51, 51,
+ 51, 51, 51, 33, 32, 10, 24, 49, 33, 33,
+ 59, 32, 33, 33, 10, 33
+};
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# endif
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror ("syntax error: cannot back up");\
+ YYERROR; \
+ } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+/* YYLLOC_DEFAULT -- Compute the default location (before the actions
+ are run). */
+
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ ((Current).first_line = (Rhs)[1].first_line, \
+ (Current).first_column = (Rhs)[1].first_column, \
+ (Current).last_line = (Rhs)[N].last_line, \
+ (Current).last_column = (Rhs)[N].last_column)
+#endif
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (0)
+
+# define YYDSYMPRINT(Args) \
+do { \
+ if (yydebug) \
+ yysymprint Args; \
+} while (0)
+
+# define YYDSYMPRINTF(Title, Token, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yysymprint (stderr, \
+ Token, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (0)
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_stack_print (short *bottom, short *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ short *bottom;
+ short *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (/* Nothing. */; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_reduce_print (int yyrule)
+#else
+static void
+yy_reduce_print (yyrule)
+ int yyrule;
+#endif
+{
+ int yyi;
+ unsigned int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ",
+ yyrule - 1, yylno);
+ /* Print the symbols being reduced, and their result. */
+ for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
+ YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]);
+ YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]);
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (Rule); \
+} while (0)
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YYDSYMPRINT(Args)
+# define YYDSYMPRINTF(Title, Token, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#if defined (YYMAXDEPTH) && YYMAXDEPTH == 0
+# undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined (__GLIBC__) && defined (_STRING_H)
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+static YYSIZE_T
+# if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+# else
+yystrlen (yystr)
+ const char *yystr;
+# endif
+{
+ register const char *yys = yystr;
+
+ while (*yys++ != '\0')
+ continue;
+
+ return yys - yystr - 1;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+static char *
+# if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+# else
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+# endif
+{
+ register char *yyd = yydest;
+ register const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+#endif /* !YYERROR_VERBOSE */
+
+
+
+#if YYDEBUG
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yysymprint (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+
+ if (yytype < YYNTOKENS)
+ {
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+# ifdef YYPRINT
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+ }
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+ YYFPRINTF (yyoutput, ")");
+}
+
+#endif /* ! YYDEBUG */
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yydestruct (int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yytype, yyvaluep)
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM);
+# else
+int yyparse ();
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM)
+# else
+int yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+ register int yystate;
+ register int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken = 0;
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ short yyssa[YYINITDEPTH];
+ short *yyss = yyssa;
+ register short *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ register YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK (yyvsp--, yyssp--)
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* When reducing, the number of symbols on the RHS of the reduced
+ rule. */
+ int yylen;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks.
+ */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow ("parser stack overflow",
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyoverflowlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyoverflowlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ short *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyoverflowlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+ YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken]));
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 3:
+#line 130 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { add_versions (yyvsp[0].version); }
+ break;
+
+ case 6:
+#line 138 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ if (likely (ld_state.entry == NULL))
+ ld_state.entry = yyvsp[-2].str;
+ }
+ break;
+
+ case 7:
+#line 143 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ ld_new_searchdir (yyvsp[-2].str);
+ }
+ break;
+
+ case 8:
+#line 147 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ if (likely (ld_state.pagesize == 0))
+ ld_state.pagesize = yyvsp[-2].num;
+ }
+ break;
+
+ case 9:
+#line 152 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ if (likely (ld_state.interp == NULL))
+ ld_state.interp = yyvsp[-2].str;
+ }
+ break;
+
+ case 10:
+#line 157 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ new_segment (yyvsp[-3].num, yyvsp[-1].output_rule);
+ }
+ break;
+
+ case 11:
+#line 161 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ fputs_unlocked (gettext ("mode for segment invalid\n"),
+ stderr);
+ new_segment (0, yyvsp[-1].output_rule);
+ }
+ break;
+
+ case 12:
+#line 167 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ /* First little optimization. If there is only one
+ file in the group don't do anything. */
+ if (yyvsp[-1].filename_list != yyvsp[-1].filename_list->next)
+ {
+ yyvsp[-1].filename_list->next->group_start = 1;
+ yyvsp[-1].filename_list->group_end = 1;
+ }
+ add_inputfiles (yyvsp[-1].filename_list);
+ }
+ break;
+
+ case 13:
+#line 178 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { add_inputfiles (yyvsp[-1].filename_list); }
+ break;
+
+ case 14:
+#line 180 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { add_versions (yyvsp[-1].version); }
+ break;
+
+ case 15:
+#line 182 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { /* XXX TODO */ }
+ break;
+
+ case 16:
+#line 186 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyvsp[0].output_rule->next = yyvsp[-1].output_rule->next;
+ yyval.output_rule = yyvsp[-1].output_rule->next = yyvsp[0].output_rule;
+ }
+ break;
+
+ case 17:
+#line 191 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.output_rule = yyvsp[0].output_rule; }
+ break;
+
+ case 18:
+#line 195 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyval.output_rule = new_output_rule (output_assignment);
+ yyval.output_rule->val.assignment = yyvsp[-1].assignment;
+ }
+ break;
+
+ case 19:
+#line 200 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyval.output_rule = new_output_rule (output_section);
+ yyval.output_rule->val.section.name = yyvsp[-3].str;
+ yyval.output_rule->val.section.input = yyvsp[-1].input_rule->next;
+ if (ld_state.strip == strip_debug
+ && ebl_debugscn_p (ld_state.ebl, yyvsp[-3].str))
+ yyval.output_rule->val.section.ignored = true;
+ else
+ yyval.output_rule->val.section.ignored = false;
+ yyvsp[-1].input_rule->next = NULL;
+ }
+ break;
+
+ case 20:
+#line 212 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ /* This is a short cut for "ID { *(ID) }". */
+ yyval.output_rule = new_output_rule (output_section);
+ yyval.output_rule->val.section.name = yyvsp[-1].str;
+ yyval.output_rule->val.section.input = new_input_rule (input_section);
+ yyval.output_rule->val.section.input->next = NULL;
+ yyval.output_rule->val.section.input->val.section =
+ (struct filemask_section_name *)
+ obstack_alloc (&ld_state.smem,
+ sizeof (struct filemask_section_name));
+ yyval.output_rule->val.section.input->val.section->filemask = NULL;
+ yyval.output_rule->val.section.input->val.section->excludemask = NULL;
+ yyval.output_rule->val.section.input->val.section->section_name =
+ new_input_section_name (yyvsp[-1].str, false);
+ yyval.output_rule->val.section.input->val.section->keep_flag = false;
+ if (ld_state.strip == strip_debug
+ && ebl_debugscn_p (ld_state.ebl, yyvsp[-1].str))
+ yyval.output_rule->val.section.ignored = true;
+ else
+ yyval.output_rule->val.section.ignored = false;
+ }
+ break;
+
+ case 21:
+#line 236 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.assignment = new_assignment (yyvsp[-2].str, yyvsp[0].expr, false); }
+ break;
+
+ case 22:
+#line 238 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.assignment = new_assignment (yyvsp[-3].str, yyvsp[-1].expr, true); }
+ break;
+
+ case 23:
+#line 242 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyvsp[0].input_rule->next = yyvsp[-1].input_rule->next;
+ yyval.input_rule = yyvsp[-1].input_rule->next = yyvsp[0].input_rule;
+ }
+ break;
+
+ case 24:
+#line 247 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.input_rule = yyvsp[0].input_rule; }
+ break;
+
+ case 25:
+#line 251 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyval.input_rule = new_input_rule (input_section);
+ yyval.input_rule->val.section = yyvsp[0].filemask_section_name;
+ }
+ break;
+
+ case 26:
+#line 256 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyvsp[-1].filemask_section_name->keep_flag = true;
+
+ yyval.input_rule = new_input_rule (input_section);
+ yyval.input_rule->val.section = yyvsp[-1].filemask_section_name;
+ }
+ break;
+
+ case 27:
+#line 263 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyval.input_rule = new_input_rule (input_assignment);
+ yyval.input_rule->val.assignment = yyvsp[-1].assignment;
+ }
+ break;
+
+ case 28:
+#line 270 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyval.filemask_section_name = (struct filemask_section_name *)
+ obstack_alloc (&ld_state.smem, sizeof (*yyval.filemask_section_name));
+ yyval.filemask_section_name->filemask = yyvsp[-4].str;
+ yyval.filemask_section_name->excludemask = yyvsp[-2].str;
+ yyval.filemask_section_name->section_name = yyvsp[-1].sectionname;
+ yyval.filemask_section_name->keep_flag = false;
+ }
+ break;
+
+ case 29:
+#line 281 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.sectionname = new_input_section_name (yyvsp[0].str, false); }
+ break;
+
+ case 30:
+#line 283 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.sectionname = new_input_section_name (yyvsp[-1].str, true); }
+ break;
+
+ case 31:
+#line 287 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.str = yyvsp[-1].str; }
+ break;
+
+ case 32:
+#line 289 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.str = NULL; }
+ break;
+
+ case 33:
+#line 293 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyval.expr = new_expr (exp_align);
+ yyval.expr->val.child = yyvsp[-1].expr;
+ }
+ break;
+
+ case 34:
+#line 298 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.expr = yyvsp[-1].expr; }
+ break;
+
+ case 35:
+#line 300 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyval.expr = new_expr (exp_mult);
+ yyval.expr->val.binary.left = yyvsp[-2].expr;
+ yyval.expr->val.binary.right = yyvsp[0].expr;
+ }
+ break;
+
+ case 36:
+#line 306 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyval.expr = new_expr (yyvsp[-1].op);
+ yyval.expr->val.binary.left = yyvsp[-2].expr;
+ yyval.expr->val.binary.right = yyvsp[0].expr;
+ }
+ break;
+
+ case 37:
+#line 312 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyval.expr = new_expr (yyvsp[-1].op);
+ yyval.expr->val.binary.left = yyvsp[-2].expr;
+ yyval.expr->val.binary.right = yyvsp[0].expr;
+ }
+ break;
+
+ case 38:
+#line 318 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyval.expr = new_expr (exp_and);
+ yyval.expr->val.binary.left = yyvsp[-2].expr;
+ yyval.expr->val.binary.right = yyvsp[0].expr;
+ }
+ break;
+
+ case 39:
+#line 324 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyval.expr = new_expr (exp_or);
+ yyval.expr->val.binary.left = yyvsp[-2].expr;
+ yyval.expr->val.binary.right = yyvsp[0].expr;
+ }
+ break;
+
+ case 40:
+#line 330 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyval.expr = new_expr (exp_num);
+ yyval.expr->val.num = yyvsp[0].num;
+ }
+ break;
+
+ case 41:
+#line 335 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyval.expr = new_expr (exp_id);
+ yyval.expr->val.str = yyvsp[0].str;
+ }
+ break;
+
+ case 42:
+#line 340 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.expr = new_expr (exp_sizeof_headers); }
+ break;
+
+ case 43:
+#line 342 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.expr = new_expr (exp_pagesize); }
+ break;
+
+ case 44:
+#line 346 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ struct filename_list *newp = new_filename_listelem (yyvsp[0].str);
+ newp->next = yyvsp[-2].filename_list->next;
+ yyval.filename_list = yyvsp[-2].filename_list->next = newp;
+ }
+ break;
+
+ case 45:
+#line 352 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.filename_list = new_filename_listelem (yyvsp[0].str); }
+ break;
+
+ case 48:
+#line 360 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyvsp[0].version->next = yyvsp[-1].version->next;
+ yyval.version = yyvsp[-1].version->next = yyvsp[0].version;
+ }
+ break;
+
+ case 49:
+#line 365 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.version = yyvsp[0].version; }
+ break;
+
+ case 50:
+#line 369 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyvsp[-2].version->versionname = "";
+ yyvsp[-2].version->parentname = NULL;
+ yyval.version = yyvsp[-2].version;
+ }
+ break;
+
+ case 51:
+#line 375 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyvsp[-2].version->versionname = yyvsp[-4].str;
+ yyvsp[-2].version->parentname = NULL;
+ yyval.version = yyvsp[-2].version;
+ }
+ break;
+
+ case 52:
+#line 381 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ yyvsp[-3].version->versionname = yyvsp[-5].str;
+ yyvsp[-3].version->parentname = yyvsp[-1].str;
+ yyval.version = yyvsp[-3].version;
+ }
+ break;
+
+ case 53:
+#line 390 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.version = merge_versions (yyvsp[-1].version, yyvsp[0].version); }
+ break;
+
+ case 54:
+#line 392 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.version = yyvsp[0].version; }
+ break;
+
+ case 55:
+#line 396 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.version = new_version (NULL, yyvsp[0].id_list); }
+ break;
+
+ case 56:
+#line 398 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.version = new_version (yyvsp[0].id_list, NULL); }
+ break;
+
+ case 57:
+#line 403 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ {
+ struct id_list *newp = new_id_listelem (yyvsp[-1].str);
+ newp->next = yyvsp[-2].id_list->next;
+ yyval.id_list = yyvsp[-2].id_list->next = newp;
+ }
+ break;
+
+ case 58:
+#line 409 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.id_list = new_id_listelem (yyvsp[-1].str); }
+ break;
+
+ case 59:
+#line 413 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.str = yyvsp[0].str; }
+ break;
+
+ case 60:
+#line 415 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.str = yyvsp[0].str; }
+ break;
+
+ case 61:
+#line 419 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.str = yyvsp[0].str; }
+ break;
+
+ case 62:
+#line 421 "/home/drepper/gnu/elfutils/src/ldscript.y"
+ { yyval.str = NULL; }
+ break;
+
+
+ }
+
+/* Line 1000 of yacc.c. */
+#line 1654 "ldscript.c"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+
+
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (YYPACT_NINF < yyn && yyn < YYLAST)
+ {
+ YYSIZE_T yysize = 0;
+ int yytype = YYTRANSLATE (yychar);
+ const char* yyprefix;
+ char *yymsg;
+ int yyx;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 0;
+
+ yyprefix = ", expecting ";
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]);
+ yycount += 1;
+ if (yycount == 5)
+ {
+ yysize = 0;
+ break;
+ }
+ }
+ yysize += (sizeof ("syntax error, unexpected ")
+ + yystrlen (yytname[yytype]));
+ yymsg = (char *) YYSTACK_ALLOC (yysize);
+ if (yymsg != 0)
+ {
+ char *yyp = yystpcpy (yymsg, "syntax error, unexpected ");
+ yyp = yystpcpy (yyp, yytname[yytype]);
+
+ if (yycount < 5)
+ {
+ yyprefix = ", expecting ";
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ yyp = yystpcpy (yyp, yyprefix);
+ yyp = yystpcpy (yyp, yytname[yyx]);
+ yyprefix = " or ";
+ }
+ }
+ yyerror (yymsg);
+ YYSTACK_FREE (yymsg);
+ }
+ else
+ yyerror ("syntax error; also virtual memory exhausted");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror ("syntax error");
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* If at end of input, pop the error token,
+ then the rest of the stack, then return failure. */
+ if (yychar == YYEOF)
+ for (;;)
+ {
+ YYPOPSTACK;
+ if (yyssp == yyss)
+ YYABORT;
+ YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+ yydestruct (yystos[*yyssp], yyvsp);
+ }
+ }
+ else
+ {
+ YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc);
+ yydestruct (yytoken, &yylval);
+ yychar = YYEMPTY;
+
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+#ifdef __GNUC__
+ /* Pacify GCC when the user code never invokes YYERROR and the label
+ yyerrorlab therefore never appears in user code. */
+ if (0)
+ goto yyerrorlab;
+#endif
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+ YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+ yydestruct (yystos[yystate], yyvsp);
+ YYPOPSTACK;
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ YYDPRINTF ((stderr, "Shifting error token, "));
+
+ *++yyvsp = yylval;
+
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*----------------------------------------------.
+| yyoverflowlab -- parser overflow comes here. |
+`----------------------------------------------*/
+yyoverflowlab:
+ yyerror ("parser stack overflow");
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+ return yyresult;
+}
+
+
+#line 424 "/home/drepper/gnu/elfutils/src/ldscript.y"
+
+
+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 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/ldscript.h b/src/ldscript.h
new file mode 100644
index 00000000..6464d8da
--- /dev/null
+++ b/src/ldscript.h
@@ -0,0 +1,116 @@
+/* A Bison parser, made by GNU Bison 1.875c. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ kADD_OP = 258,
+ kALIGN = 259,
+ kENTRY = 260,
+ kEXCLUDE_FILE = 261,
+ kFILENAME = 262,
+ kGLOBAL = 263,
+ kGROUP = 264,
+ kID = 265,
+ kINPUT = 266,
+ kINTERP = 267,
+ kKEEP = 268,
+ kLOCAL = 269,
+ kMODE = 270,
+ kMUL_OP = 271,
+ kNUM = 272,
+ kOUTPUT_FORMAT = 273,
+ kPAGESIZE = 274,
+ kPROVIDE = 275,
+ kSEARCH_DIR = 276,
+ kSEGMENT = 277,
+ kSIZEOF_HEADERS = 278,
+ kSORT = 279,
+ kVERSION = 280,
+ kVERSION_SCRIPT = 281,
+ ADD_OP = 282,
+ MUL_OP = 283
+ };
+#endif
+#define kADD_OP 258
+#define kALIGN 259
+#define kENTRY 260
+#define kEXCLUDE_FILE 261
+#define kFILENAME 262
+#define kGLOBAL 263
+#define kGROUP 264
+#define kID 265
+#define kINPUT 266
+#define kINTERP 267
+#define kKEEP 268
+#define kLOCAL 269
+#define kMODE 270
+#define kMUL_OP 271
+#define kNUM 272
+#define kOUTPUT_FORMAT 273
+#define kPAGESIZE 274
+#define kPROVIDE 275
+#define kSEARCH_DIR 276
+#define kSEGMENT 277
+#define kSIZEOF_HEADERS 278
+#define kSORT 279
+#define kVERSION 280
+#define kVERSION_SCRIPT 281
+#define ADD_OP 282
+#define MUL_OP 283
+
+
+
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+#line 58 "/home/drepper/gnu/elfutils/src/ldscript.y"
+typedef union YYSTYPE {
+ 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;
+} YYSTYPE;
+/* Line 1275 of yacc.c. */
+#line 108 "ldscript.h"
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+extern YYSTYPE ldlval;
+
+
+
diff --git a/src/ldscript.y b/src/ldscript.y
new file mode 100644
index 00000000..8f68078d
--- /dev/null
+++ b/src/ldscript.y
@@ -0,0 +1,764 @@
+%{
+/* Parser for linker scripts.
+ Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc.
+ Written by Ulrich Drepper <drepper@redhat.com>, 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 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 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); }
+ | 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: 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 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..1a297054
--- /dev/null
+++ b/src/nm.c
@@ -0,0 +1,1302 @@
+/* Print information from ELF file in human-readable form.
+ Copyright (C) 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
+ Written by Ulrich Drepper <drepper@redhat.com>, 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 <libebl.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>
+
+
+/* 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;
+
+
+/* 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);
+
+/* Function to print some extra text in the help message. */
+static char *more_help (int key, const char *text, void *input);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, args_doc, doc, NULL, more_help, 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, /*@unused@*/ struct argp_state *state)
+{
+ 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\
+"), "2004");
+ fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
+}
+
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg, /*@unused@*/ struct argp_state *state)
+{
+ 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;
+}
+
+
+static char *
+more_help (int key, const char *text, /*@unused@*/ void *input)
+{
+ char *buf;
+
+ switch (key)
+ {
+ case ARGP_KEY_HELP_EXTRA:
+ /* We print some extra information. */
+ if (asprintf (&buf, gettext ("Please report bugs to %s.\n"),
+ PACKAGE_BUGREPORT) < 0)
+ buf = NULL;
+ return buf;
+
+ default:
+ break;
+ }
+ return (char *) text;
+}
+
+
+/* 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, 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, Dwarf_Global *global, void *arg)
+{
+ 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 (Ebl *ebl, 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_Ehdr *ehdr, 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_Ehdr *ehdr, GElf_Word strndx,
+ const char *prefix, const char *fname,
+ 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 (ebl, 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, ehdr, shdr->sh_link, prefix, fname,
+ fullname, sym_mem, nentries);
+ break;
+
+ case format_posix:
+ default:
+ assert (format == format_posix);
+ show_symbols_posix (ebl->elf, ehdr, shdr->sh_link, prefix, fname,
+ 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..6129e29d
--- /dev/null
+++ b/src/readelf.c
@@ -0,0 +1,4541 @@
+/* Print information from ELF file in human-readable form.
+ Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
+ Written by Ulrich Drepper <drepper@redhat.com>, 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 <libebl.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 "../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;
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { NULL, 0, NULL, 0, N_("Output selection:") },
+ { "all", 'a', NULL, 0, N_("Equivalent to: -h -l") },
+ { "dynamic", 'd', NULL, 0, N_("Display the dynamic segment") },
+ { "file-header", 'h', NULL, 0, N_("Display the ELF file header") },
+ { "histogram", 'I', NULL, 0,
+ N_("Display histogram of bucket list lengths") },
+ { "program-headers", 'l', NULL, 0, N_("Display the program headers") },
+ { "relocs", 'r', NULL, 0, N_("Display relocations") },
+ { "section-headers", 'S', NULL, 0, N_("Display the sections' header") },
+ { "symbols", 's', NULL, 0, N_("Display the symbol table") },
+ { "version-info", 'V', NULL, 0, N_("Display versioning information") },
+ { "debug-dump", 'w', "SECTION", OPTION_ARG_OPTIONAL,
+ N_("Display DWARF section content. SECTION can be one of abbrev, "
+ "aranges, frame, info, loc, line, pubnames, str, or macinfo.") },
+ { "notes", 'n', NULL, 0, N_("Display the core notes") },
+ { "arch-specific", 'A', NULL, 0,
+ N_("Display architecture specific information (if any)") },
+
+ { NULL, 0, NULL, 0, N_("Output control:") },
+
+ { NULL, 0, NULL, 0, NULL }
+};
+
+/* 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);
+
+/* Function to print some extra text in the help message. */
+static char *more_help (int key, const char *text, void *input);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, args_doc, doc, NULL, more_help
+};
+
+
+/* Flags set by the option controlling the output. */
+
+/* True if any of the control options is set. */
+static bool any_control_option;
+
+/* 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_all = (section_abbrev | section_aranges | section_frame
+ | section_info | section_line | section_loc
+ | section_pubnames | section_str | section_macinfo)
+} 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, GElf_Ehdr *ehdr);
+static void print_dynamic (Ebl *ebl, GElf_Ehdr *ehdr);
+static void print_relocs (Ebl *ebl, GElf_Ehdr *ehdr);
+static void handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
+ GElf_Shdr *shdr);
+static void handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
+ GElf_Shdr *shdr);
+static void print_symtab (Ebl *ebl, GElf_Ehdr *ehdr, int type);
+static void handle_symtab (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
+ GElf_Shdr *shdr);
+static void print_verinfo (Ebl *ebl, GElf_Ehdr *ehdr);
+static void handle_verneed (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
+ GElf_Shdr *shdr);
+static void handle_verdef (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
+ GElf_Shdr *shdr);
+static void handle_versym (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
+ GElf_Shdr *shdr);
+static void print_debug (Ebl *ebl, GElf_Ehdr *ehdr);
+static void handle_hash (Ebl *ebl, GElf_Ehdr *ehdr);
+static void handle_notes (Ebl *ebl, GElf_Ehdr *ehdr);
+static void print_liblist (Ebl *ebl, GElf_Ehdr *ehdr);
+
+
+int
+main (int argc, char *argv[])
+{
+ int remaining;
+ bool only_one;
+
+ /* Set locale. */
+ setlocale (LC_ALL, "");
+
+ /* Initialize the message catalog. */
+ textdomain (PACKAGE);
+
+ /* Parse and process arguments. */
+ argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+ /* If no control option or no ELF file is given punt. */
+ if ((any_control_option == 0 && print_debug_sections == 0)
+ || remaining >= argc)
+ {
+ argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
+ program_invocation_short_name);
+ exit (1);
+ }
+
+ /* 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. */
+ only_one = remaining + 1 == argc;
+ do
+ {
+ int fd;
+ Elf *elf;
+
+ /* Open the file. */
+ fd = open (argv[remaining], O_RDONLY);
+ if (fd == -1)
+ {
+ error (0, errno, gettext ("cannot open input file"));
+ continue;
+ }
+
+ /* Create an `Elf' descriptor. */
+ 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)
+{
+ 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, "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);
+ }
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+static char *
+more_help (int key, const char *text, void *input)
+{
+ char *buf;
+
+ switch (key)
+ {
+ case ARGP_KEY_HELP_EXTRA:
+ /* We print some extra information. */
+ if (asprintf (&buf, gettext ("Please report bugs to %s.\n"),
+ PACKAGE_BUGREPORT) < 0)
+ buf = NULL;
+ return buf;
+
+ default:
+ break;
+ }
+ return (char *) text;
+}
+
+
+/* Print the version information. */
+static void
+print_version (FILE *stream, struct argp_state *state)
+{
+ 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\
+"), "2004");
+ 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, ehdr);
+ if (print_dynamic_table)
+ print_dynamic (ebl, ehdr);
+ if (print_relocations)
+ print_relocs (ebl, ehdr);
+ if (print_histogram)
+ handle_hash (ebl, ehdr);
+ if (print_symbol_table)
+ print_symtab (ebl, ehdr, SHT_DYNSYM);
+ if (print_version_info)
+ print_verinfo (ebl, ehdr);
+ if (print_symbol_table)
+ print_symtab (ebl, ehdr, SHT_SYMTAB);
+ if (print_arch)
+ print_liblist (ebl, ehdr);
+ 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;
+ 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);
+
+ 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)
+ fputs_unlocked ("]", stdout);
+
+ /* Finish the line. */
+ fputc_unlocked ('\n', stdout);
+ }
+}
+
+
+static void
+handle_scngrp (Ebl *ebl, GElf_Ehdr *ehdr, 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, GElf_Ehdr *ehdr)
+{
+ /* 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, ehdr, 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, GElf_Ehdr *ehdr, 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, GElf_Ehdr *ehdr)
+{
+ /* 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, ehdr, scn, shdr);
+ break;
+ }
+ }
+}
+
+
+/* Print relocations. */
+static void
+print_relocs (Ebl *ebl, GElf_Ehdr *ehdr)
+{
+ /* 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, ehdr, scn, shdr);
+ else if (shdr->sh_type == SHT_RELA)
+ handle_relocs_rela (ebl, ehdr, scn, shdr);
+ }
+ }
+}
+
+
+/* Handle a relocation section. */
+static void
+handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, 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, GElf_Ehdr *ehdr, 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, GElf_Ehdr *ehdr, 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, ehdr, scn, shdr);
+ }
+}
+
+
+static void
+handle_symtab (Ebl *ebl, GElf_Ehdr *ehdr, 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, GElf_Ehdr *ehdr)
+{
+ /* 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, ehdr, scn, shdr);
+ else if (shdr->sh_type == SHT_GNU_verdef)
+ handle_verdef (ebl, ehdr, scn, shdr);
+ else if (shdr->sh_type == SHT_GNU_versym)
+ handle_versym (ebl, ehdr, 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, GElf_Ehdr *ehdr, 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, GElf_Ehdr *ehdr, 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, GElf_Ehdr *ehdr, 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, GElf_Ehdr *ehdr)
+{
+ /* 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, GElf_Ehdr *ehdr)
+{
+ /* 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",
+ };
+ 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"
+ };
+ 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[100];
+ snprintf (buf, sizeof (buf), "lo_user+%u", lang - DW_LANG_lo_user);
+ return buf;
+ }
+
+ return "???";
+}
+
+
+static void
+print_ops (Dwarf *dbg, int level, unsigned int addrsize, Dwarf_Word len,
+ 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 = *((unsigned char *) data);
+ ++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",
+ (int) (20 + level * 2), "", (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",
+ (int) (20 + level * 2), "", (uintmax_t) offset,
+ known[op] ?: "???", *((uint8_t *) data));
+ ++data;
+ --len;
+ offset += 2;
+ break;
+
+ case DW_OP_const2u:
+ printf (" %*s [%4" PRIuMAX "] %s %" PRIu16 "\n",
+ (int) (20 + level * 2), "", (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",
+ (int) (20 + level * 2), "", (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",
+ (int) (20 + level * 2), "", (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",
+ (int) (20 + level * 2), "", (uintmax_t) offset,
+ known[op] ?: "???", *((int8_t *) data));
+ ++data;
+ --len;
+ offset += 2;
+ break;
+
+ case DW_OP_const2s:
+ printf (" %*s [%4" PRIuMAX "] %s %" PRId16 "\n",
+ (int) (20 + level * 2), "", (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",
+ (int) (20 + level * 2), "", (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",
+ (int) (20 + level * 2), "", (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:;
+ unsigned char *start = data;
+ unsigned int uleb;
+ get_uleb128 (uleb, data);
+ printf (" %*s [%4" PRIuMAX "] %s %u\n",
+ (int) (20 + level * 2), "", (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",
+ (int) (20 + level * 2), "", (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",
+ (int) (20 + level * 2), "", (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",
+ (int) (20 + level * 2), "", (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",
+ (int) (20 + level * 2), "", (uintmax_t) offset,
+ known[op] ?: "???");
+ ++offset;
+ break;
+ }
+ }
+}
+
+
+static void
+print_debug_abbrev_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
+ 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 (1)
+ {
+ size_t length;
+ Dwarf_Abbrev abbrev;
+
+ if (dwarf_offabbrev (dbg, offset, &length, &abbrev) != 0)
+ {
+ printf (gettext (" *** error while reading abbreviation: %s\n"),
+ dwarf_errmsg (-1));
+ break;
+ }
+
+ if (length == 1)
+ /* This is the NUL byte at the end of the section. */
+ 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, GElf_Ehdr *ehdr, Elf_Scn *scn,
+ 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);
+ }
+}
+
+
+static void
+print_debug_frame_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
+ GElf_Shdr *shdr, Dwarf *dbg)
+{
+}
+
+
+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),
+ (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;
+
+ if (attr == DW_AT_language)
+ {
+ printf (" %*s%-20s %s (%d)\n",
+ (int) (level * 2), "", dwarf_attr_string (attr),
+ dwarf_lang_string (num), (int) num);
+ break;
+ }
+
+ printf (" %*s%-20s %" PRIuMAX "\n",
+ (int) (level * 2), "", dwarf_attr_string (attr),
+ (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);
+
+ if (attr == DW_AT_data_member_location)
+ print_ops (cbargs->dbg, level, cbargs->addrsize, block.length,
+ block.data);
+ 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, GElf_Ehdr *ehdr, Elf_Scn *scn,
+ 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;
+
+ size_t 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 == -1l)
+ {
+ 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;
+ }
+
+ 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 < sizeof (lowtags) / sizeof (lowtags[0]))
+ tagstr = lowtags[tag];
+ else
+ tagstr = "???";
+ break;
+ }
+
+ 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, 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;
+
+ 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, GElf_Ehdr *ehdr, Elf_Scn *scn,
+ GElf_Shdr *shdr, Dwarf *dbg)
+{
+ printf (gettext ("\
+\nDWARF section '%s' at offset %#" PRIx64 ":\n"),
+ ".debug_loc", (uint64_t) shdr->sh_offset);
+
+ // XXX add something
+}
+
+
+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, GElf_Ehdr *ehdr, 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. */
+ ptrdiff_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, 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, GElf_Ehdr *ehdr, Elf_Scn *scn,
+ 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, GElf_Ehdr *ehdr, Elf_Scn *scn,
+ 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),
+ { ".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.
+ */
+ size_t align = class == ELFCLASS32 ? 4 : 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..dc559409
--- /dev/null
+++ b/src/sectionhash.c
@@ -0,0 +1,69 @@
+/* Section hash table implementation.
+ Copyright (C) 2001, 2002 Red Hat, Inc.
+ Written by Ulrich Drepper <drepper@redhat.com>, 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_Xword 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 <drepper@redhat.com>, 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..e9edb677
--- /dev/null
+++ b/src/size.c
@@ -0,0 +1,698 @@
+/* Print size information from ELF file.
+ Copyright (C) 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
+ Written by Ulrich Drepper <drepper@redhat.com>, 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;
+
+
+/* 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:") },
+ { "format", OPT_FORMAT, "FORMAT", 0, N_("Use the output format FORMAT. FORMAT can be `bsd' or `sysv'. The default is `bsd'") },
+ { NULL, 'A', NULL, 0, N_("Same as `--format=sysv'") },
+ { NULL, 'B', NULL, 0, N_("Same as `--format=bsd'") },
+ { "radix", OPT_RADIX, "RADIX", 0, N_("Use RADIX for printing symbol values") },
+ { NULL, 'd', NULL, 0, N_("Same as `--radix=10'") },
+ { NULL, 'o', NULL, 0, N_("Same as `--radix=8'") },
+ { NULL, 'x', NULL, 0, N_("Same as `--radix=16'") },
+ { NULL, 'f', NULL, 0, N_("Similar to `--format=sysv' output but in one line") },
+
+ { NULL, 0, NULL, 0, N_("Output options:") },
+ { NULL, 'F', NULL, 0, N_("Print size and permission flags for loadable segments") },
+ { "totals", 't', NULL, 0, N_("Display the total sizes (bsd only)") },
+ { NULL, 0, NULL, 0, NULL }
+};
+
+/* 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);
+
+/* Function to print some extra text in the help message. */
+static char *more_help (int key, const char *text, void *input);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, args_doc, doc, NULL, more_help
+};
+
+
+/* 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)
+{
+ 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\
+"), "2004");
+ 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)
+{
+ 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 char *
+more_help (int key, const char *text, void *input)
+{
+ char *buf;
+
+ switch (key)
+ {
+ case ARGP_KEY_HELP_EXTRA:
+ /* We print some extra information. */
+ if (asprintf (&buf, gettext ("Please report bugs to %s.\n"),
+ PACKAGE_BUGREPORT) < 0)
+ buf = NULL;
+ return buf;
+
+ default:
+ break;
+ }
+ return (char *) text;
+}
+
+
+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, 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,
+ 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, const char *prefix, const char *fname,
+ const char *fullname)
+{
+ 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 *prefix, const char *fname,
+ 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, prefix, fname, fullname);
+ else if (format == format_segments)
+ show_segments (elf, prefix, fname, 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..e5c4289c
--- /dev/null
+++ b/src/strip.c
@@ -0,0 +1,1769 @@
+/* Discard section not used at runtime from object files.
+ Copyright (C) 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
+ Written by Ulrich Drepper <drepper@redhat.com>, 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;
+
+
+/* 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:") },
+ { NULL, 'o', "FILE", 0, N_("Place stripped output into FILE") },
+ { NULL, 'f', "FILE", 0, N_("Extract the removed sections into FILE") },
+
+ { NULL, 0, NULL, 0, N_("Output options:") },
+ { "strip-debug", 'g', NULL, 0, N_("Remove all debugging symbols") },
+ { "preserve-dates", 'p', NULL, 0,
+ N_("Copy modified/access timestamps to the output") },
+ { "remove-comment", OPT_REMOVE_COMMENT, NULL, 0,
+ N_("Remove .comment section") },
+ { "permissive", OPT_PERMISSIVE, NULL, 0,
+ N_("Relax a few rules to handle slightly broken ELF files") },
+ { NULL, 0, NULL, 0, NULL }
+};
+
+/* 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);
+
+/* Function to print some extra text in the help message. */
+static char *more_help (int key, const char *text, void *input);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, args_doc, doc, NULL, more_help
+};
+
+
+/* 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;
+
+/* 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. */
+ 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
+ {
+ /* 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)
+{
+ 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\
+"), "2004");
+ 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)
+{
+ switch (key)
+ {
+ case 'f':
+ debug_fname = arg;
+ break;
+
+ case 'o':
+ 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 char *
+more_help (int key, const char *text, void *input)
+{
+ char *buf;
+
+ switch (key)
+ {
+ case ARGP_KEY_HELP_EXTRA:
+ /* We print some extra information. */
+ if (asprintf (&buf, gettext ("Please report bugs to %s.\n"),
+ PACKAGE_BUGREPORT) < 0)
+ buf = NULL;
+ return buf;
+
+ default:
+ break;
+ }
+ return (char *) text;
+}
+
+
+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 uint32_t
+crc32_file (int fd, uint32_t *resp)
+{
+ unsigned char buffer[1024 * 8];
+ uint32_t crc = 0;
+ ssize_t count;
+
+ /* We have to rewind. */
+ if (lseek (fd, 0, SEEK_SET) < 0)
+ return 1;
+
+ while ((count = TEMP_FAILURE_RETRY (read (fd, buffer, sizeof (buffer)))) > 0)
+ crc = crc32 (crc, buffer, count);
+
+ *resp = crc;
+
+ return count != 0;
+}
+
+
+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;
+ uint32_t debug_crc;
+ 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 (SECTION_STRIP_P (ebl, elf, 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);
+
+ /* Write out a copy of all the 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;
+ int 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. */
+ *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;
+ }
+
+ /* 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;
+ }
+
+ }
+
+ /* 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);
+ 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);
+ /* Store the crc value in the correct byteorder */
+ if ((__BYTE_ORDER == __LITTLE_ENDIAN
+ && ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
+ || (__BYTE_ORDER == __BIG_ENDIAN
+ && ehdr->e_ident[EI_DATA] == ELFDATA2LSB))
+ debug_crc = bswap_32 (debug_crc);
+ memcpy ((char *)shdr_info[cnt].data->d_buf + crc_offset,
+ (char *) &debug_crc, 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)
+ /* Ignore sections which are discarded. */
+ 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 (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;
+
+ /* 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 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);
+
+ /* Convert to the correct byte order. */
+ if (gelf_xlatetom (newelf, hashd, hashd,
+ BYTE_ORDER == LITTLE_ENDIAN
+ ? ELFDATA2LSB : ELFDATA2MSB) == NULL)
+ INTERNAL_ERROR (fname);
+
+ /* 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);
+
+ /* Convert to the correct byte order. */
+ if (gelf_xlatetom (newelf, hashd, hashd,
+ BYTE_ORDER == LITTLE_ENDIAN
+ ? ELFDATA2LSB : ELFDATA2MSB) == NULL)
+ INTERNAL_ERROR (fname);
+
+ /* 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;
+ }
+ }
+ }
+
+ /* Convert back to the file byte order. */
+ if (gelf_xlatetof (newelf, hashd, hashd,
+ BYTE_ORDER == LITTLE_ENDIAN
+ ? ELFDATA2LSB : ELFDATA2MSB) == NULL)
+ INTERNAL_ERROR (fname);
+ }
+ 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;
+
+ /* 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);
+
+ /* Convert to the correct byte order. */
+ if (gelf_xlatetom (newelf, verd, verd,
+ BYTE_ORDER == LITTLE_ENDIAN
+ ? ELFDATA2LSB : ELFDATA2MSB) == NULL)
+ INTERNAL_ERROR (fname);
+
+ /* 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);
+
+ /* Convert back to the file byte order. */
+ if (gelf_xlatetof (newelf, verd, verd,
+ BYTE_ORDER == LITTLE_ENDIAN
+ ? ELFDATA2LSB : ELFDATA2MSB) == NULL)
+ INTERNAL_ERROR (fname);
+ }
+ 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);
+ }
+ }
+ }
+ }
+
+ /* 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 <drepper@redhat.com>, 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 <drepper@redhat.com>, 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 <drepper@redhat.com>, 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 <drepper@redhat.com>, 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 <drepper@redhat.com>, 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 <drepper@redhat.com>, 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 <tromey@cygnus.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, 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