aboutsummaryrefslogtreecommitdiff
path: root/gold
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2006-11-14 19:21:05 +0000
committerIan Lance Taylor <iant@google.com>2006-11-14 19:21:05 +0000
commitdbe717effbdf31236088837f4686fd5ad5e71893 (patch)
treefd5ed267334d62fadcaf7ff7132c0a7287553ed8 /gold
parent* peXXigen.c: Updates for PE/COFF V8.0, and clarification (diff)
downloadbinutils-gdb-dbe717effbdf31236088837f4686fd5ad5e71893.tar.gz
binutils-gdb-dbe717effbdf31236088837f4686fd5ad5e71893.tar.bz2
binutils-gdb-dbe717effbdf31236088837f4686fd5ad5e71893.zip
More dynamic object support, initial scripting support.
Diffstat (limited to 'gold')
-rw-r--r--gold/Makefile.am16
-rw-r--r--gold/Makefile.in91
-rw-r--r--gold/common.cc2
-rwxr-xr-xgold/configure44
-rw-r--r--gold/configure.ac1
-rw-r--r--gold/dynobj.cc670
-rw-r--r--gold/dynobj.h67
-rw-r--r--gold/gold.cc2
-rw-r--r--gold/i386.cc20
-rw-r--r--gold/layout.cc74
-rw-r--r--gold/layout.h30
-rw-r--r--gold/object.cc196
-rw-r--r--gold/object.h76
-rw-r--r--gold/options.cc75
-rw-r--r--gold/options.h100
-rw-r--r--gold/output.cc79
-rw-r--r--gold/output.h100
-rw-r--r--gold/po/POTFILES.in4
-rw-r--r--gold/po/gold.pot273
-rw-r--r--gold/readsyms.cc44
-rw-r--r--gold/readsyms.h6
-rw-r--r--gold/reloc.cc1
-rw-r--r--gold/script-c.h53
-rw-r--r--gold/script.cc1188
-rw-r--r--gold/script.h39
-rw-r--r--gold/symtab.cc213
-rw-r--r--gold/symtab.h23
-rw-r--r--gold/target.h7
-rw-r--r--gold/yyscript.y168
29 files changed, 3293 insertions, 369 deletions
diff --git a/gold/Makefile.am b/gold/Makefile.am
index 13aae2678e4..12ec7499207 100644
--- a/gold/Makefile.am
+++ b/gold/Makefile.am
@@ -15,6 +15,8 @@ INCLUDES = -D_GNU_SOURCE \
-DLOCALEDIR="\"$(datadir)/locale\"" \
@INCINTL@
+YFLAGS = -d
+
noinst_PROGRAMS = ld-new
CCFILES = \
@@ -22,6 +24,7 @@ CCFILES = \
common.cc \
defstd.cc \
dirsearch.cc \
+ dynobj.cc \
fileread.cc \
gold.cc \
gold-threads.cc \
@@ -32,6 +35,7 @@ CCFILES = \
readsyms.cc \
reloc.cc \
resolve.cc \
+ script.cc \
symtab.cc \
stringpool.cc \
target-select.cc \
@@ -42,6 +46,7 @@ HFILES = \
common.h \
defstd.h \
dirsearch.h \
+ dynobj.h \
fileread.h \
gold.h \
gold-threads.h \
@@ -51,6 +56,7 @@ HFILES = \
output.h \
readsyms.h \
reloc.h \
+ script.h \
stringpool.h \
symtab.h \
target.h \
@@ -61,7 +67,10 @@ HFILES = \
TARGETFILES = \
i386.cc
-OFILES = gold.o options.o
+YFILES = \
+ yyscript.y
+
+EXTRA_DIST = yyscript.c yyscript.h
POTFILES= $(CCFILES) $(HFILES) $(TARGETFILES)
@@ -69,10 +78,13 @@ po/POTFILES.in: @MAINT@ Makefile
for f in $(POTFILES); do echo $$f; done | LC_COLLATE= sort > tmp \
&& mv tmp $(srcdir)/po/POTFILES.in
-ld_new_SOURCES = $(CCFILES) $(HFILES) $(TARGETFILES)
+ld_new_SOURCES = $(CCFILES) $(HFILES) $(TARGETFILES) $(YFILES)
ld_new_DEPENDENCIES = $(LIBINTL_DEP)
ld_new_LDADD = $(LIBINTL)
+# Use an explicit dependency for the bison generated header file.
+script.$(OBJEXT): yyscript.h
+
.PHONY: install-exec-local
install-exec-local: ld-new$(EXEEXT)
diff --git a/gold/Makefile.in b/gold/Makefile.in
index d4c0c67c5ac..deef51c43ce 100644
--- a/gold/Makefile.in
+++ b/gold/Makefile.in
@@ -45,7 +45,8 @@ DIST_COMMON = README $(am__configure_deps) $(srcdir)/../config.guess \
$(srcdir)/../install-sh $(srcdir)/../missing \
$(srcdir)/../mkinstalldirs $(srcdir)/Makefile.am \
$(srcdir)/Makefile.in $(srcdir)/config.in \
- $(top_srcdir)/configure $(top_srcdir)/po/Make-in
+ $(top_srcdir)/configure $(top_srcdir)/po/Make-in yyscript.c \
+ yyscript.h
subdir = .
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/../config/depstand.m4 \
@@ -63,29 +64,32 @@ CONFIG_HEADER = config.h
CONFIG_CLEAN_FILES = po/Makefile.in
PROGRAMS = $(noinst_PROGRAMS)
am__objects_1 = archive.$(OBJEXT) common.$(OBJEXT) defstd.$(OBJEXT) \
- dirsearch.$(OBJEXT) fileread.$(OBJEXT) gold.$(OBJEXT) \
- gold-threads.$(OBJEXT) layout.$(OBJEXT) object.$(OBJEXT) \
- options.$(OBJEXT) output.$(OBJEXT) readsyms.$(OBJEXT) \
- reloc.$(OBJEXT) resolve.$(OBJEXT) symtab.$(OBJEXT) \
- stringpool.$(OBJEXT) target-select.$(OBJEXT) \
- workqueue.$(OBJEXT)
+ dirsearch.$(OBJEXT) dynobj.$(OBJEXT) fileread.$(OBJEXT) \
+ gold.$(OBJEXT) gold-threads.$(OBJEXT) layout.$(OBJEXT) \
+ object.$(OBJEXT) options.$(OBJEXT) output.$(OBJEXT) \
+ readsyms.$(OBJEXT) reloc.$(OBJEXT) resolve.$(OBJEXT) \
+ script.$(OBJEXT) symtab.$(OBJEXT) stringpool.$(OBJEXT) \
+ target-select.$(OBJEXT) workqueue.$(OBJEXT)
am__objects_2 =
am__objects_3 = i386.$(OBJEXT)
-am_ld_new_OBJECTS = $(am__objects_1) $(am__objects_2) $(am__objects_3)
+am__objects_4 = yyscript.$(OBJEXT)
+am_ld_new_OBJECTS = $(am__objects_1) $(am__objects_2) $(am__objects_3) \
+ $(am__objects_4)
ld_new_OBJECTS = $(am_ld_new_OBJECTS)
am__DEPENDENCIES_1 =
DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
depcomp = $(SHELL) $(top_srcdir)/../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 $@
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
CXXLD = $(CXX)
CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \
-o $@
-COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
- $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-CCLD = $(CC)
-LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS)
SOURCES = $(ld_new_SOURCES)
DIST_SOURCES = $(ld_new_SOURCES)
RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
@@ -175,6 +179,7 @@ VERSION = @VERSION@
WARN_CFLAGS = @WARN_CFLAGS@
WARN_CXXFLAGS = @WARN_CXXFLAGS@
XGETTEXT = @XGETTEXT@
+YACC = @YACC@
ac_ct_CC = @ac_ct_CC@
ac_ct_CXX = @ac_ct_CXX@
ac_ct_STRIP = @ac_ct_STRIP@
@@ -229,11 +234,13 @@ INCLUDES = -D_GNU_SOURCE \
-DLOCALEDIR="\"$(datadir)/locale\"" \
@INCINTL@
+YFLAGS = -d
CCFILES = \
archive.cc \
common.cc \
defstd.cc \
dirsearch.cc \
+ dynobj.cc \
fileread.cc \
gold.cc \
gold-threads.cc \
@@ -244,6 +251,7 @@ CCFILES = \
readsyms.cc \
reloc.cc \
resolve.cc \
+ script.cc \
symtab.cc \
stringpool.cc \
target-select.cc \
@@ -254,6 +262,7 @@ HFILES = \
common.h \
defstd.h \
dirsearch.h \
+ dynobj.h \
fileread.h \
gold.h \
gold-threads.h \
@@ -263,6 +272,7 @@ HFILES = \
output.h \
readsyms.h \
reloc.h \
+ script.h \
stringpool.h \
symtab.h \
target.h \
@@ -273,16 +283,19 @@ HFILES = \
TARGETFILES = \
i386.cc
-OFILES = gold.o options.o
+YFILES = \
+ yyscript.y
+
+EXTRA_DIST = yyscript.c yyscript.h
POTFILES = $(CCFILES) $(HFILES) $(TARGETFILES)
-ld_new_SOURCES = $(CCFILES) $(HFILES) $(TARGETFILES)
+ld_new_SOURCES = $(CCFILES) $(HFILES) $(TARGETFILES) $(YFILES)
ld_new_DEPENDENCIES = $(LIBINTL_DEP)
ld_new_LDADD = $(LIBINTL)
all: config.h
$(MAKE) $(AM_MAKEFLAGS) all-recursive
.SUFFIXES:
-.SUFFIXES: .cc .o .obj
+.SUFFIXES: .c .cc .o .obj .y
am--refresh:
@:
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
@@ -338,6 +351,11 @@ po/Makefile.in: $(top_builddir)/config.status $(top_srcdir)/po/Make-in
clean-noinstPROGRAMS:
-test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
+yyscript.h: yyscript.c
+ @if test ! -f $@; then \
+ rm -f yyscript.c; \
+ $(MAKE) yyscript.c; \
+ else :; fi
ld-new$(EXEEXT): $(ld_new_OBJECTS) $(ld_new_DEPENDENCIES)
@rm -f ld-new$(EXEEXT)
$(CXXLINK) $(ld_new_LDFLAGS) $(ld_new_OBJECTS) $(ld_new_LDADD) $(LIBS)
@@ -352,6 +370,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/defstd.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dirsearch.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dynobj.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileread.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gold-threads.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gold.Po@am__quote@
@@ -363,10 +382,26 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readsyms.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reloc.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resolve.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stringpool.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symtab.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/target-select.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/workqueue.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/yyscript.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) '$<'`
.cc.o:
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
@@ -381,6 +416,27 @@ distclean-compile:
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.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:
# This directory's subdirectories are mostly independent; you can cd
@@ -686,6 +742,8 @@ distclean-generic:
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 yyscript.c
+ -rm -f yyscript.h
clean: clean-recursive
clean-am: clean-generic clean-noinstPROGRAMS mostlyclean-am
@@ -761,6 +819,9 @@ po/POTFILES.in: @MAINT@ Makefile
for f in $(POTFILES); do echo $$f; done | LC_COLLATE= sort > tmp \
&& mv tmp $(srcdir)/po/POTFILES.in
+# Use an explicit dependency for the bison generated header file.
+script.$(OBJEXT): yyscript.h
+
.PHONY: install-exec-local
install-exec-local: ld-new$(EXEEXT)
diff --git a/gold/common.cc b/gold/common.cc
index 358ed8d0d94..7ba8adc2697 100644
--- a/gold/common.cc
+++ b/gold/common.cc
@@ -171,7 +171,7 @@ Symbol_table::do_allocate_commons(const General_options&,
// Place them in a newly allocated .bss section.
- Output_section_common *poc = new Output_section_common(addralign);
+ Output_data_common *poc = new Output_data_common(addralign);
layout->add_output_section_data(".bss", elfcpp::SHT_NOBITS,
elfcpp::SHF_WRITE | elfcpp::SHF_ALLOC,
diff --git a/gold/configure b/gold/configure
index a64cd709b5c..8be7e913a5d 100755
--- a/gold/configure
+++ b/gold/configure
@@ -309,7 +309,7 @@ ac_includes_default="\
# include <unistd.h>
#endif"
-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE USE_NLS LIBINTL LIBINTL_DEP INCINTL XGETTEXT GMSGFMT POSUB CATALOGS DATADIRNAME INSTOBJEXT GENCAT CATOBJEXT MKINSTALLDIRS MSGFMT MSGMERGE WARN_CFLAGS NO_WERROR WARN_CXXFLAGS LFS_CXXFLAGS CXXCPP EGREP MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT LIBOBJS LTLIBOBJS'
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE YACC USE_NLS LIBINTL LIBINTL_DEP INCINTL XGETTEXT GMSGFMT POSUB CATALOGS DATADIRNAME INSTOBJEXT GENCAT CATOBJEXT MKINSTALLDIRS MSGFMT MSGMERGE WARN_CFLAGS NO_WERROR WARN_CXXFLAGS LFS_CXXFLAGS CXXCPP EGREP MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT LIBOBJS LTLIBOBJS'
ac_subst_files=''
# Initialize some variables set by options.
@@ -3409,6 +3409,47 @@ else
fi
+for ac_prog in 'bison -y' byacc
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_YACC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$YACC"; then
+ ac_cv_prog_YACC="$YACC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_YACC="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+YACC=$ac_cv_prog_YACC
+if test -n "$YACC"; then
+ echo "$as_me:$LINENO: result: $YACC" >&5
+echo "${ECHO_T}$YACC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$YACC" && break
+done
+test -n "$YACC" || YACC="yacc"
+
# Find a good install program. We prefer a C program (faster),
# so one script is as good as another. But avoid the broken or
# incompatible versions:
@@ -5746,6 +5787,7 @@ s,@ac_ct_CXX@,$ac_ct_CXX,;t t
s,@CXXDEPMODE@,$CXXDEPMODE,;t t
s,@am__fastdepCXX_TRUE@,$am__fastdepCXX_TRUE,;t t
s,@am__fastdepCXX_FALSE@,$am__fastdepCXX_FALSE,;t t
+s,@YACC@,$YACC,;t t
s,@USE_NLS@,$USE_NLS,;t t
s,@LIBINTL@,$LIBINTL,;t t
s,@LIBINTL_DEP@,$LIBINTL_DEP,;t t
diff --git a/gold/configure.ac b/gold/configure.ac
index 42cba7f8b4a..10e0db98b27 100644
--- a/gold/configure.ac
+++ b/gold/configure.ac
@@ -12,6 +12,7 @@ AM_CONFIG_HEADER(config.h:config.in)
AC_PROG_CC
AC_PROG_CXX
+AC_PROG_YACC
AC_PROG_INSTALL
ZW_GNU_GETTEXT_SISTER_DIR
AM_PO_SUBDIRS
diff --git a/gold/dynobj.cc b/gold/dynobj.cc
new file mode 100644
index 00000000000..ba1fb151a38
--- /dev/null
+++ b/gold/dynobj.cc
@@ -0,0 +1,670 @@
+// dynobj.cc -- dynamic object support for gold
+
+#include "gold.h"
+
+#include <vector>
+#include <cstring>
+
+#include "symtab.h"
+#include "dynobj.h"
+
+namespace gold
+{
+
+// Class Sized_dynobj.
+
+template<int size, bool big_endian>
+Sized_dynobj<size, big_endian>::Sized_dynobj(
+ const std::string& name,
+ Input_file* input_file,
+ off_t offset,
+ const elfcpp::Ehdr<size, big_endian>& ehdr)
+ : Dynobj(name, input_file, offset),
+ elf_file_(this, ehdr),
+ soname_()
+{
+}
+
+// Set up the object.
+
+template<int size, bool big_endian>
+void
+Sized_dynobj<size, big_endian>::setup(
+ const elfcpp::Ehdr<size, big_endian>& ehdr)
+{
+ this->set_target(ehdr.get_e_machine(), size, big_endian,
+ ehdr.get_e_ident()[elfcpp::EI_OSABI],
+ ehdr.get_e_ident()[elfcpp::EI_ABIVERSION]);
+
+ const unsigned int shnum = this->elf_file_.shnum();
+ this->set_shnum(shnum);
+}
+
+// Find the SHT_DYNSYM section and the various version sections, and
+// the dynamic section, given the section headers.
+
+template<int size, bool big_endian>
+void
+Sized_dynobj<size, big_endian>::find_dynsym_sections(
+ const unsigned char* pshdrs,
+ unsigned int* pdynsym_shndx,
+ unsigned int* pversym_shndx,
+ unsigned int* pverdef_shndx,
+ unsigned int* pverneed_shndx,
+ unsigned int* pdynamic_shndx)
+{
+ *pdynsym_shndx = -1U;
+ *pversym_shndx = -1U;
+ *pverdef_shndx = -1U;
+ *pverneed_shndx = -1U;
+ *pdynamic_shndx = -1U;
+
+ const unsigned int shnum = this->shnum();
+ const unsigned char* p = pshdrs;
+ for (unsigned int i = 0; i < shnum; ++i, p += This::shdr_size)
+ {
+ typename This::Shdr shdr(p);
+
+ unsigned int* pi;
+ switch (shdr.get_sh_type())
+ {
+ case elfcpp::SHT_DYNSYM:
+ pi = pdynsym_shndx;
+ break;
+ case elfcpp::SHT_GNU_versym:
+ pi = pversym_shndx;
+ break;
+ case elfcpp::SHT_GNU_verdef:
+ pi = pverdef_shndx;
+ break;
+ case elfcpp::SHT_GNU_verneed:
+ pi = pverneed_shndx;
+ break;
+ case elfcpp::SHT_DYNAMIC:
+ pi = pdynamic_shndx;
+ break;
+ default:
+ pi = NULL;
+ break;
+ }
+
+ if (pi == NULL)
+ continue;
+
+ if (*pi != -1U)
+ {
+ fprintf(stderr,
+ _("%s: %s: unexpected duplicate type %u section: %u, %u\n"),
+ program_name, this->name().c_str(), shdr.get_sh_type(),
+ *pi, i);
+ gold_exit(false);
+ }
+
+ *pi = i;
+ }
+}
+
+// Read the contents of section SHNDX. PSHDRS points to the section
+// headers. TYPE is the expected section type. LINK is the expected
+// section link. Store the data in *VIEW and *VIEW_SIZE. The
+// section's sh_info field is stored in *VIEW_INFO.
+
+template<int size, bool big_endian>
+void
+Sized_dynobj<size, big_endian>::read_dynsym_section(
+ const unsigned char* pshdrs,
+ unsigned int shndx,
+ elfcpp::SHT type,
+ unsigned int link,
+ File_view** view,
+ off_t* view_size,
+ unsigned int* view_info)
+{
+ if (shndx == -1U)
+ {
+ *view = NULL;
+ *view_size = 0;
+ *view_info = 0;
+ return;
+ }
+
+ typename This::Shdr shdr(pshdrs + shndx * This::shdr_size);
+
+ assert(shdr.get_sh_type() == type);
+
+ if (shdr.get_sh_link() != link)
+ {
+ fprintf(stderr,
+ _("%s: %s: unexpected link in section %u header: %u != %u\n"),
+ program_name, this->name().c_str(), shndx,
+ shdr.get_sh_link(), link);
+ gold_exit(false);
+ }
+
+ *view = this->get_lasting_view(shdr.get_sh_offset(), shdr.get_sh_size());
+ *view_size = shdr.get_sh_size();
+ *view_info = shdr.get_sh_info();
+}
+
+// Set soname_ if this shared object has a DT_SONAME tag. PSHDRS
+// points to the section headers. DYNAMIC_SHNDX is the section index
+// of the SHT_DYNAMIC section. STRTAB_SHNDX, STRTAB, and STRTAB_SIZE
+// are the section index and contents of a string table which may be
+// the one associated with the SHT_DYNAMIC section.
+
+template<int size, bool big_endian>
+void
+Sized_dynobj<size, big_endian>::set_soname(const unsigned char* pshdrs,
+ unsigned int dynamic_shndx,
+ unsigned int strtab_shndx,
+ const unsigned char* strtabu,
+ off_t strtab_size)
+{
+ typename This::Shdr dynamicshdr(pshdrs + dynamic_shndx * This::shdr_size);
+ assert(dynamicshdr.get_sh_type() == elfcpp::SHT_DYNAMIC);
+
+ const off_t dynamic_size = dynamicshdr.get_sh_size();
+ const unsigned char* pdynamic = this->get_view(dynamicshdr.get_sh_offset(),
+ dynamic_size);
+
+ const unsigned int link = dynamicshdr.get_sh_link();
+ if (link != strtab_shndx)
+ {
+ if (link >= this->shnum())
+ {
+ fprintf(stderr,
+ _("%s: %s: DYNAMIC section %u link out of range: %u\n"),
+ program_name, this->name().c_str(),
+ dynamic_shndx, link);
+ gold_exit(false);
+ }
+
+ typename This::Shdr strtabshdr(pshdrs + link * This::shdr_size);
+ if (strtabshdr.get_sh_type() != elfcpp::SHT_STRTAB)
+ {
+ fprintf(stderr,
+ _("%s: %s: DYNAMIC section %u link %u is not a strtab\n"),
+ program_name, this->name().c_str(),
+ dynamic_shndx, link);
+ gold_exit(false);
+ }
+
+ strtab_size = strtabshdr.get_sh_size();
+ strtabu = this->get_view(strtabshdr.get_sh_offset(), strtab_size);
+ }
+
+ for (const unsigned char* p = pdynamic;
+ p < pdynamic + dynamic_size;
+ p += This::dyn_size)
+ {
+ typename This::Dyn dyn(p);
+
+ if (dyn.get_d_tag() == elfcpp::DT_SONAME)
+ {
+ off_t val = dyn.get_d_val();
+ if (val >= strtab_size)
+ {
+ fprintf(stderr,
+ _("%s: %s: DT_SONAME value out of range: "
+ "%lld >= %lld\n"),
+ program_name, this->name().c_str(),
+ static_cast<long long>(val),
+ static_cast<long long>(strtab_size));
+ gold_exit(false);
+ }
+
+ const char* strtab = reinterpret_cast<const char*>(strtabu);
+ this->soname_ = std::string(strtab + val);
+ return;
+ }
+
+ if (dyn.get_d_tag() == elfcpp::DT_NULL)
+ return;
+ }
+
+ fprintf(stderr, _("%s: %s: missing DT_NULL in dynamic segment\n"),
+ program_name, this->name().c_str());
+ gold_exit(false);
+}
+
+// Read the symbols and sections from a dynamic object. We read the
+// dynamic symbols, not the normal symbols.
+
+template<int size, bool big_endian>
+void
+Sized_dynobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
+{
+ this->read_section_data(&this->elf_file_, sd);
+
+ const unsigned char* const pshdrs = sd->section_headers->data();
+
+ unsigned int dynsym_shndx;
+ unsigned int versym_shndx;
+ unsigned int verdef_shndx;
+ unsigned int verneed_shndx;
+ unsigned int dynamic_shndx;
+ this->find_dynsym_sections(pshdrs, &dynsym_shndx, &versym_shndx,
+ &verdef_shndx, &verneed_shndx, &dynamic_shndx);
+
+ unsigned int strtab_shndx = -1U;
+
+ if (dynsym_shndx == -1U)
+ {
+ sd->symbols = NULL;
+ sd->symbols_size = 0;
+ sd->symbol_names = NULL;
+ sd->symbol_names_size = 0;
+ }
+ else
+ {
+ // Get the dynamic symbols.
+ typename This::Shdr dynsymshdr(pshdrs + dynsym_shndx * This::shdr_size);
+ assert(dynsymshdr.get_sh_type() == elfcpp::SHT_DYNSYM);
+
+ sd->symbols = this->get_lasting_view(dynsymshdr.get_sh_offset(),
+ dynsymshdr.get_sh_size());
+ sd->symbols_size = dynsymshdr.get_sh_size();
+
+ // Get the symbol names.
+ strtab_shndx = dynsymshdr.get_sh_link();
+ if (strtab_shndx >= this->shnum())
+ {
+ fprintf(stderr,
+ _("%s: %s: invalid dynamic symbol table name index: %u\n"),
+ program_name, this->name().c_str(), strtab_shndx);
+ gold_exit(false);
+ }
+ typename This::Shdr strtabshdr(pshdrs + strtab_shndx * This::shdr_size);
+ if (strtabshdr.get_sh_type() != elfcpp::SHT_STRTAB)
+ {
+ fprintf(stderr,
+ _("%s: %s: dynamic symbol table name section "
+ "has wrong type: %u\n"),
+ program_name, this->name().c_str(),
+ static_cast<unsigned int>(strtabshdr.get_sh_type()));
+ gold_exit(false);
+ }
+
+ sd->symbol_names = this->get_lasting_view(strtabshdr.get_sh_offset(),
+ strtabshdr.get_sh_size());
+ sd->symbol_names_size = strtabshdr.get_sh_size();
+
+ // Get the version information.
+
+ unsigned int dummy;
+ this->read_dynsym_section(pshdrs, versym_shndx, elfcpp::SHT_GNU_versym,
+ dynsym_shndx, &sd->versym, &sd->versym_size,
+ &dummy);
+
+ // We require that the version definition and need section link
+ // to the same string table as the dynamic symbol table. This
+ // is not a technical requirement, but it always happens in
+ // practice. We could change this if necessary.
+
+ this->read_dynsym_section(pshdrs, verdef_shndx, elfcpp::SHT_GNU_verdef,
+ strtab_shndx, &sd->verdef, &sd->verdef_size,
+ &sd->verdef_info);
+
+ this->read_dynsym_section(pshdrs, verneed_shndx, elfcpp::SHT_GNU_verneed,
+ strtab_shndx, &sd->verneed, &sd->verneed_size,
+ &sd->verneed_info);
+ }
+
+ // Read the SHT_DYNAMIC section to find whether this shared object
+ // has a DT_SONAME tag. This doesn't really have anything to do
+ // with reading the symbols, but this is a convenient place to do
+ // it.
+ if (dynamic_shndx != -1U)
+ this->set_soname(pshdrs, dynamic_shndx, strtab_shndx,
+ (sd->symbol_names == NULL
+ ? NULL
+ : sd->symbol_names->data()),
+ sd->symbol_names_size);
+}
+
+// Lay out the input sections for a dynamic object. We don't want to
+// include sections from a dynamic object, so all that we actually do
+// here is check for .gnu.warning sections.
+
+template<int size, bool big_endian>
+void
+Sized_dynobj<size, big_endian>::do_layout(const General_options&,
+ Symbol_table* symtab,
+ Layout*,
+ Read_symbols_data* sd)
+{
+ const unsigned int shnum = this->shnum();
+ if (shnum == 0)
+ return;
+
+ // Get the section headers.
+ const unsigned char* pshdrs = sd->section_headers->data();
+
+ // Get the section names.
+ const unsigned char* pnamesu = sd->section_names->data();
+ const char* pnames = reinterpret_cast<const char*>(pnamesu);
+
+ // Skip the first, dummy, section.
+ pshdrs += This::shdr_size;
+ for (unsigned int i = 1; i < shnum; ++i, pshdrs += This::shdr_size)
+ {
+ typename This::Shdr shdr(pshdrs);
+
+ if (shdr.get_sh_name() >= sd->section_names_size)
+ {
+ fprintf(stderr,
+ _("%s: %s: bad section name offset for section %u: %lu\n"),
+ program_name, this->name().c_str(), i,
+ static_cast<unsigned long>(shdr.get_sh_name()));
+ gold_exit(false);
+ }
+
+ const char* name = pnames + shdr.get_sh_name();
+
+ this->handle_gnu_warning_section(name, i, symtab);
+ }
+
+ delete sd->section_headers;
+ sd->section_headers = NULL;
+ delete sd->section_names;
+ sd->section_names = NULL;
+}
+
+// Add an entry to the vector mapping version numbers to version
+// strings.
+
+template<int size, bool big_endian>
+void
+Sized_dynobj<size, big_endian>::set_version_map(
+ Version_map* version_map,
+ unsigned int ndx,
+ const char* name) const
+{
+ assert(ndx < version_map->size());
+ if ((*version_map)[ndx] != NULL)
+ {
+ fprintf(stderr, _("%s: %s: duplicate definition for version %u\n"),
+ program_name, this->name().c_str(), ndx);
+ gold_exit(false);
+ }
+ (*version_map)[ndx] = name;
+}
+
+// Create a vector mapping version numbers to version strings.
+
+template<int size, bool big_endian>
+void
+Sized_dynobj<size, big_endian>::make_version_map(
+ Read_symbols_data* sd,
+ Version_map* version_map) const
+{
+ if (sd->verdef == NULL && sd->verneed == NULL)
+ return;
+
+ // First find the largest version index.
+ unsigned int maxver = 0;
+
+ if (sd->verdef != NULL)
+ {
+ const unsigned char* pverdef = sd->verdef->data();
+ off_t verdef_size = sd->verdef_size;
+ const unsigned int count = sd->verdef_info;
+
+ const unsigned char* p = pverdef;
+ for (unsigned int i = 0; i < count; ++i)
+ {
+ elfcpp::Verdef<size, big_endian> verdef(p);
+
+ const unsigned int vd_ndx = verdef.get_vd_ndx();
+
+ // The GNU linker clears the VERSYM_HIDDEN bit. I'm not
+ // sure why.
+
+ if (vd_ndx > maxver)
+ maxver = vd_ndx;
+
+ const unsigned int vd_next = verdef.get_vd_next();
+ if ((p - pverdef) + vd_next >= verdef_size)
+ {
+ fprintf(stderr,
+ _("%s: %s: verdef vd_next field out of range: %u\n"),
+ program_name, this->name().c_str(), vd_next);
+ gold_exit(false);
+ }
+
+ p += vd_next;
+ }
+ }
+
+ if (sd->verneed != NULL)
+ {
+ const unsigned char* pverneed = sd->verneed->data();
+ off_t verneed_size = sd->verneed_size;
+ const unsigned int count = sd->verneed_info;
+
+ const unsigned char* p = pverneed;
+ for (unsigned int i = 0; i < count; ++i)
+ {
+ elfcpp::Verneed<size, big_endian> verneed(p);
+
+ const unsigned int vn_aux = verneed.get_vn_aux();
+ if ((p - pverneed) + vn_aux >= verneed_size)
+ {
+ fprintf(stderr,
+ _("%s: %s: verneed vn_aux field out of range: %u\n"),
+ program_name, this->name().c_str(), vn_aux);
+ gold_exit(false);
+ }
+
+ const unsigned int vn_cnt = verneed.get_vn_cnt();
+ const unsigned char* pvna = p + vn_aux;
+ for (unsigned int j = 0; j < vn_cnt; ++j)
+ {
+ elfcpp::Vernaux<size, big_endian> vernaux(pvna);
+
+ const unsigned int vna_other = vernaux.get_vna_other();
+ if (vna_other > maxver)
+ maxver = vna_other;
+
+ const unsigned int vna_next = vernaux.get_vna_next();
+ if ((pvna - pverneed) + vna_next >= verneed_size)
+ {
+ fprintf(stderr,
+ _("%s: %s: verneed vna_next field "
+ "out of range: %u\n"),
+ program_name, this->name().c_str(), vna_next);
+ gold_exit(false);
+ }
+
+ pvna += vna_next;
+ }
+
+ const unsigned int vn_next = verneed.get_vn_next();
+ if ((p - pverneed) + vn_next >= verneed_size)
+ {
+ fprintf(stderr,
+ _("%s: %s: verneed vn_next field out of range: %u\n"),
+ program_name, this->name().c_str(), vn_next);
+ gold_exit(false);
+ }
+
+ p += vn_next;
+ }
+ }
+
+ // Now MAXVER is the largest version index we have seen.
+
+ version_map->resize(maxver + 1);
+
+ const char* names = reinterpret_cast<const char*>(sd->symbol_names->data());
+ off_t names_size = sd->symbol_names_size;
+
+ if (sd->verdef != NULL)
+ {
+ const unsigned char* pverdef = sd->verdef->data();
+ off_t verdef_size = sd->verdef_size;
+ const unsigned int count = sd->verdef_info;
+
+ const unsigned char* p = pverdef;
+ for (unsigned int i = 0; i < count; ++i)
+ {
+ elfcpp::Verdef<size, big_endian> verdef(p);
+
+ const unsigned int vd_cnt = verdef.get_vd_cnt();
+ if (vd_cnt < 1)
+ {
+ fprintf(stderr, _("%s: %s: verdef vd_cnt field too small: %u\n"),
+ program_name, this->name().c_str(), vd_cnt);
+ gold_exit(false);
+ }
+
+ const unsigned int vd_aux = verdef.get_vd_aux();
+ if ((p - pverdef) + vd_aux >= verdef_size)
+ {
+ fprintf(stderr,
+ _("%s: %s: verdef vd_aux field out of range: %u\n"),
+ program_name, this->name().c_str(), vd_aux);
+ gold_exit(false);
+ }
+
+ const unsigned char* pvda = p + vd_aux;
+ elfcpp::Verdaux<size, big_endian> verdaux(pvda);
+
+ const unsigned int vda_name = verdaux.get_vda_name();
+ if (vda_name >= names_size)
+ {
+ fprintf(stderr,
+ _("%s: %s: verdaux vda_name field out of range: %u\n"),
+ program_name, this->name().c_str(), vda_name);
+ gold_exit(false);
+ }
+
+ this->set_version_map(version_map, verdef.get_vd_ndx(),
+ names + vda_name);
+
+ const unsigned int vd_next = verdef.get_vd_next();
+ if ((p - pverdef) + vd_next >= verdef_size)
+ {
+ fprintf(stderr,
+ _("%s: %s: verdef vd_next field out of range: %u\n"),
+ program_name, this->name().c_str(), vd_next);
+ gold_exit(false);
+ }
+
+ p += vd_next;
+ }
+ }
+
+ if (sd->verneed != NULL)
+ {
+ const unsigned char* pverneed = sd->verneed->data();
+ const unsigned int count = sd->verneed_info;
+
+ const unsigned char* p = pverneed;
+ for (unsigned int i = 0; i < count; ++i)
+ {
+ elfcpp::Verneed<size, big_endian> verneed(p);
+
+ const unsigned int vn_aux = verneed.get_vn_aux();
+ const unsigned int vn_cnt = verneed.get_vn_cnt();
+ const unsigned char* pvna = p + vn_aux;
+ for (unsigned int j = 0; j < vn_cnt; ++j)
+ {
+ elfcpp::Vernaux<size, big_endian> vernaux(pvna);
+
+ const unsigned int vna_name = vernaux.get_vna_name();
+ if (vna_name >= names_size)
+ {
+ fprintf(stderr,
+ _("%s: %s: vernaux vna_name field "
+ "out of range: %u\n"),
+ program_name, this->name().c_str(), vna_name);
+ gold_exit(false);
+ }
+
+ this->set_version_map(version_map, vernaux.get_vna_other(),
+ names + vna_name);
+
+ pvna += vernaux.get_vna_next();
+ }
+
+ p += verneed.get_vn_next();
+ }
+ }
+}
+
+// Add the dynamic symbols to the symbol table.
+
+template<int size, bool big_endian>
+void
+Sized_dynobj<size, big_endian>::do_add_symbols(Symbol_table* symtab,
+ Read_symbols_data* sd)
+{
+ if (sd->symbols == NULL)
+ {
+ assert(sd->symbol_names == NULL);
+ assert(sd->versym == NULL && sd->verdef == NULL && sd->verneed == NULL);
+ return;
+ }
+
+ const int sym_size = This::sym_size;
+ const size_t symcount = sd->symbols_size / sym_size;
+ if (symcount * sym_size != sd->symbols_size)
+ {
+ fprintf(stderr,
+ _("%s: %s: size of dynamic symbols is not "
+ "multiple of symbol size\n"),
+ program_name, this->name().c_str());
+ gold_exit(false);
+ }
+
+ Version_map version_map;
+ this->make_version_map(sd, &version_map);
+
+ const char* sym_names =
+ reinterpret_cast<const char*>(sd->symbol_names->data());
+ symtab->add_from_dynobj(this, sd->symbols->data(), symcount,
+ sym_names, sd->symbol_names_size,
+ (sd->versym == NULL
+ ? NULL
+ : sd->versym->data()),
+ sd->versym_size,
+ &version_map);
+
+ delete sd->symbols;
+ sd->symbols = NULL;
+ delete sd->symbol_names;
+ sd->symbol_names = NULL;
+ if (sd->versym != NULL)
+ {
+ delete sd->versym;
+ sd->versym = NULL;
+ }
+ if (sd->verdef != NULL)
+ {
+ delete sd->verdef;
+ sd->verdef = NULL;
+ }
+ if (sd->verneed != NULL)
+ {
+ delete sd->verneed;
+ sd->verneed = NULL;
+ }
+}
+
+// Instantiate the templates we need. We could use the configure
+// script to restrict this to only the ones for implemented targets.
+
+template
+class Sized_dynobj<32, false>;
+
+template
+class Sized_dynobj<32, true>;
+
+template
+class Sized_dynobj<64, false>;
+
+template
+class Sized_dynobj<64, true>;
+
+} // End namespace gold.
diff --git a/gold/dynobj.h b/gold/dynobj.h
index 99e78830d15..04a2c631a3f 100644
--- a/gold/dynobj.h
+++ b/gold/dynobj.h
@@ -3,6 +3,8 @@
#ifndef GOLD_DYNOBJ_H
#define GOLD_DYNOBJ_H
+#include <vector>
+
#include "object.h"
namespace gold
@@ -28,6 +30,10 @@ class Sized_dynobj : public Dynobj
Sized_dynobj(const std::string& name, Input_file* input_file, off_t offset,
const typename elfcpp::Ehdr<size, big_endian>&);
+ // Set up the object file based on the ELF header.
+ void
+ setup(const typename elfcpp::Ehdr<size, big_endian>&);
+
// Read the symbols.
void
do_read_symbols(Read_symbols_data*);
@@ -41,14 +47,65 @@ class Sized_dynobj : public Dynobj
void
do_add_symbols(Symbol_table*, Read_symbols_data*);
+ // Get the name of a section.
+ std::string
+ do_section_name(unsigned int shndx)
+ { return this->elf_file_.section_name(shndx); }
+
// Return a view of the contents of a section. Set *PLEN to the
// size.
- const unsigned char*
- do_section_contents(unsigned int shnum, off_t* plen) = 0;
+ Object::Location
+ do_section_contents(unsigned int shndx)
+ { return this->elf_file_.section_contents(shndx); }
- // Get the name of a section.
- std::string
- do_section_name(unsigned int shnum);
+ private:
+ // For convenience.
+ typedef Sized_dynobj<size, big_endian> This;
+ static const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
+ static const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
+ static const int dyn_size = elfcpp::Elf_sizes<size>::dyn_size;
+ typedef elfcpp::Shdr<size, big_endian> Shdr;
+ typedef elfcpp::Dyn<size, big_endian> Dyn;
+
+ // Find the dynamic symbol table and the version sections, given the
+ // section headers.
+ void
+ find_dynsym_sections(const unsigned char* pshdrs,
+ unsigned int* pdynshm_shndx,
+ unsigned int* pversym_shndx,
+ unsigned int* pverdef_shndx,
+ unsigned int* pverneed_shndx,
+ unsigned int* pdynamic_shndx);
+
+ // Read the dynamic symbol section SHNDX.
+ void
+ read_dynsym_section(const unsigned char* pshdrs, unsigned int shndx,
+ elfcpp::SHT type, unsigned int link,
+ File_view** view, off_t* view_size,
+ unsigned int* view_info);
+
+ // Set the SONAME from the SHT_DYNAMIC section at DYNAMIC_SHNDX.
+ // The STRTAB parameters may have the relevant string table.
+ void
+ set_soname(const unsigned char* pshdrs, unsigned int dynamic_shndx,
+ unsigned int strtab_shndx, const unsigned char* strtabu,
+ off_t strtab_size);
+
+ // Mapping from version number to version name.
+ typedef std::vector<const char*> Version_map;
+
+ // Create the version map.
+ void
+ make_version_map(Read_symbols_data* sd, Version_map*) const;
+
+ // Add an entry to the version map.
+ void
+ set_version_map(Version_map*, unsigned int ndx, const char* name) const;
+
+ // General access to the ELF file.
+ elfcpp::Elf_file<size, big_endian, Object> elf_file_;
+ // The DT_SONAME name, if any.
+ std::string soname_;
};
} // End namespace gold.
diff --git a/gold/gold.cc b/gold/gold.cc
index c2372adf526..3073b1869e6 100644
--- a/gold/gold.cc
+++ b/gold/gold.cc
@@ -113,7 +113,7 @@ queue_initial_tasks(const General_options& options,
Task_token* next_blocker = new Task_token();
next_blocker->add_blocker();
workqueue->queue(new Read_symbols(options, input_objects, symtab, layout,
- search_path, *p, NULL, this_blocker,
+ search_path, &*p, NULL, this_blocker,
next_blocker));
this_blocker = next_blocker;
}
diff --git a/gold/i386.cc b/gold/i386.cc
index 15376f9598e..dc13dca1fba 100644
--- a/gold/i386.cc
+++ b/gold/i386.cc
@@ -149,7 +149,7 @@ class Target_i386 : public Sized_target<32, false>
optimize_tls_reloc(const General_options*, bool is_local, int r_type);
// Get the GOT section, creating it if necessary.
- Output_section_got<32, false>*
+ Output_data_got<32, false>*
got_section(Symbol_table*, Layout*);
// Information about this specific target which we pass to the
@@ -157,7 +157,7 @@ class Target_i386 : public Sized_target<32, false>
static const Target::Target_info i386_info;
// The GOT section.
- Output_section_got<32, false>* got_;
+ Output_data_got<32, false>* got_;
};
const Target::Target_info Target_i386::i386_info =
@@ -166,20 +166,21 @@ const Target::Target_info Target_i386::i386_info =
false, // is_big_endian
elfcpp::EM_386, // machine_code
false, // has_make_symbol
- false, // has_resolve,
- 0x08048000, // text_segment_address,
+ false, // has_resolve
+ "/usr/lib/libc.so.1", // dynamic_linker
+ 0x08048000, // text_segment_address
0x1000, // abi_pagesize
0x1000 // common_pagesize
};
// Get the GOT section, creating it if necessary.
-Output_section_got<32, false>*
+Output_data_got<32, false>*
Target_i386::got_section(Symbol_table* symtab, Layout* layout)
{
if (this->got_ == NULL)
{
- this->got_ = new Output_section_got<32, false>();
+ this->got_ = new Output_data_got<32, false>();
assert(symtab != NULL && layout != NULL);
layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
@@ -393,13 +394,8 @@ Target_i386::Scan::global(const General_options& options,
case elfcpp::R_386_GOT32:
// The symbol requires a GOT entry.
- if (!gsym->has_got_offset())
+ if (target->got_section(symtab, layout)->add_global(gsym))
{
- Output_section_got<32, false>* got = target->got_section(symtab,
- layout);
- const unsigned int got_offset = got->add_global(gsym);
- gsym->set_got_offset(got_offset);
-
// If this symbol is not resolved locally, we need to add a
// dynamic relocation for it.
if (!gsym->is_resolved_locally())
diff --git a/gold/layout.cc b/gold/layout.cc
index e969a806a08..bcb10295371 100644
--- a/gold/layout.cc
+++ b/gold/layout.cc
@@ -355,13 +355,30 @@ Layout::find_first_load_seg()
off_t
Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab)
{
+ const int size = input_objects->target()->get_size();
+
+ Output_segment* phdr_seg = NULL;
if (input_objects->any_dynamic())
{
- // If there are any dynamic objects in the link, then we need
- // some additional segments: PT_PHDRS, PT_INTERP, and
- // PT_DYNAMIC. We also need to finalize the dynamic symbol
- // table and create the dynamic hash table.
- abort();
+ // There was a dynamic object in the link. We need to create
+ // some information for the dynamic linker.
+
+ // Create the PT_PHDR segment which will hold the program
+ // headers.
+ phdr_seg = new Output_segment(elfcpp::PT_PHDR, elfcpp::PF_R);
+ this->segment_list_.push_back(phdr_seg);
+
+ // Create the dynamic symbol table, including the hash table,
+ // the dynamic relocations, and the version sections.
+ this->create_dynamic_symtab(size, symtab);
+
+ // Create the .dynamic section to hold the dynamic data, and put
+ // it in a PT_DYNAMIC segment.
+ this->create_dynamic_section();
+
+ // Create the .interp section to hold the name of the
+ // interpreter, and put it in a PT_INTERP segment.
+ this->create_interp(input_objects->target());
}
// FIXME: Handle PT_GNU_STACK.
@@ -369,14 +386,14 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab)
Output_segment* load_seg = this->find_first_load_seg();
// Lay out the segment headers.
- int size = input_objects->target()->get_size();
bool big_endian = input_objects->target()->is_big_endian();
Output_segment_headers* segment_headers;
segment_headers = new Output_segment_headers(size, big_endian,
this->segment_list_);
load_seg->add_initial_output_data(segment_headers);
this->special_output_list_.push_back(segment_headers);
- // FIXME: Attach them to PT_PHDRS if necessary.
+ if (phdr_seg != NULL)
+ phdr_seg->add_initial_output_data(segment_headers);
// Lay out the file header.
Output_file_header* file_header;
@@ -736,6 +753,49 @@ Layout::create_shdrs(int size, bool big_endian, off_t* poff)
return oshdrs;
}
+// Create the dynamic symbol table.
+
+void
+Layout::create_dynamic_symtab(int, Symbol_table*)
+{
+ abort();
+}
+
+// Create the .dynamic section and PT_DYNAMIC segment.
+
+void
+Layout::create_dynamic_section()
+{
+ abort();
+}
+
+// Create the .interp section and PT_INTERP segment.
+
+void
+Layout::create_interp(const Target* target)
+{
+ const char* interp = this->options_.dynamic_linker();
+ if (interp == NULL)
+ {
+ interp = target->dynamic_linker();
+ assert(interp != NULL);
+ }
+
+ size_t len = strlen(interp) + 1;
+
+ Output_section_data* odata = new Output_data_const(interp, len, 1);
+
+ const char* interp_name = this->namepool_.add(".interp", NULL);
+ Output_section* osec = this->make_output_section(interp_name,
+ elfcpp::SHT_PROGBITS,
+ elfcpp::SHF_ALLOC);
+ osec->add_output_section_data(odata);
+
+ Output_segment* oseg = new Output_segment(elfcpp::PT_INTERP, elfcpp::PF_R);
+ this->segment_list_.push_back(oseg);
+ oseg->add_initial_output_section(osec, elfcpp::PF_R);
+}
+
// The mapping of .gnu.linkonce section names to real section names.
#define MAPPING_INIT(f, t) { f, sizeof(f) - 1, t, sizeof(t) - 1 }
diff --git a/gold/layout.h b/gold/layout.h
index fa34617a3be..759fd85f840 100644
--- a/gold/layout.h
+++ b/gold/layout.h
@@ -154,15 +154,6 @@ class Layout
Output_segment*
find_first_load_seg();
- // Set the final file offsets of all the segments.
- off_t
- set_segment_offsets(const Target*, Output_segment*, unsigned int* pshndx);
-
- // Set the final file offsets and section indices of all the
- // sections not associated with a segment.
- off_t
- set_section_offsets(off_t, unsigned int *pshndx);
-
// Create the output sections for the symbol table.
void
create_symtab_sections(int size, const Input_objects*, Symbol_table*, off_t*,
@@ -177,6 +168,18 @@ class Layout
Output_section_headers*
create_shdrs(int size, bool big_endian, off_t*);
+ // Create the dynamic symbol table.
+ void
+ create_dynamic_symtab(int size, Symbol_table*);
+
+ // Create the .dynamic section and PT_DYNAMIC segment.
+ void
+ create_dynamic_section();
+
+ // Create the .interp section and PT_INTERP segment.
+ void
+ create_interp(const Target* target);
+
// Return whether to include this section in the link.
template<int size, bool big_endian>
bool
@@ -204,6 +207,15 @@ class Layout
make_output_section(const char* name, elfcpp::Elf_Word type,
elfcpp::Elf_Xword flags);
+ // Set the final file offsets of all the segments.
+ off_t
+ set_segment_offsets(const Target*, Output_segment*, unsigned int* pshndx);
+
+ // Set the final file offsets and section indices of all the
+ // sections not associated with a segment.
+ off_t
+ set_section_offsets(off_t, unsigned int *pshndx);
+
// Return whether SEG1 comes before SEG2 in the output file.
static bool
segment_precedes(const Output_segment* seg1, const Output_segment* seg2);
diff --git a/gold/object.cc b/gold/object.cc
index 677c731fb74..1bfd9698e06 100644
--- a/gold/object.cc
+++ b/gold/object.cc
@@ -19,6 +19,22 @@ namespace gold
// Class Object.
+// Set the target based on fields in the ELF file header.
+
+void
+Object::set_target(int machine, int size, bool big_endian, int osabi,
+ int abiversion)
+{
+ Target* target = select_target(machine, size, big_endian, osabi, abiversion);
+ if (target == NULL)
+ {
+ fprintf(stderr, _("%s: %s: unsupported ELF machine number %d\n"),
+ program_name, this->name().c_str(), machine);
+ gold_exit(false);
+ }
+ this->target_ = target;
+}
+
// Report an error for the elfcpp::Elf_file interface.
void
@@ -45,6 +61,58 @@ Object::section_contents(unsigned int shndx, off_t* plen)
return this->get_view(loc.file_offset, loc.data_size);
}
+// Read the section data into SD. This is code common to Sized_relobj
+// and Sized_dynobj, so we put it into Object.
+
+template<int size, bool big_endian>
+void
+Object::read_section_data(elfcpp::Elf_file<size, big_endian, Object>* elf_file,
+ Read_symbols_data* sd)
+{
+ const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
+
+ // Read the section headers.
+ const off_t shoff = elf_file->shoff();
+ const unsigned int shnum = this->shnum();
+ sd->section_headers = this->get_lasting_view(shoff, shnum * shdr_size);
+
+ // Read the section names.
+ const unsigned char* pshdrs = sd->section_headers->data();
+ const unsigned char* pshdrnames = pshdrs + elf_file->shstrndx() * shdr_size;
+ typename elfcpp::Shdr<size, big_endian> shdrnames(pshdrnames);
+
+ if (shdrnames.get_sh_type() != elfcpp::SHT_STRTAB)
+ {
+ fprintf(stderr,
+ _("%s: %s: section name section has wrong type: %u\n"),
+ program_name, this->name().c_str(),
+ static_cast<unsigned int>(shdrnames.get_sh_type()));
+ gold_exit(false);
+ }
+
+ sd->section_names_size = shdrnames.get_sh_size();
+ sd->section_names = this->get_lasting_view(shdrnames.get_sh_offset(),
+ sd->section_names_size);
+}
+
+// If NAME is the name of a special .gnu.warning section, arrange for
+// the warning to be issued. SHNDX is the section index. Return
+// whether it is a warning section.
+
+bool
+Object::handle_gnu_warning_section(const char* name, unsigned int shndx,
+ Symbol_table* symtab)
+{
+ const char warn_prefix[] = ".gnu.warning.";
+ const int warn_prefix_len = sizeof warn_prefix - 1;
+ if (strncmp(name, warn_prefix, warn_prefix_len) == 0)
+ {
+ symtab->add_warning(name + warn_prefix_len, this, shndx);
+ return true;
+ }
+ return false;
+}
+
// Class Sized_relobj.
template<int size, bool big_endian>
@@ -55,8 +123,7 @@ Sized_relobj<size, big_endian>::Sized_relobj(
const elfcpp::Ehdr<size, big_endian>& ehdr)
: Relobj(name, input_file, offset),
elf_file_(this, ehdr),
- section_headers_(NULL),
- symtab_shndx_(0),
+ symtab_shndx_(-1U),
local_symbol_count_(0),
output_local_symbol_count_(0),
symbols_(NULL),
@@ -78,43 +145,41 @@ void
Sized_relobj<size, big_endian>::setup(
const elfcpp::Ehdr<size, big_endian>& ehdr)
{
- int machine = ehdr.get_e_machine();
- Target* target = select_target(machine, size, big_endian,
- ehdr.get_e_ident()[elfcpp::EI_OSABI],
- ehdr.get_e_ident()[elfcpp::EI_ABIVERSION]);
- if (target == NULL)
- {
- fprintf(stderr, _("%s: %s: unsupported ELF machine number %d\n"),
- program_name, this->name().c_str(), machine);
- gold_exit(false);
- }
- this->set_target(target);
+ this->set_target(ehdr.get_e_machine(), size, big_endian,
+ ehdr.get_e_ident()[elfcpp::EI_OSABI],
+ ehdr.get_e_ident()[elfcpp::EI_ABIVERSION]);
- unsigned int shnum = this->elf_file_.shnum();
+ const unsigned int shnum = this->elf_file_.shnum();
this->set_shnum(shnum);
- if (shnum == 0)
- return;
-
- // We store the section headers in a File_view until do_read_symbols.
- off_t shoff = this->elf_file_.shoff();
- this->section_headers_ = this->get_lasting_view(shoff,
- shnum * This::shdr_size);
+}
- // Find the SHT_SYMTAB section. The ELF standard says that maybe in
- // the future there can be more than one SHT_SYMTAB section. Until
- // somebody figures out how that could work, we assume there is only
- // one.
- const unsigned char* p = this->section_headers_->data();
+// Find the SHT_SYMTAB section, given the section headers. The ELF
+// standard says that maybe in the future there can be more than one
+// SHT_SYMTAB section. Until somebody figures out how that could
+// work, we assume there is only one.
- // Skip the first section, which is always empty.
- p += This::shdr_size;
- for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size)
+template<int size, bool big_endian>
+void
+Sized_relobj<size, big_endian>::find_symtab(const unsigned char* pshdrs)
+{
+ const unsigned int shnum = this->shnum();
+ this->symtab_shndx_ = 0;
+ if (shnum > 0)
{
- typename This::Shdr shdr(p);
- if (shdr.get_sh_type() == elfcpp::SHT_SYMTAB)
+ // Look through the sections in reverse order, since gas tends
+ // to put the symbol table at the end.
+ const unsigned char* p = pshdrs + shnum * This::shdr_size;
+ unsigned int i = shnum;
+ while (i > 0)
{
- this->symtab_shndx_ = i;
- break;
+ --i;
+ p -= This::shdr_size;
+ typename This::Shdr shdr(p);
+ if (shdr.get_sh_type() == elfcpp::SHT_SYMTAB)
+ {
+ this->symtab_shndx_ = i;
+ break;
+ }
}
}
}
@@ -125,19 +190,11 @@ template<int size, bool big_endian>
void
Sized_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
{
- // Transfer our view of the section headers to SD.
- sd->section_headers = this->section_headers_;
- this->section_headers_ = NULL;
+ this->read_section_data(&this->elf_file_, sd);
- // Read the section names.
- const unsigned char* pshdrs = sd->section_headers->data();
- const unsigned char* pshdrnames = (pshdrs
- + (this->elf_file_.shstrndx()
- * This::shdr_size));
- typename This::Shdr shdrnames(pshdrnames);
- sd->section_names_size = shdrnames.get_sh_size();
- sd->section_names = this->get_lasting_view(shdrnames.get_sh_offset(),
- sd->section_names_size);
+ const unsigned char* const pshdrs = sd->section_headers->data();
+
+ this->find_symtab(pshdrs);
if (this->symtab_shndx_ == 0)
{
@@ -166,15 +223,14 @@ Sized_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
File_view* fvsymtab = this->get_lasting_view(extoff, extsize);
// Read the section header for the symbol names.
- unsigned int shnum = this->shnum();
- unsigned int strtab_shnum = symtabshdr.get_sh_link();
- if (strtab_shnum == 0 || strtab_shnum >= shnum)
+ unsigned int strtab_shndx = symtabshdr.get_sh_link();
+ if (strtab_shndx >= this->shnum())
{
fprintf(stderr, _("%s: %s: invalid symbol table name index: %u\n"),
- program_name, this->name().c_str(), strtab_shnum);
+ program_name, this->name().c_str(), strtab_shndx);
gold_exit(false);
}
- typename This::Shdr strtabshdr(pshdrs + strtab_shnum * This::shdr_size);
+ typename This::Shdr strtabshdr(pshdrs + strtab_shndx * This::shdr_size);
if (strtabshdr.get_sh_type() != elfcpp::SHT_STRTAB)
{
fprintf(stderr,
@@ -336,7 +392,7 @@ Sized_relobj<size, big_endian>::do_layout(const General_options& options,
Layout* layout,
Read_symbols_data* sd)
{
- unsigned int shnum = this->shnum();
+ const unsigned int shnum = this->shnum();
if (shnum == 0)
return;
@@ -353,9 +409,6 @@ Sized_relobj<size, big_endian>::do_layout(const General_options& options,
// Keep track of which sections to omit.
std::vector<bool> omit(shnum, false);
- const char warn_prefix[] = ".gnu.warning.";
- const int warn_prefix_len = sizeof warn_prefix - 1;
-
// Skip the first, dummy, section.
pshdrs += This::shdr_size;
for (unsigned int i = 1; i < shnum; ++i, pshdrs += This::shdr_size)
@@ -373,9 +426,8 @@ Sized_relobj<size, big_endian>::do_layout(const General_options& options,
const char* name = pnames + shdr.get_sh_name();
- if (strncmp(name, warn_prefix, warn_prefix_len) == 0)
+ if (this->handle_gnu_warning_section(name, i, symtab))
{
- symtab->add_warning(name + warn_prefix_len, this, i);
if (!options.is_relocatable())
omit[i] = true;
}
@@ -442,10 +494,8 @@ Sized_relobj<size, big_endian>::do_add_symbols(Symbol_table* symtab,
const char* sym_names =
reinterpret_cast<const char*>(sd->symbol_names->data());
- symtab->add_from_object<size, big_endian>(this, sd->symbols->data(),
- symcount, sym_names,
- sd->symbol_names_size,
- this->symbols_);
+ symtab->add_from_relobj(this, sd->symbols->data(), symcount, sym_names,
+ sd->symbol_names_size, this->symbols_);
delete sd->symbols;
sd->symbols = NULL;
@@ -464,6 +514,7 @@ off_t
Sized_relobj<size, big_endian>::do_finalize_local_symbols(off_t off,
Stringpool* pool)
{
+ assert(this->symtab_shndx_ != -1U);
if (this->symtab_shndx_ == 0)
{
// This object has no symbols. Weird but legal.
@@ -577,6 +628,7 @@ void
Sized_relobj<size, big_endian>::write_local_symbols(Output_file* of,
const Stringpool* sympool)
{
+ assert(this->symtab_shndx_ != -1U);
if (this->symtab_shndx_ == 0)
{
// This object has no symbols. Weird but legal.
@@ -711,13 +763,6 @@ make_elf_sized_object(const std::string& name, Input_file* input_file,
off_t offset, const elfcpp::Ehdr<size, big_endian>& ehdr)
{
int et = ehdr.get_e_type();
- if (et != elfcpp::ET_REL && et != elfcpp::ET_DYN)
- {
- fprintf(stderr, "%s: %s: unsupported ELF type %d\n",
- program_name, name.c_str(), static_cast<int>(et));
- gold_exit(false);
- }
-
if (et == elfcpp::ET_REL)
{
Sized_relobj<size, big_endian>* obj =
@@ -725,17 +770,18 @@ make_elf_sized_object(const std::string& name, Input_file* input_file,
obj->setup(ehdr);
return obj;
}
+ else if (et == elfcpp::ET_DYN)
+ {
+ Sized_dynobj<size, big_endian>* obj =
+ new Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr);
+ obj->setup(ehdr);
+ return obj;
+ }
else
{
- // elfcpp::ET_DYN
- fprintf(stderr, _("%s: %s: dynamic objects are not yet supported\n"),
- program_name, name.c_str());
+ fprintf(stderr, _("%s: %s: unsupported ELF file type %d\n"),
+ program_name, name.c_str(), et);
gold_exit(false);
-// Sized_dynobj<size, big_endian>* obj =
-// new Sized_dynobj<size, big_endian>(this->input_.name(), input_file,
-// offset, ehdr);
-// obj->setup(ehdr);
-// return obj;
}
}
diff --git a/gold/object.h b/gold/object.h
index 54f0350016e..63613569493 100644
--- a/gold/object.h
+++ b/gold/object.h
@@ -40,6 +40,19 @@ struct Read_symbols_data
File_view* symbol_names;
// Size of symbol name data in bytes.
off_t symbol_names_size;
+
+ // Version information. This is only used on dynamic objects.
+ // Version symbol data (from SHT_GNU_versym section).
+ File_view* versym;
+ off_t versym_size;
+ // Version definition data (from SHT_GNU_verdef section).
+ File_view* verdef;
+ off_t verdef_size;
+ unsigned int verdef_info;
+ // Needed version data (from SHT_GNU_verneed section).
+ File_view* verneed;
+ off_t verneed_size;
+ unsigned int verneed_info;
};
// Data about a single relocation section. This is read in
@@ -84,7 +97,7 @@ class Object
// file--0 for a .o or .so file, something else for a .a file.
Object(const std::string& name, Input_file* input_file, bool is_dynamic,
off_t offset = 0)
- : name_(name), input_file_(input_file), offset_(offset),
+ : name_(name), input_file_(input_file), offset_(offset), shnum_(-1U),
is_dynamic_(is_dynamic), target_(NULL)
{ }
@@ -256,8 +269,33 @@ class Object
// Set the target.
void
- set_target(Target* target)
- { this->target_ = target; }
+ set_target(int machine, int size, bool big_endian, int osabi,
+ int abiversion);
+
+ // Get the number of sections.
+ unsigned int
+ shnum() const
+ { return this->shnum_; }
+
+ // Set the number of sections.
+ void
+ set_shnum(int shnum)
+ { this->shnum_ = shnum; }
+
+ // Functions used by both Sized_relobj and Sized_dynobj.
+
+ // Read the section data into a Read_symbols_data object.
+ template<int size, bool big_endian>
+ void
+ read_section_data(elfcpp::Elf_file<size, big_endian, Object>*,
+ Read_symbols_data*);
+
+ // If NAME is the name of a special .gnu.warning section, arrange
+ // for the warning to be issued. SHNDX is the section index.
+ // Return whether it is a warning section.
+ bool
+ handle_gnu_warning_section(const char* name, unsigned int shndx,
+ Symbol_table*);
private:
// This class may not be copied.
@@ -271,6 +309,8 @@ class Object
// Offset within the file--0 for an object file, non-0 for an
// archive.
off_t offset_;
+ // Number of input sections.
+ unsigned int shnum_;
// Whether this is a dynamic object.
bool is_dynamic_;
// Target functions--may be NULL if the target is not known.
@@ -377,24 +417,12 @@ class Relobj : public Object
do_relocate(const General_options& options, const Symbol_table* symtab,
const Layout*, Output_file* of) = 0;
- // Get the number of sections.
- unsigned int
- shnum() const
- { return this->shnum_; }
-
- // Set the number of sections.
- void
- set_shnum(int shnum)
- { this->shnum_ = shnum; }
-
// Return the vector mapping input sections to output sections.
std::vector<Map_to_output>&
map_to_output()
{ return this->map_to_output_; }
private:
- // Number of input sections.
- unsigned int shnum_;
// Mapping from input sections to output section.
std::vector<Map_to_output> map_to_output_;
};
@@ -428,6 +456,11 @@ class Sized_relobj : public Relobj
void
do_read_symbols(Read_symbols_data*);
+ // Lay out the input sections.
+ void
+ do_layout(const General_options&, Symbol_table*, Layout*,
+ Read_symbols_data*);
+
// Add the symbols to the symbol table.
void
do_add_symbols(Symbol_table*, Read_symbols_data*);
@@ -441,11 +474,6 @@ class Sized_relobj : public Relobj
do_scan_relocs(const General_options&, Symbol_table*, Layout*,
Read_relocs_data*);
- // Lay out the input sections.
- void
- do_layout(const General_options&, Symbol_table*, Layout*,
- Read_symbols_data*);
-
// Finalize the local symbols.
off_t
do_finalize_local_symbols(off_t, Stringpool*);
@@ -461,7 +489,7 @@ class Sized_relobj : public Relobj
{ return this->elf_file_.section_name(shndx); }
// Return the location of the contents of a section.
- Location
+ Object::Location
do_section_contents(unsigned int shndx)
{ return this->elf_file_.section_contents(shndx); }
@@ -482,6 +510,10 @@ class Sized_relobj : public Relobj
static const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
typedef elfcpp::Shdr<size, big_endian> Shdr;
+ // Find the SHT_SYMTAB section, given the section headers.
+ void
+ find_symtab(const unsigned char* pshdrs);
+
// Whether to include a section group in the link.
bool
include_section_group(Layout*, unsigned int,
@@ -520,8 +552,6 @@ class Sized_relobj : public Relobj
// General access to the ELF file.
elfcpp::Elf_file<size, big_endian, Object> elf_file_;
- // If non-NULL, a view of the section header data.
- File_view* section_headers_;
// Index of SHT_SYMTAB section.
unsigned int symtab_shndx_;
// The number of local symbols.
diff --git a/gold/options.cc b/gold/options.cc
index 2fea87da39a..6b88429a882 100644
--- a/gold/options.cc
+++ b/gold/options.cc
@@ -231,6 +231,9 @@ options::Command_line_options::options[] =
TWO_DASHES, &start_group),
SPECIAL(')', "end-group", N_("End a library search group"), NULL,
TWO_DASHES, &end_group),
+ GENERAL_ARG('I', "dynamic-linker", N_("Set dynamic linker path"),
+ N_("-I PROGRAM, --dynamic-linker PROGRAM"), TWO_DASHES,
+ &General_options::set_dynamic_linker),
GENERAL_ARG('L', "library-path", N_("Add directory to search path"),
N_("-L DIR, --library-path DIR"), TWO_DASHES,
&General_options::add_to_search_path),
@@ -245,6 +248,12 @@ options::Command_line_options::options[] =
NULL, ONE_DASH, &General_options::set_shared),
GENERAL_NOARG('\0', "static", N_("Do not link against shared libraries"),
NULL, ONE_DASH, &General_options::set_static),
+ POSDEP_NOARG('\0', "as-needed",
+ N_("Only set DT_NEEDED for following dynamic libs if used"),
+ NULL, TWO_DASHES, &Position_dependent_options::set_as_needed),
+ POSDEP_NOARG('\0', "no-as-needed",
+ N_("Always DT_NEEDED for following dynamic libs (default)"),
+ NULL, TWO_DASHES, &Position_dependent_options::clear_as_needed),
SPECIAL('\0', "help", N_("Report usage information"), NULL,
TWO_DASHES, &help)
};
@@ -255,7 +264,8 @@ const int options::Command_line_options::options_size =
// The default values for the general options.
General_options::General_options()
- : search_path_(),
+ : dynamic_linker_(NULL),
+ search_path_(),
output_file_name_("a.out"),
is_relocatable_(false),
is_shared_(false),
@@ -270,10 +280,47 @@ Position_dependent_options::Position_dependent_options()
{
}
+// Input_arguments methods.
+
+// Add a file to the list.
+
+void
+Input_arguments::add_file(const Input_file_argument& file)
+{
+ if (!this->in_group_)
+ this->input_argument_list_.push_back(Input_argument(file));
+ else
+ {
+ assert(!this->input_argument_list_.empty());
+ assert(this->input_argument_list_.back().is_group());
+ this->input_argument_list_.back().group()->add_file(file);
+ }
+}
+
+// Start a group.
+
+void
+Input_arguments::start_group()
+{
+ assert(!this->in_group_);
+ Input_file_group* group = new Input_file_group();
+ this->input_argument_list_.push_back(Input_argument(group));
+ this->in_group_ = true;
+}
+
+// End a group.
+
+void
+Input_arguments::end_group()
+{
+ assert(this->in_group_);
+ this->in_group_ = false;
+}
+
// Command_line options.
Command_line::Command_line()
- : options_(), position_options_(), inputs_(), in_group_(false)
+ : options_(), position_options_(), inputs_()
{
}
@@ -409,7 +456,7 @@ Command_line::process(int argc, char** argv)
}
}
- if (this->in_group_)
+ if (this->inputs_.in_group())
{
fprintf(stderr, _("%s: missing group end"), program_name);
this->usage();
@@ -452,14 +499,7 @@ void
Command_line::add_file(const char* name, bool is_lib)
{
Input_file_argument file(name, is_lib, this->position_options_);
- if (!this->in_group_)
- this->inputs_.push_back(Input_argument(file));
- else
- {
- assert(!this->inputs_.empty());
- assert(this->inputs_.back().is_group());
- this->inputs_.back().group()->add_file(file);
- }
+ this->inputs_.add_file(file);
}
// Handle the -l option, which requires special treatment.
@@ -492,14 +532,9 @@ Command_line::process_l_option(int argc, char** argv, char* arg)
void
Command_line::start_group(const char* arg)
{
- if (this->in_group_)
+ if (this->inputs_.in_group())
this->usage(_("may not nest groups"), arg);
-
- // This object is leaked.
- Input_file_group* group = new Input_file_group();
- this->inputs_.push_back(Input_argument(group));
-
- this->in_group_ = true;
+ this->inputs_.start_group();
}
// Handle the --end-group option.
@@ -507,9 +542,9 @@ Command_line::start_group(const char* arg)
void
Command_line::end_group(const char* arg)
{
- if (!this->in_group_)
+ if (!this->inputs_.in_group())
this->usage(_("group end without group start"), arg);
- this->in_group_ = false;
+ this->inputs_.end_group();
}
// Report a usage error. */
diff --git a/gold/options.h b/gold/options.h
index 27db787edc2..ac515246236 100644
--- a/gold/options.h
+++ b/gold/options.h
@@ -38,6 +38,11 @@ class General_options
public:
General_options();
+ // -I: dynamic linker name.
+ const char*
+ dynamic_linker() const
+ { return this->dynamic_linker_; }
+
// -L: Library search path.
typedef std::list<const char*> Dir_list;
@@ -66,10 +71,18 @@ class General_options
{ return this->is_static_; }
private:
+ // Don't copy this structure.
+ General_options(const General_options&);
+ General_options& operator=(const General_options&);
+
friend class Command_line;
friend class options::Command_line_options;
void
+ set_dynamic_linker(const char* arg)
+ { this->dynamic_linker_ = arg; }
+
+ void
add_to_search_path(const char* arg)
{ this->search_path_.push_back(arg); }
@@ -93,15 +106,12 @@ class General_options
ignore(const char*)
{ }
+ const char* dynamic_linker_;
Dir_list search_path_;
const char* output_file_name_;
bool is_relocatable_;
bool is_shared_;
bool is_static_;
-
- // Don't copy this structure.
- General_options(const General_options&);
- General_options& operator=(const General_options&);
};
// The current state of the position dependent options.
@@ -112,14 +122,16 @@ class Position_dependent_options
Position_dependent_options();
// -Bstatic: Whether we are searching for a static archive rather
- // -than a shared object.
+ // than a shared object.
bool
- do_static_search()
+ do_static_search() const
{ return this->do_static_search_; }
- private:
- friend class Command_line;
- friend class options::Command_line_options;
+ // --as-needed: Whether to add a DT_NEEDED argument only if the
+ // dynamic object is used.
+ bool
+ as_needed() const
+ { return this->as_needed_; }
void
set_static_search()
@@ -129,7 +141,17 @@ class Position_dependent_options
set_dynamic_search()
{ this->do_static_search_ = false; }
+ void
+ set_as_needed()
+ { this->as_needed_ = true; }
+
+ void
+ clear_as_needed()
+ { this->as_needed_ = false; }
+
+ private:
bool do_static_search_;
+ bool as_needed_;
};
// A single file or library argument from the command line.
@@ -138,7 +160,7 @@ class Input_file_argument
{
public:
Input_file_argument()
- : name_(NULL), is_lib_(false), options_()
+ : name_(), is_lib_(false), options_()
{ }
Input_file_argument(const char* name, bool is_lib,
@@ -148,7 +170,7 @@ class Input_file_argument
const char*
name() const
- { return this->name_; }
+ { return this->name_.c_str(); }
const Position_dependent_options&
options() const
@@ -159,7 +181,10 @@ class Input_file_argument
{ return this->is_lib_; }
private:
- const char* name_;
+ // We use std::string, not const char*, here for convenience when
+ // using script files, so that we do not have to preserve the string
+ // in that case.
+ std::string name_;
bool is_lib_;
Position_dependent_options options_;
};
@@ -250,12 +275,60 @@ class Input_file_group
Files files_;
};
+// A list of files from the command line or a script.
+
+class Input_arguments
+{
+ public:
+ typedef std::vector<Input_argument> Input_argument_list;
+ typedef Input_argument_list::const_iterator const_iterator;
+
+ Input_arguments()
+ : input_argument_list_(), in_group_(false)
+ { }
+
+ // Add a file.
+ void
+ add_file(const Input_file_argument& arg);
+
+ // Start a group (the --start-group option).
+ void
+ start_group();
+
+ // End a group (the --end-group option).
+ void
+ end_group();
+
+ // Return whether we are currently in a group.
+ bool
+ in_group() const
+ { return this->in_group_; }
+
+ // Iterators to iterate over the list of input files.
+
+ const_iterator
+ begin() const
+ { return this->input_argument_list_.begin(); }
+
+ const_iterator
+ end() const
+ { return this->input_argument_list_.end(); }
+
+ // Return whether the list is empty.
+ bool
+ empty() const
+ { return this->input_argument_list_.empty(); }
+
+ private:
+ Input_argument_list input_argument_list_;
+ bool in_group_;
+};
+
// All the information read from the command line.
class Command_line
{
public:
- typedef std::vector<Input_argument> Input_arguments;
typedef Input_arguments::const_iterator const_iterator;
Command_line();
@@ -315,7 +388,6 @@ class Command_line
General_options options_;
Position_dependent_options position_options_;
Input_arguments inputs_;
- bool in_group_;
};
} // End namespace gold.
diff --git a/gold/output.cc b/gold/output.cc
index 40d58668c07..aba8ee17059 100644
--- a/gold/output.cc
+++ b/gold/output.cc
@@ -48,14 +48,6 @@ Output_data::default_alignment(int size)
abort();
}
-// Output_data_const methods.
-
-void
-Output_data_const::do_write(Output_file* output)
-{
- output->write(this->offset(), data_.data(), data_.size());
-}
-
// Output_section_header methods. This currently assumes that the
// segment and section lists are complete at construction time.
@@ -357,13 +349,30 @@ Output_file_header::do_sized_write(Output_file* of)
of->write_output_view(0, ehdr_size, view);
}
-// Output_section_got::Got_entry methods.
+// Output_data_const methods.
+
+void
+Output_data_const::do_write(Output_file* output)
+{
+ output->write(this->offset(), data_.data(), data_.size());
+}
+
+// Output_section_data methods.
+
+unsigned int
+Output_section_data::do_out_shndx() const
+{
+ assert(this->output_section_ != NULL);
+ return this->output_section_->out_shndx();
+}
+
+// Output_data_got::Got_entry methods.
// Write out the entry.
template<int size, bool big_endian>
void
-Output_section_got<size, big_endian>::Got_entry::write(unsigned char* pov)
+Output_data_got<size, big_endian>::Got_entry::write(unsigned char* pov)
const
{
Valtype val = 0;
@@ -402,22 +411,30 @@ Output_section_got<size, big_endian>::Got_entry::write(unsigned char* pov)
elfcpp::Swap<size, big_endian>::writeval(povv, val);
}
-// Output_section_data methods.
+// Output_data_got methods.
-unsigned int
-Output_section_data::do_out_shndx() const
+// Add an entry for a global symbol to the GOT. This returns true if
+// this is a new GOT entry, false if the symbol already had a GOT
+// entry.
+
+template<int size, bool big_endian>
+bool
+Output_data_got<size, big_endian>::add_global(Symbol* gsym)
{
- assert(this->output_section_ != NULL);
- return this->output_section_->out_shndx();
-}
+ if (gsym->has_got_offset())
+ return false;
-// Output_section_got methods.
+ this->entries_.push_back(Got_entry(gsym));
+ this->set_got_size();
+ gsym->set_got_offset(this->last_got_offset());
+ return true;
+}
// Write out the GOT.
template<int size, bool big_endian>
void
-Output_section_got<size, big_endian>::do_write(Output_file* of)
+Output_data_got<size, big_endian>::do_write(Output_file* of)
{
const int add = size / 8;
@@ -655,7 +672,8 @@ Output_segment::Output_segment(elfcpp::Elf_Word type, elfcpp::Elf_Word flags)
void
Output_segment::add_output_section(Output_section* os,
- elfcpp::Elf_Word seg_flags)
+ elfcpp::Elf_Word seg_flags,
+ bool front)
{
assert((os->flags() & elfcpp::SHF_ALLOC) != 0);
assert(!this->is_align_known_);
@@ -690,6 +708,7 @@ Output_segment::add_output_section(Output_section* os,
--p;
if ((*p)->is_section_type(elfcpp::SHT_NOTE))
{
+ // We don't worry about the FRONT parameter.
++p;
pdl->insert(p, os);
return;
@@ -730,6 +749,7 @@ Output_segment::add_output_section(Output_section* os,
if (insert)
{
+ // We don't worry about the FRONT parameter.
++p;
pdl->insert(p, os);
return;
@@ -737,11 +757,14 @@ Output_segment::add_output_section(Output_section* os,
}
while (p != pdl->begin());
- // There are no TLS sections yet; put this one at the end of the
- // section list.
+ // There are no TLS sections yet; put this one at the requested
+ // location in the section list.
}
- pdl->push_back(os);
+ if (front)
+ pdl->push_front(os);
+ else
+ pdl->push_back(os);
}
// Add an Output_data (which is not an Output_section) to the start of
@@ -1121,19 +1144,15 @@ Output_section::add_input_section<64, true>(
const elfcpp::Shdr<64, true>& shdr);
template
-void
-Output_section_got<32, false>::do_write(Output_file* of);
+class Output_data_got<32, false>;
template
-void
-Output_section_got<32, true>::do_write(Output_file* of);
+class Output_data_got<32, true>;
template
-void
-Output_section_got<64, false>::do_write(Output_file* of);
+class Output_data_got<64, false>;
template
-void
-Output_section_got<64, true>::do_write(Output_file* of);
+class Output_data_got<64, true>;
} // End namespace gold.
diff --git a/gold/output.h b/gold/output.h
index dc1653a987c..9763d749018 100644
--- a/gold/output.h
+++ b/gold/output.h
@@ -160,34 +160,6 @@ class Output_data
off_t offset_;
};
-// A simple case of Output_data in which we have constant data to
-// output.
-
-class Output_data_const : public Output_data
-{
- public:
- Output_data_const(const std::string& data, uint64_t addralign)
- : Output_data(data.size()), data_(data), addralign_(addralign)
- { }
-
- Output_data_const(const char* p, off_t len, uint64_t addralign)
- : Output_data(len), data_(p, len), addralign_(addralign)
- { }
-
- // Write the data to the file.
- void
- do_write(Output_file* output);
-
- // Return the required alignment.
- uint64_t
- do_addralign() const
- { return this->addralign_; }
-
- private:
- std::string data_;
- uint64_t addralign_;
-};
-
// Output the section headers.
class Output_section_headers : public Output_data
@@ -340,13 +312,40 @@ class Output_section_data : public Output_data
uint64_t addralign_;
};
-// Output_section_common is used to handle the common symbols. This
-// is quite simple.
+// A simple case of Output_data in which we have constant data to
+// output.
-class Output_section_common : public Output_section_data
+class Output_data_const : public Output_section_data
{
public:
- Output_section_common(uint64_t addralign)
+ Output_data_const(const std::string& data, uint64_t addralign)
+ : Output_section_data(data.size(), addralign), data_(data)
+ { }
+
+ Output_data_const(const char* p, off_t len, uint64_t addralign)
+ : Output_section_data(len, addralign), data_(p, len)
+ { }
+
+ Output_data_const(const unsigned char* p, off_t len, uint64_t addralign)
+ : Output_section_data(len, addralign),
+ data_(reinterpret_cast<const char*>(p), len)
+ { }
+
+ // Write the data to the file.
+ void
+ do_write(Output_file* output);
+
+ private:
+ std::string data_;
+};
+
+// Output_data_common is used to handle the common symbols. This is
+// quite simple.
+
+class Output_data_common : public Output_section_data
+{
+ public:
+ Output_data_common(uint64_t addralign)
: Output_section_data(addralign)
{ }
@@ -362,32 +361,26 @@ class Output_section_common : public Output_section_data
{ }
};
-// Output_section_got is used to manage a GOT. Each entry in the GOT
-// is for one symbol--either a global symbol or a local symbol in an
+// Output_data_got is used to manage a GOT. Each entry in the GOT is
+// for one symbol--either a global symbol or a local symbol in an
// object. The target specific code adds entries to the GOT as
-// needed. The GOT code is then responsible for writing out the data
-// and for generating relocs as required.
+// needed.
template<int size, bool big_endian>
-class Output_section_got : public Output_section_data
+class Output_data_got : public Output_section_data
{
public:
typedef typename elfcpp::Elf_types<size>::Elf_Addr Valtype;
- Output_section_got()
+ Output_data_got()
: Output_section_data(Output_data::default_alignment(size)),
entries_()
{ }
- // Add an entry for a global symbol to the GOT. This returns the
- // offset of the new entry from the start of the GOT.
- unsigned int
- add_global(Symbol* gsym)
- {
- this->entries_.push_back(Got_entry(gsym));
- this->set_got_size();
- return this->last_got_offset();
- }
+ // Add an entry for a global symbol to the GOT. Return true if this
+ // is a new GOT entry, false if the symbol was already in the GOT.
+ bool
+ add_global(Symbol* gsym);
// Add an entry for a local symbol to the GOT. This returns the
// offset of the new entry from the start of the GOT.
@@ -789,7 +782,13 @@ class Output_segment
// Add an Output_section to this segment.
void
- add_output_section(Output_section*, elfcpp::Elf_Word seg_flags);
+ add_output_section(Output_section* os, elfcpp::Elf_Word seg_flags)
+ { this->add_output_section(os, seg_flags, false); }
+
+ // Add an Output_section to the start of this segment.
+ void
+ add_initial_output_section(Output_section* os, elfcpp::Elf_Word seg_flags)
+ { this->add_output_section(os, seg_flags, true); }
// Add an Output_data (which is not an Output_section) to the start
// of this segment.
@@ -832,6 +831,11 @@ class Output_segment
typedef std::list<Output_data*> Output_data_list;
+ // Add an Output_section to this segment, specifying front or back.
+ void
+ add_output_section(Output_section*, elfcpp::Elf_Word seg_flags,
+ bool front);
+
// Find the maximum alignment in an Output_data_list.
static uint64_t
maximum_alignment(const Output_data_list*);
diff --git a/gold/po/POTFILES.in b/gold/po/POTFILES.in
index 5d4a4a092ba..d13ddf5ab02 100644
--- a/gold/po/POTFILES.in
+++ b/gold/po/POTFILES.in
@@ -6,6 +6,8 @@ defstd.cc
defstd.h
dirsearch.cc
dirsearch.h
+dynobj.cc
+dynobj.h
fileread.cc
fileread.h
gold.cc
@@ -26,6 +28,8 @@ readsyms.h
reloc.cc
reloc.h
resolve.cc
+script.cc
+script.h
stringpool.cc
stringpool.h
symtab.cc
diff --git a/gold/po/gold.pot b/gold/po/gold.pot
index a6c2aa2172d..c002c5c3411 100644
--- a/gold/po/gold.pot
+++ b/gold/po/gold.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2006-11-07 10:50-0800\n"
+"POT-Creation-Date: 2006-11-14 11:17-0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -61,6 +61,101 @@ msgstr ""
msgid "can not read directory %s"
msgstr ""
+#: dynobj.cc:97
+#, c-format
+msgid "%s: %s: unexpected duplicate type %u section: %u, %u\n"
+msgstr ""
+
+#: dynobj.cc:138
+#, c-format
+msgid "%s: %s: unexpected link in section %u header: %u != %u\n"
+msgstr ""
+
+#: dynobj.cc:176
+#, c-format
+msgid "%s: %s: DYNAMIC section %u link out of range: %u\n"
+msgstr ""
+
+#: dynobj.cc:186
+#, c-format
+msgid "%s: %s: DYNAMIC section %u link %u is not a strtab\n"
+msgstr ""
+
+#: dynobj.cc:208
+#, c-format
+msgid "%s: %s: DT_SONAME value out of range: %lld >= %lld\n"
+msgstr ""
+
+#: dynobj.cc:225
+#, c-format
+msgid "%s: %s: missing DT_NULL in dynamic segment\n"
+msgstr ""
+
+#: dynobj.cc:273
+#, c-format
+msgid "%s: %s: invalid dynamic symbol table name index: %u\n"
+msgstr ""
+
+#: dynobj.cc:281
+#, c-format
+msgid "%s: %s: dynamic symbol table name section has wrong type: %u\n"
+msgstr ""
+
+#: dynobj.cc:356 object.cc:421
+#, c-format
+msgid "%s: %s: bad section name offset for section %u: %lu\n"
+msgstr ""
+
+#: dynobj.cc:386
+#, c-format
+msgid "%s: %s: duplicate definition for version %u\n"
+msgstr ""
+
+#: dynobj.cc:430 dynobj.cc:549
+#, c-format
+msgid "%s: %s: verdef vd_next field out of range: %u\n"
+msgstr ""
+
+#: dynobj.cc:454
+#, c-format
+msgid "%s: %s: verneed vn_aux field out of range: %u\n"
+msgstr ""
+
+#: dynobj.cc:473
+#, c-format
+msgid "%s: %s: verneed vna_next field out of range: %u\n"
+msgstr ""
+
+#: dynobj.cc:486
+#, c-format
+msgid "%s: %s: verneed vn_next field out of range: %u\n"
+msgstr ""
+
+#: dynobj.cc:516
+#, c-format
+msgid "%s: %s: verdef vd_cnt field too small: %u\n"
+msgstr ""
+
+#: dynobj.cc:525
+#, c-format
+msgid "%s: %s: verdef vd_aux field out of range: %u\n"
+msgstr ""
+
+#: dynobj.cc:537
+#, c-format
+msgid "%s: %s: verdaux vda_name field out of range: %u\n"
+msgstr ""
+
+#: dynobj.cc:579
+#, c-format
+msgid "%s: %s: vernaux vna_name field out of range: %u\n"
+msgstr ""
+
+#: dynobj.cc:615
+#, c-format
+msgid "%s: %s: size of dynamic symbols is not multiple of symbol size\n"
+msgstr ""
+
#: fileread.cc:56
#, c-format
msgid "%s: warning: close(%s) failed: %s"
@@ -145,148 +240,147 @@ msgstr ""
msgid "%s: missing expected TLS relocation\n"
msgstr ""
-#: i386.cc:306 i386.cc:434 i386.cc:627
+#: i386.cc:307 i386.cc:430 i386.cc:623
#, c-format
msgid "%s: %s: unexpected reloc %u in object file\n"
msgstr ""
-#: i386.cc:339 i386.cc:358
+#: i386.cc:340 i386.cc:359
#, c-format
msgid "%s: %s: unsupported reloc %u against local symbol\n"
msgstr ""
-#: i386.cc:415 i386.cc:469 i386.cc:487
+#: i386.cc:411 i386.cc:465 i386.cc:483
#, c-format
msgid "%s: %s: unsupported reloc %u against global symbol %s\n"
msgstr ""
-#: i386.cc:509
+#: i386.cc:505
#, c-format
msgid "%s: %s: unsupported RELA reloc section\n"
msgstr ""
-#: i386.cc:548
+#: i386.cc:544
#, c-format
msgid "%s: %s: missing expected TLS relocation\n"
msgstr ""
-#: i386.cc:594 i386.cc:659 i386.cc:732 i386.cc:743
+#: i386.cc:590 i386.cc:655 i386.cc:728 i386.cc:739
#, c-format
msgid "%s: %s: unsupported reloc %u\n"
msgstr ""
-#: i386.cc:686
+#: i386.cc:682
#, c-format
msgid "%s: %s: TLS reloc but no TLS segment\n"
msgstr ""
-#: i386.cc:717
+#: i386.cc:713
#, c-format
msgid "%s: %s: unsupported reloc type %u\n"
msgstr ""
-#: i386.cc:926
+#: i386.cc:922
#, c-format
msgid "%s: %s: TLS relocation out of range\n"
msgstr ""
-#: i386.cc:944
+#: i386.cc:940
#, c-format
msgid "%s: %s: TLS relocation against invalid instruction\n"
msgstr ""
-#: object.cc:87
+#: object.cc:31
#, c-format
msgid "%s: %s: unsupported ELF machine number %d\n"
msgstr ""
-#: object.cc:173
+#: object.cc:87
+#, c-format
+msgid "%s: %s: section name section has wrong type: %u\n"
+msgstr ""
+
+#: object.cc:229
#, c-format
msgid "%s: %s: invalid symbol table name index: %u\n"
msgstr ""
-#: object.cc:181
+#: object.cc:237
#, c-format
msgid "%s: %s: symbol table name section has wrong type: %u\n"
msgstr ""
-#: object.cc:237
+#: object.cc:293
#, c-format
msgid "%s: %s: section group %u info %u out of range\n"
msgstr ""
-#: object.cc:254
+#: object.cc:310
#, c-format
msgid "%s: %s: symbol %u name offset %u out of range\n"
msgstr ""
-#: object.cc:288
+#: object.cc:344
#, c-format
msgid "%s: %s: section %u in section group %u out of range"
msgstr ""
-#: object.cc:368
-#, c-format
-msgid "%s: %s: bad section name offset for section %u: %lu\n"
-msgstr ""
-
-#: object.cc:436
+#: object.cc:488
#, c-format
msgid "%s: %s: size of symbols is not multiple of symbol size\n"
msgstr ""
-#: object.cc:521
+#: object.cc:572
#, c-format
msgid "%s: %s: unknown section index %u for local symbol %u\n"
msgstr ""
-#: object.cc:532
+#: object.cc:583
#, c-format
msgid "%s: %s: local symbol %u section index %u out of range\n"
msgstr ""
-#: object.cc:554
+#: object.cc:605
#, c-format
msgid "%s: %s: local symbol %u section name out of range: %u >= %u\n"
msgstr ""
-#. elfcpp::ET_DYN
-#: object.cc:731
+#: object.cc:782
#, c-format
-msgid "%s: %s: dynamic objects are not yet supported\n"
+msgid "%s: %s: unsupported ELF file type %d\n"
msgstr ""
-#: object.cc:755 object.cc:808 object.cc:829
+#: object.cc:801 object.cc:854 object.cc:875
#, c-format
msgid "%s: %s: ELF file too short\n"
msgstr ""
-#: object.cc:764
+#: object.cc:810
#, c-format
msgid "%s: %s: invalid ELF version 0\n"
msgstr ""
-#: object.cc:767
+#: object.cc:813
#, c-format
msgid "%s: %s: unsupported ELF version %d\n"
msgstr ""
-#: object.cc:775
+#: object.cc:821
#, c-format
msgid "%s: %s: invalid ELF class 0\n"
msgstr ""
-#: object.cc:782
+#: object.cc:828
#, c-format
msgid "%s: %s: unsupported ELF class %d\n"
msgstr ""
-#: object.cc:790
+#: object.cc:836
#, c-format
msgid "%s: %s: invalid ELF data encoding\n"
msgstr ""
-#: object.cc:797
+#: object.cc:843
#, c-format
msgid "%s: %s: unsupported ELF data encoding %d\n"
msgstr ""
@@ -315,139 +409,160 @@ msgid "End a library search group"
msgstr ""
#: options.cc:234
-msgid "Add directory to search path"
+msgid "Set dynamic linker path"
msgstr ""
#: options.cc:235
-msgid "-L DIR, --library-path DIR"
+msgid "-I PROGRAM, --dynamic-linker PROGRAM"
msgstr ""
#: options.cc:237
+msgid "Add directory to search path"
+msgstr ""
+
+#: options.cc:238
+msgid "-L DIR, --library-path DIR"
+msgstr ""
+
+#: options.cc:240
msgid "Ignored for compatibility"
msgstr ""
-#: options.cc:239
+#: options.cc:242
msgid "Set output file name"
msgstr ""
-#: options.cc:240
+#: options.cc:243
msgid "-o FILE, --output FILE"
msgstr ""
-#: options.cc:242
+#: options.cc:245
msgid "Generate relocatable output"
msgstr ""
-#: options.cc:244
+#: options.cc:247
msgid "Generate shared library"
msgstr ""
-#: options.cc:246
+#: options.cc:249
msgid "Do not link against shared libraries"
msgstr ""
-#: options.cc:248
+#: options.cc:252
+msgid "Only set DT_NEEDED for following dynamic libs if used"
+msgstr ""
+
+#: options.cc:255
+msgid "Always DT_NEEDED for following dynamic libs (default)"
+msgstr ""
+
+#: options.cc:257
msgid "Report usage information"
msgstr ""
-#: options.cc:346 options.cc:397 options.cc:483
+#: options.cc:393 options.cc:444 options.cc:523
msgid "missing argument"
msgstr ""
-#: options.cc:359 options.cc:406
+#: options.cc:406 options.cc:453
msgid "unknown option"
msgstr ""
-#: options.cc:414
+#: options.cc:461
#, c-format
msgid "%s: missing group end"
msgstr ""
-#: options.cc:496
+#: options.cc:536
msgid "may not nest groups"
msgstr ""
-#: options.cc:511
+#: options.cc:546
msgid "group end without group start"
msgstr ""
-#: options.cc:521
+#: options.cc:556
#, c-format
msgid "%s: use the --help option for usage information\n"
msgstr ""
-#: options.cc:530
+#: options.cc:565 script.cc:1128
#, c-format
msgid "%s: %s: %s\n"
msgstr ""
-#: options.cc:539
+#: options.cc:574
#, c-format
msgid "%s: -%c: %s\n"
msgstr ""
-#: output.cc:521
+#: output.cc:538
#, c-format
msgid "%s: %s: invalid alignment %lu for section \"%s\"\n"
msgstr ""
-#: output.cc:1033
+#: output.cc:1056
#, c-format
msgid "%s: %s: open: %s\n"
msgstr ""
-#: output.cc:1042
+#: output.cc:1065
#, c-format
msgid "%s: %s: lseek: %s\n"
msgstr ""
-#: output.cc:1049
+#: output.cc:1072
#, c-format
msgid "%s: %s: write: %s\n"
msgstr ""
-#: output.cc:1059
+#: output.cc:1082
#, c-format
msgid "%s: %s: mmap: %s\n"
msgstr ""
-#: output.cc:1073
+#: output.cc:1096
#, c-format
msgid "%s: %s: munmap: %s\n"
msgstr ""
-#: output.cc:1081
+#: output.cc:1104
#, c-format
msgid "%s: %s: close: %s\n"
msgstr ""
-#: readsyms.cc:85
+#: readsyms.cc:93
#, c-format
msgid "%s: %s: ordinary object found in input group\n"
msgstr ""
+#: readsyms.cc:136
+#, c-format
+msgid "%s: %s: file is empty\n"
+msgstr ""
+
#. Here we have to handle any other input file types we need.
-#: readsyms.cc:129
+#: readsyms.cc:149
#, c-format
msgid "%s: %s: not an object or archive\n"
msgstr ""
-#: reloc.cc:168 reloc.cc:408
+#: reloc.cc:168 reloc.cc:409
#, c-format
msgid "%s: %s: relocation section %u has bad info %u\n"
msgstr ""
-#: reloc.cc:187 reloc.cc:425
+#: reloc.cc:187 reloc.cc:426
#, c-format
msgid "%s: %s: relocation section %u uses unexpected symbol table %u\n"
msgstr ""
-#: reloc.cc:203 reloc.cc:444
+#: reloc.cc:203 reloc.cc:445
#, c-format
msgid "%s: %s: unexpected entsize for reloc section %u: %lu != %u"
msgstr ""
-#: reloc.cc:214 reloc.cc:455
+#: reloc.cc:214 reloc.cc:456
#, c-format
msgid "%s: %s: reloc section %u size %lu uneven"
msgstr ""
@@ -462,22 +577,42 @@ msgstr ""
msgid "%s: %s: unsupported symbol binding %d for symbol %s\n"
msgstr ""
-#: symtab.cc:441
+#: symtab.cc:443 symtab.cc:540
#, c-format
msgid "%s: %s: mixing 32-bit and 64-bit ELF objects\n"
msgstr ""
-#: symtab.cc:458
+#: symtab.cc:460
#, c-format
msgid "%s: %s: bad global symbol name offset %u at %lu\n"
msgstr ""
-#: symtab.cc:883 symtab.cc:1022
+#: symtab.cc:547
+#, c-format
+msgid "%s: %s: too few symbol versions\n"
+msgstr ""
+
+#: symtab.cc:567
+#, c-format
+msgid "%s: %s: bad symbol name offset %u at %lu\n"
+msgstr ""
+
+#: symtab.cc:611
+#, c-format
+msgid "%s: %s: versym for symbol %zu out of range: %u\n"
+msgstr ""
+
+#: symtab.cc:619
+#, c-format
+msgid "%s: %s: versym for symbol %zu has no name: %u\n"
+msgstr ""
+
+#: symtab.cc:1010 symtab.cc:1149
#, c-format
msgid "%s: %s: unsupported symbol section 0x%x\n"
msgstr ""
-#: symtab.cc:1135
+#: symtab.cc:1262
#, c-format
msgid "%s: %s: warning: %s\n"
msgstr ""
diff --git a/gold/readsyms.cc b/gold/readsyms.cc
index c120fcb7ccd..c301d16040c 100644
--- a/gold/readsyms.cc
+++ b/gold/readsyms.cc
@@ -10,6 +10,7 @@
#include "symtab.h"
#include "object.h"
#include "archive.h"
+#include "script.h"
#include "readsyms.h"
namespace gold
@@ -30,8 +31,8 @@ Read_symbols::~Read_symbols()
Task::Is_runnable_type
Read_symbols::is_runnable(Workqueue*)
{
- if (this->input_.is_file()
- && this->input_.file().is_lib()
+ if (this->input_argument_->is_file()
+ && this->input_argument_->file().is_lib()
&& this->dirpath_.token().is_blocked())
return IS_BLOCKED;
@@ -53,14 +54,14 @@ Read_symbols::locks(Workqueue*)
void
Read_symbols::run(Workqueue* workqueue)
{
- if (this->input_.is_group())
+ if (this->input_argument_->is_group())
{
assert(this->input_group_ == NULL);
this->do_group(workqueue);
return;
}
- Input_file* input_file = new Input_file(this->input_.file());
+ Input_file* input_file = new Input_file(this->input_argument_->file());
input_file->open(this->options_, this->dirpath_);
// Read enough of the file to pick up the entire ELF header.
@@ -79,7 +80,14 @@ Read_symbols::run(Workqueue* workqueue)
{
// This is an ELF object.
- if (this->input_group_ != NULL)
+ Object* obj = make_elf_object(input_file->filename(),
+ input_file, 0, p, bytes);
+
+ // We don't have a way to record a non-archive in an input
+ // group. If this is an ordinary object file, we can't
+ // include it more than once anyhow. If this is a dynamic
+ // object, then including it a second time changes nothing.
+ if (this->input_group_ != NULL && !obj->is_dynamic())
{
fprintf(stderr,
_("%s: %s: ordinary object found in input group\n"),
@@ -87,9 +95,6 @@ Read_symbols::run(Workqueue* workqueue)
gold_exit(false);
}
- Object* obj = make_elf_object(this->input_.file().name(),
- input_file, 0, p, bytes);
-
Read_symbols_data* sd = new Read_symbols_data;
obj->read_symbols(sd);
workqueue->queue_front(new Add_symbols(this->options_,
@@ -111,7 +116,8 @@ Read_symbols::run(Workqueue* workqueue)
if (memcmp(p, Archive::armag, Archive::sarmag) == 0)
{
// This is an archive.
- Archive* arch = new Archive(this->input_.file().name(), input_file);
+ Archive* arch = new Archive(this->input_argument_->file().name(),
+ input_file);
arch->setup();
workqueue->queue(new Add_archive_symbols(this->options_,
this->symtab_,
@@ -125,6 +131,20 @@ Read_symbols::run(Workqueue* workqueue)
}
}
+ if (bytes == 0)
+ {
+ fprintf(stderr, _("%s: %s: file is empty\n"),
+ program_name, input_file->file().filename().c_str());
+ gold_exit(false);
+ }
+
+ // Try to parse this file as a script.
+ if (read_input_script(workqueue, this->options_, this->symtab_,
+ this->layout_, this->dirpath_, this->input_objects_,
+ this->input_group_, this->input_argument_, input_file,
+ p, bytes, this->this_blocker_, this->next_blocker_))
+ return;
+
// Here we have to handle any other input file types we need.
fprintf(stderr, _("%s: %s: not an object or archive\n"),
program_name, input_file->file().filename().c_str());
@@ -143,14 +163,14 @@ Read_symbols::do_group(Workqueue* workqueue)
{
Input_group* input_group = new Input_group();
- const Input_file_group* group = this->input_.group();
+ const Input_file_group* group = this->input_argument_->group();
Task_token* this_blocker = this->this_blocker_;
for (Input_file_group::const_iterator p = group->begin();
p != group->end();
++p)
{
- const Input_argument& arg(*p);
- assert(arg.is_file());
+ const Input_argument* arg = &*p;
+ assert(arg->is_file());
Task_token* next_blocker = new Task_token();
next_blocker->add_blocker();
diff --git a/gold/readsyms.h b/gold/readsyms.h
index 6631011eb40..d5ada61d25e 100644
--- a/gold/readsyms.h
+++ b/gold/readsyms.h
@@ -36,10 +36,10 @@ class Read_symbols : public Task
// symbols.
Read_symbols(const General_options& options, Input_objects* input_objects,
Symbol_table* symtab, Layout* layout, const Dirsearch& dirpath,
- const Input_argument& input, Input_group* input_group,
+ const Input_argument* input_argument, Input_group* input_group,
Task_token* this_blocker, Task_token* next_blocker)
: options_(options), input_objects_(input_objects), symtab_(symtab),
- layout_(layout), dirpath_(dirpath), input_(input),
+ layout_(layout), dirpath_(dirpath), input_argument_(input_argument),
input_group_(input_group), this_blocker_(this_blocker),
next_blocker_(next_blocker)
{ }
@@ -67,7 +67,7 @@ class Read_symbols : public Task
Symbol_table* symtab_;
Layout* layout_;
const Dirsearch& dirpath_;
- const Input_argument& input_;
+ const Input_argument* input_argument_;
Input_group* input_group_;
Task_token* this_blocker_;
Task_token* next_blocker_;
diff --git a/gold/reloc.cc b/gold/reloc.cc
index bc38904094a..dd3eef12fee 100644
--- a/gold/reloc.cc
+++ b/gold/reloc.cc
@@ -227,6 +227,7 @@ Sized_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
}
// Read the local symbols.
+ assert(this->symtab_shndx_ != -1U);
if (this->symtab_shndx_ == 0 || this->local_symbol_count_ == 0)
rd->local_symbols = NULL;
else
diff --git a/gold/script-c.h b/gold/script-c.h
new file mode 100644
index 00000000000..e40488985d0
--- /dev/null
+++ b/gold/script-c.h
@@ -0,0 +1,53 @@
+/* script-c.h -- C interface for linker scripts in gold. */
+
+/* This file exists so that both the bison parser and script.cc can
+ include it, so that they can communicate back and forth. */
+
+#ifndef GOLD_SCRIPT_C_H
+#define GOLD_SCRIPT_C_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "yyscript.h"
+
+/* The bison parser function. */
+
+extern int
+yyparse(void* closure);
+
+/* Called by the bison parser skeleton to return the next token. */
+
+extern int
+yylex(YYSTYPE*, void* closure);
+
+/* Called by the bison parser skeleton to report an error. */
+
+extern void
+yyerror(void* closure, const char*);
+
+/* Called by the bison parser to add a file to the link. */
+
+extern void
+script_add_file(void* closure, const char*);
+
+/* Called by the bison parser to start and stop a group. */
+
+extern void
+script_start_group(void* closure);
+extern void
+script_end_group(void* closure);
+
+/* Called by the bison parser to start and end an AS_NEEDED list. */
+
+extern void
+script_start_as_needed(void* closure);
+extern void
+script_end_as_needed(void* closure);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !defined(GOLD_SCRIPT_C_H) */
diff --git a/gold/script.cc b/gold/script.cc
new file mode 100644
index 00000000000..b22611fab81
--- /dev/null
+++ b/gold/script.cc
@@ -0,0 +1,1188 @@
+// script.cc -- handle linker scripts for gold.
+
+#include "gold.h"
+
+#include <string>
+#include <vector>
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
+
+#include "options.h"
+#include "fileread.h"
+#include "workqueue.h"
+#include "readsyms.h"
+#include "yyscript.h"
+#include "script.h"
+#include "script-c.h"
+
+namespace gold
+{
+
+// A token read from a script file. We don't implement keywords here;
+// all keywords are simply represented as a string.
+
+class Token
+{
+ public:
+ // Token classification.
+ enum Classification
+ {
+ // Token is invalid.
+ TOKEN_INVALID,
+ // Token indicates end of input.
+ TOKEN_EOF,
+ // Token is a string of characters.
+ TOKEN_STRING,
+ // Token is an operator.
+ TOKEN_OPERATOR,
+ // Token is a number (an integer).
+ TOKEN_INTEGER
+ };
+
+ // We need an empty constructor so that we can put this STL objects.
+ Token()
+ : classification_(TOKEN_INVALID), value_(), opcode_(0),
+ lineno_(0), charpos_(0)
+ { }
+
+ // A general token with no value.
+ Token(Classification classification, int lineno, int charpos)
+ : classification_(classification), value_(), opcode_(0),
+ lineno_(lineno), charpos_(charpos)
+ { assert(classification == TOKEN_INVALID || classification == TOKEN_EOF); }
+
+ // A general token with a value.
+ Token(Classification classification, const std::string& value,
+ int lineno, int charpos)
+ : classification_(classification), value_(value), opcode_(0),
+ lineno_(lineno), charpos_(charpos)
+ { assert(classification != TOKEN_INVALID && classification != TOKEN_EOF); }
+
+ // A token representing a string of characters.
+ Token(const std::string& s, int lineno, int charpos)
+ : classification_(TOKEN_STRING), value_(s), opcode_(0),
+ lineno_(lineno), charpos_(charpos)
+ { }
+
+ // A token representing an operator.
+ Token(int opcode, int lineno, int charpos)
+ : classification_(TOKEN_OPERATOR), value_(), opcode_(opcode),
+ lineno_(lineno), charpos_(charpos)
+ { }
+
+ // Return whether the token is invalid.
+ bool
+ is_invalid() const
+ { return this->classification_ == TOKEN_INVALID; }
+
+ // Return whether this is an EOF token.
+ bool
+ is_eof() const
+ { return this->classification_ == TOKEN_EOF; }
+
+ // Return the token classification.
+ Classification
+ classification() const
+ { return this->classification_; }
+
+ // Return the line number at which the token starts.
+ int
+ lineno() const
+ { return this->lineno_; }
+
+ // Return the character position at this the token starts.
+ int
+ charpos() const
+ { return this->charpos_; }
+
+ // Get the value of a token.
+
+ const std::string&
+ string_value() const
+ {
+ assert(this->classification_ == TOKEN_STRING);
+ return this->value_;
+ }
+
+ int
+ operator_value() const
+ {
+ assert(this->classification_ == TOKEN_OPERATOR);
+ return this->opcode_;
+ }
+
+ int64_t
+ integer_value() const
+ {
+ assert(this->classification_ == TOKEN_INTEGER);
+ return strtoll(this->value_.c_str(), NULL, 0);
+ }
+
+ private:
+ // The token classification.
+ Classification classification_;
+ // The token value, for TOKEN_STRING or TOKEN_INTEGER.
+ std::string value_;
+ // The token value, for TOKEN_OPERATOR.
+ int opcode_;
+ // The line number where this token started (one based).
+ int lineno_;
+ // The character position within the line where this token started
+ // (one based).
+ int charpos_;
+};
+
+// This class handles lexing a file into a sequence of tokens. We
+// don't expect linker scripts to be large, so we just read them and
+// tokenize them all at once.
+
+class Lex
+{
+ public:
+ Lex(Input_file* input_file)
+ : input_file_(input_file), tokens_()
+ { }
+
+ // Tokenize the file. Return the final token, which will be either
+ // an invalid token or an EOF token. An invalid token indicates
+ // that tokenization failed.
+ Token
+ tokenize();
+
+ // A token sequence.
+ typedef std::vector<Token> Token_sequence;
+
+ // Return the tokens.
+ const Token_sequence&
+ tokens() const
+ { return this->tokens_; }
+
+ private:
+ Lex(const Lex&);
+ Lex& operator=(const Lex&);
+
+ // Read the file into a string buffer.
+ void
+ read_file(std::string*);
+
+ // Make a general token with no value at the current location.
+ Token
+ make_token(Token::Classification c, const char* p) const
+ { return Token(c, this->lineno_, p - this->linestart_ + 1); }
+
+ // Make a general token with a value at the current location.
+ Token
+ make_token(Token::Classification c, const std::string& v, const char* p)
+ const
+ { return Token(c, v, this->lineno_, p - this->linestart_ + 1); }
+
+ // Make an operator token at the current location.
+ Token
+ make_token(int opcode, const char* p) const
+ { return Token(opcode, this->lineno_, p - this->linestart_ + 1); }
+
+ // Make an invalid token at the current location.
+ Token
+ make_invalid_token(const char* p)
+ { return this->make_token(Token::TOKEN_INVALID, p); }
+
+ // Make an EOF token at the current location.
+ Token
+ make_eof_token(const char* p)
+ { return this->make_token(Token::TOKEN_EOF, p); }
+
+ // Return whether C can be the first character in a name. C2 is the
+ // next character, since we sometimes need that.
+ static inline bool
+ can_start_name(char c, char c2);
+
+ // Return whether C can appear in a name which has already started.
+ static inline bool
+ can_continue_name(char c);
+
+ // Return whether C, C2, C3 can start a hex number.
+ static inline bool
+ can_start_hex(char c, char c2, char c3);
+
+ // Return whether C can appear in a hex number.
+ static inline bool
+ can_continue_hex(char c);
+
+ // Return whether C can start a non-hex number.
+ static inline bool
+ can_start_number(char c);
+
+ // Return whether C can appear in a non-hex number.
+ static inline bool
+ can_continue_number(char c)
+ { return Lex::can_start_number(c); }
+
+ // If C1 C2 C3 form a valid three character operator, return the
+ // opcode. Otherwise return 0.
+ static inline int
+ three_char_operator(char c1, char c2, char c3);
+
+ // If C1 C2 form a valid two character operator, return the opcode.
+ // Otherwise return 0.
+ static inline int
+ two_char_operator(char c1, char c2);
+
+ // If C1 is a valid one character operator, return the opcode.
+ // Otherwise return 0.
+ static inline int
+ one_char_operator(char c1);
+
+ // Read the next token.
+ Token
+ get_token(const char**);
+
+ // Skip a C style /* */ comment. Return false if the comment did
+ // not end.
+ bool
+ skip_c_comment(const char**);
+
+ // Skip a line # comment. Return false if there was no newline.
+ bool
+ skip_line_comment(const char**);
+
+ // Build a token CLASSIFICATION from all characters that match
+ // CAN_CONTINUE_FN. The token starts at START. Start matching from
+ // MATCH. Set *PP to the character following the token.
+ inline Token
+ gather_token(Token::Classification, bool (*can_continue_fn)(char),
+ const char* start, const char* match, const char** pp);
+
+ // Build a token from a quoted string.
+ Token
+ gather_quoted_string(const char** pp);
+
+ // The file we are reading.
+ Input_file* input_file_;
+ // The token sequence we create.
+ Token_sequence tokens_;
+ // The current line number.
+ int lineno_;
+ // The start of the current line in the buffer.
+ const char* linestart_;
+};
+
+// Read the whole file into memory. We don't expect linker scripts to
+// be large, so we just use a std::string as a buffer. We ignore the
+// data we've already read, so that we read aligned buffers.
+
+void
+Lex::read_file(std::string* contents)
+{
+ contents->clear();
+ off_t off = 0;
+ off_t got;
+ unsigned char buf[BUFSIZ];
+ do
+ {
+ this->input_file_->file().read(off, sizeof buf, buf, &got);
+ contents->append(reinterpret_cast<char*>(&buf[0]), got);
+ }
+ while (got == sizeof buf);
+}
+
+// Return whether C can be the start of a name, if the next character
+// is C2. A name can being with a letter, underscore, period, or
+// dollar sign. Because a name can be a file name, we also permit
+// forward slash, backslash, and tilde. Tilde is the tricky case
+// here; GNU ld also uses it as a bitwise not operator. It is only
+// recognized as the operator if it is not immediately followed by
+// some character which can appear in a symbol. That is, "~0" is a
+// symbol name, and "~ 0" is an expression using bitwise not. We are
+// compatible.
+
+inline bool
+Lex::can_start_name(char c, char c2)
+{
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'Q': case 'P': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'q': case 'p': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '_': case '.': case '$': case '/': case '\\':
+ return true;
+
+ case '~':
+ return can_continue_name(c2);
+
+ default:
+ return false;
+ }
+}
+
+// Return whether C can continue a name which has already started.
+// Subsequent characters in a name are the same as the leading
+// characters, plus digits and "=+-:[],?*". So in general the linker
+// script language requires spaces around operators.
+
+inline bool
+Lex::can_continue_name(char c)
+{
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'Q': case 'P': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'q': case 'p': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '_': case '.': case '$': case '/': case '\\':
+ case '~':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '=': case '+': case '-': case ':': case '[': case ']':
+ case ',': case '?': case '*':
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+// For a number we accept 0x followed by hex digits, or any sequence
+// of digits. The old linker accepts leading '$' for hex, and
+// trailing HXBOD. Those are for MRI compatibility and we don't
+// accept them. The old linker also accepts trailing MK for mega or
+// kilo. Those are mentioned in the documentation, and we accept
+// them.
+
+// Return whether C1 C2 C3 can start a hex number.
+
+inline bool
+Lex::can_start_hex(char c1, char c2, char c3)
+{
+ if (c1 == '0' && (c2 == 'x' || c2 == 'X'))
+ return Lex::can_continue_hex(c3);
+ return false;
+}
+
+// Return whether C can appear in a hex number.
+
+inline bool
+Lex::can_continue_hex(char c)
+{
+ switch (c)
+ {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+// Return whether C can start a non-hex number.
+
+inline bool
+Lex::can_start_number(char c)
+{
+ switch (c)
+ {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+// If C1 C2 C3 form a valid three character operator, return the
+// opcode (defined in the yyscript.h file generated from yyscript.y).
+// Otherwise return 0.
+
+inline int
+Lex::three_char_operator(char c1, char c2, char c3)
+{
+ switch (c1)
+ {
+ case '<':
+ if (c2 == '<' && c3 == '=')
+ return LSHIFTEQ;
+ break;
+ case '>':
+ if (c2 == '>' && c3 == '=')
+ return RSHIFTEQ;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+// If C1 C2 form a valid two character operator, return the opcode
+// (defined in the yyscript.h file generated from yyscript.y).
+// Otherwise return 0.
+
+inline int
+Lex::two_char_operator(char c1, char c2)
+{
+ switch (c1)
+ {
+ case '=':
+ if (c2 == '=')
+ return EQ;
+ break;
+ case '!':
+ if (c2 == '=')
+ return NE;
+ break;
+ case '+':
+ if (c2 == '=')
+ return PLUSEQ;
+ break;
+ case '-':
+ if (c2 == '=')
+ return MINUSEQ;
+ break;
+ case '*':
+ if (c2 == '=')
+ return MULTEQ;
+ break;
+ case '/':
+ if (c2 == '=')
+ return DIVEQ;
+ break;
+ case '|':
+ if (c2 == '=')
+ return OREQ;
+ if (c2 == '|')
+ return OROR;
+ break;
+ case '&':
+ if (c2 == '=')
+ return ANDEQ;
+ if (c2 == '&')
+ return ANDAND;
+ break;
+ case '>':
+ if (c2 == '=')
+ return GE;
+ if (c2 == '>')
+ return RSHIFT;
+ break;
+ case '<':
+ if (c2 == '=')
+ return LE;
+ if (c2 == '<')
+ return LSHIFT;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+// If C1 is a valid operator, return the opcode. Otherwise return 0.
+
+inline int
+Lex::one_char_operator(char c1)
+{
+ switch (c1)
+ {
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '%':
+ case '!':
+ case '&':
+ case '|':
+ case '^':
+ case '~':
+ case '<':
+ case '>':
+ case '=':
+ case '?':
+ case ',':
+ case '(':
+ case ')':
+ case '{':
+ case '}':
+ case '[':
+ case ']':
+ case ':':
+ case ';':
+ return c1;
+ default:
+ return 0;
+ }
+}
+
+// Skip a C style comment. *PP points to just after the "/*". Return
+// false if the comment did not end.
+
+bool
+Lex::skip_c_comment(const char** pp)
+{
+ const char* p = *pp;
+ while (p[0] != '*' || p[1] != '/')
+ {
+ if (*p == '\0')
+ {
+ *pp = p;
+ return false;
+ }
+
+ if (*p == '\n')
+ {
+ ++this->lineno_;
+ this->linestart_ = p + 1;
+ }
+ ++p;
+ }
+
+ *pp = p + 2;
+ return true;
+}
+
+// Skip a line # comment. Return false if there was no newline.
+
+bool
+Lex::skip_line_comment(const char** pp)
+{
+ const char* p = *pp;
+ size_t skip = strcspn(p, "\n");
+ if (p[skip] == '\0')
+ {
+ *pp = p + skip;
+ return false;
+ }
+
+ p += skip + 1;
+ ++this->lineno_;
+ this->linestart_ = p;
+ *pp = p;
+
+ return true;
+}
+
+// Build a token CLASSIFICATION from all characters that match
+// CAN_CONTINUE_FN. Update *PP.
+
+inline Token
+Lex::gather_token(Token::Classification classification,
+ bool (*can_continue_fn)(char),
+ const char* start,
+ const char* match,
+ const char **pp)
+{
+ while ((*can_continue_fn)(*match))
+ ++match;
+ *pp = match;
+ return this->make_token(classification,
+ std::string(start, match - start),
+ start);
+}
+
+// Build a token from a quoted string.
+
+Token
+Lex::gather_quoted_string(const char** pp)
+{
+ const char* start = *pp;
+ const char* p = start;
+ ++p;
+ size_t skip = strcspn(p, "\"\n");
+ if (p[skip] != '"')
+ return this->make_invalid_token(start);
+ *pp = p + skip + 1;
+ return this->make_token(Token::TOKEN_STRING,
+ std::string(p, skip),
+ start);
+}
+
+// Return the next token at *PP. Update *PP. General guideline: we
+// require linker scripts to be simple ASCII. No unicode linker
+// scripts. In particular we can assume that any '\0' is the end of
+// the input.
+
+Token
+Lex::get_token(const char** pp)
+{
+ const char* p = *pp;
+
+ while (true)
+ {
+ if (*p == '\0')
+ {
+ *pp = p;
+ return this->make_eof_token(p);
+ }
+
+ // Skip whitespace quickly.
+ while (*p == ' ' || *p == '\t')
+ ++p;
+
+ if (*p == '\n')
+ {
+ ++p;
+ ++this->lineno_;
+ this->linestart_ = p;
+ continue;
+ }
+
+ // Skip C style comments.
+ if (p[0] == '/' && p[1] == '*')
+ {
+ int lineno = this->lineno_;
+ int charpos = p - this->linestart_ + 1;
+
+ *pp = p + 2;
+ if (!this->skip_c_comment(pp))
+ return Token(Token::TOKEN_INVALID, lineno, charpos);
+ p = *pp;
+
+ continue;
+ }
+
+ // Skip line comments.
+ if (*p == '#')
+ {
+ *pp = p + 1;
+ if (!this->skip_line_comment(pp))
+ return this->make_eof_token(p);
+ p = *pp;
+ continue;
+ }
+
+ // Check for a name.
+ if (Lex::can_start_name(p[0], p[1]))
+ return this->gather_token(Token::TOKEN_STRING,
+ Lex::can_continue_name,
+ p, p + 2, pp);
+
+ // We accept any arbitrary name in double quotes, as long as it
+ // does not cross a line boundary.
+ if (*p == '"')
+ {
+ *pp = p;
+ return this->gather_quoted_string(pp);
+ }
+
+ // Check for a number.
+
+ if (Lex::can_start_hex(p[0], p[1], p[2]))
+ return this->gather_token(Token::TOKEN_INTEGER,
+ Lex::can_continue_hex,
+ p, p + 3, pp);
+
+ if (Lex::can_start_number(p[0]))
+ return this->gather_token(Token::TOKEN_INTEGER,
+ Lex::can_continue_number,
+ p, p + 1, pp);
+
+ // Check for operators.
+
+ int opcode = Lex::three_char_operator(p[0], p[1], p[2]);
+ if (opcode != 0)
+ {
+ *pp = p + 3;
+ return this->make_token(opcode, p);
+ }
+
+ opcode = Lex::two_char_operator(p[0], p[1]);
+ if (opcode != 0)
+ {
+ *pp = p + 2;
+ return this->make_token(opcode, p);
+ }
+
+ opcode = Lex::one_char_operator(p[0]);
+ if (opcode != 0)
+ {
+ *pp = p + 1;
+ return this->make_token(opcode, p);
+ }
+
+ return this->make_token(Token::TOKEN_INVALID, p);
+ }
+}
+
+// Tokenize the file. Return the final token.
+
+Token
+Lex::tokenize()
+{
+ std::string contents;
+ this->read_file(&contents);
+
+ const char* p = contents.c_str();
+
+ this->lineno_ = 1;
+ this->linestart_ = p;
+
+ while (true)
+ {
+ Token t(this->get_token(&p));
+
+ // Don't let an early null byte fool us into thinking that we've
+ // reached the end of the file.
+ if (t.is_eof()
+ && static_cast<size_t>(p - contents.c_str()) < contents.length())
+ t = this->make_invalid_token(p);
+
+ if (t.is_invalid() || t.is_eof())
+ return t;
+
+ this->tokens_.push_back(t);
+ }
+}
+
+// A trivial task which waits for THIS_BLOCKER to be clear and then
+// clears NEXT_BLOCKER. THIS_BLOCKER may be NULL.
+
+class Script_unblock : public Task
+{
+ public:
+ Script_unblock(Task_token* this_blocker, Task_token* next_blocker)
+ : this_blocker_(this_blocker), next_blocker_(next_blocker)
+ { }
+
+ ~Script_unblock()
+ {
+ if (this->this_blocker_ != NULL)
+ delete this->this_blocker_;
+ }
+
+ Is_runnable_type
+ is_runnable(Workqueue*)
+ {
+ if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked())
+ return IS_BLOCKED;
+ return IS_RUNNABLE;
+ }
+
+ Task_locker*
+ locks(Workqueue* workqueue)
+ {
+ return new Task_locker_block(*this->next_blocker_, workqueue);
+ }
+
+ void
+ run(Workqueue*)
+ { }
+
+ private:
+ Task_token* this_blocker_;
+ Task_token* next_blocker_;
+};
+
+// This class holds data passed through the parser to the lexer and to
+// the parser support functions. This avoids global variables. We
+// can't use global variables because we need not be called in the
+// main thread.
+
+class Parser_closure
+{
+ public:
+ Parser_closure(const char* filename,
+ const Position_dependent_options& posdep_options,
+ bool in_group,
+ const Lex::Token_sequence* tokens)
+ : filename_(filename), posdep_options_(posdep_options),
+ in_group_(in_group), tokens_(tokens),
+ next_token_index_(0), inputs_(NULL)
+ { }
+
+ // Return the file name.
+ const char*
+ filename() const
+ { return this->filename_; }
+
+ // Return the position dependent options. The caller may modify
+ // this.
+ Position_dependent_options&
+ position_dependent_options()
+ { return this->posdep_options_; }
+
+ // Return whether this script is being run in a group.
+ bool
+ in_group() const
+ { return this->in_group_; }
+
+ // Whether we are at the end of the token list.
+ bool
+ at_eof() const
+ { return this->next_token_index_ >= this->tokens_->size(); }
+
+ // Return the next token.
+ const Token*
+ next_token()
+ {
+ const Token* ret = &(*this->tokens_)[this->next_token_index_];
+ ++this->next_token_index_;
+ return ret;
+ }
+
+ // Return the list of input files, creating it if necessary. This
+ // is a space leak--we never free the INPUTS_ pointer.
+ Input_arguments*
+ inputs()
+ {
+ if (this->inputs_ == NULL)
+ this->inputs_ = new Input_arguments();
+ return this->inputs_;
+ }
+
+ // Return whether we saw any input files.
+ bool
+ saw_inputs() const
+ { return this->inputs_ != NULL && !this->inputs_->empty(); }
+
+ private:
+ // The name of the file we are reading.
+ const char* filename_;
+ // The position dependent options.
+ Position_dependent_options posdep_options_;
+ // Whether we are currently in a --start-group/--end-group.
+ bool in_group_;
+
+ // The tokens to be returned by the lexer.
+ const Lex::Token_sequence* tokens_;
+ // The index of the next token to return.
+ unsigned int next_token_index_;
+ // New input files found to add to the link.
+ Input_arguments* inputs_;
+};
+
+// FILE was found as an argument on the command line. Try to read it
+// as a script. We've already read BYTES of data into P, but we
+// ignore that. Return true if the file was handled.
+
+bool
+read_input_script(Workqueue* workqueue, const General_options& options,
+ Symbol_table* symtab, Layout* layout,
+ const Dirsearch& dirsearch, Input_objects* input_objects,
+ Input_group* input_group,
+ const Input_argument* input_argument,
+ Input_file* input_file, const unsigned char*, off_t,
+ Task_token* this_blocker, Task_token* next_blocker)
+{
+ Lex lex(input_file);
+ if (lex.tokenize().is_invalid())
+ return false;
+
+ Parser_closure closure(input_file->filename().c_str(),
+ input_argument->file().options(),
+ input_group != NULL,
+ &lex.tokens());
+
+ if (yyparse(&closure) != 0)
+ return false;
+
+ // THIS_BLOCKER must be clear before we may add anything to the
+ // symbol table. We are responsible for unblocking NEXT_BLOCKER
+ // when we are done. We are responsible for deleting THIS_BLOCKER
+ // when it is unblocked.
+
+ if (!closure.saw_inputs())
+ {
+ // The script did not add any files to read. Note that we are
+ // not permitted to call NEXT_BLOCKER->unblock() here even if
+ // THIS_BLOCKER is NULL, as we are not in the main thread.
+ workqueue->queue(new Script_unblock(this_blocker, next_blocker));
+ return true;
+ }
+
+ for (Input_arguments::const_iterator p = closure.inputs()->begin();
+ p != closure.inputs()->end();
+ ++p)
+ {
+ Task_token* nb;
+ if (p + 1 == closure.inputs()->end())
+ nb = next_blocker;
+ else
+ {
+ nb = new Task_token();
+ nb->add_blocker();
+ }
+ workqueue->queue(new Read_symbols(options, input_objects, symtab,
+ layout, dirsearch, &*p,
+ input_group, this_blocker, nb));
+ this_blocker = nb;
+ }
+
+ return true;
+}
+
+// Manage mapping from keywords to the codes expected by the bison
+// parser.
+
+class Keyword_to_parsecode
+{
+ public:
+ // The structure which maps keywords to parsecodes.
+ struct Keyword_parsecode
+ {
+ // Keyword.
+ const char* keyword;
+ // Corresponding parsecode.
+ int parsecode;
+ };
+
+ // Return the parsecode corresponding KEYWORD, or 0 if it is not a
+ // keyword.
+ static int
+ keyword_to_parsecode(const char* keyword);
+
+ private:
+ // The array of all keywords.
+ static const Keyword_parsecode keyword_parsecodes_[];
+
+ // The number of keywords.
+ static const int keyword_count;
+};
+
+// Mapping from keyword string to keyword parsecode. This array must
+// be kept in sorted order. Parsecodes are looked up using bsearch.
+// This array must correspond to the list of parsecodes in yyscript.y.
+
+const Keyword_to_parsecode::Keyword_parsecode
+Keyword_to_parsecode::keyword_parsecodes_[] =
+{
+ { "ABSOLUTE", ABSOLUTE },
+ { "ADDR", ADDR },
+ { "ALIGN", ALIGN_K },
+ { "ASSERT", ASSERT_K },
+ { "AS_NEEDED", AS_NEEDED },
+ { "AT", AT },
+ { "BIND", BIND },
+ { "BLOCK", BLOCK },
+ { "BYTE", BYTE },
+ { "CONSTANT", CONSTANT },
+ { "CONSTRUCTORS", CONSTRUCTORS },
+ { "COPY", COPY },
+ { "CREATE_OBJECT_SYMBOLS", CREATE_OBJECT_SYMBOLS },
+ { "DATA_SEGMENT_ALIGN", DATA_SEGMENT_ALIGN },
+ { "DATA_SEGMENT_END", DATA_SEGMENT_END },
+ { "DATA_SEGMENT_RELRO_END", DATA_SEGMENT_RELRO_END },
+ { "DEFINED", DEFINED },
+ { "DSECT", DSECT },
+ { "ENTRY", ENTRY },
+ { "EXCLUDE_FILE", EXCLUDE_FILE },
+ { "EXTERN", EXTERN },
+ { "FILL", FILL },
+ { "FLOAT", FLOAT },
+ { "FORCE_COMMON_ALLOCATION", FORCE_COMMON_ALLOCATION },
+ { "GROUP", GROUP },
+ { "HLL", HLL },
+ { "INCLUDE", INCLUDE },
+ { "INFO", INFO },
+ { "INHIBIT_COMMON_ALLOCATION", INHIBIT_COMMON_ALLOCATION },
+ { "INPUT", INPUT },
+ { "KEEP", KEEP },
+ { "LENGTH", LENGTH },
+ { "LOADADDR", LOADADDR },
+ { "LONG", LONG },
+ { "MAP", MAP },
+ { "MAX", MAX_K },
+ { "MEMORY", MEMORY },
+ { "MIN", MIN_K },
+ { "NEXT", NEXT },
+ { "NOCROSSREFS", NOCROSSREFS },
+ { "NOFLOAT", NOFLOAT },
+ { "NOLOAD", NOLOAD },
+ { "ONLY_IF_RO", ONLY_IF_RO },
+ { "ONLY_IF_RW", ONLY_IF_RW },
+ { "ORIGIN", ORIGIN },
+ { "OUTPUT", OUTPUT },
+ { "OUTPUT_ARCH", OUTPUT_ARCH },
+ { "OUTPUT_FORMAT", OUTPUT_FORMAT },
+ { "OVERLAY", OVERLAY },
+ { "PHDRS", PHDRS },
+ { "PROVIDE", PROVIDE },
+ { "PROVIDE_HIDDEN", PROVIDE_HIDDEN },
+ { "QUAD", QUAD },
+ { "SEARCH_DIR", SEARCH_DIR },
+ { "SECTIONS", SECTIONS },
+ { "SEGMENT_START", SEGMENT_START },
+ { "SHORT", SHORT },
+ { "SIZEOF", SIZEOF },
+ { "SIZEOF_HEADERS", SIZEOF_HEADERS },
+ { "SORT_BY_ALIGNMENT", SORT_BY_ALIGNMENT },
+ { "SORT_BY_NAME", SORT_BY_NAME },
+ { "SPECIAL", SPECIAL },
+ { "SQUAD", SQUAD },
+ { "STARTUP", STARTUP },
+ { "SUBALIGN", SUBALIGN },
+ { "SYSLIB", SYSLIB },
+ { "TARGET", TARGET_K },
+ { "TRUNCATE", TRUNCATE },
+ { "VERSION", VERSIONK },
+ { "global", GLOBAL },
+ { "l", LENGTH },
+ { "len", LENGTH },
+ { "local", LOCAL },
+ { "o", ORIGIN },
+ { "org", ORIGIN },
+ { "sizeof_headers", SIZEOF_HEADERS },
+};
+
+const int Keyword_to_parsecode::keyword_count =
+ (sizeof(Keyword_to_parsecode::keyword_parsecodes_)
+ / sizeof(Keyword_to_parsecode::keyword_parsecodes_[0]));
+
+// Comparison function passed to bsearch.
+
+extern "C"
+{
+
+static int
+ktt_compare(const void* keyv, const void* kttv)
+{
+ const char* key = static_cast<const char*>(keyv);
+ const Keyword_to_parsecode::Keyword_parsecode* ktt =
+ static_cast<const Keyword_to_parsecode::Keyword_parsecode*>(kttv);
+ return strcmp(key, ktt->keyword);
+}
+
+} // End extern "C".
+
+int
+Keyword_to_parsecode::keyword_to_parsecode(const char* keyword)
+{
+ void* kttv = bsearch(keyword,
+ Keyword_to_parsecode::keyword_parsecodes_,
+ Keyword_to_parsecode::keyword_count,
+ sizeof(Keyword_to_parsecode::keyword_parsecodes_[0]),
+ ktt_compare);
+ if (kttv == NULL)
+ return 0;
+ Keyword_parsecode* ktt = static_cast<Keyword_parsecode*>(kttv);
+ return ktt->parsecode;
+}
+
+} // End namespace gold.
+
+// The remaining functions are extern "C", so it's clearer to not put
+// them in namespace gold.
+
+using namespace gold;
+
+// This function is called by the bison parser to return the next
+// token.
+
+extern "C" int
+yylex(YYSTYPE* lvalp, void* closurev)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+
+ if (closure->at_eof())
+ return 0;
+
+ const Token* token = closure->next_token();
+
+ switch (token->classification())
+ {
+ default:
+ case Token::TOKEN_INVALID:
+ case Token::TOKEN_EOF:
+ abort();
+
+ case Token::TOKEN_STRING:
+ {
+ const char* str = token->string_value().c_str();
+ int parsecode = Keyword_to_parsecode::keyword_to_parsecode(str);
+ if (parsecode != 0)
+ return parsecode;
+ lvalp->string = str;
+ return STRING;
+ }
+
+ case Token::TOKEN_OPERATOR:
+ return token->operator_value();
+
+ case Token::TOKEN_INTEGER:
+ lvalp->integer = token->integer_value();
+ return INTEGER;
+ }
+}
+
+// This function is called by the bison parser to report an error.
+
+extern "C" void
+yyerror(void* closurev, const char* message)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+
+ fprintf(stderr, _("%s: %s: %s\n"),
+ program_name, closure->filename(), message);
+ gold_exit(false);
+}
+
+// Called by the bison parser to add a file to the link.
+
+extern "C" void
+script_add_file(void* closurev, const char* name)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ Input_file_argument file(name, false, closure->position_dependent_options());
+ closure->inputs()->add_file(file);
+}
+
+// Called by the bison parser to start a group. If we are already in
+// a group, that means that this script was invoked within a
+// --start-group --end-group sequence on the command line, or that
+// this script was found in a GROUP of another script. In that case,
+// we simply continue the existing group, rather than starting a new
+// one. It is possible to construct a case in which this will do
+// something other than what would happen if we did a recursive group,
+// but it's hard to imagine why the different behaviour would be
+// useful for a real program. Avoiding recursive groups is simpler
+// and more efficient.
+
+extern "C" void
+script_start_group(void* closurev)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ if (!closure->in_group())
+ closure->inputs()->start_group();
+}
+
+// Called by the bison parser at the end of a group.
+
+extern "C" void
+script_end_group(void* closurev)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ if (!closure->in_group())
+ closure->inputs()->end_group();
+}
+
+// Called by the bison parser to start an AS_NEEDED list.
+
+extern "C" void
+script_start_as_needed(void* closurev)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ closure->position_dependent_options().set_as_needed();
+}
+
+// Called by the bison parser at the end of an AS_NEEDED list.
+
+extern "C" void
+script_end_as_needed(void* closurev)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ closure->position_dependent_options().clear_as_needed();
+}
diff --git a/gold/script.h b/gold/script.h
new file mode 100644
index 00000000000..de2e5af1a66
--- /dev/null
+++ b/gold/script.h
@@ -0,0 +1,39 @@
+// script.h -- handle linker scripts for gold -*- C++ -*-
+
+// We implement a subset of the original GNU ld linker script language
+// for compatibility. The goal is not to implement the entire
+// language. It is merely to implement enough to handle common uses.
+// In particular we need to handle /usr/lib/libc.so on a typical
+// GNU/Linux system, and we want to handle linker scripts used by the
+// Linux kernel build.
+
+#ifndef GOLD_SCRIPT_H
+#define GOLD_SCRIPT_H
+
+namespace gold
+{
+
+class General_options;
+class Symbol_table;
+class Layout;
+class Input_objects;
+class Input_group;
+class Input_file;
+class Task_token;
+
+// FILE was found as an argument on the command line, but was not
+// recognized as an ELF file. Try to read it as a script. We've
+// already read BYTES of data into P. Return true if the file was
+// handled. This has to handle /usr/lib/libc.so on a GNU/Linux
+// system.
+
+bool
+read_input_script(Workqueue*, const General_options&, Symbol_table*, Layout*,
+ const Dirsearch&, Input_objects*, Input_group*,
+ const Input_argument*, Input_file*, const unsigned char* p,
+ off_t bytes, Task_token* this_blocker,
+ Task_token* next_blocker);
+
+} // End namespace gold.
+
+#endif // !defined(GOLD_SCRIPT_H)
diff --git a/gold/symtab.cc b/gold/symtab.cc
index ac14c0121a3..9279d265672 100644
--- a/gold/symtab.cc
+++ b/gold/symtab.cc
@@ -8,6 +8,7 @@
#include <utility>
#include "object.h"
+#include "dynobj.h"
#include "output.h"
#include "target.h"
#include "workqueue.h"
@@ -194,6 +195,7 @@ Symbol_table::Symbol_table_eq::operator()(const Symbol_table_key& k1,
void
Symbol_table::make_forwarder(Symbol* from, Symbol* to)
{
+ assert(from != to);
assert(!from->is_forwarder() && !to->is_forwarder());
this->forwarders_[from] = to;
from->set_forwarder();
@@ -334,7 +336,7 @@ Symbol_table::add_from_object(Object* object,
// NAME/NULL point to NAME/VERSION.
insdef.first->second = ret;
}
- else
+ else if (insdef.first->second != ret)
{
// This is the unfortunate case where we already have
// entries for both NAME/VERSION and NAME/NULL.
@@ -424,8 +426,8 @@ Symbol_table::add_from_object(Object* object,
template<int size, bool big_endian>
void
-Symbol_table::add_from_object(
- Relobj* object,
+Symbol_table::add_from_relobj(
+ Sized_relobj<size, big_endian>* relobj,
const unsigned char* syms,
size_t count,
const char* sym_names,
@@ -436,10 +438,10 @@ Symbol_table::add_from_object(
if (this->get_size() == 0)
this->set_size(size);
- if (size != this->get_size() || size != object->target()->get_size())
+ if (size != this->get_size() || size != relobj->target()->get_size())
{
fprintf(stderr, _("%s: %s: mixing 32-bit and 64-bit ELF objects\n"),
- program_name, object->name().c_str());
+ program_name, relobj->name().c_str());
gold_exit(false);
}
@@ -456,11 +458,13 @@ Symbol_table::add_from_object(
{
fprintf(stderr,
_("%s: %s: bad global symbol name offset %u at %lu\n"),
- program_name, object->name().c_str(), st_name,
+ program_name, relobj->name().c_str(), st_name,
static_cast<unsigned long>(i));
gold_exit(false);
}
+ const char* name = sym_names + st_name;
+
// A symbol defined in a section which we are not including must
// be treated as an undefined symbol.
unsigned char symbuf[sym_size];
@@ -468,7 +472,7 @@ Symbol_table::add_from_object(
unsigned int st_shndx = psym->get_st_shndx();
if (st_shndx != elfcpp::SHN_UNDEF
&& st_shndx < elfcpp::SHN_LORESERVE
- && !object->is_section_included(st_shndx))
+ && !relobj->is_section_included(st_shndx))
{
memcpy(symbuf, p, sym_size);
elfcpp::Sym_write<size, big_endian> sw(symbuf);
@@ -476,8 +480,6 @@ Symbol_table::add_from_object(
psym = &sym2;
}
- const char* name = sym_names + st_name;
-
// In an object file, an '@' in the name separates the symbol
// name from the version name. If there are two '@' characters,
// this is the default version.
@@ -488,7 +490,7 @@ Symbol_table::add_from_object(
{
Stringpool::Key name_key;
name = this->namepool_.add(name, &name_key);
- res = this->add_from_object(object, name, name_key, NULL, 0,
+ res = this->add_from_object(relobj, name, name_key, NULL, 0,
false, *psym);
}
else
@@ -507,7 +509,7 @@ Symbol_table::add_from_object(
Stringpool::Key ver_key;
ver = this->namepool_.add(ver, &ver_key);
- res = this->add_from_object(object, name, name_key, ver, ver_key,
+ res = this->add_from_object(relobj, name, name_key, ver, ver_key,
def, *psym);
}
@@ -515,6 +517,131 @@ Symbol_table::add_from_object(
}
}
+// Add all the symbols in a dynamic object to the hash table.
+
+template<int size, bool big_endian>
+void
+Symbol_table::add_from_dynobj(
+ Sized_dynobj<size, big_endian>* dynobj,
+ const unsigned char* syms,
+ size_t count,
+ const char* sym_names,
+ size_t sym_name_size,
+ const unsigned char* versym,
+ size_t versym_size,
+ const std::vector<const char*>* version_map)
+{
+ // We take the size from the first object we see.
+ if (this->get_size() == 0)
+ this->set_size(size);
+
+ if (size != this->get_size() || size != dynobj->target()->get_size())
+ {
+ fprintf(stderr, _("%s: %s: mixing 32-bit and 64-bit ELF objects\n"),
+ program_name, dynobj->name().c_str());
+ gold_exit(false);
+ }
+
+ if (versym != NULL && versym_size / 2 < count)
+ {
+ fprintf(stderr, _("%s: %s: too few symbol versions\n"),
+ program_name, dynobj->name().c_str());
+ gold_exit(false);
+ }
+
+ const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
+
+ const unsigned char* p = syms;
+ const unsigned char* vs = versym;
+ for (size_t i = 0; i < count; ++i, p += sym_size, vs += 2)
+ {
+ elfcpp::Sym<size, big_endian> sym(p);
+
+ // Ignore symbols with local binding.
+ if (sym.get_st_bind() == elfcpp::STB_LOCAL)
+ continue;
+
+ unsigned int st_name = sym.get_st_name();
+ if (st_name >= sym_name_size)
+ {
+ fprintf(stderr, _("%s: %s: bad symbol name offset %u at %lu\n"),
+ program_name, dynobj->name().c_str(), st_name,
+ static_cast<unsigned long>(i));
+ gold_exit(false);
+ }
+
+ const char* name = sym_names + st_name;
+
+ if (versym == NULL)
+ {
+ Stringpool::Key name_key;
+ name = this->namepool_.add(name, &name_key);
+ this->add_from_object(dynobj, name, name_key, NULL, 0,
+ false, sym);
+ continue;
+ }
+
+ // Read the version information.
+
+ unsigned int v = elfcpp::Swap<16, big_endian>::readval(vs);
+
+ bool hidden = (v & elfcpp::VERSYM_HIDDEN) != 0;
+ v &= elfcpp::VERSYM_VERSION;
+
+ if (v == static_cast<unsigned int>(elfcpp::VER_NDX_LOCAL))
+ {
+ // This symbol should not be visible outside the object.
+ continue;
+ }
+
+ // At this point we are definitely going to add this symbol.
+ Stringpool::Key name_key;
+ name = this->namepool_.add(name, &name_key);
+
+ if (v == static_cast<unsigned int>(elfcpp::VER_NDX_GLOBAL))
+ {
+ // This symbol does not have a version.
+ this->add_from_object(dynobj, name, name_key, NULL, 0, false, sym);
+ continue;
+ }
+
+ if (v >= version_map->size())
+ {
+ fprintf(stderr,
+ _("%s: %s: versym for symbol %zu out of range: %u\n"),
+ program_name, dynobj->name().c_str(), i, v);
+ gold_exit(false);
+ }
+
+ const char* version = (*version_map)[v];
+ if (version == NULL)
+ {
+ fprintf(stderr, _("%s: %s: versym for symbol %zu has no name: %u\n"),
+ program_name, dynobj->name().c_str(), i, v);
+ gold_exit(false);
+ }
+
+ Stringpool::Key version_key;
+ version = this->namepool_.add(version, &version_key);
+
+ // If this is an absolute symbol, and the version name and
+ // symbol name are the same, then this is the version definition
+ // symbol. These symbols exist to support using -u to pull in
+ // particular versions. We do not want to record a version for
+ // them.
+ if (sym.get_st_shndx() == elfcpp::SHN_ABS && name_key == version_key)
+ {
+ this->add_from_object(dynobj, name, name_key, NULL, 0, false, sym);
+ continue;
+ }
+
+ const bool def = !hidden && sym.get_st_shndx() != elfcpp::SHN_UNDEF;
+
+ this->add_from_object(dynobj, name, name_key, version, version_key,
+ def, sym);
+ }
+}
+
// Create and return a specially defined symbol. If ONLY_IF_REF is
// true, then only create the symbol if there is a reference to it.
@@ -1142,8 +1269,8 @@ Warnings::issue_warning(Symbol* sym, const std::string& location) const
template
void
-Symbol_table::add_from_object<32, true>(
- Relobj* object,
+Symbol_table::add_from_relobj<32, true>(
+ Sized_relobj<32, true>* relobj,
const unsigned char* syms,
size_t count,
const char* sym_names,
@@ -1152,8 +1279,8 @@ Symbol_table::add_from_object<32, true>(
template
void
-Symbol_table::add_from_object<32, false>(
- Relobj* object,
+Symbol_table::add_from_relobj<32, false>(
+ Sized_relobj<32, false>* relobj,
const unsigned char* syms,
size_t count,
const char* sym_names,
@@ -1162,8 +1289,8 @@ Symbol_table::add_from_object<32, false>(
template
void
-Symbol_table::add_from_object<64, true>(
- Relobj* object,
+Symbol_table::add_from_relobj<64, true>(
+ Sized_relobj<64, true>* relobj,
const unsigned char* syms,
size_t count,
const char* sym_names,
@@ -1172,12 +1299,60 @@ Symbol_table::add_from_object<64, true>(
template
void
-Symbol_table::add_from_object<64, false>(
- Relobj* object,
+Symbol_table::add_from_relobj<64, false>(
+ Sized_relobj<64, false>* relobj,
const unsigned char* syms,
size_t count,
const char* sym_names,
size_t sym_name_size,
Symbol** sympointers);
+template
+void
+Symbol_table::add_from_dynobj<32, true>(
+ Sized_dynobj<32, true>* dynobj,
+ const unsigned char* syms,
+ size_t count,
+ const char* sym_names,
+ size_t sym_name_size,
+ const unsigned char* versym,
+ size_t versym_size,
+ const std::vector<const char*>* version_map);
+
+template
+void
+Symbol_table::add_from_dynobj<32, false>(
+ Sized_dynobj<32, false>* dynobj,
+ const unsigned char* syms,
+ size_t count,
+ const char* sym_names,
+ size_t sym_name_size,
+ const unsigned char* versym,
+ size_t versym_size,
+ const std::vector<const char*>* version_map);
+
+template
+void
+Symbol_table::add_from_dynobj<64, true>(
+ Sized_dynobj<64, true>* dynobj,
+ const unsigned char* syms,
+ size_t count,
+ const char* sym_names,
+ size_t sym_name_size,
+ const unsigned char* versym,
+ size_t versym_size,
+ const std::vector<const char*>* version_map);
+
+template
+void
+Symbol_table::add_from_dynobj<64, false>(
+ Sized_dynobj<64, false>* dynobj,
+ const unsigned char* syms,
+ size_t count,
+ const char* sym_names,
+ size_t sym_name_size,
+ const unsigned char* versym,
+ size_t versym_size,
+ const std::vector<const char*>* version_map);
+
} // End namespace gold.
diff --git a/gold/symtab.h b/gold/symtab.h
index 65898990c87..e9726002498 100644
--- a/gold/symtab.h
+++ b/gold/symtab.h
@@ -20,7 +20,11 @@ namespace gold
class Object;
class Relobj;
+template<int size, bool big_endian>
+class Sized_relobj;
class Dynobj;
+template<int size, bool big_endian>
+class Sized_dynobj;
class Output_data;
class Output_segment;
class Output_file;
@@ -592,16 +596,29 @@ class Symbol_table
~Symbol_table();
- // Add COUNT external symbols from the relocatable object OBJECT to
+ // Add COUNT external symbols from the relocatable object RELOBJ to
// the symbol table. SYMS is the symbols, SYM_NAMES is their names,
// SYM_NAME_SIZE is the size of SYM_NAMES. This sets SYMPOINTERS to
// point to the symbols in the symbol table.
template<int size, bool big_endian>
void
- add_from_object(Relobj* object, const unsigned char* syms,
- size_t count, const char* sym_names, size_t sym_name_size,
+ add_from_relobj(Sized_relobj<size, big_endian>* relobj,
+ const unsigned char* syms, size_t count,
+ const char* sym_names, size_t sym_name_size,
Symbol** sympointers);
+ // Add COUNT dynamic symbols from the dynamic object DYNOBJ to the
+ // symbol table. SYMS is the symbols. SYM_NAMES is their names.
+ // SYM_NAME_SIZE is the size of SYM_NAMES. The other parameters are
+ // symbol version data.
+ template<int size, bool big_endian>
+ void
+ add_from_dynobj(Sized_dynobj<size, big_endian>* dynobj,
+ const unsigned char* syms, size_t count,
+ const char* sym_names, size_t sym_name_size,
+ const unsigned char* versym, size_t versym_size,
+ const std::vector<const char*>*);
+
// Define a special symbol.
template<int size, bool big_endian>
Sized_symbol<size>*
diff --git a/gold/target.h b/gold/target.h
index 8e00a4da5ab..5a86c357dad 100644
--- a/gold/target.h
+++ b/gold/target.h
@@ -65,6 +65,11 @@ class Target
has_resolve() const
{ return this->pti_->has_resolve; }
+ // Return the default name of the dynamic linker.
+ const char*
+ dynamic_linker() const
+ { return this->pti_->dynamic_linker; }
+
// Return the default address to use for the text segment.
uint64_t
text_segment_address() const
@@ -96,6 +101,8 @@ class Target
bool has_make_symbol;
// Whether this target has a specific resolve function.
bool has_resolve;
+ // The default dynamic linker name.
+ const char* dynamic_linker;
// The default text segment address.
uint64_t text_segment_address;
// The ABI specified page size.
diff --git a/gold/yyscript.y b/gold/yyscript.y
new file mode 100644
index 00000000000..0bd2b603b2e
--- /dev/null
+++ b/gold/yyscript.y
@@ -0,0 +1,168 @@
+/* yyscript.y -- linker script grammer for gold. */
+
+/* This is a bison grammar to parse a subset of the original GNU ld
+ linker script language. */
+
+%{
+
+#include "config.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "script-c.h"
+
+%}
+
+/* We need to use a pure parser because we might be multi-threaded.
+ We pass some arguments through the parser to the lexer. */
+
+%pure-parser
+
+%parse-param {void* closure}
+%lex-param {void* closure}
+
+/* Since we require bison anyhow, we take advantage of it. */
+
+%error-verbose
+
+/* The values associated with tokens. */
+
+%union {
+ const char* string;
+ int64_t integer;
+}
+
+/* Operators, including a precedence table for expressions. */
+
+%right PLUSEQ MINUSEQ MULTEQ DIVEQ '=' LSHIFTEQ RSHIFTEQ ANDEQ OREQ
+%right '?' ':'
+%left OROR
+%left ANDAND
+%left '|'
+%left '^'
+%left '&'
+%left EQ NE
+%left '<' '>' LE GE
+%left LSHIFT RSHIFT
+%left '+' '-'
+%left '*' '/' '%'
+
+/* Constants. */
+
+%token <string> STRING
+%token <integer> INTEGER
+
+/* Keywords. This list is taken from ldgram.y and ldlex.l in the old
+ GNU linker, with the keywords which only appear in MRI mode
+ removed. Not all these keywords are actually used in this grammar.
+ In most cases the keyword is recognized as the token name in upper
+ case. The comments indicate where this is not the case. */
+
+%token ABSOLUTE
+%token ADDR
+%token ALIGN_K /* ALIGN */
+%token ASSERT_K /* ASSERT */
+%token AS_NEEDED
+%token AT
+%token BIND
+%token BLOCK
+%token BYTE
+%token CONSTANT
+%token CONSTRUCTORS
+%token COPY
+%token CREATE_OBJECT_SYMBOLS
+%token DATA_SEGMENT_ALIGN
+%token DATA_SEGMENT_END
+%token DATA_SEGMENT_RELRO_END
+%token DEFINED
+%token DSECT
+%token ENTRY
+%token EXCLUDE_FILE
+%token EXTERN
+%token FILL
+%token FLOAT
+%token FORCE_COMMON_ALLOCATION
+%token GLOBAL /* global */
+%token GROUP
+%token HLL
+%token INCLUDE
+%token INFO
+%token INHIBIT_COMMON_ALLOCATION
+%token INPUT
+%token KEEP
+%token LENGTH /* LENGTH, l, len */
+%token LOADADDR
+%token LOCAL /* local */
+%token LONG
+%token MAP
+%token MAX_K /* MAX */
+%token MEMORY
+%token MIN_K /* MIN */
+%token NEXT
+%token NOCROSSREFS
+%token NOFLOAT
+%token NOLOAD
+%token ONLY_IF_RO
+%token ONLY_IF_RW
+%token ORIGIN /* ORIGIN, o, org */
+%token OUTPUT
+%token OUTPUT_ARCH
+%token OUTPUT_FORMAT
+%token OVERLAY
+%token PHDRS
+%token PROVIDE
+%token PROVIDE_HIDDEN
+%token QUAD
+%token SEARCH_DIR
+%token SECTIONS
+%token SEGMENT_START
+%token SHORT
+%token SIZEOF
+%token SIZEOF_HEADERS /* SIZEOF_HEADERS, sizeof_headers */
+%token SORT_BY_ALIGNMENT
+%token SORT_BY_NAME
+%token SPECIAL
+%token SQUAD
+%token STARTUP
+%token SUBALIGN
+%token SYSLIB
+%token TARGET_K /* TARGET */
+%token TRUNCATE
+%token VERSIONK /* VERSION */
+
+%%
+
+file_list:
+ file_list file_cmd
+ | /* empty */
+ ;
+
+file_cmd:
+ OUTPUT_FORMAT '(' STRING ')'
+ | GROUP
+ { script_start_group(closure); }
+ '(' input_list ')'
+ { script_end_group(closure); }
+ ;
+
+input_list:
+ input_list_element
+ | input_list opt_comma input_list_element
+ ;
+
+input_list_element:
+ STRING
+ { script_add_file(closure, $1); }
+ | AS_NEEDED
+ { script_start_as_needed(closure); }
+ '(' input_list ')'
+ { script_end_as_needed(closure); }
+ ;
+
+opt_comma:
+ ','
+ | /* empty */
+ ;
+
+%%